ole32: Add big block chain to small block chain conversion routine.
[wine/wine-gecko.git] / dlls / ole32 / storage32.c
blob4a27317dd84efe4eb51ef664b680b2e753017f03
1 /*
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
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #define COBJMACROS
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winuser.h"
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
53 #include "winreg.h"
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 */
115 typedef struct
117 DWORD dwOleID;
118 DWORD dwTypeID;
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. */
126 DWORD dwDataLength;
127 BYTE *pData;
128 }OLECONVERT_OLESTREAM_DATA;
130 /* CompObj Stream structure */
131 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
132 typedef struct
134 BYTE byUnknown1[12];
135 CLSID clsid;
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];
142 BYTE byUnknown2[16];
143 }OLECONVERT_ISTORAGE_COMPOBJ;
146 /* Ole Presentation Stream structure */
147 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
148 typedef struct
150 BYTE byUnknown1[28];
151 DWORD dwExtentX;
152 DWORD dwExtentY;
153 DWORD dwSize;
154 BYTE *pData;
155 }OLECONVERT_ISTORAGE_OLEPRES;
159 /***********************************************************************
160 * Forward declaration of internal functions used by the method DestroyElement
162 static HRESULT deleteStorageProperty(
163 StorageImpl *parentStorage,
164 ULONG foundPropertyIndexToDelete,
165 StgProperty propertyToDelete);
167 static HRESULT deleteStreamProperty(
168 StorageImpl *parentStorage,
169 ULONG foundPropertyIndexToDelete,
170 StgProperty propertyToDelete);
172 static HRESULT findPlaceholder(
173 StorageImpl *storage,
174 ULONG propertyIndexToStore,
175 ULONG storagePropertyIndex,
176 INT typeOfRelation);
178 static HRESULT adjustPropertyChain(
179 StorageImpl *This,
180 StgProperty propertyToDelete,
181 StgProperty parentProperty,
182 ULONG parentPropertyId,
183 INT typeOfRelation);
185 /***********************************************************************
186 * Declaration of the functions used to manipulate StgProperty
189 static ULONG getFreeProperty(
190 StorageImpl *storage);
192 static void updatePropertyChain(
193 StorageImpl *storage,
194 ULONG newPropertyIndex,
195 StgProperty newProperty);
197 static LONG propertyNameCmp(
198 const OLECHAR *newProperty,
199 const OLECHAR *currentProperty);
202 /***********************************************************************
203 * Declaration of miscellaneous functions...
205 static HRESULT validateSTGM(DWORD stgmValue);
207 static DWORD GetShareModeFromSTGM(DWORD stgm);
208 static DWORD GetAccessModeFromSTGM(DWORD stgm);
209 static DWORD GetCreationModeFromSTGM(DWORD stgm);
211 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
214 /****************************************************************************
215 * IEnumSTATSTGImpl definitions.
217 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
218 * This class allows iterating through the content of a storage and to find
219 * specific items inside it.
221 struct IEnumSTATSTGImpl
223 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
224 * since we want to cast this in an IEnumSTATSTG pointer */
226 LONG ref; /* Reference count */
227 StorageImpl* parentStorage; /* Reference to the parent storage */
228 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
231 * The current implementation of the IEnumSTATSTGImpl class uses a stack
232 * to walk the property sets to get the content of a storage. This stack
233 * is implemented by the following 3 data members
235 ULONG stackSize;
236 ULONG stackMaxSize;
237 ULONG* stackToVisit;
239 #define ENUMSTATSGT_SIZE_INCREMENT 10
243 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
244 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
245 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
246 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
247 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
248 StgProperty* buffer);
249 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
250 StgProperty *currentProperty, ULONG *propertyId);
252 /************************************************************************
253 ** Block Functions
256 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
258 if (index == 0xffffffff)
259 index = 0;
260 else
261 index ++;
263 return index * BIG_BLOCK_SIZE;
266 /************************************************************************
267 ** Storage32BaseImpl implementation
269 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
270 ULARGE_INTEGER offset,
271 void* buffer,
272 ULONG size,
273 ULONG* bytesRead)
275 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
278 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
279 ULARGE_INTEGER offset,
280 const void* buffer,
281 const ULONG size,
282 ULONG* bytesWritten)
284 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
287 /************************************************************************
288 * Storage32BaseImpl_QueryInterface (IUnknown)
290 * This method implements the common QueryInterface for all IStorage32
291 * implementations contained in this file.
293 * See Windows documentation for more details on IUnknown methods.
295 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
296 IStorage* iface,
297 REFIID riid,
298 void** ppvObject)
300 StorageBaseImpl *This = (StorageBaseImpl *)iface;
302 * Perform a sanity check on the parameters.
304 if ( (This==0) || (ppvObject==0) )
305 return E_INVALIDARG;
308 * Initialize the return parameter.
310 *ppvObject = 0;
313 * Compare the riid with the interface IDs implemented by this object.
315 if (IsEqualGUID(&IID_IUnknown, riid) ||
316 IsEqualGUID(&IID_IStorage, riid))
318 *ppvObject = This;
320 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
322 *ppvObject = &This->pssVtbl;
326 * Check that we obtained an interface.
328 if ((*ppvObject)==0)
329 return E_NOINTERFACE;
332 * Query Interface always increases the reference count by one when it is
333 * successful
335 IStorage_AddRef(iface);
337 return S_OK;
340 /************************************************************************
341 * Storage32BaseImpl_AddRef (IUnknown)
343 * This method implements the common AddRef for all IStorage32
344 * implementations contained in this file.
346 * See Windows documentation for more details on IUnknown methods.
348 static ULONG WINAPI StorageBaseImpl_AddRef(
349 IStorage* iface)
351 StorageBaseImpl *This = (StorageBaseImpl *)iface;
352 ULONG ref = InterlockedIncrement(&This->ref);
354 TRACE("(%p) AddRef to %d\n", This, ref);
356 return ref;
359 /************************************************************************
360 * Storage32BaseImpl_Release (IUnknown)
362 * This method implements the common Release for all IStorage32
363 * implementations contained in this file.
365 * See Windows documentation for more details on IUnknown methods.
367 static ULONG WINAPI StorageBaseImpl_Release(
368 IStorage* iface)
370 StorageBaseImpl *This = (StorageBaseImpl *)iface;
372 * Decrease the reference count on this object.
374 ULONG ref = InterlockedDecrement(&This->ref);
376 TRACE("(%p) ReleaseRef to %d\n", This, ref);
379 * If the reference count goes down to 0, perform suicide.
381 if (ref == 0)
384 * Since we are using a system of base-classes, we want to call the
385 * destructor of the appropriate derived class. To do this, we are
386 * using virtual functions to implement the destructor.
388 This->v_destructor(This);
391 return ref;
394 /************************************************************************
395 * Storage32BaseImpl_OpenStream (IStorage)
397 * This method will open the specified stream object from the current storage.
399 * See Windows documentation for more details on IStorage methods.
401 static HRESULT WINAPI StorageBaseImpl_OpenStream(
402 IStorage* iface,
403 const OLECHAR* pwcsName, /* [string][in] */
404 void* reserved1, /* [unique][in] */
405 DWORD grfMode, /* [in] */
406 DWORD reserved2, /* [in] */
407 IStream** ppstm) /* [out] */
409 StorageBaseImpl *This = (StorageBaseImpl *)iface;
410 IEnumSTATSTGImpl* propertyEnumeration;
411 StgStreamImpl* newStream;
412 StgProperty currentProperty;
413 ULONG foundPropertyIndex;
414 HRESULT res = STG_E_UNKNOWN;
416 TRACE("(%p, %s, %p, %x, %d, %p)\n",
417 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
420 * Perform a sanity check on the parameters.
422 if ( (pwcsName==NULL) || (ppstm==0) )
424 res = E_INVALIDARG;
425 goto end;
429 * Initialize the out parameter
431 *ppstm = NULL;
434 * Validate the STGM flags
436 if ( FAILED( validateSTGM(grfMode) ) ||
437 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
439 res = STG_E_INVALIDFLAG;
440 goto end;
444 * As documented.
446 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
448 res = STG_E_INVALIDFUNCTION;
449 goto end;
453 * Check that we're compatible with the parent's storage mode, but
454 * only if we are not in transacted mode
456 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
457 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
459 res = STG_E_ACCESSDENIED;
460 goto end;
465 * Create a property enumeration to search the properties
467 propertyEnumeration = IEnumSTATSTGImpl_Construct(
468 This->ancestorStorage,
469 This->rootPropertySetIndex);
472 * Search the enumeration for the property with the given name
474 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
475 propertyEnumeration,
476 pwcsName,
477 &currentProperty);
480 * Delete the property enumeration since we don't need it anymore
482 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
485 * If it was found, construct the stream object and return a pointer to it.
487 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
488 (currentProperty.propertyType==PROPTYPE_STREAM) )
490 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
492 if (newStream!=0)
494 newStream->grfMode = grfMode;
495 *ppstm = (IStream*)newStream;
498 * Since we are returning a pointer to the interface, we have to
499 * nail down the reference.
501 IStream_AddRef(*ppstm);
503 res = S_OK;
504 goto end;
507 res = E_OUTOFMEMORY;
508 goto end;
511 res = STG_E_FILENOTFOUND;
513 end:
514 if (res == S_OK)
515 TRACE("<-- IStream %p\n", *ppstm);
516 TRACE("<-- %08x\n", res);
517 return res;
520 /************************************************************************
521 * Storage32BaseImpl_OpenStorage (IStorage)
523 * This method will open a new storage object from the current storage.
525 * See Windows documentation for more details on IStorage methods.
527 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
528 IStorage* iface,
529 const OLECHAR* pwcsName, /* [string][unique][in] */
530 IStorage* pstgPriority, /* [unique][in] */
531 DWORD grfMode, /* [in] */
532 SNB snbExclude, /* [unique][in] */
533 DWORD reserved, /* [in] */
534 IStorage** ppstg) /* [out] */
536 StorageBaseImpl *This = (StorageBaseImpl *)iface;
537 StorageInternalImpl* newStorage;
538 IEnumSTATSTGImpl* propertyEnumeration;
539 StgProperty currentProperty;
540 ULONG foundPropertyIndex;
541 HRESULT res = STG_E_UNKNOWN;
543 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
544 iface, debugstr_w(pwcsName), pstgPriority,
545 grfMode, snbExclude, reserved, ppstg);
548 * Perform a sanity check on the parameters.
550 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
552 res = E_INVALIDARG;
553 goto end;
556 /* as documented */
557 if (snbExclude != NULL)
559 res = STG_E_INVALIDPARAMETER;
560 goto end;
564 * Validate the STGM flags
566 if ( FAILED( validateSTGM(grfMode) ))
568 res = STG_E_INVALIDFLAG;
569 goto end;
573 * As documented.
575 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
576 (grfMode & STGM_DELETEONRELEASE) ||
577 (grfMode & STGM_PRIORITY) )
579 res = STG_E_INVALIDFUNCTION;
580 goto end;
584 * Check that we're compatible with the parent's storage mode,
585 * but only if we are not transacted
587 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
588 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
590 res = STG_E_ACCESSDENIED;
591 goto end;
596 * Initialize the out parameter
598 *ppstg = NULL;
601 * Create a property enumeration to search the properties
603 propertyEnumeration = IEnumSTATSTGImpl_Construct(
604 This->ancestorStorage,
605 This->rootPropertySetIndex);
608 * Search the enumeration for the property with the given name
610 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
611 propertyEnumeration,
612 pwcsName,
613 &currentProperty);
616 * Delete the property enumeration since we don't need it anymore
618 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
621 * If it was found, construct the stream object and return a pointer to it.
623 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
624 (currentProperty.propertyType==PROPTYPE_STORAGE) )
627 * Construct a new Storage object
629 newStorage = StorageInternalImpl_Construct(
630 This->ancestorStorage,
631 grfMode,
632 foundPropertyIndex);
634 if (newStorage != 0)
636 *ppstg = (IStorage*)newStorage;
639 * Since we are returning a pointer to the interface,
640 * we have to nail down the reference.
642 StorageBaseImpl_AddRef(*ppstg);
644 res = S_OK;
645 goto end;
648 res = STG_E_INSUFFICIENTMEMORY;
649 goto end;
652 res = STG_E_FILENOTFOUND;
654 end:
655 TRACE("<-- %08x\n", res);
656 return res;
659 /************************************************************************
660 * Storage32BaseImpl_EnumElements (IStorage)
662 * This method will create an enumerator object that can be used to
663 * retrieve information about all the properties in the storage object.
665 * See Windows documentation for more details on IStorage methods.
667 static HRESULT WINAPI StorageBaseImpl_EnumElements(
668 IStorage* iface,
669 DWORD reserved1, /* [in] */
670 void* reserved2, /* [size_is][unique][in] */
671 DWORD reserved3, /* [in] */
672 IEnumSTATSTG** ppenum) /* [out] */
674 StorageBaseImpl *This = (StorageBaseImpl *)iface;
675 IEnumSTATSTGImpl* newEnum;
677 TRACE("(%p, %d, %p, %d, %p)\n",
678 iface, reserved1, reserved2, reserved3, ppenum);
681 * Perform a sanity check on the parameters.
683 if ( (This==0) || (ppenum==0))
684 return E_INVALIDARG;
687 * Construct the enumerator.
689 newEnum = IEnumSTATSTGImpl_Construct(
690 This->ancestorStorage,
691 This->rootPropertySetIndex);
693 if (newEnum!=0)
695 *ppenum = (IEnumSTATSTG*)newEnum;
698 * Don't forget to nail down a reference to the new object before
699 * returning it.
701 IEnumSTATSTG_AddRef(*ppenum);
703 return S_OK;
706 return E_OUTOFMEMORY;
709 /************************************************************************
710 * Storage32BaseImpl_Stat (IStorage)
712 * This method will retrieve information about this storage object.
714 * See Windows documentation for more details on IStorage methods.
716 static HRESULT WINAPI StorageBaseImpl_Stat(
717 IStorage* iface,
718 STATSTG* pstatstg, /* [out] */
719 DWORD grfStatFlag) /* [in] */
721 StorageBaseImpl *This = (StorageBaseImpl *)iface;
722 StgProperty curProperty;
723 BOOL readSuccessful;
724 HRESULT res = STG_E_UNKNOWN;
726 TRACE("(%p, %p, %x)\n",
727 iface, pstatstg, grfStatFlag);
730 * Perform a sanity check on the parameters.
732 if ( (This==0) || (pstatstg==0))
734 res = E_INVALIDARG;
735 goto end;
739 * Read the information from the property.
741 readSuccessful = StorageImpl_ReadProperty(
742 This->ancestorStorage,
743 This->rootPropertySetIndex,
744 &curProperty);
746 if (readSuccessful)
748 StorageUtl_CopyPropertyToSTATSTG(
749 pstatstg,
750 &curProperty,
751 grfStatFlag);
753 pstatstg->grfMode = This->openFlags;
754 pstatstg->grfStateBits = This->stateBits;
756 res = S_OK;
757 goto end;
760 res = E_FAIL;
762 end:
763 if (res == S_OK)
765 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);
767 TRACE("<-- %08x\n", res);
768 return res;
771 /************************************************************************
772 * Storage32BaseImpl_RenameElement (IStorage)
774 * This method will rename the specified element.
776 * See Windows documentation for more details on IStorage methods.
778 * Implementation notes: The method used to rename consists of creating a clone
779 * of the deleted StgProperty object setting it with the new name and to
780 * perform a DestroyElement of the old StgProperty.
782 static HRESULT WINAPI StorageBaseImpl_RenameElement(
783 IStorage* iface,
784 const OLECHAR* pwcsOldName, /* [in] */
785 const OLECHAR* pwcsNewName) /* [in] */
787 StorageBaseImpl *This = (StorageBaseImpl *)iface;
788 IEnumSTATSTGImpl* propertyEnumeration;
789 StgProperty currentProperty;
790 ULONG foundPropertyIndex;
792 TRACE("(%p, %s, %s)\n",
793 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
796 * Create a property enumeration to search the properties
798 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
799 This->rootPropertySetIndex);
802 * Search the enumeration for the new property name
804 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
805 pwcsNewName,
806 &currentProperty);
808 if (foundPropertyIndex != PROPERTY_NULL)
811 * There is already a property with the new name
813 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
814 return STG_E_FILEALREADYEXISTS;
817 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
820 * Search the enumeration for the old property name
822 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
823 pwcsOldName,
824 &currentProperty);
827 * Delete the property enumeration since we don't need it anymore
829 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
831 if (foundPropertyIndex != PROPERTY_NULL)
833 StgProperty renamedProperty;
834 ULONG renamedPropertyIndex;
837 * Setup a new property for the renamed property
839 renamedProperty.sizeOfNameString =
840 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
842 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
843 return STG_E_INVALIDNAME;
845 strcpyW(renamedProperty.name, pwcsNewName);
847 renamedProperty.propertyType = currentProperty.propertyType;
848 renamedProperty.startingBlock = currentProperty.startingBlock;
849 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
850 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
852 renamedProperty.previousProperty = PROPERTY_NULL;
853 renamedProperty.nextProperty = PROPERTY_NULL;
856 * Bring the dirProperty link in case it is a storage and in which
857 * case the renamed storage elements don't require to be reorganized.
859 renamedProperty.dirProperty = currentProperty.dirProperty;
861 /* call CoFileTime to get the current time
862 renamedProperty.timeStampS1
863 renamedProperty.timeStampD1
864 renamedProperty.timeStampS2
865 renamedProperty.timeStampD2
866 renamedProperty.propertyUniqueID
870 * Obtain a free property in the property chain
872 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
875 * Save the new property into the new property spot
877 StorageImpl_WriteProperty(
878 This->ancestorStorage,
879 renamedPropertyIndex,
880 &renamedProperty);
883 * Find a spot in the property chain for our newly created property.
885 updatePropertyChain(
886 (StorageImpl*)This,
887 renamedPropertyIndex,
888 renamedProperty);
891 * At this point the renamed property has been inserted in the tree,
892 * now, before Destroying the old property we must zero its dirProperty
893 * otherwise the DestroyProperty below will zap it all and we do not want
894 * this to happen.
895 * Also, we fake that the old property is a storage so the DestroyProperty
896 * will not do a SetSize(0) on the stream data.
898 * This means that we need to tweak the StgProperty if it is a stream or a
899 * non empty storage.
901 StorageImpl_ReadProperty(This->ancestorStorage,
902 foundPropertyIndex,
903 &currentProperty);
905 currentProperty.dirProperty = PROPERTY_NULL;
906 currentProperty.propertyType = PROPTYPE_STORAGE;
907 StorageImpl_WriteProperty(
908 This->ancestorStorage,
909 foundPropertyIndex,
910 &currentProperty);
913 * Invoke Destroy to get rid of the ole property and automatically redo
914 * the linking of its previous and next members...
916 IStorage_DestroyElement(iface, pwcsOldName);
919 else
922 * There is no property with the old name
924 return STG_E_FILENOTFOUND;
927 return S_OK;
930 /************************************************************************
931 * Storage32BaseImpl_CreateStream (IStorage)
933 * This method will create a stream object within this storage
935 * See Windows documentation for more details on IStorage methods.
937 static HRESULT WINAPI StorageBaseImpl_CreateStream(
938 IStorage* iface,
939 const OLECHAR* pwcsName, /* [string][in] */
940 DWORD grfMode, /* [in] */
941 DWORD reserved1, /* [in] */
942 DWORD reserved2, /* [in] */
943 IStream** ppstm) /* [out] */
945 StorageBaseImpl *This = (StorageBaseImpl *)iface;
946 IEnumSTATSTGImpl* propertyEnumeration;
947 StgStreamImpl* newStream;
948 StgProperty currentProperty, newStreamProperty;
949 ULONG foundPropertyIndex, newPropertyIndex;
951 TRACE("(%p, %s, %x, %d, %d, %p)\n",
952 iface, debugstr_w(pwcsName), grfMode,
953 reserved1, reserved2, ppstm);
956 * Validate parameters
958 if (ppstm == 0)
959 return STG_E_INVALIDPOINTER;
961 if (pwcsName == 0)
962 return STG_E_INVALIDNAME;
964 if (reserved1 || reserved2)
965 return STG_E_INVALIDPARAMETER;
968 * Validate the STGM flags
970 if ( FAILED( validateSTGM(grfMode) ))
971 return STG_E_INVALIDFLAG;
973 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
974 return STG_E_INVALIDFLAG;
977 * As documented.
979 if ((grfMode & STGM_DELETEONRELEASE) ||
980 (grfMode & STGM_TRANSACTED))
981 return STG_E_INVALIDFUNCTION;
983 /* Can't create a stream on read-only storage */
984 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
985 return STG_E_ACCESSDENIED;
988 * Check that we're compatible with the parent's storage mode
989 * if not in transacted mode
991 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
992 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
993 return STG_E_ACCESSDENIED;
996 if(This->ancestorStorage->base.openFlags & STGM_SIMPLE)
997 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
1000 * Initialize the out parameter
1002 *ppstm = 0;
1005 * Create a property enumeration to search the properties
1007 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
1008 This->rootPropertySetIndex);
1010 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1011 pwcsName,
1012 &currentProperty);
1014 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1016 if (foundPropertyIndex != PROPERTY_NULL)
1019 * An element with this name already exists
1021 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1023 StgStreamImpl *strm;
1025 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
1027 if (strm->ownerProperty == foundPropertyIndex)
1029 TRACE("Stream deleted %p\n", strm);
1030 strm->parentStorage = NULL;
1031 list_remove(&strm->StrmListEntry);
1034 IStorage_DestroyElement(iface, pwcsName);
1036 else
1037 return STG_E_FILEALREADYEXISTS;
1039 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1041 WARN("read-only storage\n");
1042 return STG_E_ACCESSDENIED;
1046 * memset the empty property
1048 memset(&newStreamProperty, 0, sizeof(StgProperty));
1050 newStreamProperty.sizeOfNameString =
1051 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1053 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1054 return STG_E_INVALIDNAME;
1056 strcpyW(newStreamProperty.name, pwcsName);
1058 newStreamProperty.propertyType = PROPTYPE_STREAM;
1059 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1060 newStreamProperty.size.u.LowPart = 0;
1061 newStreamProperty.size.u.HighPart = 0;
1063 newStreamProperty.previousProperty = PROPERTY_NULL;
1064 newStreamProperty.nextProperty = PROPERTY_NULL;
1065 newStreamProperty.dirProperty = PROPERTY_NULL;
1067 /* call CoFileTime to get the current time
1068 newStreamProperty.timeStampS1
1069 newStreamProperty.timeStampD1
1070 newStreamProperty.timeStampS2
1071 newStreamProperty.timeStampD2
1074 /* newStreamProperty.propertyUniqueID */
1077 * Get a free property or create a new one
1079 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1082 * Save the new property into the new property spot
1084 StorageImpl_WriteProperty(
1085 This->ancestorStorage,
1086 newPropertyIndex,
1087 &newStreamProperty);
1090 * Find a spot in the property chain for our newly created property.
1092 updatePropertyChain(
1093 (StorageImpl*)This,
1094 newPropertyIndex,
1095 newStreamProperty);
1098 * Open the stream to return it.
1100 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1102 if (newStream != 0)
1104 *ppstm = (IStream*)newStream;
1107 * Since we are returning a pointer to the interface, we have to nail down
1108 * the reference.
1110 IStream_AddRef(*ppstm);
1112 else
1114 return STG_E_INSUFFICIENTMEMORY;
1117 return S_OK;
1120 /************************************************************************
1121 * Storage32BaseImpl_SetClass (IStorage)
1123 * This method will write the specified CLSID in the property of this
1124 * storage.
1126 * See Windows documentation for more details on IStorage methods.
1128 static HRESULT WINAPI StorageBaseImpl_SetClass(
1129 IStorage* iface,
1130 REFCLSID clsid) /* [in] */
1132 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1133 HRESULT hRes = E_FAIL;
1134 StgProperty curProperty;
1135 BOOL success;
1137 TRACE("(%p, %p)\n", iface, clsid);
1139 success = StorageImpl_ReadProperty(This->ancestorStorage,
1140 This->rootPropertySetIndex,
1141 &curProperty);
1142 if (success)
1144 curProperty.propertyUniqueID = *clsid;
1146 success = StorageImpl_WriteProperty(This->ancestorStorage,
1147 This->rootPropertySetIndex,
1148 &curProperty);
1149 if (success)
1150 hRes = S_OK;
1153 return hRes;
1156 /************************************************************************
1157 ** Storage32Impl implementation
1160 /************************************************************************
1161 * Storage32Impl_CreateStorage (IStorage)
1163 * This method will create the storage object within the provided storage.
1165 * See Windows documentation for more details on IStorage methods.
1167 static HRESULT WINAPI StorageImpl_CreateStorage(
1168 IStorage* iface,
1169 const OLECHAR *pwcsName, /* [string][in] */
1170 DWORD grfMode, /* [in] */
1171 DWORD reserved1, /* [in] */
1172 DWORD reserved2, /* [in] */
1173 IStorage **ppstg) /* [out] */
1175 StorageImpl* const This=(StorageImpl*)iface;
1177 IEnumSTATSTGImpl *propertyEnumeration;
1178 StgProperty currentProperty;
1179 StgProperty newProperty;
1180 ULONG foundPropertyIndex;
1181 ULONG newPropertyIndex;
1182 HRESULT hr;
1184 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1185 iface, debugstr_w(pwcsName), grfMode,
1186 reserved1, reserved2, ppstg);
1189 * Validate parameters
1191 if (ppstg == 0)
1192 return STG_E_INVALIDPOINTER;
1194 if (pwcsName == 0)
1195 return STG_E_INVALIDNAME;
1198 * Initialize the out parameter
1200 *ppstg = NULL;
1203 * Validate the STGM flags
1205 if ( FAILED( validateSTGM(grfMode) ) ||
1206 (grfMode & STGM_DELETEONRELEASE) )
1208 WARN("bad grfMode: 0x%x\n", grfMode);
1209 return STG_E_INVALIDFLAG;
1213 * Check that we're compatible with the parent's storage mode
1215 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1217 WARN("access denied\n");
1218 return STG_E_ACCESSDENIED;
1222 * Create a property enumeration and search the properties
1224 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1225 This->base.rootPropertySetIndex);
1227 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1228 pwcsName,
1229 &currentProperty);
1230 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1232 if (foundPropertyIndex != PROPERTY_NULL)
1235 * An element with this name already exists
1237 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1238 STGM_ACCESS_MODE(This->base.openFlags) != STGM_READ)
1240 hr = IStorage_DestroyElement(iface, pwcsName);
1241 if (FAILED(hr))
1242 return hr;
1244 else
1246 WARN("file already exists\n");
1247 return STG_E_FILEALREADYEXISTS;
1250 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1252 WARN("read-only storage\n");
1253 return STG_E_ACCESSDENIED;
1257 * memset the empty property
1259 memset(&newProperty, 0, sizeof(StgProperty));
1261 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1263 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1265 FIXME("name too long\n");
1266 return STG_E_INVALIDNAME;
1269 strcpyW(newProperty.name, pwcsName);
1271 newProperty.propertyType = PROPTYPE_STORAGE;
1272 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1273 newProperty.size.u.LowPart = 0;
1274 newProperty.size.u.HighPart = 0;
1276 newProperty.previousProperty = PROPERTY_NULL;
1277 newProperty.nextProperty = PROPERTY_NULL;
1278 newProperty.dirProperty = PROPERTY_NULL;
1280 /* call CoFileTime to get the current time
1281 newProperty.timeStampS1
1282 newProperty.timeStampD1
1283 newProperty.timeStampS2
1284 newProperty.timeStampD2
1287 /* newStorageProperty.propertyUniqueID */
1290 * Obtain a free property in the property chain
1292 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1295 * Save the new property into the new property spot
1297 StorageImpl_WriteProperty(
1298 This->base.ancestorStorage,
1299 newPropertyIndex,
1300 &newProperty);
1303 * Find a spot in the property chain for our newly created property.
1305 updatePropertyChain(
1306 This,
1307 newPropertyIndex,
1308 newProperty);
1311 * Open it to get a pointer to return.
1313 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1315 if( (hr != S_OK) || (*ppstg == NULL))
1317 return hr;
1321 return S_OK;
1325 /***************************************************************************
1327 * Internal Method
1329 * Get a free property or create a new one.
1331 static ULONG getFreeProperty(
1332 StorageImpl *storage)
1334 ULONG currentPropertyIndex = 0;
1335 ULONG newPropertyIndex = PROPERTY_NULL;
1336 BOOL readSuccessful = TRUE;
1337 StgProperty currentProperty;
1342 * Start by reading the root property
1344 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1345 currentPropertyIndex,
1346 &currentProperty);
1347 if (readSuccessful)
1349 if (currentProperty.sizeOfNameString == 0)
1352 * The property existis and is available, we found it.
1354 newPropertyIndex = currentPropertyIndex;
1357 else
1360 * We exhausted the property list, we will create more space below
1362 newPropertyIndex = currentPropertyIndex;
1364 currentPropertyIndex++;
1366 } while (newPropertyIndex == PROPERTY_NULL);
1369 * grow the property chain
1371 if (! readSuccessful)
1373 StgProperty emptyProperty;
1374 ULARGE_INTEGER newSize;
1375 ULONG propertyIndex;
1376 ULONG lastProperty = 0;
1377 ULONG blockCount = 0;
1380 * obtain the new count of property blocks
1382 blockCount = BlockChainStream_GetCount(
1383 storage->base.ancestorStorage->rootBlockChain)+1;
1386 * initialize the size used by the property stream
1388 newSize.u.HighPart = 0;
1389 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1392 * add a property block to the property chain
1394 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1397 * memset the empty property in order to initialize the unused newly
1398 * created property
1400 memset(&emptyProperty, 0, sizeof(StgProperty));
1403 * initialize them
1405 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1407 for(
1408 propertyIndex = newPropertyIndex;
1409 propertyIndex < lastProperty;
1410 propertyIndex++)
1412 StorageImpl_WriteProperty(
1413 storage->base.ancestorStorage,
1414 propertyIndex,
1415 &emptyProperty);
1419 return newPropertyIndex;
1422 /****************************************************************************
1424 * Internal Method
1426 * Case insensitive comparison of StgProperty.name by first considering
1427 * their size.
1429 * Returns <0 when newProperty < currentProperty
1430 * >0 when newProperty > currentProperty
1431 * 0 when newProperty == currentProperty
1433 static LONG propertyNameCmp(
1434 const OLECHAR *newProperty,
1435 const OLECHAR *currentProperty)
1437 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1439 if (diff == 0)
1442 * We compare the string themselves only when they are of the same length
1444 diff = lstrcmpiW( newProperty, currentProperty);
1447 return diff;
1450 /****************************************************************************
1452 * Internal Method
1454 * Properly link this new element in the property chain.
1456 static void updatePropertyChain(
1457 StorageImpl *storage,
1458 ULONG newPropertyIndex,
1459 StgProperty newProperty)
1461 StgProperty currentProperty;
1464 * Read the root property
1466 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1467 storage->base.rootPropertySetIndex,
1468 &currentProperty);
1470 if (currentProperty.dirProperty != PROPERTY_NULL)
1473 * The root storage contains some element, therefore, start the research
1474 * for the appropriate location.
1476 BOOL found = 0;
1477 ULONG current, next, previous, currentPropertyId;
1480 * Keep the StgProperty sequence number of the storage first property
1482 currentPropertyId = currentProperty.dirProperty;
1485 * Read
1487 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1488 currentProperty.dirProperty,
1489 &currentProperty);
1491 previous = currentProperty.previousProperty;
1492 next = currentProperty.nextProperty;
1493 current = currentPropertyId;
1495 while (found == 0)
1497 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1499 if (diff < 0)
1501 if (previous != PROPERTY_NULL)
1503 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1504 previous,
1505 &currentProperty);
1506 current = previous;
1508 else
1510 currentProperty.previousProperty = newPropertyIndex;
1511 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1512 current,
1513 &currentProperty);
1514 found = 1;
1517 else if (diff > 0)
1519 if (next != PROPERTY_NULL)
1521 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1522 next,
1523 &currentProperty);
1524 current = next;
1526 else
1528 currentProperty.nextProperty = newPropertyIndex;
1529 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1530 current,
1531 &currentProperty);
1532 found = 1;
1535 else
1538 * Trying to insert an item with the same name in the
1539 * subtree structure.
1541 assert(FALSE);
1544 previous = currentProperty.previousProperty;
1545 next = currentProperty.nextProperty;
1548 else
1551 * The root storage is empty, link the new property to its dir property
1553 currentProperty.dirProperty = newPropertyIndex;
1554 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1555 storage->base.rootPropertySetIndex,
1556 &currentProperty);
1561 /*************************************************************************
1562 * CopyTo (IStorage)
1564 static HRESULT WINAPI StorageImpl_CopyTo(
1565 IStorage* iface,
1566 DWORD ciidExclude, /* [in] */
1567 const IID* rgiidExclude, /* [size_is][unique][in] */
1568 SNB snbExclude, /* [unique][in] */
1569 IStorage* pstgDest) /* [unique][in] */
1571 IEnumSTATSTG *elements = 0;
1572 STATSTG curElement, strStat;
1573 HRESULT hr;
1574 IStorage *pstgTmp, *pstgChild;
1575 IStream *pstrTmp, *pstrChild;
1577 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1578 FIXME("Exclude option not implemented\n");
1580 TRACE("(%p, %d, %p, %p, %p)\n",
1581 iface, ciidExclude, rgiidExclude,
1582 snbExclude, pstgDest);
1585 * Perform a sanity check
1587 if ( pstgDest == 0 )
1588 return STG_E_INVALIDPOINTER;
1591 * Enumerate the elements
1593 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1595 if ( hr != S_OK )
1596 return hr;
1599 * set the class ID
1601 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1602 IStorage_SetClass( pstgDest, &curElement.clsid );
1607 * Obtain the next element
1609 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1611 if ( hr == S_FALSE )
1613 hr = S_OK; /* done, every element has been copied */
1614 break;
1617 if (curElement.type == STGTY_STORAGE)
1620 * open child source storage
1622 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1623 STGM_READ|STGM_SHARE_EXCLUSIVE,
1624 NULL, 0, &pstgChild );
1626 if (hr != S_OK)
1627 break;
1630 * Check if destination storage is not a child of the source
1631 * storage, which will cause an infinite loop
1633 if (pstgChild == pstgDest)
1635 IEnumSTATSTG_Release(elements);
1637 return STG_E_ACCESSDENIED;
1641 * create a new storage in destination storage
1643 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1644 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1645 0, 0,
1646 &pstgTmp );
1648 * if it already exist, don't create a new one use this one
1650 if (hr == STG_E_FILEALREADYEXISTS)
1652 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1653 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1654 NULL, 0, &pstgTmp );
1657 if (hr != S_OK)
1658 break;
1662 * do the copy recursively
1664 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1665 snbExclude, pstgTmp );
1667 IStorage_Release( pstgTmp );
1668 IStorage_Release( pstgChild );
1670 else if (curElement.type == STGTY_STREAM)
1673 * create a new stream in destination storage. If the stream already
1674 * exist, it will be deleted and a new one will be created.
1676 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1677 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1678 0, 0, &pstrTmp );
1680 if (hr != S_OK)
1681 break;
1684 * open child stream storage
1686 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1687 STGM_READ|STGM_SHARE_EXCLUSIVE,
1688 0, &pstrChild );
1690 if (hr != S_OK)
1691 break;
1694 * Get the size of the source stream
1696 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1699 * Set the size of the destination stream.
1701 IStream_SetSize(pstrTmp, strStat.cbSize);
1704 * do the copy
1706 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1707 NULL, NULL );
1709 IStream_Release( pstrTmp );
1710 IStream_Release( pstrChild );
1712 else
1714 WARN("unknown element type: %d\n", curElement.type);
1717 } while (hr == S_OK);
1720 * Clean-up
1722 IEnumSTATSTG_Release(elements);
1724 return hr;
1727 /*************************************************************************
1728 * MoveElementTo (IStorage)
1730 static HRESULT WINAPI StorageImpl_MoveElementTo(
1731 IStorage* iface,
1732 const OLECHAR *pwcsName, /* [string][in] */
1733 IStorage *pstgDest, /* [unique][in] */
1734 const OLECHAR *pwcsNewName,/* [string][in] */
1735 DWORD grfFlags) /* [in] */
1737 FIXME("(%p %s %p %s %u): stub\n", iface,
1738 debugstr_w(pwcsName), pstgDest,
1739 debugstr_w(pwcsNewName), grfFlags);
1740 return E_NOTIMPL;
1743 /*************************************************************************
1744 * Commit (IStorage)
1746 * Ensures that any changes made to a storage object open in transacted mode
1747 * are reflected in the parent storage
1749 * NOTES
1750 * Wine doesn't implement transacted mode, which seems to be a basic
1751 * optimization, so we can ignore this stub for now.
1753 static HRESULT WINAPI StorageImpl_Commit(
1754 IStorage* iface,
1755 DWORD grfCommitFlags)/* [in] */
1757 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1758 return S_OK;
1761 /*************************************************************************
1762 * Revert (IStorage)
1764 * Discard all changes that have been made since the last commit operation
1766 static HRESULT WINAPI StorageImpl_Revert(
1767 IStorage* iface)
1769 FIXME("(%p): stub\n", iface);
1770 return E_NOTIMPL;
1773 /*************************************************************************
1774 * DestroyElement (IStorage)
1776 * Strategy: This implementation is built this way for simplicity not for speed.
1777 * I always delete the topmost element of the enumeration and adjust
1778 * the deleted element pointer all the time. This takes longer to
1779 * do but allow to reinvoke DestroyElement whenever we encounter a
1780 * storage object. The optimisation resides in the usage of another
1781 * enumeration strategy that would give all the leaves of a storage
1782 * first. (postfix order)
1784 static HRESULT WINAPI StorageImpl_DestroyElement(
1785 IStorage* iface,
1786 const OLECHAR *pwcsName)/* [string][in] */
1788 StorageImpl* const This=(StorageImpl*)iface;
1790 IEnumSTATSTGImpl* propertyEnumeration;
1791 HRESULT hr = S_OK;
1792 BOOL res;
1793 StgProperty propertyToDelete;
1794 StgProperty parentProperty;
1795 ULONG foundPropertyIndexToDelete;
1796 ULONG typeOfRelation;
1797 ULONG parentPropertyId = 0;
1799 TRACE("(%p, %s)\n",
1800 iface, debugstr_w(pwcsName));
1803 * Perform a sanity check on the parameters.
1805 if (pwcsName==NULL)
1806 return STG_E_INVALIDPOINTER;
1808 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
1809 return STG_E_ACCESSDENIED;
1812 * Create a property enumeration to search the property with the given name
1814 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1815 This->base.ancestorStorage,
1816 This->base.rootPropertySetIndex);
1818 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1819 propertyEnumeration,
1820 pwcsName,
1821 &propertyToDelete);
1823 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1825 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1827 return STG_E_FILENOTFOUND;
1831 * Find the parent property of the property to delete (the one that
1832 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1833 * the parent is This. Otherwise, the parent is one of its sibling...
1837 * First, read This's StgProperty..
1839 res = StorageImpl_ReadProperty(
1840 This->base.ancestorStorage,
1841 This->base.rootPropertySetIndex,
1842 &parentProperty);
1844 assert(res);
1847 * Second, check to see if by any chance the actual storage (This) is not
1848 * the parent of the property to delete... We never know...
1850 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1853 * Set data as it would have been done in the else part...
1855 typeOfRelation = PROPERTY_RELATION_DIR;
1856 parentPropertyId = This->base.rootPropertySetIndex;
1858 else
1861 * Create a property enumeration to search the parent properties, and
1862 * delete it once done.
1864 IEnumSTATSTGImpl* propertyEnumeration2;
1866 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1867 This->base.ancestorStorage,
1868 This->base.rootPropertySetIndex);
1870 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1871 propertyEnumeration2,
1872 foundPropertyIndexToDelete,
1873 &parentProperty,
1874 &parentPropertyId);
1876 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1879 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1881 hr = deleteStorageProperty(
1882 This,
1883 foundPropertyIndexToDelete,
1884 propertyToDelete);
1886 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1888 hr = deleteStreamProperty(
1889 This,
1890 foundPropertyIndexToDelete,
1891 propertyToDelete);
1894 if (hr!=S_OK)
1895 return hr;
1898 * Adjust the property chain
1900 hr = adjustPropertyChain(
1901 This,
1902 propertyToDelete,
1903 parentProperty,
1904 parentPropertyId,
1905 typeOfRelation);
1907 return hr;
1911 /************************************************************************
1912 * StorageImpl_Stat (IStorage)
1914 * This method will retrieve information about this storage object.
1916 * See Windows documentation for more details on IStorage methods.
1918 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1919 STATSTG* pstatstg, /* [out] */
1920 DWORD grfStatFlag) /* [in] */
1922 StorageImpl* const This = (StorageImpl*)iface;
1923 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1925 if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1927 CoTaskMemFree(pstatstg->pwcsName);
1928 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1929 strcpyW(pstatstg->pwcsName, This->pwcsName);
1932 return result;
1935 /******************************************************************************
1936 * Internal stream list handlers
1939 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1941 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1942 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1945 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1947 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1948 list_remove(&(strm->StrmListEntry));
1951 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1953 struct list *cur, *cur2;
1954 StgStreamImpl *strm=NULL;
1956 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1957 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1958 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1959 strm->parentStorage = NULL;
1960 list_remove(cur);
1965 /*********************************************************************
1967 * Internal Method
1969 * Perform the deletion of a complete storage node
1972 static HRESULT deleteStorageProperty(
1973 StorageImpl *parentStorage,
1974 ULONG indexOfPropertyToDelete,
1975 StgProperty propertyToDelete)
1977 IEnumSTATSTG *elements = 0;
1978 IStorage *childStorage = 0;
1979 STATSTG currentElement;
1980 HRESULT hr;
1981 HRESULT destroyHr = S_OK;
1984 * Open the storage and enumerate it
1986 hr = StorageBaseImpl_OpenStorage(
1987 (IStorage*)parentStorage,
1988 propertyToDelete.name,
1990 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1993 &childStorage);
1995 if (hr != S_OK)
1997 return hr;
2001 * Enumerate the elements
2003 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2008 * Obtain the next element
2010 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2011 if (hr==S_OK)
2013 destroyHr = StorageImpl_DestroyElement(childStorage, currentElement.pwcsName);
2015 CoTaskMemFree(currentElement.pwcsName);
2019 * We need to Reset the enumeration every time because we delete elements
2020 * and the enumeration could be invalid
2022 IEnumSTATSTG_Reset(elements);
2024 } while ((hr == S_OK) && (destroyHr == S_OK));
2027 * Invalidate the property by zeroing its name member.
2029 propertyToDelete.sizeOfNameString = 0;
2031 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2032 indexOfPropertyToDelete,
2033 &propertyToDelete);
2035 IStorage_Release(childStorage);
2036 IEnumSTATSTG_Release(elements);
2038 return destroyHr;
2041 /*********************************************************************
2043 * Internal Method
2045 * Perform the deletion of a stream node
2048 static HRESULT deleteStreamProperty(
2049 StorageImpl *parentStorage,
2050 ULONG indexOfPropertyToDelete,
2051 StgProperty propertyToDelete)
2053 IStream *pis;
2054 HRESULT hr;
2055 ULARGE_INTEGER size;
2057 size.u.HighPart = 0;
2058 size.u.LowPart = 0;
2060 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2061 propertyToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2063 if (hr!=S_OK)
2065 return(hr);
2069 * Zap the stream
2071 hr = IStream_SetSize(pis, size);
2073 if(hr != S_OK)
2075 return hr;
2079 * Release the stream object.
2081 IStream_Release(pis);
2084 * Invalidate the property by zeroing its name member.
2086 propertyToDelete.sizeOfNameString = 0;
2089 * Here we should re-read the property so we get the updated pointer
2090 * but since we are here to zap it, I don't do it...
2092 StorageImpl_WriteProperty(
2093 parentStorage->base.ancestorStorage,
2094 indexOfPropertyToDelete,
2095 &propertyToDelete);
2097 return S_OK;
2100 /*********************************************************************
2102 * Internal Method
2104 * Finds a placeholder for the StgProperty within the Storage
2107 static HRESULT findPlaceholder(
2108 StorageImpl *storage,
2109 ULONG propertyIndexToStore,
2110 ULONG storePropertyIndex,
2111 INT typeOfRelation)
2113 StgProperty storeProperty;
2114 BOOL res = TRUE;
2117 * Read the storage property
2119 res = StorageImpl_ReadProperty(
2120 storage->base.ancestorStorage,
2121 storePropertyIndex,
2122 &storeProperty);
2124 if(! res)
2126 return E_FAIL;
2129 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2131 if (storeProperty.previousProperty != PROPERTY_NULL)
2133 return findPlaceholder(
2134 storage,
2135 propertyIndexToStore,
2136 storeProperty.previousProperty,
2137 typeOfRelation);
2139 else
2141 storeProperty.previousProperty = propertyIndexToStore;
2144 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2146 if (storeProperty.nextProperty != PROPERTY_NULL)
2148 return findPlaceholder(
2149 storage,
2150 propertyIndexToStore,
2151 storeProperty.nextProperty,
2152 typeOfRelation);
2154 else
2156 storeProperty.nextProperty = propertyIndexToStore;
2159 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2161 if (storeProperty.dirProperty != PROPERTY_NULL)
2163 return findPlaceholder(
2164 storage,
2165 propertyIndexToStore,
2166 storeProperty.dirProperty,
2167 typeOfRelation);
2169 else
2171 storeProperty.dirProperty = propertyIndexToStore;
2175 res = StorageImpl_WriteProperty(
2176 storage->base.ancestorStorage,
2177 storePropertyIndex,
2178 &storeProperty);
2180 if(!res)
2182 return E_FAIL;
2185 return S_OK;
2188 /*************************************************************************
2190 * Internal Method
2192 * This method takes the previous and the next property link of a property
2193 * to be deleted and find them a place in the Storage.
2195 static HRESULT adjustPropertyChain(
2196 StorageImpl *This,
2197 StgProperty propertyToDelete,
2198 StgProperty parentProperty,
2199 ULONG parentPropertyId,
2200 INT typeOfRelation)
2202 ULONG newLinkProperty = PROPERTY_NULL;
2203 BOOL needToFindAPlaceholder = FALSE;
2204 ULONG storeNode = PROPERTY_NULL;
2205 ULONG toStoreNode = PROPERTY_NULL;
2206 INT relationType = 0;
2207 HRESULT hr = S_OK;
2208 BOOL res = TRUE;
2210 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2212 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2215 * Set the parent previous to the property to delete previous
2217 newLinkProperty = propertyToDelete.previousProperty;
2219 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2222 * We also need to find a storage for the other link, setup variables
2223 * to do this at the end...
2225 needToFindAPlaceholder = TRUE;
2226 storeNode = propertyToDelete.previousProperty;
2227 toStoreNode = propertyToDelete.nextProperty;
2228 relationType = PROPERTY_RELATION_NEXT;
2231 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2234 * Set the parent previous to the property to delete next
2236 newLinkProperty = propertyToDelete.nextProperty;
2240 * Link it for real...
2242 parentProperty.previousProperty = newLinkProperty;
2245 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2247 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2250 * Set the parent next to the property to delete next previous
2252 newLinkProperty = propertyToDelete.previousProperty;
2254 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2257 * We also need to find a storage for the other link, setup variables
2258 * to do this at the end...
2260 needToFindAPlaceholder = TRUE;
2261 storeNode = propertyToDelete.previousProperty;
2262 toStoreNode = propertyToDelete.nextProperty;
2263 relationType = PROPERTY_RELATION_NEXT;
2266 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2269 * Set the parent next to the property to delete next
2271 newLinkProperty = propertyToDelete.nextProperty;
2275 * Link it for real...
2277 parentProperty.nextProperty = newLinkProperty;
2279 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2281 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2284 * Set the parent dir to the property to delete previous
2286 newLinkProperty = propertyToDelete.previousProperty;
2288 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2291 * We also need to find a storage for the other link, setup variables
2292 * to do this at the end...
2294 needToFindAPlaceholder = TRUE;
2295 storeNode = propertyToDelete.previousProperty;
2296 toStoreNode = propertyToDelete.nextProperty;
2297 relationType = PROPERTY_RELATION_NEXT;
2300 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2303 * Set the parent dir to the property to delete next
2305 newLinkProperty = propertyToDelete.nextProperty;
2309 * Link it for real...
2311 parentProperty.dirProperty = newLinkProperty;
2315 * Write back the parent property
2317 res = StorageImpl_WriteProperty(
2318 This->base.ancestorStorage,
2319 parentPropertyId,
2320 &parentProperty);
2321 if(! res)
2323 return E_FAIL;
2327 * If a placeholder is required for the other link, then, find one and
2328 * get out of here...
2330 if (needToFindAPlaceholder)
2332 hr = findPlaceholder(
2333 This,
2334 toStoreNode,
2335 storeNode,
2336 relationType);
2339 return hr;
2343 /******************************************************************************
2344 * SetElementTimes (IStorage)
2346 static HRESULT WINAPI StorageImpl_SetElementTimes(
2347 IStorage* iface,
2348 const OLECHAR *pwcsName,/* [string][in] */
2349 const FILETIME *pctime, /* [in] */
2350 const FILETIME *patime, /* [in] */
2351 const FILETIME *pmtime) /* [in] */
2353 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2354 return S_OK;
2357 /******************************************************************************
2358 * SetStateBits (IStorage)
2360 static HRESULT WINAPI StorageImpl_SetStateBits(
2361 IStorage* iface,
2362 DWORD grfStateBits,/* [in] */
2363 DWORD grfMask) /* [in] */
2365 StorageImpl* const This = (StorageImpl*)iface;
2366 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2367 return S_OK;
2371 * Virtual function table for the IStorage32Impl class.
2373 static const IStorageVtbl Storage32Impl_Vtbl =
2375 StorageBaseImpl_QueryInterface,
2376 StorageBaseImpl_AddRef,
2377 StorageBaseImpl_Release,
2378 StorageBaseImpl_CreateStream,
2379 StorageBaseImpl_OpenStream,
2380 StorageImpl_CreateStorage,
2381 StorageBaseImpl_OpenStorage,
2382 StorageImpl_CopyTo,
2383 StorageImpl_MoveElementTo,
2384 StorageImpl_Commit,
2385 StorageImpl_Revert,
2386 StorageBaseImpl_EnumElements,
2387 StorageImpl_DestroyElement,
2388 StorageBaseImpl_RenameElement,
2389 StorageImpl_SetElementTimes,
2390 StorageBaseImpl_SetClass,
2391 StorageImpl_SetStateBits,
2392 StorageImpl_Stat
2395 static HRESULT StorageImpl_Construct(
2396 StorageImpl* This,
2397 HANDLE hFile,
2398 LPCOLESTR pwcsName,
2399 ILockBytes* pLkbyt,
2400 DWORD openFlags,
2401 BOOL fileBased,
2402 BOOL create)
2404 HRESULT hr = S_OK;
2405 StgProperty currentProperty;
2406 BOOL readSuccessful;
2407 ULONG currentPropertyIndex;
2409 if ( FAILED( validateSTGM(openFlags) ))
2410 return STG_E_INVALIDFLAG;
2412 memset(This, 0, sizeof(StorageImpl));
2414 list_init(&This->base.strmHead);
2416 This->base.lpVtbl = &Storage32Impl_Vtbl;
2417 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2418 This->base.v_destructor = StorageImpl_Destroy;
2419 This->base.openFlags = (openFlags & ~STGM_CREATE);
2420 This->create = create;
2423 * This is the top-level storage so initialize the ancestor pointer
2424 * to this.
2426 This->base.ancestorStorage = This;
2428 This->hFile = hFile;
2430 if(pwcsName) {
2431 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2432 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2433 if (!This->pwcsName)
2434 return STG_E_INSUFFICIENTMEMORY;
2435 strcpyW(This->pwcsName, pwcsName);
2439 * Initialize the big block cache.
2441 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2442 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2443 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2444 pLkbyt,
2445 openFlags,
2446 This->bigBlockSize,
2447 fileBased);
2449 if (This->bigBlockFile == 0)
2450 return E_FAIL;
2452 if (create)
2454 ULARGE_INTEGER size;
2455 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2458 * Initialize all header variables:
2459 * - The big block depot consists of one block and it is at block 0
2460 * - The properties start at block 1
2461 * - There is no small block depot
2463 memset( This->bigBlockDepotStart,
2464 BLOCK_UNUSED,
2465 sizeof(This->bigBlockDepotStart));
2467 This->bigBlockDepotCount = 1;
2468 This->bigBlockDepotStart[0] = 0;
2469 This->rootStartBlock = 1;
2470 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2471 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2472 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2473 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2474 This->extBigBlockDepotCount = 0;
2476 StorageImpl_SaveFileHeader(This);
2479 * Add one block for the big block depot and one block for the properties
2481 size.u.HighPart = 0;
2482 size.u.LowPart = This->bigBlockSize * 3;
2483 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2486 * Initialize the big block depot
2488 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2489 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2490 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2491 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2493 else
2496 * Load the header for the file.
2498 hr = StorageImpl_LoadFileHeader(This);
2500 if (FAILED(hr))
2502 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2504 return hr;
2509 * There is no block depot cached yet.
2511 This->indexBlockDepotCached = 0xFFFFFFFF;
2514 * Start searching for free blocks with block 0.
2516 This->prevFreeBlock = 0;
2519 * Create the block chain abstractions.
2521 if(!(This->rootBlockChain =
2522 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2523 return STG_E_READFAULT;
2525 if(!(This->smallBlockDepotChain =
2526 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2527 PROPERTY_NULL)))
2528 return STG_E_READFAULT;
2531 * Write the root property (memory only)
2533 if (create)
2535 StgProperty rootProp;
2537 * Initialize the property chain
2539 memset(&rootProp, 0, sizeof(rootProp));
2540 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2541 sizeof(rootProp.name)/sizeof(WCHAR) );
2542 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2543 rootProp.propertyType = PROPTYPE_ROOT;
2544 rootProp.previousProperty = PROPERTY_NULL;
2545 rootProp.nextProperty = PROPERTY_NULL;
2546 rootProp.dirProperty = PROPERTY_NULL;
2547 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2548 rootProp.size.u.HighPart = 0;
2549 rootProp.size.u.LowPart = 0;
2551 StorageImpl_WriteProperty(This, 0, &rootProp);
2555 * Find the ID of the root in the property sets.
2557 currentPropertyIndex = 0;
2561 readSuccessful = StorageImpl_ReadProperty(
2562 This,
2563 currentPropertyIndex,
2564 &currentProperty);
2566 if (readSuccessful)
2568 if ( (currentProperty.sizeOfNameString != 0 ) &&
2569 (currentProperty.propertyType == PROPTYPE_ROOT) )
2571 This->base.rootPropertySetIndex = currentPropertyIndex;
2575 currentPropertyIndex++;
2577 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2579 if (!readSuccessful)
2581 /* TODO CLEANUP */
2582 return STG_E_READFAULT;
2586 * Create the block chain abstraction for the small block root chain.
2588 if(!(This->smallBlockRootChain =
2589 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2590 return STG_E_READFAULT;
2592 return hr;
2595 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2597 StorageImpl *This = (StorageImpl*) iface;
2598 TRACE("(%p)\n", This);
2600 StorageBaseImpl_DeleteAll(&This->base);
2602 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2604 BlockChainStream_Destroy(This->smallBlockRootChain);
2605 BlockChainStream_Destroy(This->rootBlockChain);
2606 BlockChainStream_Destroy(This->smallBlockDepotChain);
2608 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2609 HeapFree(GetProcessHeap(), 0, This);
2612 /******************************************************************************
2613 * Storage32Impl_GetNextFreeBigBlock
2615 * Returns the index of the next free big block.
2616 * If the big block depot is filled, this method will enlarge it.
2619 static ULONG StorageImpl_GetNextFreeBigBlock(
2620 StorageImpl* This)
2622 ULONG depotBlockIndexPos;
2623 BYTE depotBuffer[BIG_BLOCK_SIZE];
2624 BOOL success;
2625 ULONG depotBlockOffset;
2626 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2627 ULONG nextBlockIndex = BLOCK_SPECIAL;
2628 int depotIndex = 0;
2629 ULONG freeBlock = BLOCK_UNUSED;
2631 depotIndex = This->prevFreeBlock / blocksPerDepot;
2632 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2635 * Scan the entire big block depot until we find a block marked free
2637 while (nextBlockIndex != BLOCK_UNUSED)
2639 if (depotIndex < COUNT_BBDEPOTINHEADER)
2641 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2644 * Grow the primary depot.
2646 if (depotBlockIndexPos == BLOCK_UNUSED)
2648 depotBlockIndexPos = depotIndex*blocksPerDepot;
2651 * Add a block depot.
2653 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2654 This->bigBlockDepotCount++;
2655 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2658 * Flag it as a block depot.
2660 StorageImpl_SetNextBlockInChain(This,
2661 depotBlockIndexPos,
2662 BLOCK_SPECIAL);
2664 /* Save new header information.
2666 StorageImpl_SaveFileHeader(This);
2669 else
2671 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2673 if (depotBlockIndexPos == BLOCK_UNUSED)
2676 * Grow the extended depot.
2678 ULONG extIndex = BLOCK_UNUSED;
2679 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2680 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2682 if (extBlockOffset == 0)
2684 /* We need an extended block.
2686 extIndex = Storage32Impl_AddExtBlockDepot(This);
2687 This->extBigBlockDepotCount++;
2688 depotBlockIndexPos = extIndex + 1;
2690 else
2691 depotBlockIndexPos = depotIndex * blocksPerDepot;
2694 * Add a block depot and mark it in the extended block.
2696 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2697 This->bigBlockDepotCount++;
2698 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2700 /* Flag the block depot.
2702 StorageImpl_SetNextBlockInChain(This,
2703 depotBlockIndexPos,
2704 BLOCK_SPECIAL);
2706 /* If necessary, flag the extended depot block.
2708 if (extIndex != BLOCK_UNUSED)
2709 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2711 /* Save header information.
2713 StorageImpl_SaveFileHeader(This);
2717 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2719 if (success)
2721 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2722 ( nextBlockIndex != BLOCK_UNUSED))
2724 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2726 if (nextBlockIndex == BLOCK_UNUSED)
2728 freeBlock = (depotIndex * blocksPerDepot) +
2729 (depotBlockOffset/sizeof(ULONG));
2732 depotBlockOffset += sizeof(ULONG);
2736 depotIndex++;
2737 depotBlockOffset = 0;
2741 * make sure that the block physically exists before using it
2743 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2745 This->prevFreeBlock = freeBlock;
2747 return freeBlock;
2750 /******************************************************************************
2751 * Storage32Impl_AddBlockDepot
2753 * This will create a depot block, essentially it is a block initialized
2754 * to BLOCK_UNUSEDs.
2756 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2758 BYTE blockBuffer[BIG_BLOCK_SIZE];
2761 * Initialize blocks as free
2763 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2764 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2767 /******************************************************************************
2768 * Storage32Impl_GetExtDepotBlock
2770 * Returns the index of the block that corresponds to the specified depot
2771 * index. This method is only for depot indexes equal or greater than
2772 * COUNT_BBDEPOTINHEADER.
2774 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2776 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2777 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2778 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2779 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2780 ULONG blockIndex = BLOCK_UNUSED;
2781 ULONG extBlockIndex = This->extBigBlockDepotStart;
2783 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2785 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2786 return BLOCK_UNUSED;
2788 while (extBlockCount > 0)
2790 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2791 extBlockCount--;
2794 if (extBlockIndex != BLOCK_UNUSED)
2795 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2796 extBlockOffset * sizeof(ULONG), &blockIndex);
2798 return blockIndex;
2801 /******************************************************************************
2802 * Storage32Impl_SetExtDepotBlock
2804 * Associates the specified block index to the specified depot index.
2805 * This method is only for depot indexes equal or greater than
2806 * COUNT_BBDEPOTINHEADER.
2808 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2810 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2811 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2812 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2813 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2814 ULONG extBlockIndex = This->extBigBlockDepotStart;
2816 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2818 while (extBlockCount > 0)
2820 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2821 extBlockCount--;
2824 if (extBlockIndex != BLOCK_UNUSED)
2826 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2827 extBlockOffset * sizeof(ULONG),
2828 blockIndex);
2832 /******************************************************************************
2833 * Storage32Impl_AddExtBlockDepot
2835 * Creates an extended depot block.
2837 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2839 ULONG numExtBlocks = This->extBigBlockDepotCount;
2840 ULONG nextExtBlock = This->extBigBlockDepotStart;
2841 BYTE depotBuffer[BIG_BLOCK_SIZE];
2842 ULONG index = BLOCK_UNUSED;
2843 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2844 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2845 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2847 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2848 blocksPerDepotBlock;
2850 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2853 * The first extended block.
2855 This->extBigBlockDepotStart = index;
2857 else
2859 unsigned int i;
2861 * Follow the chain to the last one.
2863 for (i = 0; i < (numExtBlocks - 1); i++)
2865 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2869 * Add the new extended block to the chain.
2871 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2872 index);
2876 * Initialize this block.
2878 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2879 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2881 return index;
2884 /******************************************************************************
2885 * Storage32Impl_FreeBigBlock
2887 * This method will flag the specified block as free in the big block depot.
2889 static void StorageImpl_FreeBigBlock(
2890 StorageImpl* This,
2891 ULONG blockIndex)
2893 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2895 if (blockIndex < This->prevFreeBlock)
2896 This->prevFreeBlock = blockIndex;
2899 /************************************************************************
2900 * Storage32Impl_GetNextBlockInChain
2902 * This method will retrieve the block index of the next big block in
2903 * in the chain.
2905 * Params: This - Pointer to the Storage object.
2906 * blockIndex - Index of the block to retrieve the chain
2907 * for.
2908 * nextBlockIndex - receives the return value.
2910 * Returns: This method returns the index of the next block in the chain.
2911 * It will return the constants:
2912 * BLOCK_SPECIAL - If the block given was not part of a
2913 * chain.
2914 * BLOCK_END_OF_CHAIN - If the block given was the last in
2915 * a chain.
2916 * BLOCK_UNUSED - If the block given was not past of a chain
2917 * and is available.
2918 * BLOCK_EXTBBDEPOT - This block is part of the extended
2919 * big block depot.
2921 * See Windows documentation for more details on IStorage methods.
2923 static HRESULT StorageImpl_GetNextBlockInChain(
2924 StorageImpl* This,
2925 ULONG blockIndex,
2926 ULONG* nextBlockIndex)
2928 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2929 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2930 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2931 BYTE depotBuffer[BIG_BLOCK_SIZE];
2932 BOOL success;
2933 ULONG depotBlockIndexPos;
2934 int index;
2936 *nextBlockIndex = BLOCK_SPECIAL;
2938 if(depotBlockCount >= This->bigBlockDepotCount)
2940 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2941 This->bigBlockDepotCount);
2942 return STG_E_READFAULT;
2946 * Cache the currently accessed depot block.
2948 if (depotBlockCount != This->indexBlockDepotCached)
2950 This->indexBlockDepotCached = depotBlockCount;
2952 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2954 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2956 else
2959 * We have to look in the extended depot.
2961 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2964 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2966 if (!success)
2967 return STG_E_READFAULT;
2969 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2971 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2972 This->blockDepotCached[index] = *nextBlockIndex;
2976 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2978 return S_OK;
2981 /******************************************************************************
2982 * Storage32Impl_GetNextExtendedBlock
2984 * Given an extended block this method will return the next extended block.
2986 * NOTES:
2987 * The last ULONG of an extended block is the block index of the next
2988 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2989 * depot.
2991 * Return values:
2992 * - The index of the next extended block
2993 * - BLOCK_UNUSED: there is no next extended block.
2994 * - Any other return values denotes failure.
2996 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2998 ULONG nextBlockIndex = BLOCK_SPECIAL;
2999 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3001 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3002 &nextBlockIndex);
3004 return nextBlockIndex;
3007 /******************************************************************************
3008 * Storage32Impl_SetNextBlockInChain
3010 * This method will write the index of the specified block's next block
3011 * in the big block depot.
3013 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3014 * do the following
3016 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3017 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3018 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3021 static void StorageImpl_SetNextBlockInChain(
3022 StorageImpl* This,
3023 ULONG blockIndex,
3024 ULONG nextBlock)
3026 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3027 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3028 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3029 ULONG depotBlockIndexPos;
3031 assert(depotBlockCount < This->bigBlockDepotCount);
3032 assert(blockIndex != nextBlock);
3034 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3036 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3038 else
3041 * We have to look in the extended depot.
3043 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3046 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3047 nextBlock);
3049 * Update the cached block depot, if necessary.
3051 if (depotBlockCount == This->indexBlockDepotCached)
3053 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3057 /******************************************************************************
3058 * Storage32Impl_LoadFileHeader
3060 * This method will read in the file header, i.e. big block index -1.
3062 static HRESULT StorageImpl_LoadFileHeader(
3063 StorageImpl* This)
3065 HRESULT hr = STG_E_FILENOTFOUND;
3066 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3067 BOOL success;
3068 int index;
3070 TRACE("\n");
3072 * Get a pointer to the big block of data containing the header.
3074 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3077 * Extract the information from the header.
3079 if (success)
3082 * Check for the "magic number" signature and return an error if it is not
3083 * found.
3085 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3087 return STG_E_OLDFORMAT;
3090 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3092 return STG_E_INVALIDHEADER;
3095 StorageUtl_ReadWord(
3096 headerBigBlock,
3097 OFFSET_BIGBLOCKSIZEBITS,
3098 &This->bigBlockSizeBits);
3100 StorageUtl_ReadWord(
3101 headerBigBlock,
3102 OFFSET_SMALLBLOCKSIZEBITS,
3103 &This->smallBlockSizeBits);
3105 StorageUtl_ReadDWord(
3106 headerBigBlock,
3107 OFFSET_BBDEPOTCOUNT,
3108 &This->bigBlockDepotCount);
3110 StorageUtl_ReadDWord(
3111 headerBigBlock,
3112 OFFSET_ROOTSTARTBLOCK,
3113 &This->rootStartBlock);
3115 StorageUtl_ReadDWord(
3116 headerBigBlock,
3117 OFFSET_SBDEPOTSTART,
3118 &This->smallBlockDepotStart);
3120 StorageUtl_ReadDWord(
3121 headerBigBlock,
3122 OFFSET_EXTBBDEPOTSTART,
3123 &This->extBigBlockDepotStart);
3125 StorageUtl_ReadDWord(
3126 headerBigBlock,
3127 OFFSET_EXTBBDEPOTCOUNT,
3128 &This->extBigBlockDepotCount);
3130 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3132 StorageUtl_ReadDWord(
3133 headerBigBlock,
3134 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3135 &(This->bigBlockDepotStart[index]));
3139 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3141 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3142 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3145 * Right now, the code is making some assumptions about the size of the
3146 * blocks, just make sure they are what we're expecting.
3148 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3149 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3151 WARN("Broken OLE storage file\n");
3152 hr = STG_E_INVALIDHEADER;
3154 else
3155 hr = S_OK;
3158 return hr;
3161 /******************************************************************************
3162 * Storage32Impl_SaveFileHeader
3164 * This method will save to the file the header, i.e. big block -1.
3166 static void StorageImpl_SaveFileHeader(
3167 StorageImpl* This)
3169 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3170 int index;
3171 BOOL success;
3174 * Get a pointer to the big block of data containing the header.
3176 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3179 * If the block read failed, the file is probably new.
3181 if (!success)
3184 * Initialize for all unknown fields.
3186 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3189 * Initialize the magic number.
3191 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3194 * And a bunch of things we don't know what they mean
3196 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3197 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3198 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3199 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3203 * Write the information to the header.
3205 StorageUtl_WriteWord(
3206 headerBigBlock,
3207 OFFSET_BIGBLOCKSIZEBITS,
3208 This->bigBlockSizeBits);
3210 StorageUtl_WriteWord(
3211 headerBigBlock,
3212 OFFSET_SMALLBLOCKSIZEBITS,
3213 This->smallBlockSizeBits);
3215 StorageUtl_WriteDWord(
3216 headerBigBlock,
3217 OFFSET_BBDEPOTCOUNT,
3218 This->bigBlockDepotCount);
3220 StorageUtl_WriteDWord(
3221 headerBigBlock,
3222 OFFSET_ROOTSTARTBLOCK,
3223 This->rootStartBlock);
3225 StorageUtl_WriteDWord(
3226 headerBigBlock,
3227 OFFSET_SBDEPOTSTART,
3228 This->smallBlockDepotStart);
3230 StorageUtl_WriteDWord(
3231 headerBigBlock,
3232 OFFSET_SBDEPOTCOUNT,
3233 This->smallBlockDepotChain ?
3234 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3236 StorageUtl_WriteDWord(
3237 headerBigBlock,
3238 OFFSET_EXTBBDEPOTSTART,
3239 This->extBigBlockDepotStart);
3241 StorageUtl_WriteDWord(
3242 headerBigBlock,
3243 OFFSET_EXTBBDEPOTCOUNT,
3244 This->extBigBlockDepotCount);
3246 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3248 StorageUtl_WriteDWord(
3249 headerBigBlock,
3250 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3251 (This->bigBlockDepotStart[index]));
3255 * Write the big block back to the file.
3257 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3260 /******************************************************************************
3261 * Storage32Impl_ReadProperty
3263 * This method will read the specified property from the property chain.
3265 BOOL StorageImpl_ReadProperty(
3266 StorageImpl* This,
3267 ULONG index,
3268 StgProperty* buffer)
3270 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3271 ULARGE_INTEGER offsetInPropSet;
3272 HRESULT readRes;
3273 ULONG bytesRead;
3275 offsetInPropSet.u.HighPart = 0;
3276 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3278 readRes = BlockChainStream_ReadAt(
3279 This->rootBlockChain,
3280 offsetInPropSet,
3281 PROPSET_BLOCK_SIZE,
3282 currentProperty,
3283 &bytesRead);
3285 if (SUCCEEDED(readRes))
3287 /* replace the name of root entry (often "Root Entry") by the file name */
3288 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3289 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3291 memset(buffer->name, 0, sizeof(buffer->name));
3292 memcpy(
3293 buffer->name,
3294 propName,
3295 PROPERTY_NAME_BUFFER_LEN );
3296 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3298 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3300 StorageUtl_ReadWord(
3301 currentProperty,
3302 OFFSET_PS_NAMELENGTH,
3303 &buffer->sizeOfNameString);
3305 StorageUtl_ReadDWord(
3306 currentProperty,
3307 OFFSET_PS_PREVIOUSPROP,
3308 &buffer->previousProperty);
3310 StorageUtl_ReadDWord(
3311 currentProperty,
3312 OFFSET_PS_NEXTPROP,
3313 &buffer->nextProperty);
3315 StorageUtl_ReadDWord(
3316 currentProperty,
3317 OFFSET_PS_DIRPROP,
3318 &buffer->dirProperty);
3320 StorageUtl_ReadGUID(
3321 currentProperty,
3322 OFFSET_PS_GUID,
3323 &buffer->propertyUniqueID);
3325 StorageUtl_ReadDWord(
3326 currentProperty,
3327 OFFSET_PS_TSS1,
3328 &buffer->timeStampS1);
3330 StorageUtl_ReadDWord(
3331 currentProperty,
3332 OFFSET_PS_TSD1,
3333 &buffer->timeStampD1);
3335 StorageUtl_ReadDWord(
3336 currentProperty,
3337 OFFSET_PS_TSS2,
3338 &buffer->timeStampS2);
3340 StorageUtl_ReadDWord(
3341 currentProperty,
3342 OFFSET_PS_TSD2,
3343 &buffer->timeStampD2);
3345 StorageUtl_ReadDWord(
3346 currentProperty,
3347 OFFSET_PS_STARTBLOCK,
3348 &buffer->startingBlock);
3350 StorageUtl_ReadDWord(
3351 currentProperty,
3352 OFFSET_PS_SIZE,
3353 &buffer->size.u.LowPart);
3355 buffer->size.u.HighPart = 0;
3358 return SUCCEEDED(readRes) ? TRUE : FALSE;
3361 /*********************************************************************
3362 * Write the specified property into the property chain
3364 BOOL StorageImpl_WriteProperty(
3365 StorageImpl* This,
3366 ULONG index,
3367 const StgProperty* buffer)
3369 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3370 ULARGE_INTEGER offsetInPropSet;
3371 HRESULT writeRes;
3372 ULONG bytesWritten;
3374 offsetInPropSet.u.HighPart = 0;
3375 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3377 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3379 memcpy(
3380 currentProperty + OFFSET_PS_NAME,
3381 buffer->name,
3382 PROPERTY_NAME_BUFFER_LEN );
3384 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3386 StorageUtl_WriteWord(
3387 currentProperty,
3388 OFFSET_PS_NAMELENGTH,
3389 buffer->sizeOfNameString);
3391 StorageUtl_WriteDWord(
3392 currentProperty,
3393 OFFSET_PS_PREVIOUSPROP,
3394 buffer->previousProperty);
3396 StorageUtl_WriteDWord(
3397 currentProperty,
3398 OFFSET_PS_NEXTPROP,
3399 buffer->nextProperty);
3401 StorageUtl_WriteDWord(
3402 currentProperty,
3403 OFFSET_PS_DIRPROP,
3404 buffer->dirProperty);
3406 StorageUtl_WriteGUID(
3407 currentProperty,
3408 OFFSET_PS_GUID,
3409 &buffer->propertyUniqueID);
3411 StorageUtl_WriteDWord(
3412 currentProperty,
3413 OFFSET_PS_TSS1,
3414 buffer->timeStampS1);
3416 StorageUtl_WriteDWord(
3417 currentProperty,
3418 OFFSET_PS_TSD1,
3419 buffer->timeStampD1);
3421 StorageUtl_WriteDWord(
3422 currentProperty,
3423 OFFSET_PS_TSS2,
3424 buffer->timeStampS2);
3426 StorageUtl_WriteDWord(
3427 currentProperty,
3428 OFFSET_PS_TSD2,
3429 buffer->timeStampD2);
3431 StorageUtl_WriteDWord(
3432 currentProperty,
3433 OFFSET_PS_STARTBLOCK,
3434 buffer->startingBlock);
3436 StorageUtl_WriteDWord(
3437 currentProperty,
3438 OFFSET_PS_SIZE,
3439 buffer->size.u.LowPart);
3441 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3442 offsetInPropSet,
3443 PROPSET_BLOCK_SIZE,
3444 currentProperty,
3445 &bytesWritten);
3446 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3449 static BOOL StorageImpl_ReadBigBlock(
3450 StorageImpl* This,
3451 ULONG blockIndex,
3452 void* buffer)
3454 ULARGE_INTEGER ulOffset;
3455 DWORD read;
3457 ulOffset.u.HighPart = 0;
3458 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3460 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3461 return (read == This->bigBlockSize);
3464 static BOOL StorageImpl_ReadDWordFromBigBlock(
3465 StorageImpl* This,
3466 ULONG blockIndex,
3467 ULONG offset,
3468 DWORD* value)
3470 ULARGE_INTEGER ulOffset;
3471 DWORD read;
3472 DWORD tmp;
3474 ulOffset.u.HighPart = 0;
3475 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3476 ulOffset.u.LowPart += offset;
3478 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3479 *value = lendian32toh(tmp);
3480 return (read == sizeof(DWORD));
3483 static BOOL StorageImpl_WriteBigBlock(
3484 StorageImpl* This,
3485 ULONG blockIndex,
3486 const void* buffer)
3488 ULARGE_INTEGER ulOffset;
3489 DWORD wrote;
3491 ulOffset.u.HighPart = 0;
3492 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3494 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3495 return (wrote == This->bigBlockSize);
3498 static BOOL StorageImpl_WriteDWordToBigBlock(
3499 StorageImpl* This,
3500 ULONG blockIndex,
3501 ULONG offset,
3502 DWORD value)
3504 ULARGE_INTEGER ulOffset;
3505 DWORD wrote;
3507 ulOffset.u.HighPart = 0;
3508 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3509 ulOffset.u.LowPart += offset;
3511 value = htole32(value);
3512 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3513 return (wrote == sizeof(DWORD));
3516 /******************************************************************************
3517 * Storage32Impl_SmallBlocksToBigBlocks
3519 * This method will convert a small block chain to a big block chain.
3520 * The small block chain will be destroyed.
3522 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3523 StorageImpl* This,
3524 SmallBlockChainStream** ppsbChain)
3526 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3527 ULARGE_INTEGER size, offset;
3528 ULONG cbRead, cbWritten;
3529 ULARGE_INTEGER cbTotalRead;
3530 ULONG propertyIndex;
3531 HRESULT resWrite = S_OK;
3532 HRESULT resRead;
3533 StgProperty chainProperty;
3534 BYTE *buffer;
3535 BlockChainStream *bbTempChain = NULL;
3536 BlockChainStream *bigBlockChain = NULL;
3539 * Create a temporary big block chain that doesn't have
3540 * an associated property. This temporary chain will be
3541 * used to copy data from small blocks to big blocks.
3543 bbTempChain = BlockChainStream_Construct(This,
3544 &bbHeadOfChain,
3545 PROPERTY_NULL);
3546 if(!bbTempChain) return NULL;
3548 * Grow the big block chain.
3550 size = SmallBlockChainStream_GetSize(*ppsbChain);
3551 BlockChainStream_SetSize(bbTempChain, size);
3554 * Copy the contents of the small block chain to the big block chain
3555 * by small block size increments.
3557 offset.u.LowPart = 0;
3558 offset.u.HighPart = 0;
3559 cbTotalRead.QuadPart = 0;
3561 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3564 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3565 offset,
3566 This->smallBlockSize,
3567 buffer,
3568 &cbRead);
3569 if (FAILED(resRead))
3570 break;
3572 if (cbRead > 0)
3574 cbTotalRead.QuadPart += cbRead;
3576 resWrite = BlockChainStream_WriteAt(bbTempChain,
3577 offset,
3578 cbRead,
3579 buffer,
3580 &cbWritten);
3582 if (FAILED(resWrite))
3583 break;
3585 offset.u.LowPart += This->smallBlockSize;
3587 } while (cbTotalRead.QuadPart < size.QuadPart);
3588 HeapFree(GetProcessHeap(),0,buffer);
3590 size.u.HighPart = 0;
3591 size.u.LowPart = 0;
3593 if (FAILED(resRead) || FAILED(resWrite))
3595 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3596 BlockChainStream_SetSize(bbTempChain, size);
3597 BlockChainStream_Destroy(bbTempChain);
3598 return NULL;
3602 * Destroy the small block chain.
3604 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3605 SmallBlockChainStream_SetSize(*ppsbChain, size);
3606 SmallBlockChainStream_Destroy(*ppsbChain);
3607 *ppsbChain = 0;
3610 * Change the property information. This chain is now a big block chain
3611 * and it doesn't reside in the small blocks chain anymore.
3613 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3615 chainProperty.startingBlock = bbHeadOfChain;
3617 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3620 * Destroy the temporary propertyless big block chain.
3621 * Create a new big block chain associated with this property.
3623 BlockChainStream_Destroy(bbTempChain);
3624 bigBlockChain = BlockChainStream_Construct(This,
3625 NULL,
3626 propertyIndex);
3628 return bigBlockChain;
3631 /******************************************************************************
3632 * Storage32Impl_BigBlocksToSmallBlocks
3634 * This method will convert a big block chain to a small block chain.
3635 * The big block chain will be destroyed on success.
3637 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3638 StorageImpl* This,
3639 BlockChainStream** ppbbChain)
3641 ULARGE_INTEGER size, offset, cbTotalRead;
3642 ULONG cbRead, cbWritten, propertyIndex, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3643 HRESULT resWrite = S_OK, resRead;
3644 StgProperty chainProperty;
3645 BYTE* buffer;
3646 SmallBlockChainStream* sbTempChain;
3648 TRACE("%p %p\n", This, ppbbChain);
3650 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3651 PROPERTY_NULL);
3653 if(!sbTempChain)
3654 return NULL;
3656 size = BlockChainStream_GetSize(*ppbbChain);
3657 SmallBlockChainStream_SetSize(sbTempChain, size);
3659 offset.u.HighPart = 0;
3660 offset.u.LowPart = 0;
3661 cbTotalRead.QuadPart = 0;
3662 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3665 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3666 This->bigBlockSize, buffer, &cbRead);
3668 if(FAILED(resRead))
3669 break;
3671 if(cbRead > 0)
3673 cbTotalRead.QuadPart += cbRead;
3675 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3676 cbRead, buffer, &cbWritten);
3678 if(FAILED(resWrite))
3679 break;
3681 offset.u.LowPart += This->bigBlockSize;
3683 }while(cbTotalRead.QuadPart < size.QuadPart);
3684 HeapFree(GetProcessHeap(), 0, buffer);
3686 size.u.HighPart = 0;
3687 size.u.LowPart = 0;
3689 if(FAILED(resRead) || FAILED(resWrite))
3691 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3692 SmallBlockChainStream_SetSize(sbTempChain, size);
3693 SmallBlockChainStream_Destroy(sbTempChain);
3694 return NULL;
3697 /* destroy the original big block chain */
3698 propertyIndex = (*ppbbChain)->ownerPropertyIndex;
3699 BlockChainStream_SetSize(*ppbbChain, size);
3700 BlockChainStream_Destroy(*ppbbChain);
3701 *ppbbChain = NULL;
3703 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3704 chainProperty.startingBlock = sbHeadOfChain;
3705 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3707 SmallBlockChainStream_Destroy(sbTempChain);
3708 return SmallBlockChainStream_Construct(This, NULL, propertyIndex);
3711 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3713 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3715 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3716 HeapFree(GetProcessHeap(), 0, This);
3719 /******************************************************************************
3721 ** Storage32InternalImpl_Commit
3723 ** The non-root storages cannot be opened in transacted mode thus this function
3724 ** does nothing.
3726 static HRESULT WINAPI StorageInternalImpl_Commit(
3727 IStorage* iface,
3728 DWORD grfCommitFlags) /* [in] */
3730 return S_OK;
3733 /******************************************************************************
3735 ** Storage32InternalImpl_Revert
3737 ** The non-root storages cannot be opened in transacted mode thus this function
3738 ** does nothing.
3740 static HRESULT WINAPI StorageInternalImpl_Revert(
3741 IStorage* iface)
3743 return S_OK;
3746 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3748 IStorage_Release((IStorage*)This->parentStorage);
3749 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3750 HeapFree(GetProcessHeap(), 0, This);
3753 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3754 IEnumSTATSTG* iface,
3755 REFIID riid,
3756 void** ppvObject)
3758 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3761 * Perform a sanity check on the parameters.
3763 if (ppvObject==0)
3764 return E_INVALIDARG;
3767 * Initialize the return parameter.
3769 *ppvObject = 0;
3772 * Compare the riid with the interface IDs implemented by this object.
3774 if (IsEqualGUID(&IID_IUnknown, riid) ||
3775 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3777 *ppvObject = This;
3778 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3779 return S_OK;
3782 return E_NOINTERFACE;
3785 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3786 IEnumSTATSTG* iface)
3788 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3789 return InterlockedIncrement(&This->ref);
3792 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3793 IEnumSTATSTG* iface)
3795 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3797 ULONG newRef;
3799 newRef = InterlockedDecrement(&This->ref);
3802 * If the reference count goes down to 0, perform suicide.
3804 if (newRef==0)
3806 IEnumSTATSTGImpl_Destroy(This);
3809 return newRef;
3812 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3813 IEnumSTATSTG* iface,
3814 ULONG celt,
3815 STATSTG* rgelt,
3816 ULONG* pceltFetched)
3818 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3820 StgProperty currentProperty;
3821 STATSTG* currentReturnStruct = rgelt;
3822 ULONG objectFetched = 0;
3823 ULONG currentSearchNode;
3826 * Perform a sanity check on the parameters.
3828 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3829 return E_INVALIDARG;
3832 * To avoid the special case, get another pointer to a ULONG value if
3833 * the caller didn't supply one.
3835 if (pceltFetched==0)
3836 pceltFetched = &objectFetched;
3839 * Start the iteration, we will iterate until we hit the end of the
3840 * linked list or until we hit the number of items to iterate through
3842 *pceltFetched = 0;
3845 * Start with the node at the top of the stack.
3847 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3849 while ( ( *pceltFetched < celt) &&
3850 ( currentSearchNode!=PROPERTY_NULL) )
3853 * Remove the top node from the stack
3855 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3858 * Read the property from the storage.
3860 StorageImpl_ReadProperty(This->parentStorage,
3861 currentSearchNode,
3862 &currentProperty);
3865 * Copy the information to the return buffer.
3867 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3868 &currentProperty,
3869 STATFLAG_DEFAULT);
3872 * Step to the next item in the iteration
3874 (*pceltFetched)++;
3875 currentReturnStruct++;
3878 * Push the next search node in the search stack.
3880 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3883 * continue the iteration.
3885 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3888 if (*pceltFetched == celt)
3889 return S_OK;
3891 return S_FALSE;
3895 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3896 IEnumSTATSTG* iface,
3897 ULONG celt)
3899 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3901 StgProperty currentProperty;
3902 ULONG objectFetched = 0;
3903 ULONG currentSearchNode;
3906 * Start with the node at the top of the stack.
3908 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3910 while ( (objectFetched < celt) &&
3911 (currentSearchNode!=PROPERTY_NULL) )
3914 * Remove the top node from the stack
3916 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3919 * Read the property from the storage.
3921 StorageImpl_ReadProperty(This->parentStorage,
3922 currentSearchNode,
3923 &currentProperty);
3926 * Step to the next item in the iteration
3928 objectFetched++;
3931 * Push the next search node in the search stack.
3933 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3936 * continue the iteration.
3938 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3941 if (objectFetched == celt)
3942 return S_OK;
3944 return S_FALSE;
3947 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3948 IEnumSTATSTG* iface)
3950 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3952 StgProperty rootProperty;
3953 BOOL readSuccessful;
3956 * Re-initialize the search stack to an empty stack
3958 This->stackSize = 0;
3961 * Read the root property from the storage.
3963 readSuccessful = StorageImpl_ReadProperty(
3964 This->parentStorage,
3965 This->firstPropertyNode,
3966 &rootProperty);
3968 if (readSuccessful)
3970 assert(rootProperty.sizeOfNameString!=0);
3973 * Push the search node in the search stack.
3975 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3978 return S_OK;
3981 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3982 IEnumSTATSTG* iface,
3983 IEnumSTATSTG** ppenum)
3985 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3987 IEnumSTATSTGImpl* newClone;
3990 * Perform a sanity check on the parameters.
3992 if (ppenum==0)
3993 return E_INVALIDARG;
3995 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3996 This->firstPropertyNode);
4000 * The new clone enumeration must point to the same current node as
4001 * the ole one.
4003 newClone->stackSize = This->stackSize ;
4004 newClone->stackMaxSize = This->stackMaxSize ;
4005 newClone->stackToVisit =
4006 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
4008 memcpy(
4009 newClone->stackToVisit,
4010 This->stackToVisit,
4011 sizeof(ULONG) * newClone->stackSize);
4013 *ppenum = (IEnumSTATSTG*)newClone;
4016 * Don't forget to nail down a reference to the clone before
4017 * returning it.
4019 IEnumSTATSTGImpl_AddRef(*ppenum);
4021 return S_OK;
4024 static INT IEnumSTATSTGImpl_FindParentProperty(
4025 IEnumSTATSTGImpl *This,
4026 ULONG childProperty,
4027 StgProperty *currentProperty,
4028 ULONG *thisNodeId)
4030 ULONG currentSearchNode;
4031 ULONG foundNode;
4034 * To avoid the special case, get another pointer to a ULONG value if
4035 * the caller didn't supply one.
4037 if (thisNodeId==0)
4038 thisNodeId = &foundNode;
4041 * Start with the node at the top of the stack.
4043 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4046 while (currentSearchNode!=PROPERTY_NULL)
4049 * Store the current node in the returned parameters
4051 *thisNodeId = currentSearchNode;
4054 * Remove the top node from the stack
4056 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4059 * Read the property from the storage.
4061 StorageImpl_ReadProperty(
4062 This->parentStorage,
4063 currentSearchNode,
4064 currentProperty);
4066 if (currentProperty->previousProperty == childProperty)
4067 return PROPERTY_RELATION_PREVIOUS;
4069 else if (currentProperty->nextProperty == childProperty)
4070 return PROPERTY_RELATION_NEXT;
4072 else if (currentProperty->dirProperty == childProperty)
4073 return PROPERTY_RELATION_DIR;
4076 * Push the next search node in the search stack.
4078 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4081 * continue the iteration.
4083 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4086 return PROPERTY_NULL;
4089 static ULONG IEnumSTATSTGImpl_FindProperty(
4090 IEnumSTATSTGImpl* This,
4091 const OLECHAR* lpszPropName,
4092 StgProperty* currentProperty)
4094 ULONG currentSearchNode;
4097 * Start with the node at the top of the stack.
4099 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4101 while (currentSearchNode!=PROPERTY_NULL)
4104 * Remove the top node from the stack
4106 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4109 * Read the property from the storage.
4111 StorageImpl_ReadProperty(This->parentStorage,
4112 currentSearchNode,
4113 currentProperty);
4115 if (propertyNameCmp(currentProperty->name, lpszPropName) == 0)
4116 return currentSearchNode;
4119 * Push the next search node in the search stack.
4121 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4124 * continue the iteration.
4126 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4129 return PROPERTY_NULL;
4132 static void IEnumSTATSTGImpl_PushSearchNode(
4133 IEnumSTATSTGImpl* This,
4134 ULONG nodeToPush)
4136 StgProperty rootProperty;
4137 BOOL readSuccessful;
4140 * First, make sure we're not trying to push an unexisting node.
4142 if (nodeToPush==PROPERTY_NULL)
4143 return;
4146 * First push the node to the stack
4148 if (This->stackSize == This->stackMaxSize)
4150 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4152 This->stackToVisit = HeapReAlloc(
4153 GetProcessHeap(),
4155 This->stackToVisit,
4156 sizeof(ULONG) * This->stackMaxSize);
4159 This->stackToVisit[This->stackSize] = nodeToPush;
4160 This->stackSize++;
4163 * Read the root property from the storage.
4165 readSuccessful = StorageImpl_ReadProperty(
4166 This->parentStorage,
4167 nodeToPush,
4168 &rootProperty);
4170 if (readSuccessful)
4172 assert(rootProperty.sizeOfNameString!=0);
4175 * Push the previous search node in the search stack.
4177 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4181 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4182 IEnumSTATSTGImpl* This,
4183 BOOL remove)
4185 ULONG topNode;
4187 if (This->stackSize == 0)
4188 return PROPERTY_NULL;
4190 topNode = This->stackToVisit[This->stackSize-1];
4192 if (remove)
4193 This->stackSize--;
4195 return topNode;
4199 * Virtual function table for the IEnumSTATSTGImpl class.
4201 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4203 IEnumSTATSTGImpl_QueryInterface,
4204 IEnumSTATSTGImpl_AddRef,
4205 IEnumSTATSTGImpl_Release,
4206 IEnumSTATSTGImpl_Next,
4207 IEnumSTATSTGImpl_Skip,
4208 IEnumSTATSTGImpl_Reset,
4209 IEnumSTATSTGImpl_Clone
4212 /******************************************************************************
4213 ** IEnumSTATSTGImpl implementation
4216 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4217 StorageImpl* parentStorage,
4218 ULONG firstPropertyNode)
4220 IEnumSTATSTGImpl* newEnumeration;
4222 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4224 if (newEnumeration!=0)
4227 * Set-up the virtual function table and reference count.
4229 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4230 newEnumeration->ref = 0;
4233 * We want to nail-down the reference to the storage in case the
4234 * enumeration out-lives the storage in the client application.
4236 newEnumeration->parentStorage = parentStorage;
4237 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4239 newEnumeration->firstPropertyNode = firstPropertyNode;
4242 * Initialize the search stack
4244 newEnumeration->stackSize = 0;
4245 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4246 newEnumeration->stackToVisit =
4247 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4250 * Make sure the current node of the iterator is the first one.
4252 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4255 return newEnumeration;
4259 * Virtual function table for the Storage32InternalImpl class.
4261 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4263 StorageBaseImpl_QueryInterface,
4264 StorageBaseImpl_AddRef,
4265 StorageBaseImpl_Release,
4266 StorageBaseImpl_CreateStream,
4267 StorageBaseImpl_OpenStream,
4268 StorageImpl_CreateStorage,
4269 StorageBaseImpl_OpenStorage,
4270 StorageImpl_CopyTo,
4271 StorageImpl_MoveElementTo,
4272 StorageInternalImpl_Commit,
4273 StorageInternalImpl_Revert,
4274 StorageBaseImpl_EnumElements,
4275 StorageImpl_DestroyElement,
4276 StorageBaseImpl_RenameElement,
4277 StorageImpl_SetElementTimes,
4278 StorageBaseImpl_SetClass,
4279 StorageImpl_SetStateBits,
4280 StorageBaseImpl_Stat
4283 /******************************************************************************
4284 ** Storage32InternalImpl implementation
4287 static StorageInternalImpl* StorageInternalImpl_Construct(
4288 StorageImpl* ancestorStorage,
4289 DWORD openFlags,
4290 ULONG rootPropertyIndex)
4292 StorageInternalImpl* newStorage;
4295 * Allocate space for the new storage object
4297 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4299 if (newStorage!=0)
4302 * Initialize the stream list
4304 list_init(&newStorage->base.strmHead);
4307 * Initialize the virtual function table.
4309 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4310 newStorage->base.v_destructor = StorageInternalImpl_Destroy;
4311 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4314 * Keep the ancestor storage pointer and nail a reference to it.
4316 newStorage->base.ancestorStorage = ancestorStorage;
4317 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4320 * Keep the index of the root property set for this storage,
4322 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4324 return newStorage;
4327 return 0;
4330 /******************************************************************************
4331 ** StorageUtl implementation
4334 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4336 WORD tmp;
4338 memcpy(&tmp, buffer+offset, sizeof(WORD));
4339 *value = lendian16toh(tmp);
4342 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4344 value = htole16(value);
4345 memcpy(buffer+offset, &value, sizeof(WORD));
4348 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4350 DWORD tmp;
4352 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4353 *value = lendian32toh(tmp);
4356 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4358 value = htole32(value);
4359 memcpy(buffer+offset, &value, sizeof(DWORD));
4362 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4363 ULARGE_INTEGER* value)
4365 #ifdef WORDS_BIGENDIAN
4366 ULARGE_INTEGER tmp;
4368 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4369 value->u.LowPart = htole32(tmp.u.HighPart);
4370 value->u.HighPart = htole32(tmp.u.LowPart);
4371 #else
4372 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4373 #endif
4376 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4377 const ULARGE_INTEGER *value)
4379 #ifdef WORDS_BIGENDIAN
4380 ULARGE_INTEGER tmp;
4382 tmp.u.LowPart = htole32(value->u.HighPart);
4383 tmp.u.HighPart = htole32(value->u.LowPart);
4384 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4385 #else
4386 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4387 #endif
4390 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4392 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4393 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4394 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4396 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4399 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4401 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4402 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4403 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4405 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4408 void StorageUtl_CopyPropertyToSTATSTG(
4409 STATSTG* destination,
4410 const StgProperty* source,
4411 int statFlags)
4414 * The copy of the string occurs only when the flag is not set
4416 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4417 (source->name == NULL) ||
4418 (source->name[0] == 0) )
4420 destination->pwcsName = 0;
4422 else
4424 destination->pwcsName =
4425 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4427 strcpyW(destination->pwcsName, source->name);
4430 switch (source->propertyType)
4432 case PROPTYPE_STORAGE:
4433 case PROPTYPE_ROOT:
4434 destination->type = STGTY_STORAGE;
4435 break;
4436 case PROPTYPE_STREAM:
4437 destination->type = STGTY_STREAM;
4438 break;
4439 default:
4440 destination->type = STGTY_STREAM;
4441 break;
4444 destination->cbSize = source->size;
4446 currentReturnStruct->mtime = {0}; TODO
4447 currentReturnStruct->ctime = {0};
4448 currentReturnStruct->atime = {0};
4450 destination->grfMode = 0;
4451 destination->grfLocksSupported = 0;
4452 destination->clsid = source->propertyUniqueID;
4453 destination->grfStateBits = 0;
4454 destination->reserved = 0;
4457 /******************************************************************************
4458 ** BlockChainStream implementation
4461 BlockChainStream* BlockChainStream_Construct(
4462 StorageImpl* parentStorage,
4463 ULONG* headOfStreamPlaceHolder,
4464 ULONG propertyIndex)
4466 BlockChainStream* newStream;
4467 ULONG blockIndex;
4469 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4471 newStream->parentStorage = parentStorage;
4472 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4473 newStream->ownerPropertyIndex = propertyIndex;
4474 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4475 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4476 newStream->numBlocks = 0;
4478 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4480 while (blockIndex != BLOCK_END_OF_CHAIN)
4482 newStream->numBlocks++;
4483 newStream->tailIndex = blockIndex;
4485 if(FAILED(StorageImpl_GetNextBlockInChain(
4486 parentStorage,
4487 blockIndex,
4488 &blockIndex)))
4490 HeapFree(GetProcessHeap(), 0, newStream);
4491 return NULL;
4495 return newStream;
4498 void BlockChainStream_Destroy(BlockChainStream* This)
4500 HeapFree(GetProcessHeap(), 0, This);
4503 /******************************************************************************
4504 * BlockChainStream_GetHeadOfChain
4506 * Returns the head of this stream chain.
4507 * Some special chains don't have properties, their heads are kept in
4508 * This->headOfStreamPlaceHolder.
4511 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4513 StgProperty chainProperty;
4514 BOOL readSuccessful;
4516 if (This->headOfStreamPlaceHolder != 0)
4517 return *(This->headOfStreamPlaceHolder);
4519 if (This->ownerPropertyIndex != PROPERTY_NULL)
4521 readSuccessful = StorageImpl_ReadProperty(
4522 This->parentStorage,
4523 This->ownerPropertyIndex,
4524 &chainProperty);
4526 if (readSuccessful)
4528 return chainProperty.startingBlock;
4532 return BLOCK_END_OF_CHAIN;
4535 /******************************************************************************
4536 * BlockChainStream_GetCount
4538 * Returns the number of blocks that comprises this chain.
4539 * This is not the size of the stream as the last block may not be full!
4542 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4544 ULONG blockIndex;
4545 ULONG count = 0;
4547 blockIndex = BlockChainStream_GetHeadOfChain(This);
4549 while (blockIndex != BLOCK_END_OF_CHAIN)
4551 count++;
4553 if(FAILED(StorageImpl_GetNextBlockInChain(
4554 This->parentStorage,
4555 blockIndex,
4556 &blockIndex)))
4557 return 0;
4560 return count;
4563 /******************************************************************************
4564 * BlockChainStream_ReadAt
4566 * Reads a specified number of bytes from this chain at the specified offset.
4567 * bytesRead may be NULL.
4568 * Failure will be returned if the specified number of bytes has not been read.
4570 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4571 ULARGE_INTEGER offset,
4572 ULONG size,
4573 void* buffer,
4574 ULONG* bytesRead)
4576 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4577 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4578 ULONG bytesToReadInBuffer;
4579 ULONG blockIndex;
4580 BYTE* bufferWalker;
4582 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4585 * Find the first block in the stream that contains part of the buffer.
4587 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4588 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4589 (blockNoInSequence < This->lastBlockNoInSequence) )
4591 blockIndex = BlockChainStream_GetHeadOfChain(This);
4592 This->lastBlockNoInSequence = blockNoInSequence;
4594 else
4596 ULONG temp = blockNoInSequence;
4598 blockIndex = This->lastBlockNoInSequenceIndex;
4599 blockNoInSequence -= This->lastBlockNoInSequence;
4600 This->lastBlockNoInSequence = temp;
4603 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4605 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4606 return STG_E_DOCFILECORRUPT;
4607 blockNoInSequence--;
4610 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4611 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4613 This->lastBlockNoInSequenceIndex = blockIndex;
4616 * Start reading the buffer.
4618 *bytesRead = 0;
4619 bufferWalker = buffer;
4621 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4623 ULARGE_INTEGER ulOffset;
4624 DWORD bytesReadAt;
4626 * Calculate how many bytes we can copy from this big block.
4628 bytesToReadInBuffer =
4629 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4631 TRACE("block %i\n",blockIndex);
4632 ulOffset.u.HighPart = 0;
4633 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4634 offsetInBlock;
4636 StorageImpl_ReadAt(This->parentStorage,
4637 ulOffset,
4638 bufferWalker,
4639 bytesToReadInBuffer,
4640 &bytesReadAt);
4642 * Step to the next big block.
4644 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4645 return STG_E_DOCFILECORRUPT;
4647 bufferWalker += bytesReadAt;
4648 size -= bytesReadAt;
4649 *bytesRead += bytesReadAt;
4650 offsetInBlock = 0; /* There is no offset on the next block */
4652 if (bytesToReadInBuffer != bytesReadAt)
4653 break;
4656 return (size == 0) ? S_OK : STG_E_READFAULT;
4659 /******************************************************************************
4660 * BlockChainStream_WriteAt
4662 * Writes the specified number of bytes to this chain at the specified offset.
4663 * Will fail if not all specified number of bytes have been written.
4665 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4666 ULARGE_INTEGER offset,
4667 ULONG size,
4668 const void* buffer,
4669 ULONG* bytesWritten)
4671 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4672 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4673 ULONG bytesToWrite;
4674 ULONG blockIndex;
4675 const BYTE* bufferWalker;
4678 * Find the first block in the stream that contains part of the buffer.
4680 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4681 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4682 (blockNoInSequence < This->lastBlockNoInSequence) )
4684 blockIndex = BlockChainStream_GetHeadOfChain(This);
4685 This->lastBlockNoInSequence = blockNoInSequence;
4687 else
4689 ULONG temp = blockNoInSequence;
4691 blockIndex = This->lastBlockNoInSequenceIndex;
4692 blockNoInSequence -= This->lastBlockNoInSequence;
4693 This->lastBlockNoInSequence = temp;
4696 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4698 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4699 &blockIndex)))
4700 return STG_E_DOCFILECORRUPT;
4701 blockNoInSequence--;
4704 This->lastBlockNoInSequenceIndex = blockIndex;
4706 /* BlockChainStream_SetSize should have already been called to ensure we have
4707 * enough blocks in the chain to write into */
4708 if (blockIndex == BLOCK_END_OF_CHAIN)
4710 ERR("not enough blocks in chain to write data\n");
4711 return STG_E_DOCFILECORRUPT;
4714 *bytesWritten = 0;
4715 bufferWalker = buffer;
4717 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4719 ULARGE_INTEGER ulOffset;
4720 DWORD bytesWrittenAt;
4722 * Calculate how many bytes we can copy from this big block.
4724 bytesToWrite =
4725 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4727 TRACE("block %i\n",blockIndex);
4728 ulOffset.u.HighPart = 0;
4729 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4730 offsetInBlock;
4732 StorageImpl_WriteAt(This->parentStorage,
4733 ulOffset,
4734 bufferWalker,
4735 bytesToWrite,
4736 &bytesWrittenAt);
4739 * Step to the next big block.
4741 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4742 &blockIndex)))
4743 return STG_E_DOCFILECORRUPT;
4745 bufferWalker += bytesWrittenAt;
4746 size -= bytesWrittenAt;
4747 *bytesWritten += bytesWrittenAt;
4748 offsetInBlock = 0; /* There is no offset on the next block */
4750 if (bytesWrittenAt != bytesToWrite)
4751 break;
4754 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4757 /******************************************************************************
4758 * BlockChainStream_Shrink
4760 * Shrinks this chain in the big block depot.
4762 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4763 ULARGE_INTEGER newSize)
4765 ULONG blockIndex, extraBlock;
4766 ULONG numBlocks;
4767 ULONG count = 1;
4770 * Reset the last accessed block cache.
4772 This->lastBlockNoInSequence = 0xFFFFFFFF;
4773 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4776 * Figure out how many blocks are needed to contain the new size
4778 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4780 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4781 numBlocks++;
4783 blockIndex = BlockChainStream_GetHeadOfChain(This);
4786 * Go to the new end of chain
4788 while (count < numBlocks)
4790 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4791 &blockIndex)))
4792 return FALSE;
4793 count++;
4796 /* Get the next block before marking the new end */
4797 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4798 &extraBlock)))
4799 return FALSE;
4801 /* Mark the new end of chain */
4802 StorageImpl_SetNextBlockInChain(
4803 This->parentStorage,
4804 blockIndex,
4805 BLOCK_END_OF_CHAIN);
4807 This->tailIndex = blockIndex;
4808 This->numBlocks = numBlocks;
4811 * Mark the extra blocks as free
4813 while (extraBlock != BLOCK_END_OF_CHAIN)
4815 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4816 &blockIndex)))
4817 return FALSE;
4818 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4819 extraBlock = blockIndex;
4822 return TRUE;
4825 /******************************************************************************
4826 * BlockChainStream_Enlarge
4828 * Grows this chain in the big block depot.
4830 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4831 ULARGE_INTEGER newSize)
4833 ULONG blockIndex, currentBlock;
4834 ULONG newNumBlocks;
4835 ULONG oldNumBlocks = 0;
4837 blockIndex = BlockChainStream_GetHeadOfChain(This);
4840 * Empty chain. Create the head.
4842 if (blockIndex == BLOCK_END_OF_CHAIN)
4844 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4845 StorageImpl_SetNextBlockInChain(This->parentStorage,
4846 blockIndex,
4847 BLOCK_END_OF_CHAIN);
4849 if (This->headOfStreamPlaceHolder != 0)
4851 *(This->headOfStreamPlaceHolder) = blockIndex;
4853 else
4855 StgProperty chainProp;
4856 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4858 StorageImpl_ReadProperty(
4859 This->parentStorage,
4860 This->ownerPropertyIndex,
4861 &chainProp);
4863 chainProp.startingBlock = blockIndex;
4865 StorageImpl_WriteProperty(
4866 This->parentStorage,
4867 This->ownerPropertyIndex,
4868 &chainProp);
4871 This->tailIndex = blockIndex;
4872 This->numBlocks = 1;
4876 * Figure out how many blocks are needed to contain this stream
4878 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4880 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4881 newNumBlocks++;
4884 * Go to the current end of chain
4886 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4888 currentBlock = blockIndex;
4890 while (blockIndex != BLOCK_END_OF_CHAIN)
4892 This->numBlocks++;
4893 currentBlock = blockIndex;
4895 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4896 &blockIndex)))
4897 return FALSE;
4900 This->tailIndex = currentBlock;
4903 currentBlock = This->tailIndex;
4904 oldNumBlocks = This->numBlocks;
4907 * Add new blocks to the chain
4909 if (oldNumBlocks < newNumBlocks)
4911 while (oldNumBlocks < newNumBlocks)
4913 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4915 StorageImpl_SetNextBlockInChain(
4916 This->parentStorage,
4917 currentBlock,
4918 blockIndex);
4920 StorageImpl_SetNextBlockInChain(
4921 This->parentStorage,
4922 blockIndex,
4923 BLOCK_END_OF_CHAIN);
4925 currentBlock = blockIndex;
4926 oldNumBlocks++;
4929 This->tailIndex = blockIndex;
4930 This->numBlocks = newNumBlocks;
4933 return TRUE;
4936 /******************************************************************************
4937 * BlockChainStream_SetSize
4939 * Sets the size of this stream. The big block depot will be updated.
4940 * The file will grow if we grow the chain.
4942 * TODO: Free the actual blocks in the file when we shrink the chain.
4943 * Currently, the blocks are still in the file. So the file size
4944 * doesn't shrink even if we shrink streams.
4946 BOOL BlockChainStream_SetSize(
4947 BlockChainStream* This,
4948 ULARGE_INTEGER newSize)
4950 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4952 if (newSize.u.LowPart == size.u.LowPart)
4953 return TRUE;
4955 if (newSize.u.LowPart < size.u.LowPart)
4957 BlockChainStream_Shrink(This, newSize);
4959 else
4961 BlockChainStream_Enlarge(This, newSize);
4964 return TRUE;
4967 /******************************************************************************
4968 * BlockChainStream_GetSize
4970 * Returns the size of this chain.
4971 * Will return the block count if this chain doesn't have a property.
4973 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4975 StgProperty chainProperty;
4977 if(This->headOfStreamPlaceHolder == NULL)
4980 * This chain is a data stream read the property and return
4981 * the appropriate size
4983 StorageImpl_ReadProperty(
4984 This->parentStorage,
4985 This->ownerPropertyIndex,
4986 &chainProperty);
4988 return chainProperty.size;
4990 else
4993 * this chain is a chain that does not have a property, figure out the
4994 * size by making the product number of used blocks times the
4995 * size of them
4997 ULARGE_INTEGER result;
4998 result.u.HighPart = 0;
5000 result.u.LowPart =
5001 BlockChainStream_GetCount(This) *
5002 This->parentStorage->bigBlockSize;
5004 return result;
5008 /******************************************************************************
5009 ** SmallBlockChainStream implementation
5012 SmallBlockChainStream* SmallBlockChainStream_Construct(
5013 StorageImpl* parentStorage,
5014 ULONG* headOfStreamPlaceHolder,
5015 ULONG propertyIndex)
5017 SmallBlockChainStream* newStream;
5019 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
5021 newStream->parentStorage = parentStorage;
5022 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5023 newStream->ownerPropertyIndex = propertyIndex;
5025 return newStream;
5028 void SmallBlockChainStream_Destroy(
5029 SmallBlockChainStream* This)
5031 HeapFree(GetProcessHeap(), 0, This);
5034 /******************************************************************************
5035 * SmallBlockChainStream_GetHeadOfChain
5037 * Returns the head of this chain of small blocks.
5039 static ULONG SmallBlockChainStream_GetHeadOfChain(
5040 SmallBlockChainStream* This)
5042 StgProperty chainProperty;
5043 BOOL readSuccessful;
5045 if (This->headOfStreamPlaceHolder != NULL)
5046 return *(This->headOfStreamPlaceHolder);
5048 if (This->ownerPropertyIndex)
5050 readSuccessful = StorageImpl_ReadProperty(
5051 This->parentStorage,
5052 This->ownerPropertyIndex,
5053 &chainProperty);
5055 if (readSuccessful)
5057 return chainProperty.startingBlock;
5062 return BLOCK_END_OF_CHAIN;
5065 /******************************************************************************
5066 * SmallBlockChainStream_GetNextBlockInChain
5068 * Returns the index of the next small block in this chain.
5070 * Return Values:
5071 * - BLOCK_END_OF_CHAIN: end of this chain
5072 * - BLOCK_UNUSED: small block 'blockIndex' is free
5074 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5075 SmallBlockChainStream* This,
5076 ULONG blockIndex,
5077 ULONG* nextBlockInChain)
5079 ULARGE_INTEGER offsetOfBlockInDepot;
5080 DWORD buffer;
5081 ULONG bytesRead;
5082 HRESULT res;
5084 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5086 offsetOfBlockInDepot.u.HighPart = 0;
5087 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5090 * Read those bytes in the buffer from the small block file.
5092 res = BlockChainStream_ReadAt(
5093 This->parentStorage->smallBlockDepotChain,
5094 offsetOfBlockInDepot,
5095 sizeof(DWORD),
5096 &buffer,
5097 &bytesRead);
5099 if (SUCCEEDED(res))
5101 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5102 return S_OK;
5105 return res;
5108 /******************************************************************************
5109 * SmallBlockChainStream_SetNextBlockInChain
5111 * Writes the index of the next block of the specified block in the small
5112 * block depot.
5113 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5114 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5116 static void SmallBlockChainStream_SetNextBlockInChain(
5117 SmallBlockChainStream* This,
5118 ULONG blockIndex,
5119 ULONG nextBlock)
5121 ULARGE_INTEGER offsetOfBlockInDepot;
5122 DWORD buffer;
5123 ULONG bytesWritten;
5125 offsetOfBlockInDepot.u.HighPart = 0;
5126 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5128 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5131 * Read those bytes in the buffer from the small block file.
5133 BlockChainStream_WriteAt(
5134 This->parentStorage->smallBlockDepotChain,
5135 offsetOfBlockInDepot,
5136 sizeof(DWORD),
5137 &buffer,
5138 &bytesWritten);
5141 /******************************************************************************
5142 * SmallBlockChainStream_FreeBlock
5144 * Flag small block 'blockIndex' as free in the small block depot.
5146 static void SmallBlockChainStream_FreeBlock(
5147 SmallBlockChainStream* This,
5148 ULONG blockIndex)
5150 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5153 /******************************************************************************
5154 * SmallBlockChainStream_GetNextFreeBlock
5156 * Returns the index of a free small block. The small block depot will be
5157 * enlarged if necessary. The small block chain will also be enlarged if
5158 * necessary.
5160 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5161 SmallBlockChainStream* This)
5163 ULARGE_INTEGER offsetOfBlockInDepot;
5164 DWORD buffer;
5165 ULONG bytesRead;
5166 ULONG blockIndex = 0;
5167 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5168 HRESULT res = S_OK;
5169 ULONG smallBlocksPerBigBlock;
5171 offsetOfBlockInDepot.u.HighPart = 0;
5174 * Scan the small block depot for a free block
5176 while (nextBlockIndex != BLOCK_UNUSED)
5178 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5180 res = BlockChainStream_ReadAt(
5181 This->parentStorage->smallBlockDepotChain,
5182 offsetOfBlockInDepot,
5183 sizeof(DWORD),
5184 &buffer,
5185 &bytesRead);
5188 * If we run out of space for the small block depot, enlarge it
5190 if (SUCCEEDED(res))
5192 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5194 if (nextBlockIndex != BLOCK_UNUSED)
5195 blockIndex++;
5197 else
5199 ULONG count =
5200 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5202 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5203 ULONG nextBlock, newsbdIndex;
5204 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5206 nextBlock = sbdIndex;
5207 while (nextBlock != BLOCK_END_OF_CHAIN)
5209 sbdIndex = nextBlock;
5210 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5213 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5214 if (sbdIndex != BLOCK_END_OF_CHAIN)
5215 StorageImpl_SetNextBlockInChain(
5216 This->parentStorage,
5217 sbdIndex,
5218 newsbdIndex);
5220 StorageImpl_SetNextBlockInChain(
5221 This->parentStorage,
5222 newsbdIndex,
5223 BLOCK_END_OF_CHAIN);
5226 * Initialize all the small blocks to free
5228 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5229 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5231 if (count == 0)
5234 * We have just created the small block depot.
5236 StgProperty rootProp;
5237 ULONG sbStartIndex;
5240 * Save it in the header
5242 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5243 StorageImpl_SaveFileHeader(This->parentStorage);
5246 * And allocate the first big block that will contain small blocks
5248 sbStartIndex =
5249 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5251 StorageImpl_SetNextBlockInChain(
5252 This->parentStorage,
5253 sbStartIndex,
5254 BLOCK_END_OF_CHAIN);
5256 StorageImpl_ReadProperty(
5257 This->parentStorage,
5258 This->parentStorage->base.rootPropertySetIndex,
5259 &rootProp);
5261 rootProp.startingBlock = sbStartIndex;
5262 rootProp.size.u.HighPart = 0;
5263 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5265 StorageImpl_WriteProperty(
5266 This->parentStorage,
5267 This->parentStorage->base.rootPropertySetIndex,
5268 &rootProp);
5270 else
5271 StorageImpl_SaveFileHeader(This->parentStorage);
5275 smallBlocksPerBigBlock =
5276 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5279 * Verify if we have to allocate big blocks to contain small blocks
5281 if (blockIndex % smallBlocksPerBigBlock == 0)
5283 StgProperty rootProp;
5284 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5286 StorageImpl_ReadProperty(
5287 This->parentStorage,
5288 This->parentStorage->base.rootPropertySetIndex,
5289 &rootProp);
5291 if (rootProp.size.u.LowPart <
5292 (blocksRequired * This->parentStorage->bigBlockSize))
5294 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5296 BlockChainStream_SetSize(
5297 This->parentStorage->smallBlockRootChain,
5298 rootProp.size);
5300 StorageImpl_WriteProperty(
5301 This->parentStorage,
5302 This->parentStorage->base.rootPropertySetIndex,
5303 &rootProp);
5307 return blockIndex;
5310 /******************************************************************************
5311 * SmallBlockChainStream_ReadAt
5313 * Reads a specified number of bytes from this chain at the specified offset.
5314 * bytesRead may be NULL.
5315 * Failure will be returned if the specified number of bytes has not been read.
5317 HRESULT SmallBlockChainStream_ReadAt(
5318 SmallBlockChainStream* This,
5319 ULARGE_INTEGER offset,
5320 ULONG size,
5321 void* buffer,
5322 ULONG* bytesRead)
5324 HRESULT rc = S_OK;
5325 ULARGE_INTEGER offsetInBigBlockFile;
5326 ULONG blockNoInSequence =
5327 offset.u.LowPart / This->parentStorage->smallBlockSize;
5329 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5330 ULONG bytesToReadInBuffer;
5331 ULONG blockIndex;
5332 ULONG bytesReadFromBigBlockFile;
5333 BYTE* bufferWalker;
5336 * This should never happen on a small block file.
5338 assert(offset.u.HighPart==0);
5341 * Find the first block in the stream that contains part of the buffer.
5343 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5345 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5347 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5348 if(FAILED(rc))
5349 return rc;
5350 blockNoInSequence--;
5354 * Start reading the buffer.
5356 *bytesRead = 0;
5357 bufferWalker = buffer;
5359 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5362 * Calculate how many bytes we can copy from this small block.
5364 bytesToReadInBuffer =
5365 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5368 * Calculate the offset of the small block in the small block file.
5370 offsetInBigBlockFile.u.HighPart = 0;
5371 offsetInBigBlockFile.u.LowPart =
5372 blockIndex * This->parentStorage->smallBlockSize;
5374 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5377 * Read those bytes in the buffer from the small block file.
5378 * The small block has already been identified so it shouldn't fail
5379 * unless the file is corrupt.
5381 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5382 offsetInBigBlockFile,
5383 bytesToReadInBuffer,
5384 bufferWalker,
5385 &bytesReadFromBigBlockFile);
5387 if (FAILED(rc))
5388 return rc;
5391 * Step to the next big block.
5393 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5394 if(FAILED(rc))
5395 return STG_E_DOCFILECORRUPT;
5397 bufferWalker += bytesReadFromBigBlockFile;
5398 size -= bytesReadFromBigBlockFile;
5399 *bytesRead += bytesReadFromBigBlockFile;
5400 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5403 return (size == 0) ? S_OK : STG_E_READFAULT;
5406 /******************************************************************************
5407 * SmallBlockChainStream_WriteAt
5409 * Writes the specified number of bytes to this chain at the specified offset.
5410 * Will fail if not all specified number of bytes have been written.
5412 HRESULT SmallBlockChainStream_WriteAt(
5413 SmallBlockChainStream* This,
5414 ULARGE_INTEGER offset,
5415 ULONG size,
5416 const void* buffer,
5417 ULONG* bytesWritten)
5419 ULARGE_INTEGER offsetInBigBlockFile;
5420 ULONG blockNoInSequence =
5421 offset.u.LowPart / This->parentStorage->smallBlockSize;
5423 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5424 ULONG bytesToWriteInBuffer;
5425 ULONG blockIndex;
5426 ULONG bytesWrittenToBigBlockFile;
5427 const BYTE* bufferWalker;
5428 HRESULT res;
5431 * This should never happen on a small block file.
5433 assert(offset.u.HighPart==0);
5436 * Find the first block in the stream that contains part of the buffer.
5438 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5440 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5442 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5443 return STG_E_DOCFILECORRUPT;
5444 blockNoInSequence--;
5448 * Start writing the buffer.
5450 *bytesWritten = 0;
5451 bufferWalker = buffer;
5452 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5455 * Calculate how many bytes we can copy to this small block.
5457 bytesToWriteInBuffer =
5458 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5461 * Calculate the offset of the small block in the small block file.
5463 offsetInBigBlockFile.u.HighPart = 0;
5464 offsetInBigBlockFile.u.LowPart =
5465 blockIndex * This->parentStorage->smallBlockSize;
5467 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5470 * Write those bytes in the buffer to the small block file.
5472 res = BlockChainStream_WriteAt(
5473 This->parentStorage->smallBlockRootChain,
5474 offsetInBigBlockFile,
5475 bytesToWriteInBuffer,
5476 bufferWalker,
5477 &bytesWrittenToBigBlockFile);
5478 if (FAILED(res))
5479 return res;
5482 * Step to the next big block.
5484 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5485 &blockIndex)))
5486 return FALSE;
5487 bufferWalker += bytesWrittenToBigBlockFile;
5488 size -= bytesWrittenToBigBlockFile;
5489 *bytesWritten += bytesWrittenToBigBlockFile;
5490 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5493 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5496 /******************************************************************************
5497 * SmallBlockChainStream_Shrink
5499 * Shrinks this chain in the small block depot.
5501 static BOOL SmallBlockChainStream_Shrink(
5502 SmallBlockChainStream* This,
5503 ULARGE_INTEGER newSize)
5505 ULONG blockIndex, extraBlock;
5506 ULONG numBlocks;
5507 ULONG count = 0;
5509 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5511 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5512 numBlocks++;
5514 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5517 * Go to the new end of chain
5519 while (count < numBlocks)
5521 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5522 &blockIndex)))
5523 return FALSE;
5524 count++;
5528 * If the count is 0, we have a special case, the head of the chain was
5529 * just freed.
5531 if (count == 0)
5533 StgProperty chainProp;
5535 StorageImpl_ReadProperty(This->parentStorage,
5536 This->ownerPropertyIndex,
5537 &chainProp);
5539 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5541 StorageImpl_WriteProperty(This->parentStorage,
5542 This->ownerPropertyIndex,
5543 &chainProp);
5546 * We start freeing the chain at the head block.
5548 extraBlock = blockIndex;
5550 else
5552 /* Get the next block before marking the new end */
5553 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5554 &extraBlock)))
5555 return FALSE;
5557 /* Mark the new end of chain */
5558 SmallBlockChainStream_SetNextBlockInChain(
5559 This,
5560 blockIndex,
5561 BLOCK_END_OF_CHAIN);
5565 * Mark the extra blocks as free
5567 while (extraBlock != BLOCK_END_OF_CHAIN)
5569 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5570 &blockIndex)))
5571 return FALSE;
5572 SmallBlockChainStream_FreeBlock(This, extraBlock);
5573 extraBlock = blockIndex;
5576 return TRUE;
5579 /******************************************************************************
5580 * SmallBlockChainStream_Enlarge
5582 * Grows this chain in the small block depot.
5584 static BOOL SmallBlockChainStream_Enlarge(
5585 SmallBlockChainStream* This,
5586 ULARGE_INTEGER newSize)
5588 ULONG blockIndex, currentBlock;
5589 ULONG newNumBlocks;
5590 ULONG oldNumBlocks = 0;
5592 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5595 * Empty chain. Create the head.
5597 if (blockIndex == BLOCK_END_OF_CHAIN)
5599 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5600 SmallBlockChainStream_SetNextBlockInChain(
5601 This,
5602 blockIndex,
5603 BLOCK_END_OF_CHAIN);
5605 if (This->headOfStreamPlaceHolder != NULL)
5607 *(This->headOfStreamPlaceHolder) = blockIndex;
5609 else
5611 StgProperty chainProp;
5613 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5614 &chainProp);
5616 chainProp.startingBlock = blockIndex;
5618 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5619 &chainProp);
5623 currentBlock = blockIndex;
5626 * Figure out how many blocks are needed to contain this stream
5628 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5630 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5631 newNumBlocks++;
5634 * Go to the current end of chain
5636 while (blockIndex != BLOCK_END_OF_CHAIN)
5638 oldNumBlocks++;
5639 currentBlock = blockIndex;
5640 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5641 return FALSE;
5645 * Add new blocks to the chain
5647 while (oldNumBlocks < newNumBlocks)
5649 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5650 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5652 SmallBlockChainStream_SetNextBlockInChain(
5653 This,
5654 blockIndex,
5655 BLOCK_END_OF_CHAIN);
5657 currentBlock = blockIndex;
5658 oldNumBlocks++;
5661 return TRUE;
5664 /******************************************************************************
5665 * SmallBlockChainStream_SetSize
5667 * Sets the size of this stream.
5668 * The file will grow if we grow the chain.
5670 * TODO: Free the actual blocks in the file when we shrink the chain.
5671 * Currently, the blocks are still in the file. So the file size
5672 * doesn't shrink even if we shrink streams.
5674 BOOL SmallBlockChainStream_SetSize(
5675 SmallBlockChainStream* This,
5676 ULARGE_INTEGER newSize)
5678 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5680 if (newSize.u.LowPart == size.u.LowPart)
5681 return TRUE;
5683 if (newSize.u.LowPart < size.u.LowPart)
5685 SmallBlockChainStream_Shrink(This, newSize);
5687 else
5689 SmallBlockChainStream_Enlarge(This, newSize);
5692 return TRUE;
5695 /******************************************************************************
5696 * SmallBlockChainStream_GetCount
5698 * Returns the number of small blocks that comprises this chain.
5699 * This is not the size of the stream as the last block may not be full!
5702 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5704 ULONG blockIndex;
5705 ULONG count = 0;
5707 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5709 while(blockIndex != BLOCK_END_OF_CHAIN)
5711 count++;
5713 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
5714 blockIndex, &blockIndex)))
5715 return 0;
5718 return count;
5721 /******************************************************************************
5722 * SmallBlockChainStream_GetSize
5724 * Returns the size of this chain.
5726 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5728 StgProperty chainProperty;
5730 if(This->headOfStreamPlaceHolder != NULL)
5732 ULARGE_INTEGER result;
5733 result.u.HighPart = 0;
5735 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
5736 This->parentStorage->smallBlockSize;
5738 return result;
5741 StorageImpl_ReadProperty(
5742 This->parentStorage,
5743 This->ownerPropertyIndex,
5744 &chainProperty);
5746 return chainProperty.size;
5749 /******************************************************************************
5750 * StgCreateDocfile [OLE32.@]
5751 * Creates a new compound file storage object
5753 * PARAMS
5754 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5755 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5756 * reserved [ ?] unused?, usually 0
5757 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5759 * RETURNS
5760 * S_OK if the file was successfully created
5761 * some STG_E_ value if error
5762 * NOTES
5763 * if pwcsName is NULL, create file with new unique name
5764 * the function can returns
5765 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5766 * (unrealized now)
5768 HRESULT WINAPI StgCreateDocfile(
5769 LPCOLESTR pwcsName,
5770 DWORD grfMode,
5771 DWORD reserved,
5772 IStorage **ppstgOpen)
5774 StorageImpl* newStorage = 0;
5775 HANDLE hFile = INVALID_HANDLE_VALUE;
5776 HRESULT hr = STG_E_INVALIDFLAG;
5777 DWORD shareMode;
5778 DWORD accessMode;
5779 DWORD creationMode;
5780 DWORD fileAttributes;
5781 WCHAR tempFileName[MAX_PATH];
5783 TRACE("(%s, %x, %d, %p)\n",
5784 debugstr_w(pwcsName), grfMode,
5785 reserved, ppstgOpen);
5788 * Validate the parameters
5790 if (ppstgOpen == 0)
5791 return STG_E_INVALIDPOINTER;
5792 if (reserved != 0)
5793 return STG_E_INVALIDPARAMETER;
5795 /* if no share mode given then DENY_NONE is the default */
5796 if (STGM_SHARE_MODE(grfMode) == 0)
5797 grfMode |= STGM_SHARE_DENY_NONE;
5800 * Validate the STGM flags
5802 if ( FAILED( validateSTGM(grfMode) ))
5803 goto end;
5805 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5806 switch(STGM_ACCESS_MODE(grfMode))
5808 case STGM_WRITE:
5809 case STGM_READWRITE:
5810 break;
5811 default:
5812 goto end;
5815 /* in direct mode, can only use SHARE_EXCLUSIVE */
5816 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5817 goto end;
5819 /* but in transacted mode, any share mode is valid */
5822 * Generate a unique name.
5824 if (pwcsName == 0)
5826 WCHAR tempPath[MAX_PATH];
5827 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5829 memset(tempPath, 0, sizeof(tempPath));
5830 memset(tempFileName, 0, sizeof(tempFileName));
5832 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5833 tempPath[0] = '.';
5835 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5836 pwcsName = tempFileName;
5837 else
5839 hr = STG_E_INSUFFICIENTMEMORY;
5840 goto end;
5843 creationMode = TRUNCATE_EXISTING;
5845 else
5847 creationMode = GetCreationModeFromSTGM(grfMode);
5851 * Interpret the STGM value grfMode
5853 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5854 accessMode = GetAccessModeFromSTGM(grfMode);
5856 if (grfMode & STGM_DELETEONRELEASE)
5857 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5858 else
5859 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5861 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5862 FIXME("Storage share mode not implemented.\n");
5864 if (grfMode & STGM_TRANSACTED)
5865 FIXME("Transacted mode not implemented.\n");
5868 * Initialize the "out" parameter.
5870 *ppstgOpen = 0;
5872 hFile = CreateFileW(pwcsName,
5873 accessMode,
5874 shareMode,
5875 NULL,
5876 creationMode,
5877 fileAttributes,
5880 if (hFile == INVALID_HANDLE_VALUE)
5882 if(GetLastError() == ERROR_FILE_EXISTS)
5883 hr = STG_E_FILEALREADYEXISTS;
5884 else
5885 hr = E_FAIL;
5886 goto end;
5890 * Allocate and initialize the new IStorage32object.
5892 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5894 if (newStorage == 0)
5896 hr = STG_E_INSUFFICIENTMEMORY;
5897 goto end;
5900 hr = StorageImpl_Construct(
5901 newStorage,
5902 hFile,
5903 pwcsName,
5904 NULL,
5905 grfMode,
5906 TRUE,
5907 TRUE);
5909 if (FAILED(hr))
5911 HeapFree(GetProcessHeap(), 0, newStorage);
5912 goto end;
5916 * Get an "out" pointer for the caller.
5918 hr = StorageBaseImpl_QueryInterface(
5919 (IStorage*)newStorage,
5920 (REFIID)&IID_IStorage,
5921 (void**)ppstgOpen);
5922 end:
5923 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5925 return hr;
5928 /******************************************************************************
5929 * StgCreateStorageEx [OLE32.@]
5931 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5933 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5934 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5936 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5938 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5939 return STG_E_INVALIDPARAMETER;
5942 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5944 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5945 return STG_E_INVALIDPARAMETER;
5948 if (stgfmt == STGFMT_FILE)
5950 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5951 return STG_E_INVALIDPARAMETER;
5954 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5956 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5957 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5960 ERR("Invalid stgfmt argument\n");
5961 return STG_E_INVALIDPARAMETER;
5964 /******************************************************************************
5965 * StgCreatePropSetStg [OLE32.@]
5967 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5968 IPropertySetStorage **ppPropSetStg)
5970 HRESULT hr;
5972 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5973 if (reserved)
5974 hr = STG_E_INVALIDPARAMETER;
5975 else
5976 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5977 (void**)ppPropSetStg);
5978 return hr;
5981 /******************************************************************************
5982 * StgOpenStorageEx [OLE32.@]
5984 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5986 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5987 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5989 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5991 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5992 return STG_E_INVALIDPARAMETER;
5995 switch (stgfmt)
5997 case STGFMT_FILE:
5998 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5999 return STG_E_INVALIDPARAMETER;
6001 case STGFMT_STORAGE:
6002 break;
6004 case STGFMT_DOCFILE:
6005 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
6007 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6008 return STG_E_INVALIDPARAMETER;
6010 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6011 break;
6013 case STGFMT_ANY:
6014 WARN("STGFMT_ANY assuming storage\n");
6015 break;
6017 default:
6018 return STG_E_INVALIDPARAMETER;
6021 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
6025 /******************************************************************************
6026 * StgOpenStorage [OLE32.@]
6028 HRESULT WINAPI StgOpenStorage(
6029 const OLECHAR *pwcsName,
6030 IStorage *pstgPriority,
6031 DWORD grfMode,
6032 SNB snbExclude,
6033 DWORD reserved,
6034 IStorage **ppstgOpen)
6036 StorageImpl* newStorage = 0;
6037 HRESULT hr = S_OK;
6038 HANDLE hFile = 0;
6039 DWORD shareMode;
6040 DWORD accessMode;
6041 WCHAR fullname[MAX_PATH];
6043 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6044 debugstr_w(pwcsName), pstgPriority, grfMode,
6045 snbExclude, reserved, ppstgOpen);
6048 * Perform sanity checks
6050 if (pwcsName == 0)
6052 hr = STG_E_INVALIDNAME;
6053 goto end;
6056 if (ppstgOpen == 0)
6058 hr = STG_E_INVALIDPOINTER;
6059 goto end;
6062 if (reserved)
6064 hr = STG_E_INVALIDPARAMETER;
6065 goto end;
6068 if (grfMode & STGM_PRIORITY)
6070 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
6071 return STG_E_INVALIDFLAG;
6072 if (grfMode & STGM_DELETEONRELEASE)
6073 return STG_E_INVALIDFUNCTION;
6074 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
6075 return STG_E_INVALIDFLAG;
6076 grfMode &= ~0xf0; /* remove the existing sharing mode */
6077 grfMode |= STGM_SHARE_DENY_NONE;
6079 /* STGM_PRIORITY stops other IStorage objects on the same file from
6080 * committing until the STGM_PRIORITY IStorage is closed. it also
6081 * stops non-transacted mode StgOpenStorage calls with write access from
6082 * succeeding. obviously, both of these cannot be achieved through just
6083 * file share flags */
6084 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6088 * Validate the sharing mode
6090 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
6091 switch(STGM_SHARE_MODE(grfMode))
6093 case STGM_SHARE_EXCLUSIVE:
6094 case STGM_SHARE_DENY_WRITE:
6095 break;
6096 default:
6097 hr = STG_E_INVALIDFLAG;
6098 goto end;
6102 * Validate the STGM flags
6104 if ( FAILED( validateSTGM(grfMode) ) ||
6105 (grfMode&STGM_CREATE))
6107 hr = STG_E_INVALIDFLAG;
6108 goto end;
6111 /* shared reading requires transacted mode */
6112 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6113 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6114 !(grfMode&STGM_TRANSACTED) )
6116 hr = STG_E_INVALIDFLAG;
6117 goto end;
6121 * Interpret the STGM value grfMode
6123 shareMode = GetShareModeFromSTGM(grfMode);
6124 accessMode = GetAccessModeFromSTGM(grfMode);
6127 * Initialize the "out" parameter.
6129 *ppstgOpen = 0;
6131 hFile = CreateFileW( pwcsName,
6132 accessMode,
6133 shareMode,
6134 NULL,
6135 OPEN_EXISTING,
6136 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6139 if (hFile==INVALID_HANDLE_VALUE)
6141 DWORD last_error = GetLastError();
6143 hr = E_FAIL;
6145 switch (last_error)
6147 case ERROR_FILE_NOT_FOUND:
6148 hr = STG_E_FILENOTFOUND;
6149 break;
6151 case ERROR_PATH_NOT_FOUND:
6152 hr = STG_E_PATHNOTFOUND;
6153 break;
6155 case ERROR_ACCESS_DENIED:
6156 case ERROR_WRITE_PROTECT:
6157 hr = STG_E_ACCESSDENIED;
6158 break;
6160 case ERROR_SHARING_VIOLATION:
6161 hr = STG_E_SHAREVIOLATION;
6162 break;
6164 default:
6165 hr = E_FAIL;
6168 goto end;
6172 * Refuse to open the file if it's too small to be a structured storage file
6173 * FIXME: verify the file when reading instead of here
6175 if (GetFileSize(hFile, NULL) < 0x100)
6177 CloseHandle(hFile);
6178 hr = STG_E_FILEALREADYEXISTS;
6179 goto end;
6183 * Allocate and initialize the new IStorage32object.
6185 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6187 if (newStorage == 0)
6189 hr = STG_E_INSUFFICIENTMEMORY;
6190 goto end;
6193 /* Initialize the storage */
6194 hr = StorageImpl_Construct(
6195 newStorage,
6196 hFile,
6197 pwcsName,
6198 NULL,
6199 grfMode,
6200 TRUE,
6201 FALSE );
6203 if (FAILED(hr))
6205 HeapFree(GetProcessHeap(), 0, newStorage);
6207 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6209 if(hr == STG_E_INVALIDHEADER)
6210 hr = STG_E_FILEALREADYEXISTS;
6211 goto end;
6214 /* prepare the file name string given in lieu of the root property name */
6215 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6216 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6217 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6220 * Get an "out" pointer for the caller.
6222 hr = StorageBaseImpl_QueryInterface(
6223 (IStorage*)newStorage,
6224 (REFIID)&IID_IStorage,
6225 (void**)ppstgOpen);
6227 end:
6228 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6229 return hr;
6232 /******************************************************************************
6233 * StgCreateDocfileOnILockBytes [OLE32.@]
6235 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6236 ILockBytes *plkbyt,
6237 DWORD grfMode,
6238 DWORD reserved,
6239 IStorage** ppstgOpen)
6241 StorageImpl* newStorage = 0;
6242 HRESULT hr = S_OK;
6245 * Validate the parameters
6247 if ((ppstgOpen == 0) || (plkbyt == 0))
6248 return STG_E_INVALIDPOINTER;
6251 * Allocate and initialize the new IStorage object.
6253 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6255 if (newStorage == 0)
6256 return STG_E_INSUFFICIENTMEMORY;
6258 hr = StorageImpl_Construct(
6259 newStorage,
6262 plkbyt,
6263 grfMode,
6264 FALSE,
6265 TRUE);
6267 if (FAILED(hr))
6269 HeapFree(GetProcessHeap(), 0, newStorage);
6270 return hr;
6274 * Get an "out" pointer for the caller.
6276 hr = StorageBaseImpl_QueryInterface(
6277 (IStorage*)newStorage,
6278 (REFIID)&IID_IStorage,
6279 (void**)ppstgOpen);
6281 return hr;
6284 /******************************************************************************
6285 * StgOpenStorageOnILockBytes [OLE32.@]
6287 HRESULT WINAPI StgOpenStorageOnILockBytes(
6288 ILockBytes *plkbyt,
6289 IStorage *pstgPriority,
6290 DWORD grfMode,
6291 SNB snbExclude,
6292 DWORD reserved,
6293 IStorage **ppstgOpen)
6295 StorageImpl* newStorage = 0;
6296 HRESULT hr = S_OK;
6299 * Perform a sanity check
6301 if ((plkbyt == 0) || (ppstgOpen == 0))
6302 return STG_E_INVALIDPOINTER;
6305 * Validate the STGM flags
6307 if ( FAILED( validateSTGM(grfMode) ))
6308 return STG_E_INVALIDFLAG;
6311 * Initialize the "out" parameter.
6313 *ppstgOpen = 0;
6316 * Allocate and initialize the new IStorage object.
6318 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6320 if (newStorage == 0)
6321 return STG_E_INSUFFICIENTMEMORY;
6323 hr = StorageImpl_Construct(
6324 newStorage,
6327 plkbyt,
6328 grfMode,
6329 FALSE,
6330 FALSE);
6332 if (FAILED(hr))
6334 HeapFree(GetProcessHeap(), 0, newStorage);
6335 return hr;
6339 * Get an "out" pointer for the caller.
6341 hr = StorageBaseImpl_QueryInterface(
6342 (IStorage*)newStorage,
6343 (REFIID)&IID_IStorage,
6344 (void**)ppstgOpen);
6346 return hr;
6349 /******************************************************************************
6350 * StgSetTimes [ole32.@]
6351 * StgSetTimes [OLE32.@]
6355 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6356 FILETIME const *patime, FILETIME const *pmtime)
6358 IStorage *stg = NULL;
6359 HRESULT r;
6361 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6363 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6364 0, 0, &stg);
6365 if( SUCCEEDED(r) )
6367 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6368 IStorage_Release(stg);
6371 return r;
6374 /******************************************************************************
6375 * StgIsStorageILockBytes [OLE32.@]
6377 * Determines if the ILockBytes contains a storage object.
6379 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6381 BYTE sig[8];
6382 ULARGE_INTEGER offset;
6384 offset.u.HighPart = 0;
6385 offset.u.LowPart = 0;
6387 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6389 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6390 return S_OK;
6392 return S_FALSE;
6395 /******************************************************************************
6396 * WriteClassStg [OLE32.@]
6398 * This method will store the specified CLSID in the specified storage object
6400 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6402 HRESULT hRes;
6404 if(!pStg)
6405 return E_INVALIDARG;
6407 if(!rclsid)
6408 return STG_E_INVALIDPOINTER;
6410 hRes = IStorage_SetClass(pStg, rclsid);
6412 return hRes;
6415 /***********************************************************************
6416 * ReadClassStg (OLE32.@)
6418 * This method reads the CLSID previously written to a storage object with
6419 * the WriteClassStg.
6421 * PARAMS
6422 * pstg [I] IStorage pointer
6423 * pclsid [O] Pointer to where the CLSID is written
6425 * RETURNS
6426 * Success: S_OK.
6427 * Failure: HRESULT code.
6429 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6431 STATSTG pstatstg;
6432 HRESULT hRes;
6434 TRACE("(%p, %p)\n", pstg, pclsid);
6436 if(!pstg || !pclsid)
6437 return E_INVALIDARG;
6440 * read a STATSTG structure (contains the clsid) from the storage
6442 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6444 if(SUCCEEDED(hRes))
6445 *pclsid=pstatstg.clsid;
6447 return hRes;
6450 /***********************************************************************
6451 * OleLoadFromStream (OLE32.@)
6453 * This function loads an object from stream
6455 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6457 CLSID clsid;
6458 HRESULT res;
6459 LPPERSISTSTREAM xstm;
6461 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6463 res=ReadClassStm(pStm,&clsid);
6464 if (FAILED(res))
6465 return res;
6466 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6467 if (FAILED(res))
6468 return res;
6469 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6470 if (FAILED(res)) {
6471 IUnknown_Release((IUnknown*)*ppvObj);
6472 return res;
6474 res=IPersistStream_Load(xstm,pStm);
6475 IPersistStream_Release(xstm);
6476 /* FIXME: all refcounts ok at this point? I think they should be:
6477 * pStm : unchanged
6478 * ppvObj : 1
6479 * xstm : 0 (released)
6481 return res;
6484 /***********************************************************************
6485 * OleSaveToStream (OLE32.@)
6487 * This function saves an object with the IPersistStream interface on it
6488 * to the specified stream.
6490 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6493 CLSID clsid;
6494 HRESULT res;
6496 TRACE("(%p,%p)\n",pPStm,pStm);
6498 res=IPersistStream_GetClassID(pPStm,&clsid);
6500 if (SUCCEEDED(res)){
6502 res=WriteClassStm(pStm,&clsid);
6504 if (SUCCEEDED(res))
6506 res=IPersistStream_Save(pPStm,pStm,TRUE);
6509 TRACE("Finished Save\n");
6510 return res;
6513 /****************************************************************************
6514 * This method validate a STGM parameter that can contain the values below
6516 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6517 * The stgm values contained in 0xffff0000 are bitmasks.
6519 * STGM_DIRECT 0x00000000
6520 * STGM_TRANSACTED 0x00010000
6521 * STGM_SIMPLE 0x08000000
6523 * STGM_READ 0x00000000
6524 * STGM_WRITE 0x00000001
6525 * STGM_READWRITE 0x00000002
6527 * STGM_SHARE_DENY_NONE 0x00000040
6528 * STGM_SHARE_DENY_READ 0x00000030
6529 * STGM_SHARE_DENY_WRITE 0x00000020
6530 * STGM_SHARE_EXCLUSIVE 0x00000010
6532 * STGM_PRIORITY 0x00040000
6533 * STGM_DELETEONRELEASE 0x04000000
6535 * STGM_CREATE 0x00001000
6536 * STGM_CONVERT 0x00020000
6537 * STGM_FAILIFTHERE 0x00000000
6539 * STGM_NOSCRATCH 0x00100000
6540 * STGM_NOSNAPSHOT 0x00200000
6542 static HRESULT validateSTGM(DWORD stgm)
6544 DWORD access = STGM_ACCESS_MODE(stgm);
6545 DWORD share = STGM_SHARE_MODE(stgm);
6546 DWORD create = STGM_CREATE_MODE(stgm);
6548 if (stgm&~STGM_KNOWN_FLAGS)
6550 ERR("unknown flags %08x\n", stgm);
6551 return E_FAIL;
6554 switch (access)
6556 case STGM_READ:
6557 case STGM_WRITE:
6558 case STGM_READWRITE:
6559 break;
6560 default:
6561 return E_FAIL;
6564 switch (share)
6566 case STGM_SHARE_DENY_NONE:
6567 case STGM_SHARE_DENY_READ:
6568 case STGM_SHARE_DENY_WRITE:
6569 case STGM_SHARE_EXCLUSIVE:
6570 break;
6571 default:
6572 return E_FAIL;
6575 switch (create)
6577 case STGM_CREATE:
6578 case STGM_FAILIFTHERE:
6579 break;
6580 default:
6581 return E_FAIL;
6585 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6587 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6588 return E_FAIL;
6591 * STGM_CREATE | STGM_CONVERT
6592 * if both are false, STGM_FAILIFTHERE is set to TRUE
6594 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6595 return E_FAIL;
6598 * STGM_NOSCRATCH requires STGM_TRANSACTED
6600 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6601 return E_FAIL;
6604 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6605 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6607 if ( (stgm & STGM_NOSNAPSHOT) &&
6608 (!(stgm & STGM_TRANSACTED) ||
6609 share == STGM_SHARE_EXCLUSIVE ||
6610 share == STGM_SHARE_DENY_WRITE) )
6611 return E_FAIL;
6613 return S_OK;
6616 /****************************************************************************
6617 * GetShareModeFromSTGM
6619 * This method will return a share mode flag from a STGM value.
6620 * The STGM value is assumed valid.
6622 static DWORD GetShareModeFromSTGM(DWORD stgm)
6624 switch (STGM_SHARE_MODE(stgm))
6626 case STGM_SHARE_DENY_NONE:
6627 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6628 case STGM_SHARE_DENY_READ:
6629 return FILE_SHARE_WRITE;
6630 case STGM_SHARE_DENY_WRITE:
6631 return FILE_SHARE_READ;
6632 case STGM_SHARE_EXCLUSIVE:
6633 return 0;
6635 ERR("Invalid share mode!\n");
6636 assert(0);
6637 return 0;
6640 /****************************************************************************
6641 * GetAccessModeFromSTGM
6643 * This method will return an access mode flag from a STGM value.
6644 * The STGM value is assumed valid.
6646 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6648 switch (STGM_ACCESS_MODE(stgm))
6650 case STGM_READ:
6651 return GENERIC_READ;
6652 case STGM_WRITE:
6653 case STGM_READWRITE:
6654 return GENERIC_READ | GENERIC_WRITE;
6656 ERR("Invalid access mode!\n");
6657 assert(0);
6658 return 0;
6661 /****************************************************************************
6662 * GetCreationModeFromSTGM
6664 * This method will return a creation mode flag from a STGM value.
6665 * The STGM value is assumed valid.
6667 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6669 switch(STGM_CREATE_MODE(stgm))
6671 case STGM_CREATE:
6672 return CREATE_ALWAYS;
6673 case STGM_CONVERT:
6674 FIXME("STGM_CONVERT not implemented!\n");
6675 return CREATE_NEW;
6676 case STGM_FAILIFTHERE:
6677 return CREATE_NEW;
6679 ERR("Invalid create mode!\n");
6680 assert(0);
6681 return 0;
6685 /*************************************************************************
6686 * OLECONVERT_LoadOLE10 [Internal]
6688 * Loads the OLE10 STREAM to memory
6690 * PARAMS
6691 * pOleStream [I] The OLESTREAM
6692 * pData [I] Data Structure for the OLESTREAM Data
6694 * RETURNS
6695 * Success: S_OK
6696 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6697 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6699 * NOTES
6700 * This function is used by OleConvertOLESTREAMToIStorage only.
6702 * Memory allocated for pData must be freed by the caller
6704 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6706 DWORD dwSize;
6707 HRESULT hRes = S_OK;
6708 int nTryCnt=0;
6709 int max_try = 6;
6711 pData->pData = NULL;
6712 pData->pstrOleObjFileName = NULL;
6714 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6716 /* Get the OleID */
6717 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6718 if(dwSize != sizeof(pData->dwOleID))
6720 hRes = CONVERT10_E_OLESTREAM_GET;
6722 else if(pData->dwOleID != OLESTREAM_ID)
6724 hRes = CONVERT10_E_OLESTREAM_FMT;
6726 else
6728 hRes = S_OK;
6729 break;
6733 if(hRes == S_OK)
6735 /* Get the TypeID... more info needed for this field */
6736 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6737 if(dwSize != sizeof(pData->dwTypeID))
6739 hRes = CONVERT10_E_OLESTREAM_GET;
6742 if(hRes == S_OK)
6744 if(pData->dwTypeID != 0)
6746 /* Get the length of the OleTypeName */
6747 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6748 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6750 hRes = CONVERT10_E_OLESTREAM_GET;
6753 if(hRes == S_OK)
6755 if(pData->dwOleTypeNameLength > 0)
6757 /* Get the OleTypeName */
6758 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6759 if(dwSize != pData->dwOleTypeNameLength)
6761 hRes = CONVERT10_E_OLESTREAM_GET;
6765 if(bStrem1)
6767 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6768 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6770 hRes = CONVERT10_E_OLESTREAM_GET;
6772 if(hRes == S_OK)
6774 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6775 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6776 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6777 if(pData->pstrOleObjFileName)
6779 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6780 if(dwSize != pData->dwOleObjFileNameLength)
6782 hRes = CONVERT10_E_OLESTREAM_GET;
6785 else
6786 hRes = CONVERT10_E_OLESTREAM_GET;
6789 else
6791 /* Get the Width of the Metafile */
6792 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6793 if(dwSize != sizeof(pData->dwMetaFileWidth))
6795 hRes = CONVERT10_E_OLESTREAM_GET;
6797 if(hRes == S_OK)
6799 /* Get the Height of the Metafile */
6800 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6801 if(dwSize != sizeof(pData->dwMetaFileHeight))
6803 hRes = CONVERT10_E_OLESTREAM_GET;
6807 if(hRes == S_OK)
6809 /* Get the Length of the Data */
6810 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6811 if(dwSize != sizeof(pData->dwDataLength))
6813 hRes = CONVERT10_E_OLESTREAM_GET;
6817 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6819 if(!bStrem1) /* if it is a second OLE stream data */
6821 pData->dwDataLength -= 8;
6822 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6823 if(dwSize != sizeof(pData->strUnknown))
6825 hRes = CONVERT10_E_OLESTREAM_GET;
6829 if(hRes == S_OK)
6831 if(pData->dwDataLength > 0)
6833 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6835 /* Get Data (ex. IStorage, Metafile, or BMP) */
6836 if(pData->pData)
6838 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6839 if(dwSize != pData->dwDataLength)
6841 hRes = CONVERT10_E_OLESTREAM_GET;
6844 else
6846 hRes = CONVERT10_E_OLESTREAM_GET;
6852 return hRes;
6855 /*************************************************************************
6856 * OLECONVERT_SaveOLE10 [Internal]
6858 * Saves the OLE10 STREAM From memory
6860 * PARAMS
6861 * pData [I] Data Structure for the OLESTREAM Data
6862 * pOleStream [I] The OLESTREAM to save
6864 * RETURNS
6865 * Success: S_OK
6866 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6868 * NOTES
6869 * This function is used by OleConvertIStorageToOLESTREAM only.
6872 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6874 DWORD dwSize;
6875 HRESULT hRes = S_OK;
6878 /* Set the OleID */
6879 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6880 if(dwSize != sizeof(pData->dwOleID))
6882 hRes = CONVERT10_E_OLESTREAM_PUT;
6885 if(hRes == S_OK)
6887 /* Set the TypeID */
6888 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6889 if(dwSize != sizeof(pData->dwTypeID))
6891 hRes = CONVERT10_E_OLESTREAM_PUT;
6895 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6897 /* Set the Length of the OleTypeName */
6898 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6899 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6901 hRes = CONVERT10_E_OLESTREAM_PUT;
6904 if(hRes == S_OK)
6906 if(pData->dwOleTypeNameLength > 0)
6908 /* Set the OleTypeName */
6909 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6910 if(dwSize != pData->dwOleTypeNameLength)
6912 hRes = CONVERT10_E_OLESTREAM_PUT;
6917 if(hRes == S_OK)
6919 /* Set the width of the Metafile */
6920 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6921 if(dwSize != sizeof(pData->dwMetaFileWidth))
6923 hRes = CONVERT10_E_OLESTREAM_PUT;
6927 if(hRes == S_OK)
6929 /* Set the height of the Metafile */
6930 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6931 if(dwSize != sizeof(pData->dwMetaFileHeight))
6933 hRes = CONVERT10_E_OLESTREAM_PUT;
6937 if(hRes == S_OK)
6939 /* Set the length of the Data */
6940 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6941 if(dwSize != sizeof(pData->dwDataLength))
6943 hRes = CONVERT10_E_OLESTREAM_PUT;
6947 if(hRes == S_OK)
6949 if(pData->dwDataLength > 0)
6951 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6952 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6953 if(dwSize != pData->dwDataLength)
6955 hRes = CONVERT10_E_OLESTREAM_PUT;
6960 return hRes;
6963 /*************************************************************************
6964 * OLECONVERT_GetOLE20FromOLE10[Internal]
6966 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6967 * opens it, and copies the content to the dest IStorage for
6968 * OleConvertOLESTREAMToIStorage
6971 * PARAMS
6972 * pDestStorage [I] The IStorage to copy the data to
6973 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6974 * nBufferLength [I] The size of the buffer
6976 * RETURNS
6977 * Nothing
6979 * NOTES
6983 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6985 HRESULT hRes;
6986 HANDLE hFile;
6987 IStorage *pTempStorage;
6988 DWORD dwNumOfBytesWritten;
6989 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6990 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6992 /* Create a temp File */
6993 GetTempPathW(MAX_PATH, wstrTempDir);
6994 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6995 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6997 if(hFile != INVALID_HANDLE_VALUE)
6999 /* Write IStorage Data to File */
7000 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
7001 CloseHandle(hFile);
7003 /* Open and copy temp storage to the Dest Storage */
7004 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
7005 if(hRes == S_OK)
7007 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
7008 StorageBaseImpl_Release(pTempStorage);
7010 DeleteFileW(wstrTempFile);
7015 /*************************************************************************
7016 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7018 * Saves the OLE10 STREAM From memory
7020 * PARAMS
7021 * pStorage [I] The Src IStorage to copy
7022 * pData [I] The Dest Memory to write to.
7024 * RETURNS
7025 * The size in bytes allocated for pData
7027 * NOTES
7028 * Memory allocated for pData must be freed by the caller
7030 * Used by OleConvertIStorageToOLESTREAM only.
7033 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
7035 HANDLE hFile;
7036 HRESULT hRes;
7037 DWORD nDataLength = 0;
7038 IStorage *pTempStorage;
7039 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7040 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7042 *pData = NULL;
7044 /* Create temp Storage */
7045 GetTempPathW(MAX_PATH, wstrTempDir);
7046 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7047 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
7049 if(hRes == S_OK)
7051 /* Copy Src Storage to the Temp Storage */
7052 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
7053 StorageBaseImpl_Release(pTempStorage);
7055 /* Open Temp Storage as a file and copy to memory */
7056 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7057 if(hFile != INVALID_HANDLE_VALUE)
7059 nDataLength = GetFileSize(hFile, NULL);
7060 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
7061 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
7062 CloseHandle(hFile);
7064 DeleteFileW(wstrTempFile);
7066 return nDataLength;
7069 /*************************************************************************
7070 * OLECONVERT_CreateOleStream [Internal]
7072 * Creates the "\001OLE" stream in the IStorage if necessary.
7074 * PARAMS
7075 * pStorage [I] Dest storage to create the stream in
7077 * RETURNS
7078 * Nothing
7080 * NOTES
7081 * This function is used by OleConvertOLESTREAMToIStorage only.
7083 * This stream is still unknown, MS Word seems to have extra data
7084 * but since the data is stored in the OLESTREAM there should be
7085 * no need to recreate the stream. If the stream is manually
7086 * deleted it will create it with this default data.
7089 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7091 HRESULT hRes;
7092 IStream *pStream;
7093 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7094 BYTE pOleStreamHeader [] =
7096 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7097 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7098 0x00, 0x00, 0x00, 0x00
7101 /* Create stream if not present */
7102 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7103 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7105 if(hRes == S_OK)
7107 /* Write default Data */
7108 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7109 IStream_Release(pStream);
7113 /* write a string to a stream, preceded by its length */
7114 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7116 HRESULT r;
7117 LPSTR str;
7118 DWORD len = 0;
7120 if( string )
7121 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7122 r = IStream_Write( stm, &len, sizeof(len), NULL);
7123 if( FAILED( r ) )
7124 return r;
7125 if(len == 0)
7126 return r;
7127 str = CoTaskMemAlloc( len );
7128 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7129 r = IStream_Write( stm, str, len, NULL);
7130 CoTaskMemFree( str );
7131 return r;
7134 /* read a string preceded by its length from a stream */
7135 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7137 HRESULT r;
7138 DWORD len, count = 0;
7139 LPSTR str;
7140 LPWSTR wstr;
7142 r = IStream_Read( stm, &len, sizeof(len), &count );
7143 if( FAILED( r ) )
7144 return r;
7145 if( count != sizeof(len) )
7146 return E_OUTOFMEMORY;
7148 TRACE("%d bytes\n",len);
7150 str = CoTaskMemAlloc( len );
7151 if( !str )
7152 return E_OUTOFMEMORY;
7153 count = 0;
7154 r = IStream_Read( stm, str, len, &count );
7155 if( FAILED( r ) )
7156 return r;
7157 if( count != len )
7159 CoTaskMemFree( str );
7160 return E_OUTOFMEMORY;
7163 TRACE("Read string %s\n",debugstr_an(str,len));
7165 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7166 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7167 if( wstr )
7168 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7169 CoTaskMemFree( str );
7171 *string = wstr;
7173 return r;
7177 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7178 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7180 IStream *pstm;
7181 HRESULT r = S_OK;
7182 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7184 static const BYTE unknown1[12] =
7185 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7186 0xFF, 0xFF, 0xFF, 0xFF};
7187 static const BYTE unknown2[16] =
7188 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7189 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7191 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7192 debugstr_w(lpszUserType), debugstr_w(szClipName),
7193 debugstr_w(szProgIDName));
7195 /* Create a CompObj stream if it doesn't exist */
7196 r = IStorage_CreateStream(pstg, szwStreamName,
7197 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7198 if( FAILED (r) )
7199 return r;
7201 /* Write CompObj Structure to stream */
7202 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7204 if( SUCCEEDED( r ) )
7205 r = WriteClassStm( pstm, clsid );
7207 if( SUCCEEDED( r ) )
7208 r = STREAM_WriteString( pstm, lpszUserType );
7209 if( SUCCEEDED( r ) )
7210 r = STREAM_WriteString( pstm, szClipName );
7211 if( SUCCEEDED( r ) )
7212 r = STREAM_WriteString( pstm, szProgIDName );
7213 if( SUCCEEDED( r ) )
7214 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7216 IStream_Release( pstm );
7218 return r;
7221 /***********************************************************************
7222 * WriteFmtUserTypeStg (OLE32.@)
7224 HRESULT WINAPI WriteFmtUserTypeStg(
7225 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7227 HRESULT r;
7228 WCHAR szwClipName[0x40];
7229 CLSID clsid = CLSID_NULL;
7230 LPWSTR wstrProgID = NULL;
7231 DWORD n;
7233 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7235 /* get the clipboard format name */
7236 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7237 szwClipName[n]=0;
7239 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7241 /* FIXME: There's room to save a CLSID and its ProgID, but
7242 the CLSID is not looked up in the registry and in all the
7243 tests I wrote it was CLSID_NULL. Where does it come from?
7246 /* get the real program ID. This may fail, but that's fine */
7247 ProgIDFromCLSID(&clsid, &wstrProgID);
7249 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7251 r = STORAGE_WriteCompObj( pstg, &clsid,
7252 lpszUserType, szwClipName, wstrProgID );
7254 CoTaskMemFree(wstrProgID);
7256 return r;
7260 /******************************************************************************
7261 * ReadFmtUserTypeStg [OLE32.@]
7263 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7265 HRESULT r;
7266 IStream *stm = 0;
7267 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7268 unsigned char unknown1[12];
7269 unsigned char unknown2[16];
7270 DWORD count;
7271 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7272 CLSID clsid;
7274 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7276 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7277 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7278 if( FAILED ( r ) )
7280 WARN("Failed to open stream r = %08x\n", r);
7281 return r;
7284 /* read the various parts of the structure */
7285 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7286 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7287 goto end;
7288 r = ReadClassStm( stm, &clsid );
7289 if( FAILED( r ) )
7290 goto end;
7292 r = STREAM_ReadString( stm, &szCLSIDName );
7293 if( FAILED( r ) )
7294 goto end;
7296 r = STREAM_ReadString( stm, &szOleTypeName );
7297 if( FAILED( r ) )
7298 goto end;
7300 r = STREAM_ReadString( stm, &szProgIDName );
7301 if( FAILED( r ) )
7302 goto end;
7304 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7305 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7306 goto end;
7308 /* ok, success... now we just need to store what we found */
7309 if( pcf )
7310 *pcf = RegisterClipboardFormatW( szOleTypeName );
7311 CoTaskMemFree( szOleTypeName );
7313 if( lplpszUserType )
7314 *lplpszUserType = szCLSIDName;
7315 CoTaskMemFree( szProgIDName );
7317 end:
7318 IStream_Release( stm );
7320 return r;
7324 /*************************************************************************
7325 * OLECONVERT_CreateCompObjStream [Internal]
7327 * Creates a "\001CompObj" is the destination IStorage if necessary.
7329 * PARAMS
7330 * pStorage [I] The dest IStorage to create the CompObj Stream
7331 * if necessary.
7332 * strOleTypeName [I] The ProgID
7334 * RETURNS
7335 * Success: S_OK
7336 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7338 * NOTES
7339 * This function is used by OleConvertOLESTREAMToIStorage only.
7341 * The stream data is stored in the OLESTREAM and there should be
7342 * no need to recreate the stream. If the stream is manually
7343 * deleted it will attempt to create it by querying the registry.
7347 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7349 IStream *pStream;
7350 HRESULT hStorageRes, hRes = S_OK;
7351 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7352 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7353 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7355 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7356 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7358 /* Initialize the CompObj structure */
7359 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7360 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7361 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7364 /* Create a CompObj stream if it doesn't exist */
7365 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7366 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7367 if(hStorageRes == S_OK)
7369 /* copy the OleTypeName to the compobj struct */
7370 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7371 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7373 /* copy the OleTypeName to the compobj struct */
7374 /* Note: in the test made, these were Identical */
7375 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7376 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7378 /* Get the CLSID */
7379 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7380 bufferW, OLESTREAM_MAX_STR_LEN );
7381 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7383 if(hRes == S_OK)
7385 HKEY hKey;
7386 LONG hErr;
7387 /* Get the CLSID Default Name from the Registry */
7388 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7389 if(hErr == ERROR_SUCCESS)
7391 char strTemp[OLESTREAM_MAX_STR_LEN];
7392 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7393 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7394 if(hErr == ERROR_SUCCESS)
7396 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7398 RegCloseKey(hKey);
7402 /* Write CompObj Structure to stream */
7403 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7405 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7407 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7408 if(IStorageCompObj.dwCLSIDNameLength > 0)
7410 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7412 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7413 if(IStorageCompObj.dwOleTypeNameLength > 0)
7415 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7417 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7418 if(IStorageCompObj.dwProgIDNameLength > 0)
7420 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7422 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7423 IStream_Release(pStream);
7425 return hRes;
7429 /*************************************************************************
7430 * OLECONVERT_CreateOlePresStream[Internal]
7432 * Creates the "\002OlePres000" Stream with the Metafile data
7434 * PARAMS
7435 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7436 * dwExtentX [I] Width of the Metafile
7437 * dwExtentY [I] Height of the Metafile
7438 * pData [I] Metafile data
7439 * dwDataLength [I] Size of the Metafile data
7441 * RETURNS
7442 * Success: S_OK
7443 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7445 * NOTES
7446 * This function is used by OleConvertOLESTREAMToIStorage only.
7449 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7451 HRESULT hRes;
7452 IStream *pStream;
7453 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7454 BYTE pOlePresStreamHeader [] =
7456 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7457 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7458 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7459 0x00, 0x00, 0x00, 0x00
7462 BYTE pOlePresStreamHeaderEmpty [] =
7464 0x00, 0x00, 0x00, 0x00,
7465 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7466 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7467 0x00, 0x00, 0x00, 0x00
7470 /* Create the OlePres000 Stream */
7471 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7472 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7474 if(hRes == S_OK)
7476 DWORD nHeaderSize;
7477 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7479 memset(&OlePres, 0, sizeof(OlePres));
7480 /* Do we have any metafile data to save */
7481 if(dwDataLength > 0)
7483 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7484 nHeaderSize = sizeof(pOlePresStreamHeader);
7486 else
7488 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7489 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7491 /* Set width and height of the metafile */
7492 OlePres.dwExtentX = dwExtentX;
7493 OlePres.dwExtentY = -dwExtentY;
7495 /* Set Data and Length */
7496 if(dwDataLength > sizeof(METAFILEPICT16))
7498 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7499 OlePres.pData = &(pData[8]);
7501 /* Save OlePres000 Data to Stream */
7502 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7503 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7504 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7505 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7506 if(OlePres.dwSize > 0)
7508 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7510 IStream_Release(pStream);
7514 /*************************************************************************
7515 * OLECONVERT_CreateOle10NativeStream [Internal]
7517 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7519 * PARAMS
7520 * pStorage [I] Dest storage to create the stream in
7521 * pData [I] Ole10 Native Data (ex. bmp)
7522 * dwDataLength [I] Size of the Ole10 Native Data
7524 * RETURNS
7525 * Nothing
7527 * NOTES
7528 * This function is used by OleConvertOLESTREAMToIStorage only.
7530 * Might need to verify the data and return appropriate error message
7533 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7535 HRESULT hRes;
7536 IStream *pStream;
7537 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7539 /* Create the Ole10Native Stream */
7540 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7541 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7543 if(hRes == S_OK)
7545 /* Write info to stream */
7546 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7547 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7548 IStream_Release(pStream);
7553 /*************************************************************************
7554 * OLECONVERT_GetOLE10ProgID [Internal]
7556 * Finds the ProgID (or OleTypeID) from the IStorage
7558 * PARAMS
7559 * pStorage [I] The Src IStorage to get the ProgID
7560 * strProgID [I] the ProgID string to get
7561 * dwSize [I] the size of the string
7563 * RETURNS
7564 * Success: S_OK
7565 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7567 * NOTES
7568 * This function is used by OleConvertIStorageToOLESTREAM only.
7572 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7574 HRESULT hRes;
7575 IStream *pStream;
7576 LARGE_INTEGER iSeekPos;
7577 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7578 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7580 /* Open the CompObj Stream */
7581 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7582 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7583 if(hRes == S_OK)
7586 /*Get the OleType from the CompObj Stream */
7587 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7588 iSeekPos.u.HighPart = 0;
7590 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7591 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7592 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7593 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7594 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7595 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7596 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7598 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7599 if(*dwSize > 0)
7601 IStream_Read(pStream, strProgID, *dwSize, NULL);
7603 IStream_Release(pStream);
7605 else
7607 STATSTG stat;
7608 LPOLESTR wstrProgID;
7610 /* Get the OleType from the registry */
7611 REFCLSID clsid = &(stat.clsid);
7612 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7613 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7614 if(hRes == S_OK)
7616 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7620 return hRes;
7623 /*************************************************************************
7624 * OLECONVERT_GetOle10PresData [Internal]
7626 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7628 * PARAMS
7629 * pStorage [I] Src IStroage
7630 * pOleStream [I] Dest OleStream Mem Struct
7632 * RETURNS
7633 * Nothing
7635 * NOTES
7636 * This function is used by OleConvertIStorageToOLESTREAM only.
7638 * Memory allocated for pData must be freed by the caller
7642 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7645 HRESULT hRes;
7646 IStream *pStream;
7647 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7649 /* Initialize Default data for OLESTREAM */
7650 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7651 pOleStreamData[0].dwTypeID = 2;
7652 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7653 pOleStreamData[1].dwTypeID = 0;
7654 pOleStreamData[0].dwMetaFileWidth = 0;
7655 pOleStreamData[0].dwMetaFileHeight = 0;
7656 pOleStreamData[0].pData = NULL;
7657 pOleStreamData[1].pData = NULL;
7659 /* Open Ole10Native Stream */
7660 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7661 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7662 if(hRes == S_OK)
7665 /* Read Size and Data */
7666 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7667 if(pOleStreamData->dwDataLength > 0)
7669 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7670 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7672 IStream_Release(pStream);
7678 /*************************************************************************
7679 * OLECONVERT_GetOle20PresData[Internal]
7681 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7683 * PARAMS
7684 * pStorage [I] Src IStroage
7685 * pOleStreamData [I] Dest OleStream Mem Struct
7687 * RETURNS
7688 * Nothing
7690 * NOTES
7691 * This function is used by OleConvertIStorageToOLESTREAM only.
7693 * Memory allocated for pData must be freed by the caller
7695 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7697 HRESULT hRes;
7698 IStream *pStream;
7699 OLECONVERT_ISTORAGE_OLEPRES olePress;
7700 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7702 /* Initialize Default data for OLESTREAM */
7703 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7704 pOleStreamData[0].dwTypeID = 2;
7705 pOleStreamData[0].dwMetaFileWidth = 0;
7706 pOleStreamData[0].dwMetaFileHeight = 0;
7707 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7708 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7709 pOleStreamData[1].dwTypeID = 0;
7710 pOleStreamData[1].dwOleTypeNameLength = 0;
7711 pOleStreamData[1].strOleTypeName[0] = 0;
7712 pOleStreamData[1].dwMetaFileWidth = 0;
7713 pOleStreamData[1].dwMetaFileHeight = 0;
7714 pOleStreamData[1].pData = NULL;
7715 pOleStreamData[1].dwDataLength = 0;
7718 /* Open OlePress000 stream */
7719 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7720 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7721 if(hRes == S_OK)
7723 LARGE_INTEGER iSeekPos;
7724 METAFILEPICT16 MetaFilePict;
7725 static const char strMetafilePictName[] = "METAFILEPICT";
7727 /* Set the TypeID for a Metafile */
7728 pOleStreamData[1].dwTypeID = 5;
7730 /* Set the OleTypeName to Metafile */
7731 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7732 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7734 iSeekPos.u.HighPart = 0;
7735 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7737 /* Get Presentation Data */
7738 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7739 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7740 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7741 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7743 /*Set width and Height */
7744 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7745 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7746 if(olePress.dwSize > 0)
7748 /* Set Length */
7749 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7751 /* Set MetaFilePict struct */
7752 MetaFilePict.mm = 8;
7753 MetaFilePict.xExt = olePress.dwExtentX;
7754 MetaFilePict.yExt = olePress.dwExtentY;
7755 MetaFilePict.hMF = 0;
7757 /* Get Metafile Data */
7758 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7759 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7760 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7762 IStream_Release(pStream);
7766 /*************************************************************************
7767 * OleConvertOLESTREAMToIStorage [OLE32.@]
7769 * Read info on MSDN
7771 * TODO
7772 * DVTARGETDEVICE parameter is not handled
7773 * Still unsure of some mem fields for OLE 10 Stream
7774 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7775 * and "\001OLE" streams
7778 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7779 LPOLESTREAM pOleStream,
7780 LPSTORAGE pstg,
7781 const DVTARGETDEVICE* ptd)
7783 int i;
7784 HRESULT hRes=S_OK;
7785 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7787 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7789 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7791 if(ptd != NULL)
7793 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7796 if(pstg == NULL || pOleStream == NULL)
7798 hRes = E_INVALIDARG;
7801 if(hRes == S_OK)
7803 /* Load the OLESTREAM to Memory */
7804 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7807 if(hRes == S_OK)
7809 /* Load the OLESTREAM to Memory (part 2)*/
7810 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7813 if(hRes == S_OK)
7816 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7818 /* Do we have the IStorage Data in the OLESTREAM */
7819 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7821 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7822 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7824 else
7826 /* It must be an original OLE 1.0 source */
7827 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7830 else
7832 /* It must be an original OLE 1.0 source */
7833 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7836 /* Create CompObj Stream if necessary */
7837 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7838 if(hRes == S_OK)
7840 /*Create the Ole Stream if necessary */
7841 OLECONVERT_CreateOleStream(pstg);
7846 /* Free allocated memory */
7847 for(i=0; i < 2; i++)
7849 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7850 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7851 pOleStreamData[i].pstrOleObjFileName = NULL;
7853 return hRes;
7856 /*************************************************************************
7857 * OleConvertIStorageToOLESTREAM [OLE32.@]
7859 * Read info on MSDN
7861 * Read info on MSDN
7863 * TODO
7864 * Still unsure of some mem fields for OLE 10 Stream
7865 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7866 * and "\001OLE" streams.
7869 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7870 LPSTORAGE pstg,
7871 LPOLESTREAM pOleStream)
7873 int i;
7874 HRESULT hRes = S_OK;
7875 IStream *pStream;
7876 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7877 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7879 TRACE("%p %p\n", pstg, pOleStream);
7881 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7883 if(pstg == NULL || pOleStream == NULL)
7885 hRes = E_INVALIDARG;
7887 if(hRes == S_OK)
7889 /* Get the ProgID */
7890 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7891 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7893 if(hRes == S_OK)
7895 /* Was it originally Ole10 */
7896 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7897 if(hRes == S_OK)
7899 IStream_Release(pStream);
7900 /* Get Presentation Data for Ole10Native */
7901 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7903 else
7905 /* Get Presentation Data (OLE20) */
7906 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7909 /* Save OLESTREAM */
7910 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7911 if(hRes == S_OK)
7913 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7918 /* Free allocated memory */
7919 for(i=0; i < 2; i++)
7921 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7924 return hRes;
7927 /***********************************************************************
7928 * GetConvertStg (OLE32.@)
7930 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7931 FIXME("unimplemented stub!\n");
7932 return E_FAIL;
7935 /******************************************************************************
7936 * StgIsStorageFile [OLE32.@]
7937 * Verify if the file contains a storage object
7939 * PARAMS
7940 * fn [ I] Filename
7942 * RETURNS
7943 * S_OK if file has magic bytes as a storage object
7944 * S_FALSE if file is not storage
7946 HRESULT WINAPI
7947 StgIsStorageFile(LPCOLESTR fn)
7949 HANDLE hf;
7950 BYTE magic[8];
7951 DWORD bytes_read;
7953 TRACE("%s\n", debugstr_w(fn));
7954 hf = CreateFileW(fn, GENERIC_READ,
7955 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7956 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7958 if (hf == INVALID_HANDLE_VALUE)
7959 return STG_E_FILENOTFOUND;
7961 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7963 WARN(" unable to read file\n");
7964 CloseHandle(hf);
7965 return S_FALSE;
7968 CloseHandle(hf);
7970 if (bytes_read != 8) {
7971 WARN(" too short\n");
7972 return S_FALSE;
7975 if (!memcmp(magic,STORAGE_magic,8)) {
7976 WARN(" -> YES\n");
7977 return S_OK;
7980 WARN(" -> Invalid header.\n");
7981 return S_FALSE;
7984 /***********************************************************************
7985 * WriteClassStm (OLE32.@)
7987 * Writes a CLSID to a stream.
7989 * PARAMS
7990 * pStm [I] Stream to write to.
7991 * rclsid [I] CLSID to write.
7993 * RETURNS
7994 * Success: S_OK.
7995 * Failure: HRESULT code.
7997 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7999 TRACE("(%p,%p)\n",pStm,rclsid);
8001 if (!pStm || !rclsid)
8002 return E_INVALIDARG;
8004 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
8007 /***********************************************************************
8008 * ReadClassStm (OLE32.@)
8010 * Reads a CLSID from a stream.
8012 * PARAMS
8013 * pStm [I] Stream to read from.
8014 * rclsid [O] CLSID to read.
8016 * RETURNS
8017 * Success: S_OK.
8018 * Failure: HRESULT code.
8020 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
8022 ULONG nbByte;
8023 HRESULT res;
8025 TRACE("(%p,%p)\n",pStm,pclsid);
8027 if (!pStm || !pclsid)
8028 return E_INVALIDARG;
8030 /* clear the output args */
8031 *pclsid = CLSID_NULL;
8033 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
8035 if (FAILED(res))
8036 return res;
8038 if (nbByte != sizeof(CLSID))
8039 return STG_E_READFAULT;
8040 else
8041 return S_OK;