Name of the structured storage file root node is path of the file, not
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob7d5d09088fd7bb6de37484189572d5e7ecf638d9
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
13 #include <assert.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
18 #include "winbase.h" /* for lstrlenW() and the likes */
19 #include "winnls.h"
20 #include "wine/unicode.h"
21 #include "debugtools.h"
23 #include "storage32.h"
24 #include "ole2.h" /* For Write/ReadClassStm */
26 #include "winreg.h"
27 #include "wine/wingdi16.h"
29 DEFAULT_DEBUG_CHANNEL(storage);
31 #define FILE_BEGIN 0
34 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
35 #define OLESTREAM_ID 0x501
36 #define OLESTREAM_MAX_STR_LEN 255
38 static const char rootPropertyName[] = "Root Entry";
41 /* OLESTREAM memory structure to use for Get and Put Routines */
42 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
43 typedef struct
45 DWORD dwOleID;
46 DWORD dwTypeID;
47 DWORD dwOleTypeNameLength;
48 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
49 CHAR *pstrOleObjFileName;
50 DWORD dwOleObjFileNameLength;
51 DWORD dwMetaFileWidth;
52 DWORD dwMetaFileHeight;
53 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
54 DWORD dwDataLength;
55 BYTE *pData;
56 }OLECONVERT_OLESTREAM_DATA;
58 /* CompObj Stream structure */
59 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
60 typedef struct
62 BYTE byUnknown1[12];
63 CLSID clsid;
64 DWORD dwCLSIDNameLength;
65 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
66 DWORD dwOleTypeNameLength;
67 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
68 DWORD dwProgIDNameLength;
69 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
70 BYTE byUnknown2[16];
71 }OLECONVERT_ISTORAGE_COMPOBJ;
74 /* Ole Presention Stream structure */
75 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
76 typedef struct
78 BYTE byUnknown1[28];
79 DWORD dwExtentX;
80 DWORD dwExtentY;
81 DWORD dwSize;
82 BYTE *pData;
83 }OLECONVERT_ISTORAGE_OLEPRES;
87 /***********************************************************************
88 * Forward declaration of internal functions used by the method DestroyElement
90 static HRESULT deleteStorageProperty(
91 StorageImpl *parentStorage,
92 ULONG foundPropertyIndexToDelete,
93 StgProperty propertyToDelete);
95 static HRESULT deleteStreamProperty(
96 StorageImpl *parentStorage,
97 ULONG foundPropertyIndexToDelete,
98 StgProperty propertyToDelete);
100 static HRESULT findPlaceholder(
101 StorageImpl *storage,
102 ULONG propertyIndexToStore,
103 ULONG storagePropertyIndex,
104 INT typeOfRelation);
106 static HRESULT adjustPropertyChain(
107 StorageImpl *This,
108 StgProperty propertyToDelete,
109 StgProperty parentProperty,
110 ULONG parentPropertyId,
111 INT typeOfRelation);
113 /***********************************************************************
114 * Declaration of the functions used to manipulate StgProperty
117 static ULONG getFreeProperty(
118 StorageImpl *storage);
120 static void updatePropertyChain(
121 StorageImpl *storage,
122 ULONG newPropertyIndex,
123 StgProperty newProperty);
125 static LONG propertyNameCmp(
126 OLECHAR *newProperty,
127 OLECHAR *currentProperty);
130 /***********************************************************************
131 * Declaration of miscellaneous functions...
133 static HRESULT validateSTGM(DWORD stgmValue);
135 static DWORD GetShareModeFromSTGM(DWORD stgm);
136 static DWORD GetAccessModeFromSTGM(DWORD stgm);
137 static DWORD GetCreationModeFromSTGM(DWORD stgm);
140 * Virtual function table for the IStorage32Impl class.
142 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
144 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
145 StorageBaseImpl_QueryInterface,
146 StorageBaseImpl_AddRef,
147 StorageBaseImpl_Release,
148 StorageBaseImpl_CreateStream,
149 StorageBaseImpl_OpenStream,
150 StorageImpl_CreateStorage,
151 StorageBaseImpl_OpenStorage,
152 StorageImpl_CopyTo,
153 StorageImpl_MoveElementTo,
154 StorageImpl_Commit,
155 StorageImpl_Revert,
156 StorageBaseImpl_EnumElements,
157 StorageImpl_DestroyElement,
158 StorageBaseImpl_RenameElement,
159 StorageImpl_SetElementTimes,
160 StorageBaseImpl_SetClass,
161 StorageImpl_SetStateBits,
162 StorageImpl_Stat
166 * Virtual function table for the Storage32InternalImpl class.
168 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
170 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
171 StorageBaseImpl_QueryInterface,
172 StorageBaseImpl_AddRef,
173 StorageBaseImpl_Release,
174 StorageBaseImpl_CreateStream,
175 StorageBaseImpl_OpenStream,
176 StorageImpl_CreateStorage,
177 StorageBaseImpl_OpenStorage,
178 StorageImpl_CopyTo,
179 StorageImpl_MoveElementTo,
180 StorageInternalImpl_Commit,
181 StorageInternalImpl_Revert,
182 StorageBaseImpl_EnumElements,
183 StorageImpl_DestroyElement,
184 StorageBaseImpl_RenameElement,
185 StorageImpl_SetElementTimes,
186 StorageBaseImpl_SetClass,
187 StorageImpl_SetStateBits,
188 StorageBaseImpl_Stat
192 * Virtual function table for the IEnumSTATSTGImpl class.
194 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
196 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
197 IEnumSTATSTGImpl_QueryInterface,
198 IEnumSTATSTGImpl_AddRef,
199 IEnumSTATSTGImpl_Release,
200 IEnumSTATSTGImpl_Next,
201 IEnumSTATSTGImpl_Skip,
202 IEnumSTATSTGImpl_Reset,
203 IEnumSTATSTGImpl_Clone
210 /************************************************************************
211 ** Storage32BaseImpl implementatiion
214 /************************************************************************
215 * Storage32BaseImpl_QueryInterface (IUnknown)
217 * This method implements the common QueryInterface for all IStorage32
218 * implementations contained in this file.
220 * See Windows documentation for more details on IUnknown methods.
222 HRESULT WINAPI StorageBaseImpl_QueryInterface(
223 IStorage* iface,
224 REFIID riid,
225 void** ppvObject)
227 ICOM_THIS(StorageBaseImpl,iface);
229 * Perform a sanity check on the parameters.
231 if ( (This==0) || (ppvObject==0) )
232 return E_INVALIDARG;
235 * Initialize the return parameter.
237 *ppvObject = 0;
240 * Compare the riid with the interface IDs implemented by this object.
242 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
244 *ppvObject = (IStorage*)This;
246 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
248 *ppvObject = (IStorage*)This;
252 * Check that we obtained an interface.
254 if ((*ppvObject)==0)
255 return E_NOINTERFACE;
258 * Query Interface always increases the reference count by one when it is
259 * successful
261 StorageBaseImpl_AddRef(iface);
263 return S_OK;
266 /************************************************************************
267 * Storage32BaseImpl_AddRef (IUnknown)
269 * This method implements the common AddRef for all IStorage32
270 * implementations contained in this file.
272 * See Windows documentation for more details on IUnknown methods.
274 ULONG WINAPI StorageBaseImpl_AddRef(
275 IStorage* iface)
277 ICOM_THIS(StorageBaseImpl,iface);
278 This->ref++;
280 return This->ref;
283 /************************************************************************
284 * Storage32BaseImpl_Release (IUnknown)
286 * This method implements the common Release for all IStorage32
287 * implementations contained in this file.
289 * See Windows documentation for more details on IUnknown methods.
291 ULONG WINAPI StorageBaseImpl_Release(
292 IStorage* iface)
294 ICOM_THIS(StorageBaseImpl,iface);
296 * Decrease the reference count on this object.
298 This->ref--;
301 * If the reference count goes down to 0, perform suicide.
303 if (This->ref==0)
306 * Since we are using a system of base-classes, we want to call the
307 * destructor of the appropriate derived class. To do this, we are
308 * using virtual functions to implement the destructor.
310 This->v_destructor(This);
312 return 0;
315 return This->ref;
318 /************************************************************************
319 * Storage32BaseImpl_OpenStream (IStorage)
321 * This method will open the specified stream object from the current storage.
323 * See Windows documentation for more details on IStorage methods.
325 HRESULT WINAPI StorageBaseImpl_OpenStream(
326 IStorage* iface,
327 const OLECHAR* pwcsName, /* [string][in] */
328 void* reserved1, /* [unique][in] */
329 DWORD grfMode, /* [in] */
330 DWORD reserved2, /* [in] */
331 IStream** ppstm) /* [out] */
333 ICOM_THIS(StorageBaseImpl,iface);
334 IEnumSTATSTGImpl* propertyEnumeration;
335 StgStreamImpl* newStream;
336 StgProperty currentProperty;
337 ULONG foundPropertyIndex;
339 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
340 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
343 * Perform a sanity check on the parameters.
345 if ( (pwcsName==NULL) || (ppstm==0) )
346 return E_INVALIDARG;
349 * Initialize the out parameter
351 *ppstm = 0;
354 * Validate the STGM flags
356 if ( FAILED( validateSTGM(grfMode) ))
357 return STG_E_INVALIDFLAG;
360 * As documented.
362 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
363 (grfMode & STGM_DELETEONRELEASE) ||
364 (grfMode & STGM_TRANSACTED) )
365 return STG_E_INVALIDFUNCTION;
368 * Create a property enumeration to search the properties
370 propertyEnumeration = IEnumSTATSTGImpl_Construct(
371 This->ancestorStorage,
372 This->rootPropertySetIndex);
375 * Search the enumeration for the property with the given name
377 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
378 propertyEnumeration,
379 pwcsName,
380 &currentProperty);
383 * Delete the property enumeration since we don't need it anymore
385 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
388 * If it was found, construct the stream object and return a pointer to it.
390 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
391 (currentProperty.propertyType==PROPTYPE_STREAM) )
393 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
395 if (newStream!=0)
397 newStream->grfMode = grfMode;
398 *ppstm = (IStream*)newStream;
401 * Since we are returning a pointer to the interface, we have to
402 * nail down the reference.
404 StgStreamImpl_AddRef(*ppstm);
406 return S_OK;
409 return E_OUTOFMEMORY;
412 return STG_E_FILENOTFOUND;
415 /************************************************************************
416 * Storage32BaseImpl_OpenStorage (IStorage)
418 * This method will open a new storage object from the current storage.
420 * See Windows documentation for more details on IStorage methods.
422 HRESULT WINAPI StorageBaseImpl_OpenStorage(
423 IStorage* iface,
424 const OLECHAR* pwcsName, /* [string][unique][in] */
425 IStorage* pstgPriority, /* [unique][in] */
426 DWORD grfMode, /* [in] */
427 SNB snbExclude, /* [unique][in] */
428 DWORD reserved, /* [in] */
429 IStorage** ppstg) /* [out] */
431 ICOM_THIS(StorageBaseImpl,iface);
432 StorageInternalImpl* newStorage;
433 IEnumSTATSTGImpl* propertyEnumeration;
434 StgProperty currentProperty;
435 ULONG foundPropertyIndex;
437 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
438 iface, debugstr_w(pwcsName), pstgPriority,
439 grfMode, snbExclude, reserved, ppstg);
442 * Perform a sanity check on the parameters.
444 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
445 return E_INVALIDARG;
448 * Validate the STGM flags
450 if ( FAILED( validateSTGM(grfMode) ))
451 return STG_E_INVALIDFLAG;
454 * As documented.
456 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
457 (grfMode & STGM_DELETEONRELEASE) ||
458 (grfMode & STGM_PRIORITY) )
459 return STG_E_INVALIDFUNCTION;
462 * Initialize the out parameter
464 *ppstg = 0;
467 * Create a property enumeration to search the properties
469 propertyEnumeration = IEnumSTATSTGImpl_Construct(
470 This->ancestorStorage,
471 This->rootPropertySetIndex);
474 * Search the enumeration for the property with the given name
476 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
477 propertyEnumeration,
478 pwcsName,
479 &currentProperty);
482 * Delete the property enumeration since we don't need it anymore
484 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
487 * If it was found, construct the stream object and return a pointer to it.
489 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
490 (currentProperty.propertyType==PROPTYPE_STORAGE) )
493 * Construct a new Storage object
495 newStorage = StorageInternalImpl_Construct(
496 This->ancestorStorage,
497 foundPropertyIndex);
499 if (newStorage != 0)
501 *ppstg = (IStorage*)newStorage;
504 * Since we are returning a pointer to the interface,
505 * we have to nail down the reference.
507 StorageBaseImpl_AddRef(*ppstg);
509 return S_OK;
512 return STG_E_INSUFFICIENTMEMORY;
515 return STG_E_FILENOTFOUND;
518 /************************************************************************
519 * Storage32BaseImpl_EnumElements (IStorage)
521 * This method will create an enumerator object that can be used to
522 * retrieve informatino about all the properties in the storage object.
524 * See Windows documentation for more details on IStorage methods.
526 HRESULT WINAPI StorageBaseImpl_EnumElements(
527 IStorage* iface,
528 DWORD reserved1, /* [in] */
529 void* reserved2, /* [size_is][unique][in] */
530 DWORD reserved3, /* [in] */
531 IEnumSTATSTG** ppenum) /* [out] */
533 ICOM_THIS(StorageBaseImpl,iface);
534 IEnumSTATSTGImpl* newEnum;
536 TRACE("(%p, %ld, %p, %ld, %p)\n",
537 iface, reserved1, reserved2, reserved3, ppenum);
540 * Perform a sanity check on the parameters.
542 if ( (This==0) || (ppenum==0))
543 return E_INVALIDARG;
546 * Construct the enumerator.
548 newEnum = IEnumSTATSTGImpl_Construct(
549 This->ancestorStorage,
550 This->rootPropertySetIndex);
552 if (newEnum!=0)
554 *ppenum = (IEnumSTATSTG*)newEnum;
557 * Don't forget to nail down a reference to the new object before
558 * returning it.
560 IEnumSTATSTGImpl_AddRef(*ppenum);
562 return S_OK;
565 return E_OUTOFMEMORY;
568 /************************************************************************
569 * Storage32BaseImpl_Stat (IStorage)
571 * This method will retrieve information about this storage object.
573 * See Windows documentation for more details on IStorage methods.
575 HRESULT WINAPI StorageBaseImpl_Stat(
576 IStorage* iface,
577 STATSTG* pstatstg, /* [out] */
578 DWORD grfStatFlag) /* [in] */
580 ICOM_THIS(StorageBaseImpl,iface);
581 StgProperty curProperty;
582 BOOL readSuccessful;
584 TRACE("(%p, %p, %lx)\n",
585 iface, pstatstg, grfStatFlag);
588 * Perform a sanity check on the parameters.
590 if ( (This==0) || (pstatstg==0))
591 return E_INVALIDARG;
594 * Read the information from the property.
596 readSuccessful = StorageImpl_ReadProperty(
597 This->ancestorStorage,
598 This->rootPropertySetIndex,
599 &curProperty);
601 if (readSuccessful)
603 StorageUtl_CopyPropertyToSTATSTG(
604 pstatstg,
605 &curProperty,
606 grfStatFlag);
608 return S_OK;
611 return E_FAIL;
614 /************************************************************************
615 * Storage32BaseImpl_RenameElement (IStorage)
617 * This method will rename the specified element.
619 * See Windows documentation for more details on IStorage methods.
621 * Implementation notes: The method used to rename consists of creating a clone
622 * of the deleted StgProperty object setting it with the new name and to
623 * perform a DestroyElement of the old StgProperty.
625 HRESULT WINAPI StorageBaseImpl_RenameElement(
626 IStorage* iface,
627 const OLECHAR* pwcsOldName, /* [in] */
628 const OLECHAR* pwcsNewName) /* [in] */
630 ICOM_THIS(StorageBaseImpl,iface);
631 IEnumSTATSTGImpl* propertyEnumeration;
632 StgProperty currentProperty;
633 ULONG foundPropertyIndex;
635 TRACE("(%p, %s, %s)\n",
636 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
639 * Create a property enumeration to search the properties
641 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
642 This->rootPropertySetIndex);
645 * Search the enumeration for the new property name
647 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
648 pwcsNewName,
649 &currentProperty);
651 if (foundPropertyIndex != PROPERTY_NULL)
654 * There is already a property with the new name
656 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
657 return STG_E_FILEALREADYEXISTS;
660 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
663 * Search the enumeration for the old property name
665 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
666 pwcsOldName,
667 &currentProperty);
670 * Delete the property enumeration since we don't need it anymore
672 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
674 if (foundPropertyIndex != PROPERTY_NULL)
676 StgProperty renamedProperty;
677 ULONG renamedPropertyIndex;
680 * Setup a new property for the renamed property
682 renamedProperty.sizeOfNameString =
683 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
685 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
686 return STG_E_INVALIDNAME;
688 strcpyW(renamedProperty.name, pwcsNewName);
690 renamedProperty.propertyType = currentProperty.propertyType;
691 renamedProperty.startingBlock = currentProperty.startingBlock;
692 renamedProperty.size.s.LowPart = currentProperty.size.s.LowPart;
693 renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
695 renamedProperty.previousProperty = PROPERTY_NULL;
696 renamedProperty.nextProperty = PROPERTY_NULL;
699 * Bring the dirProperty link in case it is a storage and in which
700 * case the renamed storage elements don't require to be reorganized.
702 renamedProperty.dirProperty = currentProperty.dirProperty;
704 /* call CoFileTime to get the current time
705 renamedProperty.timeStampS1
706 renamedProperty.timeStampD1
707 renamedProperty.timeStampS2
708 renamedProperty.timeStampD2
709 renamedProperty.propertyUniqueID
713 * Obtain a free property in the property chain
715 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
718 * Save the new property into the new property spot
720 StorageImpl_WriteProperty(
721 This->ancestorStorage,
722 renamedPropertyIndex,
723 &renamedProperty);
726 * Find a spot in the property chain for our newly created property.
728 updatePropertyChain(
729 (StorageImpl*)This,
730 renamedPropertyIndex,
731 renamedProperty);
734 * At this point the renamed property has been inserted in the tree,
735 * now, before to Destroy the old property we must zeroed it's dirProperty
736 * otherwise the DestroyProperty below will zap it all and we do not want
737 * this to happen.
738 * Also, we fake that the old property is a storage so the DestroyProperty
739 * will not do a SetSize(0) on the stream data.
741 * This means that we need to tweek the StgProperty if it is a stream or a
742 * non empty storage.
744 StorageImpl_ReadProperty(This->ancestorStorage,
745 foundPropertyIndex,
746 &currentProperty);
748 currentProperty.dirProperty = PROPERTY_NULL;
749 currentProperty.propertyType = PROPTYPE_STORAGE;
750 StorageImpl_WriteProperty(
751 This->ancestorStorage,
752 foundPropertyIndex,
753 &currentProperty);
756 * Invoke Destroy to get rid of the ole property and automatically redo
757 * the linking of it's previous and next members...
759 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
762 else
765 * There is no property with the old name
767 return STG_E_FILENOTFOUND;
770 return S_OK;
773 /************************************************************************
774 * Storage32BaseImpl_CreateStream (IStorage)
776 * This method will create a stream object within this storage
778 * See Windows documentation for more details on IStorage methods.
780 HRESULT WINAPI StorageBaseImpl_CreateStream(
781 IStorage* iface,
782 const OLECHAR* pwcsName, /* [string][in] */
783 DWORD grfMode, /* [in] */
784 DWORD reserved1, /* [in] */
785 DWORD reserved2, /* [in] */
786 IStream** ppstm) /* [out] */
788 ICOM_THIS(StorageBaseImpl,iface);
789 IEnumSTATSTGImpl* propertyEnumeration;
790 StgStreamImpl* newStream;
791 StgProperty currentProperty, newStreamProperty;
792 ULONG foundPropertyIndex, newPropertyIndex;
794 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
795 iface, debugstr_w(pwcsName), grfMode,
796 reserved1, reserved2, ppstm);
799 * Validate parameters
801 if (ppstm == 0)
802 return STG_E_INVALIDPOINTER;
804 if (pwcsName == 0)
805 return STG_E_INVALIDNAME;
808 * Validate the STGM flags
810 if ( FAILED( validateSTGM(grfMode) ))
811 return STG_E_INVALIDFLAG;
814 * As documented.
816 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
817 (grfMode & STGM_DELETEONRELEASE) ||
818 (grfMode & STGM_TRANSACTED) )
819 return STG_E_INVALIDFUNCTION;
822 * Initialize the out parameter
824 *ppstm = 0;
827 * Create a property enumeration to search the properties
829 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
830 This->rootPropertySetIndex);
832 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
833 pwcsName,
834 &currentProperty);
836 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
838 if (foundPropertyIndex != PROPERTY_NULL)
841 * An element with this name already exists
843 if (grfMode & STGM_CREATE)
845 IStorage_DestroyElement(iface, pwcsName);
847 else
848 return STG_E_FILEALREADYEXISTS;
852 * memset the empty property
854 memset(&newStreamProperty, 0, sizeof(StgProperty));
856 newStreamProperty.sizeOfNameString =
857 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
859 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
860 return STG_E_INVALIDNAME;
862 strcpyW(newStreamProperty.name, pwcsName);
864 newStreamProperty.propertyType = PROPTYPE_STREAM;
865 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
866 newStreamProperty.size.s.LowPart = 0;
867 newStreamProperty.size.s.HighPart = 0;
869 newStreamProperty.previousProperty = PROPERTY_NULL;
870 newStreamProperty.nextProperty = PROPERTY_NULL;
871 newStreamProperty.dirProperty = PROPERTY_NULL;
873 /* call CoFileTime to get the current time
874 newStreamProperty.timeStampS1
875 newStreamProperty.timeStampD1
876 newStreamProperty.timeStampS2
877 newStreamProperty.timeStampD2
880 /* newStreamProperty.propertyUniqueID */
883 * Get a free property or create a new one
885 newPropertyIndex = getFreeProperty(This->ancestorStorage);
888 * Save the new property into the new property spot
890 StorageImpl_WriteProperty(
891 This->ancestorStorage,
892 newPropertyIndex,
893 &newStreamProperty);
896 * Find a spot in the property chain for our newly created property.
898 updatePropertyChain(
899 (StorageImpl*)This,
900 newPropertyIndex,
901 newStreamProperty);
904 * Open the stream to return it.
906 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
908 if (newStream != 0)
910 *ppstm = (IStream*)newStream;
913 * Since we are returning a pointer to the interface, we have to nail down
914 * the reference.
916 StgStreamImpl_AddRef(*ppstm);
918 else
920 return STG_E_INSUFFICIENTMEMORY;
923 return S_OK;
926 /************************************************************************
927 * Storage32BaseImpl_SetClass (IStorage)
929 * This method will write the specified CLSID in the property of this
930 * storage.
932 * See Windows documentation for more details on IStorage methods.
934 HRESULT WINAPI StorageBaseImpl_SetClass(
935 IStorage* iface,
936 REFCLSID clsid) /* [in] */
938 ICOM_THIS(StorageBaseImpl,iface);
939 HRESULT hRes = E_FAIL;
940 StgProperty curProperty;
941 BOOL success;
943 TRACE("(%p, %p)\n", iface, clsid);
945 success = StorageImpl_ReadProperty(This->ancestorStorage,
946 This->rootPropertySetIndex,
947 &curProperty);
948 if (success)
950 curProperty.propertyUniqueID = *clsid;
952 success = StorageImpl_WriteProperty(This->ancestorStorage,
953 This->rootPropertySetIndex,
954 &curProperty);
955 if (success)
956 hRes = S_OK;
959 return hRes;
962 /************************************************************************
963 ** Storage32Impl implementation
966 /************************************************************************
967 * Storage32Impl_CreateStorage (IStorage)
969 * This method will create the storage object within the provided storage.
971 * See Windows documentation for more details on IStorage methods.
973 HRESULT WINAPI StorageImpl_CreateStorage(
974 IStorage* iface,
975 const OLECHAR *pwcsName, /* [string][in] */
976 DWORD grfMode, /* [in] */
977 DWORD reserved1, /* [in] */
978 DWORD reserved2, /* [in] */
979 IStorage **ppstg) /* [out] */
981 StorageImpl* const This=(StorageImpl*)iface;
983 IEnumSTATSTGImpl *propertyEnumeration;
984 StgProperty currentProperty;
985 StgProperty newProperty;
986 ULONG foundPropertyIndex;
987 ULONG newPropertyIndex;
988 HRESULT hr;
990 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
991 iface, debugstr_w(pwcsName), grfMode,
992 reserved1, reserved2, ppstg);
995 * Validate parameters
997 if (ppstg == 0)
998 return STG_E_INVALIDPOINTER;
1000 if (pwcsName == 0)
1001 return STG_E_INVALIDNAME;
1004 * Validate the STGM flags
1006 if ( FAILED( validateSTGM(grfMode) ) ||
1007 (grfMode & STGM_DELETEONRELEASE) )
1008 return STG_E_INVALIDFLAG;
1011 * Initialize the out parameter
1013 *ppstg = 0;
1016 * Create a property enumeration and search the properties
1018 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1019 This->rootPropertySetIndex);
1021 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1022 pwcsName,
1023 &currentProperty);
1024 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1026 if (foundPropertyIndex != PROPERTY_NULL)
1029 * An element with this name already exists
1031 if (grfMode & STGM_CREATE)
1032 IStorage_DestroyElement(iface, pwcsName);
1033 else
1034 return STG_E_FILEALREADYEXISTS;
1038 * memset the empty property
1040 memset(&newProperty, 0, sizeof(StgProperty));
1042 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1044 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1045 return STG_E_INVALIDNAME;
1047 strcpyW(newProperty.name, pwcsName);
1049 newProperty.propertyType = PROPTYPE_STORAGE;
1050 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1051 newProperty.size.s.LowPart = 0;
1052 newProperty.size.s.HighPart = 0;
1054 newProperty.previousProperty = PROPERTY_NULL;
1055 newProperty.nextProperty = PROPERTY_NULL;
1056 newProperty.dirProperty = PROPERTY_NULL;
1058 /* call CoFileTime to get the current time
1059 newProperty.timeStampS1
1060 newProperty.timeStampD1
1061 newProperty.timeStampS2
1062 newProperty.timeStampD2
1065 /* newStorageProperty.propertyUniqueID */
1068 * Obtain a free property in the property chain
1070 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1073 * Save the new property into the new property spot
1075 StorageImpl_WriteProperty(
1076 This->ancestorStorage,
1077 newPropertyIndex,
1078 &newProperty);
1081 * Find a spot in the property chain for our newly created property.
1083 updatePropertyChain(
1084 This,
1085 newPropertyIndex,
1086 newProperty);
1089 * Open it to get a pointer to return.
1091 hr = IStorage_OpenStorage(
1092 iface,
1093 (OLECHAR*)pwcsName,
1095 grfMode,
1098 ppstg);
1100 if( (hr != S_OK) || (*ppstg == NULL))
1102 return hr;
1106 return S_OK;
1110 /***************************************************************************
1112 * Internal Method
1114 * Get a free property or create a new one.
1116 static ULONG getFreeProperty(
1117 StorageImpl *storage)
1119 ULONG currentPropertyIndex = 0;
1120 ULONG newPropertyIndex = PROPERTY_NULL;
1121 BOOL readSuccessful = TRUE;
1122 StgProperty currentProperty;
1127 * Start by reading the root property
1129 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1130 currentPropertyIndex,
1131 &currentProperty);
1132 if (readSuccessful)
1134 if (currentProperty.sizeOfNameString == 0)
1137 * The property existis and is available, we found it.
1139 newPropertyIndex = currentPropertyIndex;
1142 else
1145 * We exhausted the property list, we will create more space below
1147 newPropertyIndex = currentPropertyIndex;
1149 currentPropertyIndex++;
1151 } while (newPropertyIndex == PROPERTY_NULL);
1154 * grow the property chain
1156 if (! readSuccessful)
1158 StgProperty emptyProperty;
1159 ULARGE_INTEGER newSize;
1160 ULONG propertyIndex;
1161 ULONG lastProperty = 0;
1162 ULONG blockCount = 0;
1165 * obtain the new count of property blocks
1167 blockCount = BlockChainStream_GetCount(
1168 storage->ancestorStorage->rootBlockChain)+1;
1171 * initialize the size used by the property stream
1173 newSize.s.HighPart = 0;
1174 newSize.s.LowPart = storage->bigBlockSize * blockCount;
1177 * add a property block to the property chain
1179 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1182 * memset the empty property in order to initialize the unused newly
1183 * created property
1185 memset(&emptyProperty, 0, sizeof(StgProperty));
1188 * initialize them
1190 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1192 for(
1193 propertyIndex = newPropertyIndex;
1194 propertyIndex < lastProperty;
1195 propertyIndex++)
1197 StorageImpl_WriteProperty(
1198 storage->ancestorStorage,
1199 propertyIndex,
1200 &emptyProperty);
1204 return newPropertyIndex;
1207 /****************************************************************************
1209 * Internal Method
1211 * Case insensitive comparaison of StgProperty.name by first considering
1212 * their size.
1214 * Returns <0 when newPrpoerty < currentProperty
1215 * >0 when newPrpoerty > currentProperty
1216 * 0 when newPrpoerty == currentProperty
1218 static LONG propertyNameCmp(
1219 OLECHAR *newProperty,
1220 OLECHAR *currentProperty)
1222 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1224 if (diff == 0)
1227 * We compare the string themselves only when they are of the same lenght
1229 diff = lstrcmpiW( newProperty, currentProperty);
1232 return diff;
1235 /****************************************************************************
1237 * Internal Method
1239 * Properly link this new element in the property chain.
1241 static void updatePropertyChain(
1242 StorageImpl *storage,
1243 ULONG newPropertyIndex,
1244 StgProperty newProperty)
1246 StgProperty currentProperty;
1249 * Read the root property
1251 StorageImpl_ReadProperty(storage->ancestorStorage,
1252 storage->rootPropertySetIndex,
1253 &currentProperty);
1255 if (currentProperty.dirProperty != PROPERTY_NULL)
1258 * The root storage contains some element, therefore, start the research
1259 * for the appropriate location.
1261 BOOL found = 0;
1262 ULONG current, next, previous, currentPropertyId;
1265 * Keep the StgProperty sequence number of the storage first property
1267 currentPropertyId = currentProperty.dirProperty;
1270 * Read
1272 StorageImpl_ReadProperty(storage->ancestorStorage,
1273 currentProperty.dirProperty,
1274 &currentProperty);
1276 previous = currentProperty.previousProperty;
1277 next = currentProperty.nextProperty;
1278 current = currentPropertyId;
1280 while (found == 0)
1282 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1284 if (diff < 0)
1286 if (previous != PROPERTY_NULL)
1288 StorageImpl_ReadProperty(storage->ancestorStorage,
1289 previous,
1290 &currentProperty);
1291 current = previous;
1293 else
1295 currentProperty.previousProperty = newPropertyIndex;
1296 StorageImpl_WriteProperty(storage->ancestorStorage,
1297 current,
1298 &currentProperty);
1299 found = 1;
1302 else if (diff > 0)
1304 if (next != PROPERTY_NULL)
1306 StorageImpl_ReadProperty(storage->ancestorStorage,
1307 next,
1308 &currentProperty);
1309 current = next;
1311 else
1313 currentProperty.nextProperty = newPropertyIndex;
1314 StorageImpl_WriteProperty(storage->ancestorStorage,
1315 current,
1316 &currentProperty);
1317 found = 1;
1320 else
1323 * Trying to insert an item with the same name in the
1324 * subtree structure.
1326 assert(FALSE);
1329 previous = currentProperty.previousProperty;
1330 next = currentProperty.nextProperty;
1333 else
1336 * The root storage is empty, link the new property to it's dir property
1338 currentProperty.dirProperty = newPropertyIndex;
1339 StorageImpl_WriteProperty(storage->ancestorStorage,
1340 storage->rootPropertySetIndex,
1341 &currentProperty);
1346 /*************************************************************************
1347 * CopyTo (IStorage)
1349 HRESULT WINAPI StorageImpl_CopyTo(
1350 IStorage* iface,
1351 DWORD ciidExclude, /* [in] */
1352 const IID* rgiidExclude, /* [size_is][unique][in] */
1353 SNB snbExclude, /* [unique][in] */
1354 IStorage* pstgDest) /* [unique][in] */
1356 IEnumSTATSTG *elements = 0;
1357 STATSTG curElement, strStat;
1358 HRESULT hr;
1359 IStorage *pstgTmp, *pstgChild;
1360 IStream *pstrTmp, *pstrChild;
1362 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1363 FIXME("Exclude option not implemented\n");
1365 TRACE("(%p, %ld, %p, %p, %p)\n",
1366 iface, ciidExclude, rgiidExclude,
1367 snbExclude, pstgDest);
1370 * Perform a sanity check
1372 if ( pstgDest == 0 )
1373 return STG_E_INVALIDPOINTER;
1376 * Enumerate the elements
1378 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1380 if ( hr != S_OK )
1381 return hr;
1384 * set the class ID
1386 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1387 IStorage_SetClass( pstgDest, &curElement.clsid );
1392 * Obtain the next element
1394 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1396 if ( hr == S_FALSE )
1398 hr = S_OK; /* done, every element has been copied */
1399 break;
1402 if (curElement.type == STGTY_STORAGE)
1405 * open child source storage
1407 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1408 STGM_READ|STGM_SHARE_EXCLUSIVE,
1409 NULL, 0, &pstgChild );
1411 if (hr != S_OK)
1412 break;
1415 * Check if destination storage is not a child of the source
1416 * storage, which will cause an infinite loop
1418 if (pstgChild == pstgDest)
1420 IEnumSTATSTG_Release(elements);
1422 return STG_E_ACCESSDENIED;
1426 * create a new storage in destination storage
1428 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1429 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1430 0, 0,
1431 &pstgTmp );
1433 * if it already exist, don't create a new one use this one
1435 if (hr == STG_E_FILEALREADYEXISTS)
1437 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1438 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1439 NULL, 0, &pstgTmp );
1442 if (hr != S_OK)
1443 break;
1447 * do the copy recursively
1449 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1450 snbExclude, pstgTmp );
1452 IStorage_Release( pstgTmp );
1453 IStorage_Release( pstgChild );
1455 else if (curElement.type == STGTY_STREAM)
1458 * create a new stream in destination storage. If the stream already
1459 * exist, it will be deleted and a new one will be created.
1461 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1462 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1463 0, 0, &pstrTmp );
1465 if (hr != S_OK)
1466 break;
1469 * open child stream storage
1471 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1472 STGM_READ|STGM_SHARE_EXCLUSIVE,
1473 0, &pstrChild );
1475 if (hr != S_OK)
1476 break;
1479 * Get the size of the source stream
1481 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1484 * Set the size of the destination stream.
1486 IStream_SetSize(pstrTmp, strStat.cbSize);
1489 * do the copy
1491 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1492 NULL, NULL );
1494 IStream_Release( pstrTmp );
1495 IStream_Release( pstrChild );
1497 else
1499 WARN("unknown element type: %ld\n", curElement.type);
1502 } while (hr == S_OK);
1505 * Clean-up
1507 IEnumSTATSTG_Release(elements);
1509 return hr;
1512 /*************************************************************************
1513 * MoveElementTo (IStorage)
1515 HRESULT WINAPI StorageImpl_MoveElementTo(
1516 IStorage* iface,
1517 const OLECHAR *pwcsName, /* [string][in] */
1518 IStorage *pstgDest, /* [unique][in] */
1519 const OLECHAR *pwcsNewName,/* [string][in] */
1520 DWORD grfFlags) /* [in] */
1522 FIXME("not implemented!\n");
1523 return E_NOTIMPL;
1526 /*************************************************************************
1527 * Commit (IStorage)
1529 HRESULT WINAPI StorageImpl_Commit(
1530 IStorage* iface,
1531 DWORD grfCommitFlags)/* [in] */
1533 FIXME("(%ld): stub!\n", grfCommitFlags);
1534 return S_OK;
1537 /*************************************************************************
1538 * Revert (IStorage)
1540 HRESULT WINAPI StorageImpl_Revert(
1541 IStorage* iface)
1543 FIXME("not implemented!\n");
1544 return E_NOTIMPL;
1547 /*************************************************************************
1548 * DestroyElement (IStorage)
1550 * Stategy: This implementation is build this way for simplicity not for speed.
1551 * I always delete the top most element of the enumeration and adjust
1552 * the deleted element pointer all the time. This takes longer to
1553 * do but allow to reinvoke DestroyElement whenever we encounter a
1554 * storage object. The optimisation reside in the usage of another
1555 * enumeration stategy that would give all the leaves of a storage
1556 * first. (postfix order)
1558 HRESULT WINAPI StorageImpl_DestroyElement(
1559 IStorage* iface,
1560 const OLECHAR *pwcsName)/* [string][in] */
1562 StorageImpl* const This=(StorageImpl*)iface;
1564 IEnumSTATSTGImpl* propertyEnumeration;
1565 HRESULT hr = S_OK;
1566 BOOL res;
1567 StgProperty propertyToDelete;
1568 StgProperty parentProperty;
1569 ULONG foundPropertyIndexToDelete;
1570 ULONG typeOfRelation;
1571 ULONG parentPropertyId;
1573 TRACE("(%p, %s)\n",
1574 iface, debugstr_w(pwcsName));
1577 * Perform a sanity check on the parameters.
1579 if (pwcsName==NULL)
1580 return STG_E_INVALIDPOINTER;
1583 * Create a property enumeration to search the property with the given name
1585 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1586 This->ancestorStorage,
1587 This->rootPropertySetIndex);
1589 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1590 propertyEnumeration,
1591 pwcsName,
1592 &propertyToDelete);
1594 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1596 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1598 return STG_E_FILENOTFOUND;
1602 * Find the parent property of the property to delete (the one that
1603 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1604 * the parent is This. Otherwise, the parent is one of it's sibling...
1608 * First, read This's StgProperty..
1610 res = StorageImpl_ReadProperty(
1611 This->ancestorStorage,
1612 This->rootPropertySetIndex,
1613 &parentProperty);
1615 assert(res==TRUE);
1618 * Second, check to see if by any chance the actual storage (This) is not
1619 * the parent of the property to delete... We never know...
1621 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1624 * Set data as it would have been done in the else part...
1626 typeOfRelation = PROPERTY_RELATION_DIR;
1627 parentPropertyId = This->rootPropertySetIndex;
1629 else
1632 * Create a property enumeration to search the parent properties, and
1633 * delete it once done.
1635 IEnumSTATSTGImpl* propertyEnumeration2;
1637 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1638 This->ancestorStorage,
1639 This->rootPropertySetIndex);
1641 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1642 propertyEnumeration2,
1643 foundPropertyIndexToDelete,
1644 &parentProperty,
1645 &parentPropertyId);
1647 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1650 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1652 hr = deleteStorageProperty(
1653 This,
1654 foundPropertyIndexToDelete,
1655 propertyToDelete);
1657 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1659 hr = deleteStreamProperty(
1660 This,
1661 foundPropertyIndexToDelete,
1662 propertyToDelete);
1665 if (hr!=S_OK)
1666 return hr;
1669 * Adjust the property chain
1671 hr = adjustPropertyChain(
1672 This,
1673 propertyToDelete,
1674 parentProperty,
1675 parentPropertyId,
1676 typeOfRelation);
1678 return hr;
1682 /************************************************************************
1683 * StorageImpl_Stat (IStorage)
1685 * This method will retrieve information about this storage object.
1687 * See Windows documentation for more details on IStorage methods.
1689 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1690 STATSTG* pstatstg, /* [out] */
1691 DWORD grfStatFlag) /* [in] */
1693 StorageImpl* const This = (StorageImpl*)iface;
1694 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1696 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1698 CoTaskMemFree(pstatstg->pwcsName);
1699 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1700 strcpyW(pstatstg->pwcsName, This->pwcsName);
1703 return result;
1708 /*********************************************************************
1710 * Internal Method
1712 * Perform the deletion of a complete storage node
1715 static HRESULT deleteStorageProperty(
1716 StorageImpl *parentStorage,
1717 ULONG indexOfPropertyToDelete,
1718 StgProperty propertyToDelete)
1720 IEnumSTATSTG *elements = 0;
1721 IStorage *childStorage = 0;
1722 STATSTG currentElement;
1723 HRESULT hr;
1724 HRESULT destroyHr = S_OK;
1727 * Open the storage and enumerate it
1729 hr = StorageBaseImpl_OpenStorage(
1730 (IStorage*)parentStorage,
1731 propertyToDelete.name,
1733 STGM_SHARE_EXCLUSIVE,
1736 &childStorage);
1738 if (hr != S_OK)
1740 return hr;
1744 * Enumerate the elements
1746 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1751 * Obtain the next element
1753 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1754 if (hr==S_OK)
1756 destroyHr = StorageImpl_DestroyElement(
1757 (IStorage*)childStorage,
1758 (OLECHAR*)currentElement.pwcsName);
1760 CoTaskMemFree(currentElement.pwcsName);
1764 * We need to Reset the enumeration every time because we delete elements
1765 * and the enumeration could be invalid
1767 IEnumSTATSTG_Reset(elements);
1769 } while ((hr == S_OK) && (destroyHr == S_OK));
1772 * Invalidate the property by zeroing it's name member.
1774 propertyToDelete.sizeOfNameString = 0;
1776 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1777 indexOfPropertyToDelete,
1778 &propertyToDelete);
1780 IStorage_Release(childStorage);
1781 IEnumSTATSTG_Release(elements);
1783 return destroyHr;
1786 /*********************************************************************
1788 * Internal Method
1790 * Perform the deletion of a stream node
1793 static HRESULT deleteStreamProperty(
1794 StorageImpl *parentStorage,
1795 ULONG indexOfPropertyToDelete,
1796 StgProperty propertyToDelete)
1798 IStream *pis;
1799 HRESULT hr;
1800 ULARGE_INTEGER size;
1802 size.s.HighPart = 0;
1803 size.s.LowPart = 0;
1805 hr = StorageBaseImpl_OpenStream(
1806 (IStorage*)parentStorage,
1807 (OLECHAR*)propertyToDelete.name,
1808 NULL,
1809 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1811 &pis);
1813 if (hr!=S_OK)
1815 return(hr);
1819 * Zap the stream
1821 hr = IStream_SetSize(pis, size);
1823 if(hr != S_OK)
1825 return hr;
1829 * Release the stream object.
1831 IStream_Release(pis);
1834 * Invalidate the property by zeroing it's name member.
1836 propertyToDelete.sizeOfNameString = 0;
1839 * Here we should re-read the property so we get the updated pointer
1840 * but since we are here to zap it, I don't do it...
1842 StorageImpl_WriteProperty(
1843 parentStorage->ancestorStorage,
1844 indexOfPropertyToDelete,
1845 &propertyToDelete);
1847 return S_OK;
1850 /*********************************************************************
1852 * Internal Method
1854 * Finds a placeholder for the StgProperty within the Storage
1857 static HRESULT findPlaceholder(
1858 StorageImpl *storage,
1859 ULONG propertyIndexToStore,
1860 ULONG storePropertyIndex,
1861 INT typeOfRelation)
1863 StgProperty storeProperty;
1864 HRESULT hr = S_OK;
1865 BOOL res = TRUE;
1868 * Read the storage property
1870 res = StorageImpl_ReadProperty(
1871 storage->ancestorStorage,
1872 storePropertyIndex,
1873 &storeProperty);
1875 if(! res)
1877 return E_FAIL;
1880 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1882 if (storeProperty.previousProperty != PROPERTY_NULL)
1884 return findPlaceholder(
1885 storage,
1886 propertyIndexToStore,
1887 storeProperty.previousProperty,
1888 typeOfRelation);
1890 else
1892 storeProperty.previousProperty = propertyIndexToStore;
1895 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1897 if (storeProperty.nextProperty != PROPERTY_NULL)
1899 return findPlaceholder(
1900 storage,
1901 propertyIndexToStore,
1902 storeProperty.nextProperty,
1903 typeOfRelation);
1905 else
1907 storeProperty.nextProperty = propertyIndexToStore;
1910 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1912 if (storeProperty.dirProperty != PROPERTY_NULL)
1914 return findPlaceholder(
1915 storage,
1916 propertyIndexToStore,
1917 storeProperty.dirProperty,
1918 typeOfRelation);
1920 else
1922 storeProperty.dirProperty = propertyIndexToStore;
1926 hr = StorageImpl_WriteProperty(
1927 storage->ancestorStorage,
1928 storePropertyIndex,
1929 &storeProperty);
1931 if(! hr)
1933 return E_FAIL;
1936 return S_OK;
1939 /*************************************************************************
1941 * Internal Method
1943 * This method takes the previous and the next property link of a property
1944 * to be deleted and find them a place in the Storage.
1946 static HRESULT adjustPropertyChain(
1947 StorageImpl *This,
1948 StgProperty propertyToDelete,
1949 StgProperty parentProperty,
1950 ULONG parentPropertyId,
1951 INT typeOfRelation)
1953 ULONG newLinkProperty = PROPERTY_NULL;
1954 BOOL needToFindAPlaceholder = FALSE;
1955 ULONG storeNode = PROPERTY_NULL;
1956 ULONG toStoreNode = PROPERTY_NULL;
1957 INT relationType = 0;
1958 HRESULT hr = S_OK;
1959 BOOL res = TRUE;
1961 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1963 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1966 * Set the parent previous to the property to delete previous
1968 newLinkProperty = propertyToDelete.previousProperty;
1970 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1973 * We also need to find a storage for the other link, setup variables
1974 * to do this at the end...
1976 needToFindAPlaceholder = TRUE;
1977 storeNode = propertyToDelete.previousProperty;
1978 toStoreNode = propertyToDelete.nextProperty;
1979 relationType = PROPERTY_RELATION_NEXT;
1982 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1985 * Set the parent previous to the property to delete next
1987 newLinkProperty = propertyToDelete.nextProperty;
1991 * Link it for real...
1993 parentProperty.previousProperty = newLinkProperty;
1996 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1998 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2001 * Set the parent next to the property to delete next previous
2003 newLinkProperty = propertyToDelete.previousProperty;
2005 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2008 * We also need to find a storage for the other link, setup variables
2009 * to do this at the end...
2011 needToFindAPlaceholder = TRUE;
2012 storeNode = propertyToDelete.previousProperty;
2013 toStoreNode = propertyToDelete.nextProperty;
2014 relationType = PROPERTY_RELATION_NEXT;
2017 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2020 * Set the parent next to the property to delete next
2022 newLinkProperty = propertyToDelete.nextProperty;
2026 * Link it for real...
2028 parentProperty.nextProperty = newLinkProperty;
2030 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2032 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2035 * Set the parent dir to the property to delete previous
2037 newLinkProperty = propertyToDelete.previousProperty;
2039 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2042 * We also need to find a storage for the other link, setup variables
2043 * to do this at the end...
2045 needToFindAPlaceholder = TRUE;
2046 storeNode = propertyToDelete.previousProperty;
2047 toStoreNode = propertyToDelete.nextProperty;
2048 relationType = PROPERTY_RELATION_NEXT;
2051 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2054 * Set the parent dir to the property to delete next
2056 newLinkProperty = propertyToDelete.nextProperty;
2060 * Link it for real...
2062 parentProperty.dirProperty = newLinkProperty;
2066 * Write back the parent property
2068 res = StorageImpl_WriteProperty(
2069 This->ancestorStorage,
2070 parentPropertyId,
2071 &parentProperty);
2072 if(! res)
2074 return E_FAIL;
2078 * If a placeholder is required for the other link, then, find one and
2079 * get out of here...
2081 if (needToFindAPlaceholder)
2083 hr = findPlaceholder(
2084 This,
2085 toStoreNode,
2086 storeNode,
2087 relationType);
2090 return hr;
2094 /******************************************************************************
2095 * SetElementTimes (IStorage)
2097 HRESULT WINAPI StorageImpl_SetElementTimes(
2098 IStorage* iface,
2099 const OLECHAR *pwcsName,/* [string][in] */
2100 const FILETIME *pctime, /* [in] */
2101 const FILETIME *patime, /* [in] */
2102 const FILETIME *pmtime) /* [in] */
2104 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2105 return S_OK;
2108 /******************************************************************************
2109 * SetStateBits (IStorage)
2111 HRESULT WINAPI StorageImpl_SetStateBits(
2112 IStorage* iface,
2113 DWORD grfStateBits,/* [in] */
2114 DWORD grfMask) /* [in] */
2116 FIXME("not implemented!\n");
2117 return E_NOTIMPL;
2120 HRESULT StorageImpl_Construct(
2121 StorageImpl* This,
2122 HANDLE hFile,
2123 LPCOLESTR pwcsName,
2124 ILockBytes* pLkbyt,
2125 DWORD openFlags,
2126 BOOL fileBased,
2127 BOOL fileCreate)
2129 HRESULT hr = S_OK;
2130 StgProperty currentProperty;
2131 BOOL readSuccessful;
2132 ULONG currentPropertyIndex;
2134 if ( FAILED( validateSTGM(openFlags) ))
2135 return STG_E_INVALIDFLAG;
2137 memset(This, 0, sizeof(StorageImpl));
2140 * Initialize the virtual fgunction table.
2142 ICOM_VTBL(This) = &Storage32Impl_Vtbl;
2143 This->v_destructor = &StorageImpl_Destroy;
2146 * This is the top-level storage so initialize the ancester pointer
2147 * to this.
2149 This->ancestorStorage = This;
2152 * Initialize the physical support of the storage.
2154 This->hFile = hFile;
2157 * Store copy of file path.
2159 if(pwcsName) {
2160 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2161 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2162 if (!This->pwcsName)
2163 return STG_E_INSUFFICIENTMEMORY;
2164 strcpyW(This->pwcsName, pwcsName);
2168 * Initialize the big block cache.
2170 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2171 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2172 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2173 pLkbyt,
2174 openFlags,
2175 This->bigBlockSize,
2176 fileBased);
2178 if (This->bigBlockFile == 0)
2179 return E_FAIL;
2181 if (fileCreate)
2183 ULARGE_INTEGER size;
2184 BYTE* bigBlockBuffer;
2187 * Initialize all header variables:
2188 * - The big block depot consists of one block and it is at block 0
2189 * - The properties start at block 1
2190 * - There is no small block depot
2192 memset( This->bigBlockDepotStart,
2193 BLOCK_UNUSED,
2194 sizeof(This->bigBlockDepotStart));
2196 This->bigBlockDepotCount = 1;
2197 This->bigBlockDepotStart[0] = 0;
2198 This->rootStartBlock = 1;
2199 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2200 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2201 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2202 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2203 This->extBigBlockDepotCount = 0;
2205 StorageImpl_SaveFileHeader(This);
2208 * Add one block for the big block depot and one block for the properties
2210 size.s.HighPart = 0;
2211 size.s.LowPart = This->bigBlockSize * 3;
2212 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2215 * Initialize the big block depot
2217 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2218 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2219 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2220 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2221 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2223 else
2226 * Load the header for the file.
2228 hr = StorageImpl_LoadFileHeader(This);
2230 if (FAILED(hr))
2232 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2234 return hr;
2239 * There is no block depot cached yet.
2241 This->indexBlockDepotCached = 0xFFFFFFFF;
2244 * Start searching for free blocks with block 0.
2246 This->prevFreeBlock = 0;
2249 * Create the block chain abstractions.
2251 This->rootBlockChain =
2252 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2254 This->smallBlockDepotChain = BlockChainStream_Construct(
2255 This,
2256 &This->smallBlockDepotStart,
2257 PROPERTY_NULL);
2260 * Write the root property
2262 if (fileCreate)
2264 StgProperty rootProp;
2266 * Initialize the property chain
2268 memset(&rootProp, 0, sizeof(rootProp));
2269 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2270 sizeof(rootProp.name)/sizeof(WCHAR) );
2271 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2272 rootProp.propertyType = PROPTYPE_ROOT;
2273 rootProp.previousProperty = PROPERTY_NULL;
2274 rootProp.nextProperty = PROPERTY_NULL;
2275 rootProp.dirProperty = PROPERTY_NULL;
2276 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2277 rootProp.size.s.HighPart = 0;
2278 rootProp.size.s.LowPart = 0;
2280 StorageImpl_WriteProperty(This, 0, &rootProp);
2284 * Find the ID of the root int he property sets.
2286 currentPropertyIndex = 0;
2290 readSuccessful = StorageImpl_ReadProperty(
2291 This,
2292 currentPropertyIndex,
2293 &currentProperty);
2295 if (readSuccessful)
2297 if ( (currentProperty.sizeOfNameString != 0 ) &&
2298 (currentProperty.propertyType == PROPTYPE_ROOT) )
2300 This->rootPropertySetIndex = currentPropertyIndex;
2304 currentPropertyIndex++;
2306 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2308 if (!readSuccessful)
2310 /* TODO CLEANUP */
2311 return E_FAIL;
2315 * Create the block chain abstraction for the small block root chain.
2317 This->smallBlockRootChain = BlockChainStream_Construct(
2318 This,
2319 NULL,
2320 This->rootPropertySetIndex);
2322 return hr;
2325 void StorageImpl_Destroy(
2326 StorageImpl* This)
2328 TRACE("(%p)\n", This);
2330 if(This->pwcsName)
2331 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2333 BlockChainStream_Destroy(This->smallBlockRootChain);
2334 BlockChainStream_Destroy(This->rootBlockChain);
2335 BlockChainStream_Destroy(This->smallBlockDepotChain);
2337 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2338 return;
2341 /******************************************************************************
2342 * Storage32Impl_GetNextFreeBigBlock
2344 * Returns the index of the next free big block.
2345 * If the big block depot is filled, this method will enlarge it.
2348 ULONG StorageImpl_GetNextFreeBigBlock(
2349 StorageImpl* This)
2351 ULONG depotBlockIndexPos;
2352 void *depotBuffer;
2353 ULONG depotBlockOffset;
2354 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2355 ULONG nextBlockIndex = BLOCK_SPECIAL;
2356 int depotIndex = 0;
2357 ULONG freeBlock = BLOCK_UNUSED;
2359 depotIndex = This->prevFreeBlock / blocksPerDepot;
2360 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2363 * Scan the entire big block depot until we find a block marked free
2365 while (nextBlockIndex != BLOCK_UNUSED)
2367 if (depotIndex < COUNT_BBDEPOTINHEADER)
2369 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2372 * Grow the primary depot.
2374 if (depotBlockIndexPos == BLOCK_UNUSED)
2376 depotBlockIndexPos = depotIndex*blocksPerDepot;
2379 * Add a block depot.
2381 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2382 This->bigBlockDepotCount++;
2383 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2386 * Flag it as a block depot.
2388 StorageImpl_SetNextBlockInChain(This,
2389 depotBlockIndexPos,
2390 BLOCK_SPECIAL);
2392 /* Save new header information.
2394 StorageImpl_SaveFileHeader(This);
2397 else
2399 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2401 if (depotBlockIndexPos == BLOCK_UNUSED)
2404 * Grow the extended depot.
2406 ULONG extIndex = BLOCK_UNUSED;
2407 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2408 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2410 if (extBlockOffset == 0)
2412 /* We need an extended block.
2414 extIndex = Storage32Impl_AddExtBlockDepot(This);
2415 This->extBigBlockDepotCount++;
2416 depotBlockIndexPos = extIndex + 1;
2418 else
2419 depotBlockIndexPos = depotIndex * blocksPerDepot;
2422 * Add a block depot and mark it in the extended block.
2424 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2425 This->bigBlockDepotCount++;
2426 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2428 /* Flag the block depot.
2430 StorageImpl_SetNextBlockInChain(This,
2431 depotBlockIndexPos,
2432 BLOCK_SPECIAL);
2434 /* If necessary, flag the extended depot block.
2436 if (extIndex != BLOCK_UNUSED)
2437 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2439 /* Save header information.
2441 StorageImpl_SaveFileHeader(This);
2445 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2447 if (depotBuffer != 0)
2449 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2450 ( nextBlockIndex != BLOCK_UNUSED))
2452 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2454 if (nextBlockIndex == BLOCK_UNUSED)
2456 freeBlock = (depotIndex * blocksPerDepot) +
2457 (depotBlockOffset/sizeof(ULONG));
2460 depotBlockOffset += sizeof(ULONG);
2463 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2466 depotIndex++;
2467 depotBlockOffset = 0;
2470 This->prevFreeBlock = freeBlock;
2472 return freeBlock;
2475 /******************************************************************************
2476 * Storage32Impl_AddBlockDepot
2478 * This will create a depot block, essentially it is a block initialized
2479 * to BLOCK_UNUSEDs.
2481 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2483 BYTE* blockBuffer;
2485 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2488 * Initialize blocks as free
2490 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2492 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2495 /******************************************************************************
2496 * Storage32Impl_GetExtDepotBlock
2498 * Returns the index of the block that corresponds to the specified depot
2499 * index. This method is only for depot indexes equal or greater than
2500 * COUNT_BBDEPOTINHEADER.
2502 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2504 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2505 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2506 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2507 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2508 ULONG blockIndex = BLOCK_UNUSED;
2509 ULONG extBlockIndex = This->extBigBlockDepotStart;
2511 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2513 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2514 return BLOCK_UNUSED;
2516 while (extBlockCount > 0)
2518 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2519 extBlockCount--;
2522 if (extBlockIndex != BLOCK_UNUSED)
2524 BYTE* depotBuffer;
2526 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2528 if (depotBuffer != 0)
2530 StorageUtl_ReadDWord(depotBuffer,
2531 extBlockOffset * sizeof(ULONG),
2532 &blockIndex);
2534 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2538 return blockIndex;
2541 /******************************************************************************
2542 * Storage32Impl_SetExtDepotBlock
2544 * Associates the specified block index to the specified depot index.
2545 * This method is only for depot indexes equal or greater than
2546 * COUNT_BBDEPOTINHEADER.
2548 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2549 ULONG depotIndex,
2550 ULONG blockIndex)
2552 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2553 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2554 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2555 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2556 ULONG extBlockIndex = This->extBigBlockDepotStart;
2558 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2560 while (extBlockCount > 0)
2562 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2563 extBlockCount--;
2566 if (extBlockIndex != BLOCK_UNUSED)
2568 BYTE* depotBuffer;
2570 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2572 if (depotBuffer != 0)
2574 StorageUtl_WriteDWord(depotBuffer,
2575 extBlockOffset * sizeof(ULONG),
2576 blockIndex);
2578 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2583 /******************************************************************************
2584 * Storage32Impl_AddExtBlockDepot
2586 * Creates an extended depot block.
2588 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2590 ULONG numExtBlocks = This->extBigBlockDepotCount;
2591 ULONG nextExtBlock = This->extBigBlockDepotStart;
2592 BYTE* depotBuffer = NULL;
2593 ULONG index = BLOCK_UNUSED;
2594 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2595 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2596 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2598 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2599 blocksPerDepotBlock;
2601 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2604 * The first extended block.
2606 This->extBigBlockDepotStart = index;
2608 else
2610 int i;
2612 * Follow the chain to the last one.
2614 for (i = 0; i < (numExtBlocks - 1); i++)
2616 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2620 * Add the new extended block to the chain.
2622 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2623 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2624 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2628 * Initialize this block.
2630 depotBuffer = StorageImpl_GetBigBlock(This, index);
2631 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2632 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2634 return index;
2637 /******************************************************************************
2638 * Storage32Impl_FreeBigBlock
2640 * This method will flag the specified block as free in the big block depot.
2642 void StorageImpl_FreeBigBlock(
2643 StorageImpl* This,
2644 ULONG blockIndex)
2646 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2648 if (blockIndex < This->prevFreeBlock)
2649 This->prevFreeBlock = blockIndex;
2652 /************************************************************************
2653 * Storage32Impl_GetNextBlockInChain
2655 * This method will retrieve the block index of the next big block in
2656 * in the chain.
2658 * Params: This - Pointer to the Storage object.
2659 * blockIndex - Index of the block to retrieve the chain
2660 * for.
2662 * Returns: This method returns the index of the next block in the chain.
2663 * It will return the constants:
2664 * BLOCK_SPECIAL - If the block given was not part of a
2665 * chain.
2666 * BLOCK_END_OF_CHAIN - If the block given was the last in
2667 * a chain.
2668 * BLOCK_UNUSED - If the block given was not past of a chain
2669 * and is available.
2670 * BLOCK_EXTBBDEPOT - This block is part of the extended
2671 * big block depot.
2673 * See Windows documentation for more details on IStorage methods.
2675 ULONG StorageImpl_GetNextBlockInChain(
2676 StorageImpl* This,
2677 ULONG blockIndex)
2679 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2680 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2681 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2682 ULONG nextBlockIndex = BLOCK_SPECIAL;
2683 void* depotBuffer;
2684 ULONG depotBlockIndexPos;
2686 assert(depotBlockCount < This->bigBlockDepotCount);
2689 * Cache the currently accessed depot block.
2691 if (depotBlockCount != This->indexBlockDepotCached)
2693 This->indexBlockDepotCached = depotBlockCount;
2695 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2697 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2699 else
2702 * We have to look in the extended depot.
2704 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2707 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2709 if (depotBuffer!=0)
2711 int index;
2713 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2715 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2716 This->blockDepotCached[index] = nextBlockIndex;
2719 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2723 nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2725 return nextBlockIndex;
2728 /******************************************************************************
2729 * Storage32Impl_GetNextExtendedBlock
2731 * Given an extended block this method will return the next extended block.
2733 * NOTES:
2734 * The last ULONG of an extended block is the block index of the next
2735 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2736 * depot.
2738 * Return values:
2739 * - The index of the next extended block
2740 * - BLOCK_UNUSED: there is no next extended block.
2741 * - Any other return values denotes failure.
2743 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2745 ULONG nextBlockIndex = BLOCK_SPECIAL;
2746 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2747 void* depotBuffer;
2749 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2751 if (depotBuffer!=0)
2753 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2755 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2758 return nextBlockIndex;
2761 /******************************************************************************
2762 * Storage32Impl_SetNextBlockInChain
2764 * This method will write the index of the specified block's next block
2765 * in the big block depot.
2767 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2768 * do the following
2770 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2771 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2772 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2775 void StorageImpl_SetNextBlockInChain(
2776 StorageImpl* This,
2777 ULONG blockIndex,
2778 ULONG nextBlock)
2780 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2781 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2782 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2783 ULONG depotBlockIndexPos;
2784 void* depotBuffer;
2786 assert(depotBlockCount < This->bigBlockDepotCount);
2787 assert(blockIndex != nextBlock);
2789 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2791 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2793 else
2796 * We have to look in the extended depot.
2798 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2801 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2803 if (depotBuffer!=0)
2805 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2806 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2810 * Update the cached block depot, if necessary.
2812 if (depotBlockCount == This->indexBlockDepotCached)
2814 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2818 /******************************************************************************
2819 * Storage32Impl_LoadFileHeader
2821 * This method will read in the file header, i.e. big block index -1.
2823 HRESULT StorageImpl_LoadFileHeader(
2824 StorageImpl* This)
2826 HRESULT hr = STG_E_FILENOTFOUND;
2827 void* headerBigBlock = NULL;
2828 int index;
2831 * Get a pointer to the big block of data containing the header.
2833 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2836 * Extract the information from the header.
2838 if (headerBigBlock!=0)
2841 * Check for the "magic number" signature and return an error if it is not
2842 * found.
2844 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2846 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2847 return STG_E_OLDFORMAT;
2850 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2852 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2853 return STG_E_INVALIDHEADER;
2856 StorageUtl_ReadWord(
2857 headerBigBlock,
2858 OFFSET_BIGBLOCKSIZEBITS,
2859 &This->bigBlockSizeBits);
2861 StorageUtl_ReadWord(
2862 headerBigBlock,
2863 OFFSET_SMALLBLOCKSIZEBITS,
2864 &This->smallBlockSizeBits);
2866 StorageUtl_ReadDWord(
2867 headerBigBlock,
2868 OFFSET_BBDEPOTCOUNT,
2869 &This->bigBlockDepotCount);
2871 StorageUtl_ReadDWord(
2872 headerBigBlock,
2873 OFFSET_ROOTSTARTBLOCK,
2874 &This->rootStartBlock);
2876 StorageUtl_ReadDWord(
2877 headerBigBlock,
2878 OFFSET_SBDEPOTSTART,
2879 &This->smallBlockDepotStart);
2881 StorageUtl_ReadDWord(
2882 headerBigBlock,
2883 OFFSET_EXTBBDEPOTSTART,
2884 &This->extBigBlockDepotStart);
2886 StorageUtl_ReadDWord(
2887 headerBigBlock,
2888 OFFSET_EXTBBDEPOTCOUNT,
2889 &This->extBigBlockDepotCount);
2891 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2893 StorageUtl_ReadDWord(
2894 headerBigBlock,
2895 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2896 &(This->bigBlockDepotStart[index]));
2900 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2902 if ((1 << 2) == 4)
2904 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2905 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2907 else
2909 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2910 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2914 * Right now, the code is making some assumptions about the size of the
2915 * blocks, just make sure they are what we're expecting.
2917 assert( (This->bigBlockSize==DEF_BIG_BLOCK_SIZE) &&
2918 (This->smallBlockSize==DEF_SMALL_BLOCK_SIZE));
2921 * Release the block.
2923 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2925 hr = S_OK;
2928 return hr;
2931 /******************************************************************************
2932 * Storage32Impl_SaveFileHeader
2934 * This method will save to the file the header, i.e. big block -1.
2936 void StorageImpl_SaveFileHeader(
2937 StorageImpl* This)
2939 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2940 int index;
2941 BOOL success;
2944 * Get a pointer to the big block of data containing the header.
2946 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2949 * If the block read failed, the file is probably new.
2951 if (!success)
2954 * Initialize for all unknown fields.
2956 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2959 * Initialize the magic number.
2961 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2964 * And a bunch of things we don't know what they mean
2966 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2967 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2968 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2969 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2970 StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
2974 * Write the information to the header.
2976 if (headerBigBlock!=0)
2978 StorageUtl_WriteWord(
2979 headerBigBlock,
2980 OFFSET_BIGBLOCKSIZEBITS,
2981 This->bigBlockSizeBits);
2983 StorageUtl_WriteWord(
2984 headerBigBlock,
2985 OFFSET_SMALLBLOCKSIZEBITS,
2986 This->smallBlockSizeBits);
2988 StorageUtl_WriteDWord(
2989 headerBigBlock,
2990 OFFSET_BBDEPOTCOUNT,
2991 This->bigBlockDepotCount);
2993 StorageUtl_WriteDWord(
2994 headerBigBlock,
2995 OFFSET_ROOTSTARTBLOCK,
2996 This->rootStartBlock);
2998 StorageUtl_WriteDWord(
2999 headerBigBlock,
3000 OFFSET_SBDEPOTSTART,
3001 This->smallBlockDepotStart);
3003 StorageUtl_WriteDWord(
3004 headerBigBlock,
3005 OFFSET_EXTBBDEPOTSTART,
3006 This->extBigBlockDepotStart);
3008 StorageUtl_WriteDWord(
3009 headerBigBlock,
3010 OFFSET_EXTBBDEPOTCOUNT,
3011 This->extBigBlockDepotCount);
3013 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3015 StorageUtl_WriteDWord(
3016 headerBigBlock,
3017 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3018 (This->bigBlockDepotStart[index]));
3023 * Write the big block back to the file.
3025 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3028 /******************************************************************************
3029 * Storage32Impl_ReadProperty
3031 * This method will read the specified property from the property chain.
3033 BOOL StorageImpl_ReadProperty(
3034 StorageImpl* This,
3035 ULONG index,
3036 StgProperty* buffer)
3038 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3039 ULARGE_INTEGER offsetInPropSet;
3040 BOOL readSuccessful;
3041 ULONG bytesRead;
3043 offsetInPropSet.s.HighPart = 0;
3044 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3046 readSuccessful = BlockChainStream_ReadAt(
3047 This->rootBlockChain,
3048 offsetInPropSet,
3049 PROPSET_BLOCK_SIZE,
3050 currentProperty,
3051 &bytesRead);
3053 if (readSuccessful)
3055 memset(buffer->name, 0, sizeof(buffer->name));
3056 memcpy(
3057 buffer->name,
3058 currentProperty+OFFSET_PS_NAME,
3059 PROPERTY_NAME_BUFFER_LEN );
3061 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3063 StorageUtl_ReadWord(
3064 currentProperty,
3065 OFFSET_PS_NAMELENGTH,
3066 &buffer->sizeOfNameString);
3068 StorageUtl_ReadDWord(
3069 currentProperty,
3070 OFFSET_PS_PREVIOUSPROP,
3071 &buffer->previousProperty);
3073 StorageUtl_ReadDWord(
3074 currentProperty,
3075 OFFSET_PS_NEXTPROP,
3076 &buffer->nextProperty);
3078 StorageUtl_ReadDWord(
3079 currentProperty,
3080 OFFSET_PS_DIRPROP,
3081 &buffer->dirProperty);
3083 StorageUtl_ReadGUID(
3084 currentProperty,
3085 OFFSET_PS_GUID,
3086 &buffer->propertyUniqueID);
3088 StorageUtl_ReadDWord(
3089 currentProperty,
3090 OFFSET_PS_TSS1,
3091 &buffer->timeStampS1);
3093 StorageUtl_ReadDWord(
3094 currentProperty,
3095 OFFSET_PS_TSD1,
3096 &buffer->timeStampD1);
3098 StorageUtl_ReadDWord(
3099 currentProperty,
3100 OFFSET_PS_TSS2,
3101 &buffer->timeStampS2);
3103 StorageUtl_ReadDWord(
3104 currentProperty,
3105 OFFSET_PS_TSD2,
3106 &buffer->timeStampD2);
3108 StorageUtl_ReadDWord(
3109 currentProperty,
3110 OFFSET_PS_STARTBLOCK,
3111 &buffer->startingBlock);
3113 StorageUtl_ReadDWord(
3114 currentProperty,
3115 OFFSET_PS_SIZE,
3116 &buffer->size.s.LowPart);
3118 buffer->size.s.HighPart = 0;
3121 return readSuccessful;
3124 /*********************************************************************
3125 * Write the specified property into the property chain
3127 BOOL StorageImpl_WriteProperty(
3128 StorageImpl* This,
3129 ULONG index,
3130 StgProperty* buffer)
3132 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3133 ULARGE_INTEGER offsetInPropSet;
3134 BOOL writeSuccessful;
3135 ULONG bytesWritten;
3137 offsetInPropSet.s.HighPart = 0;
3138 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3140 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3142 memcpy(
3143 currentProperty + OFFSET_PS_NAME,
3144 buffer->name,
3145 PROPERTY_NAME_BUFFER_LEN );
3147 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3149 StorageUtl_WriteWord(
3150 currentProperty,
3151 OFFSET_PS_NAMELENGTH,
3152 buffer->sizeOfNameString);
3154 StorageUtl_WriteDWord(
3155 currentProperty,
3156 OFFSET_PS_PREVIOUSPROP,
3157 buffer->previousProperty);
3159 StorageUtl_WriteDWord(
3160 currentProperty,
3161 OFFSET_PS_NEXTPROP,
3162 buffer->nextProperty);
3164 StorageUtl_WriteDWord(
3165 currentProperty,
3166 OFFSET_PS_DIRPROP,
3167 buffer->dirProperty);
3169 StorageUtl_WriteGUID(
3170 currentProperty,
3171 OFFSET_PS_GUID,
3172 &buffer->propertyUniqueID);
3174 StorageUtl_WriteDWord(
3175 currentProperty,
3176 OFFSET_PS_TSS1,
3177 buffer->timeStampS1);
3179 StorageUtl_WriteDWord(
3180 currentProperty,
3181 OFFSET_PS_TSD1,
3182 buffer->timeStampD1);
3184 StorageUtl_WriteDWord(
3185 currentProperty,
3186 OFFSET_PS_TSS2,
3187 buffer->timeStampS2);
3189 StorageUtl_WriteDWord(
3190 currentProperty,
3191 OFFSET_PS_TSD2,
3192 buffer->timeStampD2);
3194 StorageUtl_WriteDWord(
3195 currentProperty,
3196 OFFSET_PS_STARTBLOCK,
3197 buffer->startingBlock);
3199 StorageUtl_WriteDWord(
3200 currentProperty,
3201 OFFSET_PS_SIZE,
3202 buffer->size.s.LowPart);
3204 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3205 offsetInPropSet,
3206 PROPSET_BLOCK_SIZE,
3207 currentProperty,
3208 &bytesWritten);
3209 return writeSuccessful;
3212 BOOL StorageImpl_ReadBigBlock(
3213 StorageImpl* This,
3214 ULONG blockIndex,
3215 void* buffer)
3217 void* bigBlockBuffer;
3219 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3221 if (bigBlockBuffer!=0)
3223 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3225 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3227 return TRUE;
3230 return FALSE;
3233 BOOL StorageImpl_WriteBigBlock(
3234 StorageImpl* This,
3235 ULONG blockIndex,
3236 void* buffer)
3238 void* bigBlockBuffer;
3240 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3242 if (bigBlockBuffer!=0)
3244 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3246 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3248 return TRUE;
3251 return FALSE;
3254 void* StorageImpl_GetROBigBlock(
3255 StorageImpl* This,
3256 ULONG blockIndex)
3258 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3261 void* StorageImpl_GetBigBlock(
3262 StorageImpl* This,
3263 ULONG blockIndex)
3265 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3268 void StorageImpl_ReleaseBigBlock(
3269 StorageImpl* This,
3270 void* pBigBlock)
3272 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3275 /******************************************************************************
3276 * Storage32Impl_SmallBlocksToBigBlocks
3278 * This method will convert a small block chain to a big block chain.
3279 * The small block chain will be destroyed.
3281 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3282 StorageImpl* This,
3283 SmallBlockChainStream** ppsbChain)
3285 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3286 ULARGE_INTEGER size, offset;
3287 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3288 ULONG propertyIndex;
3289 BOOL successRead, successWrite;
3290 StgProperty chainProperty;
3291 BYTE *buffer;
3292 BlockChainStream *bbTempChain = NULL;
3293 BlockChainStream *bigBlockChain = NULL;
3296 * Create a temporary big block chain that doesn't have
3297 * an associated property. This temporary chain will be
3298 * used to copy data from small blocks to big blocks.
3300 bbTempChain = BlockChainStream_Construct(This,
3301 &bbHeadOfChain,
3302 PROPERTY_NULL);
3305 * Grow the big block chain.
3307 size = SmallBlockChainStream_GetSize(*ppsbChain);
3308 BlockChainStream_SetSize(bbTempChain, size);
3311 * Copy the contents of the small block chain to the big block chain
3312 * by small block size increments.
3314 offset.s.LowPart = 0;
3315 offset.s.HighPart = 0;
3316 cbTotalRead = 0;
3317 cbTotalWritten = 0;
3319 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3322 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3323 offset,
3324 DEF_SMALL_BLOCK_SIZE,
3325 buffer,
3326 &cbRead);
3327 cbTotalRead += cbRead;
3329 successWrite = BlockChainStream_WriteAt(bbTempChain,
3330 offset,
3331 cbRead,
3332 buffer,
3333 &cbWritten);
3334 cbTotalWritten += cbWritten;
3336 offset.s.LowPart += This->smallBlockSize;
3338 } while (successRead && successWrite);
3339 HeapFree(GetProcessHeap(),0,buffer);
3341 assert(cbTotalRead == cbTotalWritten);
3344 * Destroy the small block chain.
3346 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3347 size.s.HighPart = 0;
3348 size.s.LowPart = 0;
3349 SmallBlockChainStream_SetSize(*ppsbChain, size);
3350 SmallBlockChainStream_Destroy(*ppsbChain);
3351 *ppsbChain = 0;
3354 * Change the property information. This chain is now a big block chain
3355 * and it doesn't reside in the small blocks chain anymore.
3357 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3359 chainProperty.startingBlock = bbHeadOfChain;
3361 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3364 * Destroy the temporary propertyless big block chain.
3365 * Create a new big block chain associated with this property.
3367 BlockChainStream_Destroy(bbTempChain);
3368 bigBlockChain = BlockChainStream_Construct(This,
3369 NULL,
3370 propertyIndex);
3372 return bigBlockChain;
3375 /******************************************************************************
3376 ** Storage32InternalImpl implementation
3379 StorageInternalImpl* StorageInternalImpl_Construct(
3380 StorageImpl* ancestorStorage,
3381 ULONG rootPropertyIndex)
3383 StorageInternalImpl* newStorage;
3386 * Allocate space for the new storage object
3388 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3390 if (newStorage!=0)
3392 memset(newStorage, 0, sizeof(StorageInternalImpl));
3395 * Initialize the virtual function table.
3397 ICOM_VTBL(newStorage) = &Storage32InternalImpl_Vtbl;
3398 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3401 * Keep the ancestor storage pointer and nail a reference to it.
3403 newStorage->ancestorStorage = ancestorStorage;
3404 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3407 * Keep the index of the root property set for this storage,
3409 newStorage->rootPropertySetIndex = rootPropertyIndex;
3411 return newStorage;
3414 return 0;
3417 void StorageInternalImpl_Destroy(
3418 StorageInternalImpl* This)
3420 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3421 HeapFree(GetProcessHeap(), 0, This);
3424 /******************************************************************************
3426 ** Storage32InternalImpl_Commit
3428 ** The non-root storages cannot be opened in transacted mode thus this function
3429 ** does nothing.
3431 HRESULT WINAPI StorageInternalImpl_Commit(
3432 IStorage* iface,
3433 DWORD grfCommitFlags) /* [in] */
3435 return S_OK;
3438 /******************************************************************************
3440 ** Storage32InternalImpl_Revert
3442 ** The non-root storages cannot be opened in transacted mode thus this function
3443 ** does nothing.
3445 HRESULT WINAPI StorageInternalImpl_Revert(
3446 IStorage* iface)
3448 return S_OK;
3451 /******************************************************************************
3452 ** IEnumSTATSTGImpl implementation
3455 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3456 StorageImpl* parentStorage,
3457 ULONG firstPropertyNode)
3459 IEnumSTATSTGImpl* newEnumeration;
3461 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3463 if (newEnumeration!=0)
3466 * Set-up the virtual function table and reference count.
3468 ICOM_VTBL(newEnumeration) = &IEnumSTATSTGImpl_Vtbl;
3469 newEnumeration->ref = 0;
3472 * We want to nail-down the reference to the storage in case the
3473 * enumeration out-lives the storage in the client application.
3475 newEnumeration->parentStorage = parentStorage;
3476 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3478 newEnumeration->firstPropertyNode = firstPropertyNode;
3481 * Initialize the search stack
3483 newEnumeration->stackSize = 0;
3484 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3485 newEnumeration->stackToVisit =
3486 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3489 * Make sure the current node of the iterator is the first one.
3491 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3494 return newEnumeration;
3497 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3499 IStorage_Release((IStorage*)This->parentStorage);
3500 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3501 HeapFree(GetProcessHeap(), 0, This);
3504 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3505 IEnumSTATSTG* iface,
3506 REFIID riid,
3507 void** ppvObject)
3509 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3512 * Perform a sanity check on the parameters.
3514 if (ppvObject==0)
3515 return E_INVALIDARG;
3518 * Initialize the return parameter.
3520 *ppvObject = 0;
3523 * Compare the riid with the interface IDs implemented by this object.
3525 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3527 *ppvObject = (IEnumSTATSTG*)This;
3529 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3531 *ppvObject = (IEnumSTATSTG*)This;
3535 * Check that we obtained an interface.
3537 if ((*ppvObject)==0)
3538 return E_NOINTERFACE;
3541 * Query Interface always increases the reference count by one when it is
3542 * successful
3544 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3546 return S_OK;
3549 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3550 IEnumSTATSTG* iface)
3552 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3554 This->ref++;
3555 return This->ref;
3558 ULONG WINAPI IEnumSTATSTGImpl_Release(
3559 IEnumSTATSTG* iface)
3561 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3563 ULONG newRef;
3565 This->ref--;
3566 newRef = This->ref;
3569 * If the reference count goes down to 0, perform suicide.
3571 if (newRef==0)
3573 IEnumSTATSTGImpl_Destroy(This);
3576 return newRef;;
3579 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3580 IEnumSTATSTG* iface,
3581 ULONG celt,
3582 STATSTG* rgelt,
3583 ULONG* pceltFetched)
3585 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3587 StgProperty currentProperty;
3588 STATSTG* currentReturnStruct = rgelt;
3589 ULONG objectFetched = 0;
3590 ULONG currentSearchNode;
3593 * Perform a sanity check on the parameters.
3595 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3596 return E_INVALIDARG;
3599 * To avoid the special case, get another pointer to a ULONG value if
3600 * the caller didn't supply one.
3602 if (pceltFetched==0)
3603 pceltFetched = &objectFetched;
3606 * Start the iteration, we will iterate until we hit the end of the
3607 * linked list or until we hit the number of items to iterate through
3609 *pceltFetched = 0;
3612 * Start with the node at the top of the stack.
3614 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3616 while ( ( *pceltFetched < celt) &&
3617 ( currentSearchNode!=PROPERTY_NULL) )
3620 * Remove the top node from the stack
3622 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3625 * Read the property from the storage.
3627 StorageImpl_ReadProperty(This->parentStorage,
3628 currentSearchNode,
3629 &currentProperty);
3632 * Copy the information to the return buffer.
3634 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3635 &currentProperty,
3636 STATFLAG_DEFAULT);
3639 * Step to the next item in the iteration
3641 (*pceltFetched)++;
3642 currentReturnStruct++;
3645 * Push the next search node in the search stack.
3647 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3650 * continue the iteration.
3652 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3655 if (*pceltFetched == celt)
3656 return S_OK;
3658 return S_FALSE;
3662 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3663 IEnumSTATSTG* iface,
3664 ULONG celt)
3666 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3668 StgProperty currentProperty;
3669 ULONG objectFetched = 0;
3670 ULONG currentSearchNode;
3673 * Start with the node at the top of the stack.
3675 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3677 while ( (objectFetched < celt) &&
3678 (currentSearchNode!=PROPERTY_NULL) )
3681 * Remove the top node from the stack
3683 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3686 * Read the property from the storage.
3688 StorageImpl_ReadProperty(This->parentStorage,
3689 currentSearchNode,
3690 &currentProperty);
3693 * Step to the next item in the iteration
3695 objectFetched++;
3698 * Push the next search node in the search stack.
3700 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3703 * continue the iteration.
3705 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3708 if (objectFetched == celt)
3709 return S_OK;
3711 return S_FALSE;
3714 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3715 IEnumSTATSTG* iface)
3717 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3719 StgProperty rootProperty;
3720 BOOL readSuccessful;
3723 * Re-initialize the search stack to an empty stack
3725 This->stackSize = 0;
3728 * Read the root property from the storage.
3730 readSuccessful = StorageImpl_ReadProperty(
3731 This->parentStorage,
3732 This->firstPropertyNode,
3733 &rootProperty);
3735 if (readSuccessful)
3737 assert(rootProperty.sizeOfNameString!=0);
3740 * Push the search node in the search stack.
3742 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3745 return S_OK;
3748 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3749 IEnumSTATSTG* iface,
3750 IEnumSTATSTG** ppenum)
3752 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3754 IEnumSTATSTGImpl* newClone;
3757 * Perform a sanity check on the parameters.
3759 if (ppenum==0)
3760 return E_INVALIDARG;
3762 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3763 This->firstPropertyNode);
3767 * The new clone enumeration must point to the same current node as
3768 * the ole one.
3770 newClone->stackSize = This->stackSize ;
3771 newClone->stackMaxSize = This->stackMaxSize ;
3772 newClone->stackToVisit =
3773 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3775 memcpy(
3776 newClone->stackToVisit,
3777 This->stackToVisit,
3778 sizeof(ULONG) * newClone->stackSize);
3780 *ppenum = (IEnumSTATSTG*)newClone;
3783 * Don't forget to nail down a reference to the clone before
3784 * returning it.
3786 IEnumSTATSTGImpl_AddRef(*ppenum);
3788 return S_OK;
3791 INT IEnumSTATSTGImpl_FindParentProperty(
3792 IEnumSTATSTGImpl *This,
3793 ULONG childProperty,
3794 StgProperty *currentProperty,
3795 ULONG *thisNodeId)
3797 ULONG currentSearchNode;
3798 ULONG foundNode;
3801 * To avoid the special case, get another pointer to a ULONG value if
3802 * the caller didn't supply one.
3804 if (thisNodeId==0)
3805 thisNodeId = &foundNode;
3808 * Start with the node at the top of the stack.
3810 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3813 while (currentSearchNode!=PROPERTY_NULL)
3816 * Store the current node in the returned parameters
3818 *thisNodeId = currentSearchNode;
3821 * Remove the top node from the stack
3823 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3826 * Read the property from the storage.
3828 StorageImpl_ReadProperty(
3829 This->parentStorage,
3830 currentSearchNode,
3831 currentProperty);
3833 if (currentProperty->previousProperty == childProperty)
3834 return PROPERTY_RELATION_PREVIOUS;
3836 else if (currentProperty->nextProperty == childProperty)
3837 return PROPERTY_RELATION_NEXT;
3839 else if (currentProperty->dirProperty == childProperty)
3840 return PROPERTY_RELATION_DIR;
3843 * Push the next search node in the search stack.
3845 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3848 * continue the iteration.
3850 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3853 return PROPERTY_NULL;
3856 ULONG IEnumSTATSTGImpl_FindProperty(
3857 IEnumSTATSTGImpl* This,
3858 const OLECHAR* lpszPropName,
3859 StgProperty* currentProperty)
3861 ULONG currentSearchNode;
3864 * Start with the node at the top of the stack.
3866 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3868 while (currentSearchNode!=PROPERTY_NULL)
3871 * Remove the top node from the stack
3873 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3876 * Read the property from the storage.
3878 StorageImpl_ReadProperty(This->parentStorage,
3879 currentSearchNode,
3880 currentProperty);
3882 if ( propertyNameCmp(
3883 (OLECHAR*)currentProperty->name,
3884 (OLECHAR*)lpszPropName) == 0)
3885 return currentSearchNode;
3888 * Push the next search node in the search stack.
3890 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3893 * continue the iteration.
3895 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3898 return PROPERTY_NULL;
3901 void IEnumSTATSTGImpl_PushSearchNode(
3902 IEnumSTATSTGImpl* This,
3903 ULONG nodeToPush)
3905 StgProperty rootProperty;
3906 BOOL readSuccessful;
3909 * First, make sure we're not trying to push an unexisting node.
3911 if (nodeToPush==PROPERTY_NULL)
3912 return;
3915 * First push the node to the stack
3917 if (This->stackSize == This->stackMaxSize)
3919 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3921 This->stackToVisit = HeapReAlloc(
3922 GetProcessHeap(),
3924 This->stackToVisit,
3925 sizeof(ULONG) * This->stackMaxSize);
3928 This->stackToVisit[This->stackSize] = nodeToPush;
3929 This->stackSize++;
3932 * Read the root property from the storage.
3934 readSuccessful = StorageImpl_ReadProperty(
3935 This->parentStorage,
3936 nodeToPush,
3937 &rootProperty);
3939 if (readSuccessful)
3941 assert(rootProperty.sizeOfNameString!=0);
3944 * Push the previous search node in the search stack.
3946 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3950 ULONG IEnumSTATSTGImpl_PopSearchNode(
3951 IEnumSTATSTGImpl* This,
3952 BOOL remove)
3954 ULONG topNode;
3956 if (This->stackSize == 0)
3957 return PROPERTY_NULL;
3959 topNode = This->stackToVisit[This->stackSize-1];
3961 if (remove)
3962 This->stackSize--;
3964 return topNode;
3967 /******************************************************************************
3968 ** StorageUtl implementation
3971 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
3973 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
3976 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
3978 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
3981 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
3983 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
3986 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
3988 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
3991 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
3993 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
3994 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
3995 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
3997 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4000 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4002 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4003 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4004 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4006 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4009 void StorageUtl_CopyPropertyToSTATSTG(
4010 STATSTG* destination,
4011 StgProperty* source,
4012 int statFlags)
4015 * The copy of the string occurs only when the flag is not set
4017 if ((statFlags & STATFLAG_NONAME) != 0)
4019 destination->pwcsName = 0;
4021 else
4023 destination->pwcsName =
4024 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4026 strcpyW((LPWSTR)destination->pwcsName, source->name);
4029 switch (source->propertyType)
4031 case PROPTYPE_STORAGE:
4032 case PROPTYPE_ROOT:
4033 destination->type = STGTY_STORAGE;
4034 break;
4035 case PROPTYPE_STREAM:
4036 destination->type = STGTY_STREAM;
4037 break;
4038 default:
4039 destination->type = STGTY_STREAM;
4040 break;
4043 destination->cbSize = source->size;
4045 currentReturnStruct->mtime = {0}; TODO
4046 currentReturnStruct->ctime = {0};
4047 currentReturnStruct->atime = {0};
4049 destination->grfMode = 0;
4050 destination->grfLocksSupported = 0;
4051 destination->clsid = source->propertyUniqueID;
4052 destination->grfStateBits = 0;
4053 destination->reserved = 0;
4056 /******************************************************************************
4057 ** BlockChainStream implementation
4060 BlockChainStream* BlockChainStream_Construct(
4061 StorageImpl* parentStorage,
4062 ULONG* headOfStreamPlaceHolder,
4063 ULONG propertyIndex)
4065 BlockChainStream* newStream;
4066 ULONG blockIndex;
4068 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4070 newStream->parentStorage = parentStorage;
4071 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4072 newStream->ownerPropertyIndex = propertyIndex;
4073 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4074 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4075 newStream->numBlocks = 0;
4077 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4079 while (blockIndex != BLOCK_END_OF_CHAIN)
4081 newStream->numBlocks++;
4082 newStream->tailIndex = blockIndex;
4084 blockIndex = StorageImpl_GetNextBlockInChain(
4085 parentStorage,
4086 blockIndex);
4089 return newStream;
4092 void BlockChainStream_Destroy(BlockChainStream* This)
4094 HeapFree(GetProcessHeap(), 0, This);
4097 /******************************************************************************
4098 * BlockChainStream_GetHeadOfChain
4100 * Returns the head of this stream chain.
4101 * Some special chains don't have properties, their heads are kept in
4102 * This->headOfStreamPlaceHolder.
4105 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4107 StgProperty chainProperty;
4108 BOOL readSuccessful;
4110 if (This->headOfStreamPlaceHolder != 0)
4111 return *(This->headOfStreamPlaceHolder);
4113 if (This->ownerPropertyIndex != PROPERTY_NULL)
4115 readSuccessful = StorageImpl_ReadProperty(
4116 This->parentStorage,
4117 This->ownerPropertyIndex,
4118 &chainProperty);
4120 if (readSuccessful)
4122 return chainProperty.startingBlock;
4126 return BLOCK_END_OF_CHAIN;
4129 /******************************************************************************
4130 * BlockChainStream_GetCount
4132 * Returns the number of blocks that comprises this chain.
4133 * This is not the size of the stream as the last block may not be full!
4136 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4138 ULONG blockIndex;
4139 ULONG count = 0;
4141 blockIndex = BlockChainStream_GetHeadOfChain(This);
4143 while (blockIndex != BLOCK_END_OF_CHAIN)
4145 count++;
4147 blockIndex = StorageImpl_GetNextBlockInChain(
4148 This->parentStorage,
4149 blockIndex);
4152 return count;
4155 /******************************************************************************
4156 * BlockChainStream_ReadAt
4158 * Reads a specified number of bytes from this chain at the specified offset.
4159 * bytesRead may be NULL.
4160 * Failure will be returned if the specified number of bytes has not been read.
4162 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4163 ULARGE_INTEGER offset,
4164 ULONG size,
4165 void* buffer,
4166 ULONG* bytesRead)
4168 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4169 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4170 ULONG bytesToReadInBuffer;
4171 ULONG blockIndex;
4172 BYTE* bufferWalker;
4173 BYTE* bigBlockBuffer;
4176 * Find the first block in the stream that contains part of the buffer.
4178 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4179 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4180 (blockNoInSequence < This->lastBlockNoInSequence) )
4182 blockIndex = BlockChainStream_GetHeadOfChain(This);
4183 This->lastBlockNoInSequence = blockNoInSequence;
4185 else
4187 ULONG temp = blockNoInSequence;
4189 blockIndex = This->lastBlockNoInSequenceIndex;
4190 blockNoInSequence -= This->lastBlockNoInSequence;
4191 This->lastBlockNoInSequence = temp;
4194 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4196 blockIndex =
4197 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4199 blockNoInSequence--;
4202 This->lastBlockNoInSequenceIndex = blockIndex;
4205 * Start reading the buffer.
4207 *bytesRead = 0;
4208 bufferWalker = buffer;
4210 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4213 * Calculate how many bytes we can copy from this big block.
4215 bytesToReadInBuffer =
4216 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4219 * Copy those bytes to the buffer
4221 bigBlockBuffer =
4222 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4224 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4226 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4229 * Step to the next big block.
4231 blockIndex =
4232 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4234 bufferWalker += bytesToReadInBuffer;
4235 size -= bytesToReadInBuffer;
4236 *bytesRead += bytesToReadInBuffer;
4237 offsetInBlock = 0; /* There is no offset on the next block */
4241 return (size == 0);
4244 /******************************************************************************
4245 * BlockChainStream_WriteAt
4247 * Writes the specified number of bytes to this chain at the specified offset.
4248 * bytesWritten may be NULL.
4249 * Will fail if not all specified number of bytes have been written.
4251 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4252 ULARGE_INTEGER offset,
4253 ULONG size,
4254 const void* buffer,
4255 ULONG* bytesWritten)
4257 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4258 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4259 ULONG bytesToWrite;
4260 ULONG blockIndex;
4261 BYTE* bufferWalker;
4262 BYTE* bigBlockBuffer;
4265 * Find the first block in the stream that contains part of the buffer.
4267 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4268 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4269 (blockNoInSequence < This->lastBlockNoInSequence) )
4271 blockIndex = BlockChainStream_GetHeadOfChain(This);
4272 This->lastBlockNoInSequence = blockNoInSequence;
4274 else
4276 ULONG temp = blockNoInSequence;
4278 blockIndex = This->lastBlockNoInSequenceIndex;
4279 blockNoInSequence -= This->lastBlockNoInSequence;
4280 This->lastBlockNoInSequence = temp;
4283 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4285 blockIndex =
4286 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4288 blockNoInSequence--;
4291 This->lastBlockNoInSequenceIndex = blockIndex;
4294 * Here, I'm casting away the constness on the buffer variable
4295 * This is OK since we don't intend to modify that buffer.
4297 *bytesWritten = 0;
4298 bufferWalker = (BYTE*)buffer;
4300 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4303 * Calculate how many bytes we can copy from this big block.
4305 bytesToWrite =
4306 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4309 * Copy those bytes to the buffer
4311 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4313 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4315 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4318 * Step to the next big block.
4320 blockIndex =
4321 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4323 bufferWalker += bytesToWrite;
4324 size -= bytesToWrite;
4325 *bytesWritten += bytesToWrite;
4326 offsetInBlock = 0; /* There is no offset on the next block */
4329 return (size == 0);
4332 /******************************************************************************
4333 * BlockChainStream_Shrink
4335 * Shrinks this chain in the big block depot.
4337 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4338 ULARGE_INTEGER newSize)
4340 ULONG blockIndex, extraBlock;
4341 ULONG numBlocks;
4342 ULONG count = 1;
4345 * Reset the last accessed block cache.
4347 This->lastBlockNoInSequence = 0xFFFFFFFF;
4348 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4351 * Figure out how many blocks are needed to contain the new size
4353 numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4355 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4356 numBlocks++;
4358 blockIndex = BlockChainStream_GetHeadOfChain(This);
4361 * Go to the new end of chain
4363 while (count < numBlocks)
4365 blockIndex =
4366 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4368 count++;
4371 /* Get the next block before marking the new end */
4372 extraBlock =
4373 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4375 /* Mark the new end of chain */
4376 StorageImpl_SetNextBlockInChain(
4377 This->parentStorage,
4378 blockIndex,
4379 BLOCK_END_OF_CHAIN);
4381 This->tailIndex = blockIndex;
4382 This->numBlocks = numBlocks;
4385 * Mark the extra blocks as free
4387 while (extraBlock != BLOCK_END_OF_CHAIN)
4389 blockIndex =
4390 StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4392 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4393 extraBlock = blockIndex;
4396 return TRUE;
4399 /******************************************************************************
4400 * BlockChainStream_Enlarge
4402 * Grows this chain in the big block depot.
4404 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4405 ULARGE_INTEGER newSize)
4407 ULONG blockIndex, currentBlock;
4408 ULONG newNumBlocks;
4409 ULONG oldNumBlocks = 0;
4411 blockIndex = BlockChainStream_GetHeadOfChain(This);
4414 * Empty chain. Create the head.
4416 if (blockIndex == BLOCK_END_OF_CHAIN)
4418 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4419 StorageImpl_SetNextBlockInChain(This->parentStorage,
4420 blockIndex,
4421 BLOCK_END_OF_CHAIN);
4423 if (This->headOfStreamPlaceHolder != 0)
4425 *(This->headOfStreamPlaceHolder) = blockIndex;
4427 else
4429 StgProperty chainProp;
4430 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4432 StorageImpl_ReadProperty(
4433 This->parentStorage,
4434 This->ownerPropertyIndex,
4435 &chainProp);
4437 chainProp.startingBlock = blockIndex;
4439 StorageImpl_WriteProperty(
4440 This->parentStorage,
4441 This->ownerPropertyIndex,
4442 &chainProp);
4445 This->tailIndex = blockIndex;
4446 This->numBlocks = 1;
4450 * Figure out how many blocks are needed to contain this stream
4452 newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4454 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4455 newNumBlocks++;
4458 * Go to the current end of chain
4460 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4462 currentBlock = blockIndex;
4464 while (blockIndex != BLOCK_END_OF_CHAIN)
4466 This->numBlocks++;
4467 currentBlock = blockIndex;
4469 blockIndex =
4470 StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4473 This->tailIndex = currentBlock;
4476 currentBlock = This->tailIndex;
4477 oldNumBlocks = This->numBlocks;
4480 * Add new blocks to the chain
4482 if (oldNumBlocks < newNumBlocks)
4484 while (oldNumBlocks < newNumBlocks)
4486 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4488 StorageImpl_SetNextBlockInChain(
4489 This->parentStorage,
4490 currentBlock,
4491 blockIndex);
4493 StorageImpl_SetNextBlockInChain(
4494 This->parentStorage,
4495 blockIndex,
4496 BLOCK_END_OF_CHAIN);
4498 currentBlock = blockIndex;
4499 oldNumBlocks++;
4502 This->tailIndex = blockIndex;
4503 This->numBlocks = newNumBlocks;
4506 return TRUE;
4509 /******************************************************************************
4510 * BlockChainStream_SetSize
4512 * Sets the size of this stream. The big block depot will be updated.
4513 * The file will grow if we grow the chain.
4515 * TODO: Free the actual blocks in the file when we shrink the chain.
4516 * Currently, the blocks are still in the file. So the file size
4517 * doesn't shrink even if we shrink streams.
4519 BOOL BlockChainStream_SetSize(
4520 BlockChainStream* This,
4521 ULARGE_INTEGER newSize)
4523 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4525 if (newSize.s.LowPart == size.s.LowPart)
4526 return TRUE;
4528 if (newSize.s.LowPart < size.s.LowPart)
4530 BlockChainStream_Shrink(This, newSize);
4532 else
4534 ULARGE_INTEGER fileSize =
4535 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4537 ULONG diff = newSize.s.LowPart - size.s.LowPart;
4540 * Make sure the file stays a multiple of blocksize
4542 if ((diff % This->parentStorage->bigBlockSize) != 0)
4543 diff += (This->parentStorage->bigBlockSize -
4544 (diff % This->parentStorage->bigBlockSize) );
4546 fileSize.s.LowPart += diff;
4547 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4549 BlockChainStream_Enlarge(This, newSize);
4552 return TRUE;
4555 /******************************************************************************
4556 * BlockChainStream_GetSize
4558 * Returns the size of this chain.
4559 * Will return the block count if this chain doesn't have a property.
4561 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4563 StgProperty chainProperty;
4565 if(This->headOfStreamPlaceHolder == NULL)
4568 * This chain is a data stream read the property and return
4569 * the appropriate size
4571 StorageImpl_ReadProperty(
4572 This->parentStorage,
4573 This->ownerPropertyIndex,
4574 &chainProperty);
4576 return chainProperty.size;
4578 else
4581 * this chain is a chain that does not have a property, figure out the
4582 * size by making the product number of used blocks times the
4583 * size of them
4585 ULARGE_INTEGER result;
4586 result.s.HighPart = 0;
4588 result.s.LowPart =
4589 BlockChainStream_GetCount(This) *
4590 This->parentStorage->bigBlockSize;
4592 return result;
4596 /******************************************************************************
4597 ** SmallBlockChainStream implementation
4600 SmallBlockChainStream* SmallBlockChainStream_Construct(
4601 StorageImpl* parentStorage,
4602 ULONG propertyIndex)
4604 SmallBlockChainStream* newStream;
4606 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4608 newStream->parentStorage = parentStorage;
4609 newStream->ownerPropertyIndex = propertyIndex;
4611 return newStream;
4614 void SmallBlockChainStream_Destroy(
4615 SmallBlockChainStream* This)
4617 HeapFree(GetProcessHeap(), 0, This);
4620 /******************************************************************************
4621 * SmallBlockChainStream_GetHeadOfChain
4623 * Returns the head of this chain of small blocks.
4625 ULONG SmallBlockChainStream_GetHeadOfChain(
4626 SmallBlockChainStream* This)
4628 StgProperty chainProperty;
4629 BOOL readSuccessful;
4631 if (This->ownerPropertyIndex)
4633 readSuccessful = StorageImpl_ReadProperty(
4634 This->parentStorage,
4635 This->ownerPropertyIndex,
4636 &chainProperty);
4638 if (readSuccessful)
4640 return chainProperty.startingBlock;
4645 return BLOCK_END_OF_CHAIN;
4648 /******************************************************************************
4649 * SmallBlockChainStream_GetNextBlockInChain
4651 * Returns the index of the next small block in this chain.
4653 * Return Values:
4654 * - BLOCK_END_OF_CHAIN: end of this chain
4655 * - BLOCK_UNUSED: small block 'blockIndex' is free
4657 ULONG SmallBlockChainStream_GetNextBlockInChain(
4658 SmallBlockChainStream* This,
4659 ULONG blockIndex)
4661 ULARGE_INTEGER offsetOfBlockInDepot;
4662 DWORD buffer;
4663 ULONG nextBlockInChain = BLOCK_END_OF_CHAIN;
4664 ULONG bytesRead;
4665 BOOL success;
4667 offsetOfBlockInDepot.s.HighPart = 0;
4668 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4671 * Read those bytes in the buffer from the small block file.
4673 success = BlockChainStream_ReadAt(
4674 This->parentStorage->smallBlockDepotChain,
4675 offsetOfBlockInDepot,
4676 sizeof(DWORD),
4677 &buffer,
4678 &bytesRead);
4680 if (success)
4682 StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4685 return nextBlockInChain;
4688 /******************************************************************************
4689 * SmallBlockChainStream_SetNextBlockInChain
4691 * Writes the index of the next block of the specified block in the small
4692 * block depot.
4693 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4694 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4696 void SmallBlockChainStream_SetNextBlockInChain(
4697 SmallBlockChainStream* This,
4698 ULONG blockIndex,
4699 ULONG nextBlock)
4701 ULARGE_INTEGER offsetOfBlockInDepot;
4702 DWORD buffer;
4703 ULONG bytesWritten;
4705 offsetOfBlockInDepot.s.HighPart = 0;
4706 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4708 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4711 * Read those bytes in the buffer from the small block file.
4713 BlockChainStream_WriteAt(
4714 This->parentStorage->smallBlockDepotChain,
4715 offsetOfBlockInDepot,
4716 sizeof(DWORD),
4717 &buffer,
4718 &bytesWritten);
4721 /******************************************************************************
4722 * SmallBlockChainStream_FreeBlock
4724 * Flag small block 'blockIndex' as free in the small block depot.
4726 void SmallBlockChainStream_FreeBlock(
4727 SmallBlockChainStream* This,
4728 ULONG blockIndex)
4730 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4733 /******************************************************************************
4734 * SmallBlockChainStream_GetNextFreeBlock
4736 * Returns the index of a free small block. The small block depot will be
4737 * enlarged if necessary. The small block chain will also be enlarged if
4738 * necessary.
4740 ULONG SmallBlockChainStream_GetNextFreeBlock(
4741 SmallBlockChainStream* This)
4743 ULARGE_INTEGER offsetOfBlockInDepot;
4744 DWORD buffer;
4745 ULONG bytesRead;
4746 ULONG blockIndex = 0;
4747 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4748 BOOL success = TRUE;
4749 ULONG smallBlocksPerBigBlock;
4751 offsetOfBlockInDepot.s.HighPart = 0;
4754 * Scan the small block depot for a free block
4756 while (nextBlockIndex != BLOCK_UNUSED)
4758 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4760 success = BlockChainStream_ReadAt(
4761 This->parentStorage->smallBlockDepotChain,
4762 offsetOfBlockInDepot,
4763 sizeof(DWORD),
4764 &buffer,
4765 &bytesRead);
4768 * If we run out of space for the small block depot, enlarge it
4770 if (success)
4772 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4774 if (nextBlockIndex != BLOCK_UNUSED)
4775 blockIndex++;
4777 else
4779 ULONG count =
4780 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4782 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4783 ULONG nextBlock, newsbdIndex;
4784 BYTE* smallBlockDepot;
4786 nextBlock = sbdIndex;
4787 while (nextBlock != BLOCK_END_OF_CHAIN)
4789 sbdIndex = nextBlock;
4790 nextBlock =
4791 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4794 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4795 if (sbdIndex != BLOCK_END_OF_CHAIN)
4796 StorageImpl_SetNextBlockInChain(
4797 This->parentStorage,
4798 sbdIndex,
4799 newsbdIndex);
4801 StorageImpl_SetNextBlockInChain(
4802 This->parentStorage,
4803 newsbdIndex,
4804 BLOCK_END_OF_CHAIN);
4807 * Initialize all the small blocks to free
4809 smallBlockDepot =
4810 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4812 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4813 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4815 if (count == 0)
4818 * We have just created the small block depot.
4820 StgProperty rootProp;
4821 ULONG sbStartIndex;
4824 * Save it in the header
4826 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4827 StorageImpl_SaveFileHeader(This->parentStorage);
4830 * And allocate the first big block that will contain small blocks
4832 sbStartIndex =
4833 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4835 StorageImpl_SetNextBlockInChain(
4836 This->parentStorage,
4837 sbStartIndex,
4838 BLOCK_END_OF_CHAIN);
4840 StorageImpl_ReadProperty(
4841 This->parentStorage,
4842 This->parentStorage->rootPropertySetIndex,
4843 &rootProp);
4845 rootProp.startingBlock = sbStartIndex;
4846 rootProp.size.s.HighPart = 0;
4847 rootProp.size.s.LowPart = This->parentStorage->bigBlockSize;
4849 StorageImpl_WriteProperty(
4850 This->parentStorage,
4851 This->parentStorage->rootPropertySetIndex,
4852 &rootProp);
4857 smallBlocksPerBigBlock =
4858 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4861 * Verify if we have to allocate big blocks to contain small blocks
4863 if (blockIndex % smallBlocksPerBigBlock == 0)
4865 StgProperty rootProp;
4866 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4868 StorageImpl_ReadProperty(
4869 This->parentStorage,
4870 This->parentStorage->rootPropertySetIndex,
4871 &rootProp);
4873 if (rootProp.size.s.LowPart <
4874 (blocksRequired * This->parentStorage->bigBlockSize))
4876 rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4878 BlockChainStream_SetSize(
4879 This->parentStorage->smallBlockRootChain,
4880 rootProp.size);
4882 StorageImpl_WriteProperty(
4883 This->parentStorage,
4884 This->parentStorage->rootPropertySetIndex,
4885 &rootProp);
4889 return blockIndex;
4892 /******************************************************************************
4893 * SmallBlockChainStream_ReadAt
4895 * Reads a specified number of bytes from this chain at the specified offset.
4896 * bytesRead may be NULL.
4897 * Failure will be returned if the specified number of bytes has not been read.
4899 BOOL SmallBlockChainStream_ReadAt(
4900 SmallBlockChainStream* This,
4901 ULARGE_INTEGER offset,
4902 ULONG size,
4903 void* buffer,
4904 ULONG* bytesRead)
4906 ULARGE_INTEGER offsetInBigBlockFile;
4907 ULONG blockNoInSequence =
4908 offset.s.LowPart / This->parentStorage->smallBlockSize;
4910 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4911 ULONG bytesToReadInBuffer;
4912 ULONG blockIndex;
4913 ULONG bytesReadFromBigBlockFile;
4914 BYTE* bufferWalker;
4917 * This should never happen on a small block file.
4919 assert(offset.s.HighPart==0);
4922 * Find the first block in the stream that contains part of the buffer.
4924 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4926 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4928 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4930 blockNoInSequence--;
4934 * Start reading the buffer.
4936 *bytesRead = 0;
4937 bufferWalker = buffer;
4939 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4942 * Calculate how many bytes we can copy from this small block.
4944 bytesToReadInBuffer =
4945 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
4948 * Calculate the offset of the small block in the small block file.
4950 offsetInBigBlockFile.s.HighPart = 0;
4951 offsetInBigBlockFile.s.LowPart =
4952 blockIndex * This->parentStorage->smallBlockSize;
4954 offsetInBigBlockFile.s.LowPart += offsetInBlock;
4957 * Read those bytes in the buffer from the small block file.
4959 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
4960 offsetInBigBlockFile,
4961 bytesToReadInBuffer,
4962 bufferWalker,
4963 &bytesReadFromBigBlockFile);
4965 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
4968 * Step to the next big block.
4970 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4971 bufferWalker += bytesToReadInBuffer;
4972 size -= bytesToReadInBuffer;
4973 *bytesRead += bytesToReadInBuffer;
4974 offsetInBlock = 0; /* There is no offset on the next block */
4977 return (size == 0);
4980 /******************************************************************************
4981 * SmallBlockChainStream_WriteAt
4983 * Writes the specified number of bytes to this chain at the specified offset.
4984 * bytesWritten may be NULL.
4985 * Will fail if not all specified number of bytes have been written.
4987 BOOL SmallBlockChainStream_WriteAt(
4988 SmallBlockChainStream* This,
4989 ULARGE_INTEGER offset,
4990 ULONG size,
4991 const void* buffer,
4992 ULONG* bytesWritten)
4994 ULARGE_INTEGER offsetInBigBlockFile;
4995 ULONG blockNoInSequence =
4996 offset.s.LowPart / This->parentStorage->smallBlockSize;
4998 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4999 ULONG bytesToWriteInBuffer;
5000 ULONG blockIndex;
5001 ULONG bytesWrittenFromBigBlockFile;
5002 BYTE* bufferWalker;
5005 * This should never happen on a small block file.
5007 assert(offset.s.HighPart==0);
5010 * Find the first block in the stream that contains part of the buffer.
5012 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5014 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5016 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5018 blockNoInSequence--;
5022 * Start writing the buffer.
5024 * Here, I'm casting away the constness on the buffer variable
5025 * This is OK since we don't intend to modify that buffer.
5027 *bytesWritten = 0;
5028 bufferWalker = (BYTE*)buffer;
5029 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5032 * Calculate how many bytes we can copy to this small block.
5034 bytesToWriteInBuffer =
5035 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5038 * Calculate the offset of the small block in the small block file.
5040 offsetInBigBlockFile.s.HighPart = 0;
5041 offsetInBigBlockFile.s.LowPart =
5042 blockIndex * This->parentStorage->smallBlockSize;
5044 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5047 * Write those bytes in the buffer to the small block file.
5049 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5050 offsetInBigBlockFile,
5051 bytesToWriteInBuffer,
5052 bufferWalker,
5053 &bytesWrittenFromBigBlockFile);
5055 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5058 * Step to the next big block.
5060 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5061 bufferWalker += bytesToWriteInBuffer;
5062 size -= bytesToWriteInBuffer;
5063 *bytesWritten += bytesToWriteInBuffer;
5064 offsetInBlock = 0; /* There is no offset on the next block */
5067 return (size == 0);
5070 /******************************************************************************
5071 * SmallBlockChainStream_Shrink
5073 * Shrinks this chain in the small block depot.
5075 BOOL SmallBlockChainStream_Shrink(
5076 SmallBlockChainStream* This,
5077 ULARGE_INTEGER newSize)
5079 ULONG blockIndex, extraBlock;
5080 ULONG numBlocks;
5081 ULONG count = 0;
5083 numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5085 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5086 numBlocks++;
5088 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5091 * Go to the new end of chain
5093 while (count < numBlocks)
5095 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5096 count++;
5100 * If the count is 0, we have a special case, the head of the chain was
5101 * just freed.
5103 if (count == 0)
5105 StgProperty chainProp;
5107 StorageImpl_ReadProperty(This->parentStorage,
5108 This->ownerPropertyIndex,
5109 &chainProp);
5111 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5113 StorageImpl_WriteProperty(This->parentStorage,
5114 This->ownerPropertyIndex,
5115 &chainProp);
5118 * We start freeing the chain at the head block.
5120 extraBlock = blockIndex;
5122 else
5124 /* Get the next block before marking the new end */
5125 extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5127 /* Mark the new end of chain */
5128 SmallBlockChainStream_SetNextBlockInChain(
5129 This,
5130 blockIndex,
5131 BLOCK_END_OF_CHAIN);
5135 * Mark the extra blocks as free
5137 while (extraBlock != BLOCK_END_OF_CHAIN)
5139 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
5140 SmallBlockChainStream_FreeBlock(This, extraBlock);
5141 extraBlock = blockIndex;
5144 return TRUE;
5147 /******************************************************************************
5148 * SmallBlockChainStream_Enlarge
5150 * Grows this chain in the small block depot.
5152 BOOL SmallBlockChainStream_Enlarge(
5153 SmallBlockChainStream* This,
5154 ULARGE_INTEGER newSize)
5156 ULONG blockIndex, currentBlock;
5157 ULONG newNumBlocks;
5158 ULONG oldNumBlocks = 0;
5160 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5163 * Empty chain
5165 if (blockIndex == BLOCK_END_OF_CHAIN)
5168 StgProperty chainProp;
5170 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5171 &chainProp);
5173 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5175 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5176 &chainProp);
5178 blockIndex = chainProp.startingBlock;
5179 SmallBlockChainStream_SetNextBlockInChain(
5180 This,
5181 blockIndex,
5182 BLOCK_END_OF_CHAIN);
5185 currentBlock = blockIndex;
5188 * Figure out how many blocks are needed to contain this stream
5190 newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5192 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5193 newNumBlocks++;
5196 * Go to the current end of chain
5198 while (blockIndex != BLOCK_END_OF_CHAIN)
5200 oldNumBlocks++;
5201 currentBlock = blockIndex;
5202 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5206 * Add new blocks to the chain
5208 while (oldNumBlocks < newNumBlocks)
5210 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5211 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5213 SmallBlockChainStream_SetNextBlockInChain(
5214 This,
5215 blockIndex,
5216 BLOCK_END_OF_CHAIN);
5218 currentBlock = blockIndex;
5219 oldNumBlocks++;
5222 return TRUE;
5225 /******************************************************************************
5226 * SmallBlockChainStream_GetCount
5228 * Returns the number of blocks that comprises this chain.
5229 * This is not the size of this chain as the last block may not be full!
5231 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5233 ULONG blockIndex;
5234 ULONG count = 0;
5236 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5238 while (blockIndex != BLOCK_END_OF_CHAIN)
5240 count++;
5242 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5245 return count;
5248 /******************************************************************************
5249 * SmallBlockChainStream_SetSize
5251 * Sets the size of this stream.
5252 * The file will grow if we grow the chain.
5254 * TODO: Free the actual blocks in the file when we shrink the chain.
5255 * Currently, the blocks are still in the file. So the file size
5256 * doesn't shrink even if we shrink streams.
5258 BOOL SmallBlockChainStream_SetSize(
5259 SmallBlockChainStream* This,
5260 ULARGE_INTEGER newSize)
5262 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5264 if (newSize.s.LowPart == size.s.LowPart)
5265 return TRUE;
5267 if (newSize.s.LowPart < size.s.LowPart)
5269 SmallBlockChainStream_Shrink(This, newSize);
5271 else
5273 SmallBlockChainStream_Enlarge(This, newSize);
5276 return TRUE;
5279 /******************************************************************************
5280 * SmallBlockChainStream_GetSize
5282 * Returns the size of this chain.
5284 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5286 StgProperty chainProperty;
5288 StorageImpl_ReadProperty(
5289 This->parentStorage,
5290 This->ownerPropertyIndex,
5291 &chainProperty);
5293 return chainProperty.size;
5296 /******************************************************************************
5297 * StgCreateDocfile [OLE32.144]
5299 HRESULT WINAPI StgCreateDocfile(
5300 LPCOLESTR pwcsName,
5301 DWORD grfMode,
5302 DWORD reserved,
5303 IStorage **ppstgOpen)
5305 StorageImpl* newStorage = 0;
5306 HANDLE hFile = INVALID_HANDLE_VALUE;
5307 HRESULT hr = S_OK;
5308 DWORD shareMode;
5309 DWORD accessMode;
5310 DWORD creationMode;
5311 DWORD fileAttributes;
5312 WCHAR tempFileName[MAX_PATH];
5314 TRACE("(%s, %lx, %ld, %p)\n",
5315 debugstr_w(pwcsName), grfMode,
5316 reserved, ppstgOpen);
5319 * Validate the parameters
5321 if (ppstgOpen == 0)
5322 return STG_E_INVALIDPOINTER;
5325 * Validate the STGM flags
5327 if ( FAILED( validateSTGM(grfMode) ))
5328 return STG_E_INVALIDFLAG;
5331 * Generate a unique name.
5333 if (pwcsName == 0)
5335 WCHAR tempPath[MAX_PATH];
5336 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5338 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5339 return STG_E_INVALIDFLAG;
5340 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5341 return STG_E_INVALIDFLAG;
5343 memset(tempPath, 0, sizeof(tempPath));
5344 memset(tempFileName, 0, sizeof(tempFileName));
5346 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5347 tempPath[0] = '.';
5349 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5350 pwcsName = tempFileName;
5351 else
5352 return STG_E_INSUFFICIENTMEMORY;
5354 creationMode = TRUNCATE_EXISTING;
5356 else
5358 creationMode = GetCreationModeFromSTGM(grfMode);
5362 * Interpret the STGM value grfMode
5364 shareMode = GetShareModeFromSTGM(grfMode);
5365 accessMode = GetAccessModeFromSTGM(grfMode);
5367 if (grfMode & STGM_DELETEONRELEASE)
5368 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5369 else
5370 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5372 if (grfMode & STGM_TRANSACTED)
5373 FIXME("Transacted mode not implemented.\n");
5376 * Initialize the "out" parameter.
5378 *ppstgOpen = 0;
5380 hFile = CreateFileW(pwcsName,
5381 accessMode,
5382 shareMode,
5383 NULL,
5384 creationMode,
5385 fileAttributes,
5388 if (hFile == INVALID_HANDLE_VALUE)
5390 return E_FAIL;
5394 * Allocate and initialize the new IStorage32object.
5396 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5398 if (newStorage == 0)
5399 return STG_E_INSUFFICIENTMEMORY;
5401 hr = StorageImpl_Construct(
5402 newStorage,
5403 hFile,
5404 pwcsName,
5405 NULL,
5406 grfMode,
5407 TRUE,
5408 TRUE);
5410 if (FAILED(hr))
5412 HeapFree(GetProcessHeap(), 0, newStorage);
5413 return hr;
5417 * Get an "out" pointer for the caller.
5419 hr = StorageBaseImpl_QueryInterface(
5420 (IStorage*)newStorage,
5421 (REFIID)&IID_IStorage,
5422 (void**)ppstgOpen);
5424 return hr;
5427 /******************************************************************************
5428 * StgOpenStorage [OLE32.148]
5430 HRESULT WINAPI StgOpenStorage(
5431 const OLECHAR *pwcsName,
5432 IStorage *pstgPriority,
5433 DWORD grfMode,
5434 SNB snbExclude,
5435 DWORD reserved,
5436 IStorage **ppstgOpen)
5438 StorageImpl* newStorage = 0;
5439 HRESULT hr = S_OK;
5440 HANDLE hFile = 0;
5441 DWORD shareMode;
5442 DWORD accessMode;
5444 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5445 debugstr_w(pwcsName), pstgPriority, grfMode,
5446 snbExclude, reserved, ppstgOpen);
5449 * Perform a sanity check
5451 if (( pwcsName == 0) || (ppstgOpen == 0) )
5452 return STG_E_INVALIDPOINTER;
5455 * Validate the STGM flags
5457 if ( FAILED( validateSTGM(grfMode) ))
5458 return STG_E_INVALIDFLAG;
5461 * Interpret the STGM value grfMode
5463 shareMode = GetShareModeFromSTGM(grfMode);
5464 accessMode = GetAccessModeFromSTGM(grfMode);
5467 * Initialize the "out" parameter.
5469 *ppstgOpen = 0;
5471 hFile = CreateFileW( pwcsName,
5472 accessMode,
5473 shareMode,
5474 NULL,
5475 OPEN_EXISTING,
5476 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5480 if (hFile==INVALID_HANDLE_VALUE)
5482 HRESULT hr = E_FAIL;
5483 DWORD last_error = GetLastError();
5485 switch (last_error)
5487 case ERROR_FILE_NOT_FOUND:
5488 hr = STG_E_FILENOTFOUND;
5489 break;
5491 case ERROR_PATH_NOT_FOUND:
5492 hr = STG_E_PATHNOTFOUND;
5493 break;
5495 case ERROR_ACCESS_DENIED:
5496 case ERROR_WRITE_PROTECT:
5497 hr = STG_E_ACCESSDENIED;
5498 break;
5500 case ERROR_SHARING_VIOLATION:
5501 hr = STG_E_SHAREVIOLATION;
5502 break;
5504 default:
5505 hr = E_FAIL;
5508 return hr;
5512 * Allocate and initialize the new IStorage32object.
5514 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5516 if (newStorage == 0)
5517 return STG_E_INSUFFICIENTMEMORY;
5519 hr = StorageImpl_Construct(
5520 newStorage,
5521 hFile,
5522 pwcsName,
5523 NULL,
5524 grfMode,
5525 TRUE,
5526 FALSE);
5528 if (FAILED(hr))
5530 HeapFree(GetProcessHeap(), 0, newStorage);
5532 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5534 if(hr == STG_E_INVALIDHEADER)
5535 return STG_E_FILEALREADYEXISTS;
5536 return hr;
5540 * Get an "out" pointer for the caller.
5542 hr = StorageBaseImpl_QueryInterface(
5543 (IStorage*)newStorage,
5544 (REFIID)&IID_IStorage,
5545 (void**)ppstgOpen);
5547 return hr;
5550 /******************************************************************************
5551 * StgCreateDocfileOnILockBytes [OLE32.145]
5553 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5554 ILockBytes *plkbyt,
5555 DWORD grfMode,
5556 DWORD reserved,
5557 IStorage** ppstgOpen)
5559 StorageImpl* newStorage = 0;
5560 HRESULT hr = S_OK;
5563 * Validate the parameters
5565 if ((ppstgOpen == 0) || (plkbyt == 0))
5566 return STG_E_INVALIDPOINTER;
5569 * Allocate and initialize the new IStorage object.
5571 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5573 if (newStorage == 0)
5574 return STG_E_INSUFFICIENTMEMORY;
5576 hr = StorageImpl_Construct(
5577 newStorage,
5580 plkbyt,
5581 grfMode,
5582 FALSE,
5583 TRUE);
5585 if (FAILED(hr))
5587 HeapFree(GetProcessHeap(), 0, newStorage);
5588 return hr;
5592 * Get an "out" pointer for the caller.
5594 hr = StorageBaseImpl_QueryInterface(
5595 (IStorage*)newStorage,
5596 (REFIID)&IID_IStorage,
5597 (void**)ppstgOpen);
5599 return hr;
5602 /******************************************************************************
5603 * StgOpenStorageOnILockBytes [OLE32.149]
5605 HRESULT WINAPI StgOpenStorageOnILockBytes(
5606 ILockBytes *plkbyt,
5607 IStorage *pstgPriority,
5608 DWORD grfMode,
5609 SNB snbExclude,
5610 DWORD reserved,
5611 IStorage **ppstgOpen)
5613 StorageImpl* newStorage = 0;
5614 HRESULT hr = S_OK;
5617 * Perform a sanity check
5619 if ((plkbyt == 0) || (ppstgOpen == 0))
5620 return STG_E_INVALIDPOINTER;
5623 * Validate the STGM flags
5625 if ( FAILED( validateSTGM(grfMode) ))
5626 return STG_E_INVALIDFLAG;
5629 * Initialize the "out" parameter.
5631 *ppstgOpen = 0;
5634 * Allocate and initialize the new IStorage object.
5636 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5638 if (newStorage == 0)
5639 return STG_E_INSUFFICIENTMEMORY;
5641 hr = StorageImpl_Construct(
5642 newStorage,
5645 plkbyt,
5646 grfMode,
5647 FALSE,
5648 FALSE);
5650 if (FAILED(hr))
5652 HeapFree(GetProcessHeap(), 0, newStorage);
5653 return hr;
5657 * Get an "out" pointer for the caller.
5659 hr = StorageBaseImpl_QueryInterface(
5660 (IStorage*)newStorage,
5661 (REFIID)&IID_IStorage,
5662 (void**)ppstgOpen);
5664 return hr;
5667 /******************************************************************************
5668 * StgSetTimes [ole32.150]
5669 * StgSetTimes [OLE32.150]
5673 HRESULT WINAPI StgSetTimes(OLECHAR *str, FILETIME *a, FILETIME *b, FILETIME *c )
5675 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5676 return S_OK;
5679 /******************************************************************************
5680 * StgIsStorageILockBytes [OLE32.147]
5682 * Determines if the ILockBytes contains a storage object.
5684 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5686 BYTE sig[8];
5687 ULARGE_INTEGER offset;
5689 offset.s.HighPart = 0;
5690 offset.s.LowPart = 0;
5692 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5694 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5695 return S_OK;
5697 return S_FALSE;
5700 /******************************************************************************
5701 * WriteClassStg [OLE32.158]
5703 * This method will store the specified CLSID in the specified storage object
5705 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5707 HRESULT hRes;
5709 assert(pStg != 0);
5711 hRes = IStorage_SetClass(pStg, rclsid);
5713 return hRes;
5716 /***********************************************************************
5717 * ReadClassStg (OLE32.134)
5719 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5721 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5723 STATSTG pstatstg;
5724 HRESULT hRes;
5726 TRACE("()\n");
5728 if(pclsid==NULL)
5729 return E_POINTER;
5731 * read a STATSTG structure (contains the clsid) from the storage
5733 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5735 if(SUCCEEDED(hRes))
5736 *pclsid=pstatstg.clsid;
5738 return hRes;
5741 /***********************************************************************
5742 * OleLoadFromStream (OLE32.113)
5744 * This function loads an object from stream
5746 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5748 CLSID clsid;
5749 HRESULT res;
5750 LPPERSISTSTREAM xstm;
5752 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5754 res=ReadClassStm(pStm,&clsid);
5755 if (!SUCCEEDED(res))
5756 return res;
5757 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5758 if (!SUCCEEDED(res))
5759 return res;
5760 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5761 if (!SUCCEEDED(res)) {
5762 IUnknown_Release((IUnknown*)*ppvObj);
5763 return res;
5765 res=IPersistStream_Load(xstm,pStm);
5766 IPersistStream_Release(xstm);
5767 /* FIXME: all refcounts ok at this point? I think they should be:
5768 * pStm : unchanged
5769 * ppvObj : 1
5770 * xstm : 0 (released)
5772 return res;
5775 /***********************************************************************
5776 * OleSaveToStream (OLE32.125)
5778 * This function saves an object with the IPersistStream interface on it
5779 * to the specified stream.
5781 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5784 CLSID clsid;
5785 HRESULT res;
5787 TRACE("(%p,%p)\n",pPStm,pStm);
5789 res=IPersistStream_GetClassID(pPStm,&clsid);
5791 if (SUCCEEDED(res)){
5793 res=WriteClassStm(pStm,&clsid);
5795 if (SUCCEEDED(res))
5797 res=IPersistStream_Save(pPStm,pStm,TRUE);
5800 TRACE("Finished Save\n");
5801 return res;
5804 /****************************************************************************
5805 * This method validate a STGM parameter that can contain the values below
5807 * STGM_DIRECT 0x00000000
5808 * STGM_TRANSACTED 0x00010000
5809 * STGM_SIMPLE 0x08000000
5811 * STGM_READ 0x00000000
5812 * STGM_WRITE 0x00000001
5813 * STGM_READWRITE 0x00000002
5815 * STGM_SHARE_DENY_NONE 0x00000040
5816 * STGM_SHARE_DENY_READ 0x00000030
5817 * STGM_SHARE_DENY_WRITE 0x00000020
5818 * STGM_SHARE_EXCLUSIVE 0x00000010
5820 * STGM_PRIORITY 0x00040000
5821 * STGM_DELETEONRELEASE 0x04000000
5823 * STGM_CREATE 0x00001000
5824 * STGM_CONVERT 0x00020000
5825 * STGM_FAILIFTHERE 0x00000000
5827 * STGM_NOSCRATCH 0x00100000
5828 * STGM_NOSNAPSHOT 0x00200000
5830 static HRESULT validateSTGM(DWORD stgm)
5832 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5833 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5834 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5836 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5837 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5838 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5840 BOOL bSTGM_SHARE_DENY_NONE =
5841 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5843 BOOL bSTGM_SHARE_DENY_READ =
5844 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5846 BOOL bSTGM_SHARE_DENY_WRITE =
5847 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5849 BOOL bSTGM_SHARE_EXCLUSIVE =
5850 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5852 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5853 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5855 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5856 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5859 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5861 if ( ! bSTGM_DIRECT )
5862 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5863 return E_FAIL;
5866 * STGM_WRITE | STGM_READWRITE | STGM_READ
5868 if ( ! bSTGM_READ )
5869 if( bSTGM_WRITE && bSTGM_READWRITE )
5870 return E_FAIL;
5873 * STGM_SHARE_DENY_NONE | others
5874 * (I assume here that DENY_READ implies DENY_WRITE)
5876 if ( bSTGM_SHARE_DENY_NONE )
5877 if ( bSTGM_SHARE_DENY_READ ||
5878 bSTGM_SHARE_DENY_WRITE ||
5879 bSTGM_SHARE_EXCLUSIVE)
5880 return E_FAIL;
5883 * STGM_CREATE | STGM_CONVERT
5884 * if both are false, STGM_FAILIFTHERE is set to TRUE
5886 if ( bSTGM_CREATE && bSTGM_CONVERT )
5887 return E_FAIL;
5890 * STGM_NOSCRATCH requires STGM_TRANSACTED
5892 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5893 return E_FAIL;
5896 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5897 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5899 if (bSTGM_NOSNAPSHOT)
5901 if ( ! ( bSTGM_TRANSACTED &&
5902 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5903 return E_FAIL;
5906 return S_OK;
5909 /****************************************************************************
5910 * GetShareModeFromSTGM
5912 * This method will return a share mode flag from a STGM value.
5913 * The STGM value is assumed valid.
5915 static DWORD GetShareModeFromSTGM(DWORD stgm)
5917 DWORD dwShareMode = 0;
5918 BOOL bSTGM_SHARE_DENY_NONE =
5919 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5921 BOOL bSTGM_SHARE_DENY_READ =
5922 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5924 BOOL bSTGM_SHARE_DENY_WRITE =
5925 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5927 BOOL bSTGM_SHARE_EXCLUSIVE =
5928 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5930 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5931 dwShareMode = 0;
5933 if (bSTGM_SHARE_DENY_NONE)
5934 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5936 if (bSTGM_SHARE_DENY_WRITE)
5937 dwShareMode = FILE_SHARE_READ;
5939 return dwShareMode;
5942 /****************************************************************************
5943 * GetAccessModeFromSTGM
5945 * This method will return an access mode flag from a STGM value.
5946 * The STGM value is assumed valid.
5948 static DWORD GetAccessModeFromSTGM(DWORD stgm)
5950 DWORD dwDesiredAccess = GENERIC_READ;
5951 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5952 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5953 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5955 if (bSTGM_READ)
5956 dwDesiredAccess = GENERIC_READ;
5958 if (bSTGM_WRITE)
5959 dwDesiredAccess |= GENERIC_WRITE;
5961 if (bSTGM_READWRITE)
5962 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5964 return dwDesiredAccess;
5967 /****************************************************************************
5968 * GetCreationModeFromSTGM
5970 * This method will return a creation mode flag from a STGM value.
5971 * The STGM value is assumed valid.
5973 static DWORD GetCreationModeFromSTGM(DWORD stgm)
5975 if ( stgm & STGM_CREATE)
5976 return CREATE_ALWAYS;
5977 if (stgm & STGM_CONVERT) {
5978 FIXME("STGM_CONVERT not implemented!\n");
5979 return CREATE_NEW;
5981 /* All other cases */
5982 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5983 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
5984 return CREATE_NEW;
5988 /*************************************************************************
5989 * OLECONVERT_LoadOLE10 [Internal]
5991 * Loads the OLE10 STREAM to memory
5993 * PARAMS
5994 * pOleStream [I] The OLESTREAM
5995 * pData [I] Data Structure for the OLESTREAM Data
5997 * RETURNS
5998 * Success: S_OK
5999 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6000 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6002 * NOTES
6003 * This function is used by OleConvertOLESTREAMToIStorage only.
6005 * Memory allocated for pData must be freed by the caller
6007 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6009 DWORD dwSize;
6010 HRESULT hRes = S_OK;
6011 int nTryCnt=0;
6012 int max_try = 6;
6014 pData->pData = NULL;
6015 pData->pstrOleObjFileName = (CHAR *) NULL;
6017 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6019 /* Get the OleID */
6020 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6021 if(dwSize != sizeof(pData->dwOleID))
6023 hRes = CONVERT10_E_OLESTREAM_GET;
6025 else if(pData->dwOleID != OLESTREAM_ID)
6027 hRes = CONVERT10_E_OLESTREAM_FMT;
6029 else
6031 hRes = S_OK;
6032 break;
6036 if(hRes == S_OK)
6038 /* Get the TypeID...more info needed for this field */
6039 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6040 if(dwSize != sizeof(pData->dwTypeID))
6042 hRes = CONVERT10_E_OLESTREAM_GET;
6045 if(hRes == S_OK)
6047 if(pData->dwTypeID != 0)
6049 /* Get the lenght of the OleTypeName */
6050 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6051 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6053 hRes = CONVERT10_E_OLESTREAM_GET;
6056 if(hRes == S_OK)
6058 if(pData->dwOleTypeNameLength > 0)
6060 /* Get the OleTypeName */
6061 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6062 if(dwSize != pData->dwOleTypeNameLength)
6064 hRes = CONVERT10_E_OLESTREAM_GET;
6068 if(bStrem1)
6070 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6071 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6073 hRes = CONVERT10_E_OLESTREAM_GET;
6075 if(hRes == S_OK)
6077 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6078 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6079 pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6080 if(pData->pstrOleObjFileName)
6082 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6083 if(dwSize != pData->dwOleObjFileNameLength)
6085 hRes = CONVERT10_E_OLESTREAM_GET;
6088 else
6089 hRes = CONVERT10_E_OLESTREAM_GET;
6092 else
6094 /* Get the Width of the Metafile */
6095 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6096 if(dwSize != sizeof(pData->dwMetaFileWidth))
6098 hRes = CONVERT10_E_OLESTREAM_GET;
6100 if(hRes == S_OK)
6102 /* Get the Height of the Metafile */
6103 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6104 if(dwSize != sizeof(pData->dwMetaFileHeight))
6106 hRes = CONVERT10_E_OLESTREAM_GET;
6110 if(hRes == S_OK)
6112 /* Get the Lenght of the Data */
6113 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6114 if(dwSize != sizeof(pData->dwDataLength))
6116 hRes = CONVERT10_E_OLESTREAM_GET;
6120 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6122 if(!bStrem1) /* if it is a second OLE stream data */
6124 pData->dwDataLength -= 8;
6125 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6126 if(dwSize != sizeof(pData->strUnknown))
6128 hRes = CONVERT10_E_OLESTREAM_GET;
6132 if(hRes == S_OK)
6134 if(pData->dwDataLength > 0)
6136 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6138 /* Get Data (ex. IStorage, Metafile, or BMP) */
6139 if(pData->pData)
6141 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6142 if(dwSize != pData->dwDataLength)
6144 hRes = CONVERT10_E_OLESTREAM_GET;
6147 else
6149 hRes = CONVERT10_E_OLESTREAM_GET;
6155 return hRes;
6158 /*************************************************************************
6159 * OLECONVERT_SaveOLE10 [Internal]
6161 * Saves the OLE10 STREAM From memory
6163 * PARAMS
6164 * pData [I] Data Structure for the OLESTREAM Data
6165 * pOleStream [I] The OLESTREAM to save
6167 * RETURNS
6168 * Success: S_OK
6169 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6171 * NOTES
6172 * This function is used by OleConvertIStorageToOLESTREAM only.
6175 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6177 DWORD dwSize;
6178 HRESULT hRes = S_OK;
6181 /* Set the OleID */
6182 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6183 if(dwSize != sizeof(pData->dwOleID))
6185 hRes = CONVERT10_E_OLESTREAM_PUT;
6188 if(hRes == S_OK)
6190 /* Set the TypeID */
6191 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6192 if(dwSize != sizeof(pData->dwTypeID))
6194 hRes = CONVERT10_E_OLESTREAM_PUT;
6198 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6200 /* Set the Lenght of the OleTypeName */
6201 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6202 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6204 hRes = CONVERT10_E_OLESTREAM_PUT;
6207 if(hRes == S_OK)
6209 if(pData->dwOleTypeNameLength > 0)
6211 /* Set the OleTypeName */
6212 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6213 if(dwSize != pData->dwOleTypeNameLength)
6215 hRes = CONVERT10_E_OLESTREAM_PUT;
6220 if(hRes == S_OK)
6222 /* Set the width of the Metafile */
6223 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6224 if(dwSize != sizeof(pData->dwMetaFileWidth))
6226 hRes = CONVERT10_E_OLESTREAM_PUT;
6230 if(hRes == S_OK)
6232 /* Set the height of the Metafile */
6233 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6234 if(dwSize != sizeof(pData->dwMetaFileHeight))
6236 hRes = CONVERT10_E_OLESTREAM_PUT;
6240 if(hRes == S_OK)
6242 /* Set the lenght of the Data */
6243 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6244 if(dwSize != sizeof(pData->dwDataLength))
6246 hRes = CONVERT10_E_OLESTREAM_PUT;
6250 if(hRes == S_OK)
6252 if(pData->dwDataLength > 0)
6254 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6255 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6256 if(dwSize != pData->dwDataLength)
6258 hRes = CONVERT10_E_OLESTREAM_PUT;
6263 return hRes;
6266 /*************************************************************************
6267 * OLECONVERT_GetOLE20FromOLE10[Internal]
6269 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6270 * opens it, and copies the content to the dest IStorage for
6271 * OleConvertOLESTREAMToIStorage
6274 * PARAMS
6275 * pDestStorage [I] The IStorage to copy the data to
6276 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6277 * nBufferLength [I] The size of the buffer
6279 * RETURNS
6280 * Nothing
6282 * NOTES
6286 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6288 HRESULT hRes;
6289 HANDLE hFile;
6290 IStorage *pTempStorage;
6291 DWORD dwNumOfBytesWritten;
6292 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6293 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6295 /* Create a temp File */
6296 GetTempPathW(MAX_PATH, wstrTempDir);
6297 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6298 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6300 if(hFile != INVALID_HANDLE_VALUE)
6302 /* Write IStorage Data to File */
6303 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6304 CloseHandle(hFile);
6306 /* Open and copy temp storage to the Dest Storage */
6307 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6308 if(hRes == S_OK)
6310 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6311 StorageBaseImpl_Release(pTempStorage);
6313 DeleteFileW(wstrTempFile);
6318 /*************************************************************************
6319 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6321 * Saves the OLE10 STREAM From memory
6323 * PARAMS
6324 * pStorage [I] The Src IStorage to copy
6325 * pData [I] The Dest Memory to write to.
6327 * RETURNS
6328 * The size in bytes allocated for pData
6330 * NOTES
6331 * Memory allocated for pData must be freed by the caller
6333 * Used by OleConvertIStorageToOLESTREAM only.
6336 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6338 HANDLE hFile;
6339 HRESULT hRes;
6340 DWORD nDataLength = 0;
6341 IStorage *pTempStorage;
6342 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6343 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6345 *pData = NULL;
6347 /* Create temp Storage */
6348 GetTempPathW(MAX_PATH, wstrTempDir);
6349 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6350 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6352 if(hRes == S_OK)
6354 /* Copy Src Storage to the Temp Storage */
6355 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6356 StorageBaseImpl_Release(pTempStorage);
6358 /* Open Temp Storage as a file and copy to memory */
6359 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6360 if(hFile != INVALID_HANDLE_VALUE)
6362 nDataLength = GetFileSize(hFile, NULL);
6363 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6364 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6365 CloseHandle(hFile);
6367 DeleteFileW(wstrTempFile);
6369 return nDataLength;
6372 /*************************************************************************
6373 * OLECONVERT_CreateOleStream [Internal]
6375 * Creates the "\001OLE" stream in the IStorage if neccessary.
6377 * PARAMS
6378 * pStorage [I] Dest storage to create the stream in
6380 * RETURNS
6381 * Nothing
6383 * NOTES
6384 * This function is used by OleConvertOLESTREAMToIStorage only.
6386 * This stream is still unknown, MS Word seems to have extra data
6387 * but since the data is stored in the OLESTREAM there should be
6388 * no need to recreate the stream. If the stream is manually
6389 * deleted it will create it with this default data.
6392 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6394 HRESULT hRes;
6395 IStream *pStream;
6396 WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6397 BYTE pOleStreamHeader [] =
6399 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6400 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6401 0x00, 0x00, 0x00, 0x00
6404 /* Create stream if not present */
6405 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6406 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6408 if(hRes == S_OK)
6410 /* Write default Data */
6411 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6412 IStream_Release(pStream);
6417 /*************************************************************************
6418 * OLECONVERT_CreateCompObjStream [Internal]
6420 * Creates a "\001CompObj" is the destination IStorage if necessary.
6422 * PARAMS
6423 * pStorage [I] The dest IStorage to create the CompObj Stream
6424 * if necessary.
6425 * strOleTypeName [I] The ProgID
6427 * RETURNS
6428 * Success: S_OK
6429 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6431 * NOTES
6432 * This function is used by OleConvertOLESTREAMToIStorage only.
6434 * The stream data is stored in the OLESTREAM and there should be
6435 * no need to recreate the stream. If the stream is manually
6436 * deleted it will attempt to create it by querying the registry.
6440 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6442 IStream *pStream;
6443 HRESULT hStorageRes, hRes = S_OK;
6444 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6445 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6447 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6448 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6450 /* Initialize the CompObj structure */
6451 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6452 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6453 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6456 /* Create a CompObj stream if it doesn't exist */
6457 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6458 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6459 if(hStorageRes == S_OK)
6461 /* copy the OleTypeName to the compobj struct */
6462 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6463 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6465 /* copy the OleTypeName to the compobj struct */
6466 /* Note: in the test made, these were Identical */
6467 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6468 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6470 /* Get the CLSID */
6471 hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6473 if(hRes == S_OK)
6475 HKEY hKey;
6476 LONG hErr;
6477 /* Get the CLSID Default Name from the Registry */
6478 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6479 if(hErr == ERROR_SUCCESS)
6481 char strTemp[OLESTREAM_MAX_STR_LEN];
6482 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6483 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6484 if(hErr == ERROR_SUCCESS)
6486 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6488 RegCloseKey(hKey);
6492 /* Write CompObj Structure to stream */
6493 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6495 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6497 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6498 if(IStorageCompObj.dwCLSIDNameLength > 0)
6500 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6502 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6503 if(IStorageCompObj.dwOleTypeNameLength > 0)
6505 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6507 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6508 if(IStorageCompObj.dwProgIDNameLength > 0)
6510 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6512 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6513 IStream_Release(pStream);
6515 return hRes;
6519 /*************************************************************************
6520 * OLECONVERT_CreateOlePresStream[Internal]
6522 * Creates the "\002OlePres000" Stream with the Metafile data
6524 * PARAMS
6525 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6526 * dwExtentX [I] Width of the Metafile
6527 * dwExtentY [I] Height of the Metafile
6528 * pData [I] Metafile data
6529 * dwDataLength [I] Size of the Metafile data
6531 * RETURNS
6532 * Success: S_OK
6533 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6535 * NOTES
6536 * This function is used by OleConvertOLESTREAMToIStorage only.
6539 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6541 HRESULT hRes;
6542 IStream *pStream;
6543 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6544 BYTE pOlePresStreamHeader [] =
6546 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6547 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6548 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6549 0x00, 0x00, 0x00, 0x00
6552 BYTE pOlePresStreamHeaderEmpty [] =
6554 0x00, 0x00, 0x00, 0x00,
6555 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6556 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6557 0x00, 0x00, 0x00, 0x00
6560 /* Create the OlePres000 Stream */
6561 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6562 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6564 if(hRes == S_OK)
6566 DWORD nHeaderSize;
6567 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6569 memset(&OlePres, 0, sizeof(OlePres));
6570 /* Do we have any metafile data to save */
6571 if(dwDataLength > 0)
6573 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6574 nHeaderSize = sizeof(pOlePresStreamHeader);
6576 else
6578 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6579 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6581 /* Set width and height of the metafile */
6582 OlePres.dwExtentX = dwExtentX;
6583 OlePres.dwExtentY = -dwExtentY;
6585 /* Set Data and Lenght */
6586 if(dwDataLength > sizeof(METAFILEPICT16))
6588 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6589 OlePres.pData = &(pData[8]);
6591 /* Save OlePres000 Data to Stream */
6592 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
6593 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
6594 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
6595 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
6596 if(OlePres.dwSize > 0)
6598 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
6600 IStream_Release(pStream);
6604 /*************************************************************************
6605 * OLECONVERT_CreateOle10NativeStream [Internal]
6607 * Creates the "\001Ole10Native" Stream (should contain a BMP)
6609 * PARAMS
6610 * pStorage [I] Dest storage to create the stream in
6611 * pData [I] Ole10 Native Data (ex. bmp)
6612 * dwDataLength [I] Size of the Ole10 Native Data
6614 * RETURNS
6615 * Nothing
6617 * NOTES
6618 * This function is used by OleConvertOLESTREAMToIStorage only.
6620 * Might need to verify the data and return appropriate error message
6623 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
6625 HRESULT hRes;
6626 IStream *pStream;
6627 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6629 /* Create the Ole10Native Stream */
6630 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6631 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6633 if(hRes == S_OK)
6635 /* Write info to stream */
6636 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
6637 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
6638 IStream_Release(pStream);
6643 /*************************************************************************
6644 * OLECONVERT_GetOLE10ProgID [Internal]
6646 * Finds the ProgID (or OleTypeID) from the IStorage
6648 * PARAMS
6649 * pStorage [I] The Src IStorage to get the ProgID
6650 * strProgID [I] the ProgID string to get
6651 * dwSize [I] the size of the string
6653 * RETURNS
6654 * Success: S_OK
6655 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6657 * NOTES
6658 * This function is used by OleConvertIStorageToOLESTREAM only.
6662 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
6664 HRESULT hRes;
6665 IStream *pStream;
6666 LARGE_INTEGER iSeekPos;
6667 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
6668 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6670 /* Open the CompObj Stream */
6671 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6672 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6673 if(hRes == S_OK)
6676 /*Get the OleType from the CompObj Stream */
6677 iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
6678 iSeekPos.s.HighPart = 0;
6680 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6681 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
6682 iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
6683 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6684 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
6685 iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
6686 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6688 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
6689 if(*dwSize > 0)
6691 IStream_Read(pStream, strProgID, *dwSize, NULL);
6693 IStream_Release(pStream);
6695 else
6697 STATSTG stat;
6698 LPOLESTR wstrProgID;
6700 /* Get the OleType from the registry */
6701 REFCLSID clsid = &(stat.clsid);
6702 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
6703 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
6704 if(hRes == S_OK)
6706 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
6710 return hRes;
6713 /*************************************************************************
6714 * OLECONVERT_GetOle10PresData [Internal]
6716 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
6718 * PARAMS
6719 * pStorage [I] Src IStroage
6720 * pOleStream [I] Dest OleStream Mem Struct
6722 * RETURNS
6723 * Nothing
6725 * NOTES
6726 * This function is used by OleConvertIStorageToOLESTREAM only.
6728 * Memory allocated for pData must be freed by the caller
6732 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6735 HRESULT hRes;
6736 IStream *pStream;
6737 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6739 /* Initialize Default data for OLESTREAM */
6740 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6741 pOleStreamData[0].dwTypeID = 2;
6742 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6743 pOleStreamData[1].dwTypeID = 0;
6744 pOleStreamData[0].dwMetaFileWidth = 0;
6745 pOleStreamData[0].dwMetaFileHeight = 0;
6746 pOleStreamData[0].pData = NULL;
6747 pOleStreamData[1].pData = NULL;
6749 /* Open Ole10Native Stream */
6750 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6751 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6752 if(hRes == S_OK)
6755 /* Read Size and Data */
6756 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
6757 if(pOleStreamData->dwDataLength > 0)
6759 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
6760 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
6762 IStream_Release(pStream);
6768 /*************************************************************************
6769 * OLECONVERT_GetOle20PresData[Internal]
6771 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
6773 * PARAMS
6774 * pStorage [I] Src IStroage
6775 * pOleStreamData [I] Dest OleStream Mem Struct
6777 * RETURNS
6778 * Nothing
6780 * NOTES
6781 * This function is used by OleConvertIStorageToOLESTREAM only.
6783 * Memory allocated for pData must be freed by the caller
6785 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6787 HRESULT hRes;
6788 IStream *pStream;
6789 OLECONVERT_ISTORAGE_OLEPRES olePress;
6790 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6792 /* Initialize Default data for OLESTREAM */
6793 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6794 pOleStreamData[0].dwTypeID = 2;
6795 pOleStreamData[0].dwMetaFileWidth = 0;
6796 pOleStreamData[0].dwMetaFileHeight = 0;
6797 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
6798 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6799 pOleStreamData[1].dwTypeID = 0;
6800 pOleStreamData[1].dwOleTypeNameLength = 0;
6801 pOleStreamData[1].strOleTypeName[0] = 0;
6802 pOleStreamData[1].dwMetaFileWidth = 0;
6803 pOleStreamData[1].dwMetaFileHeight = 0;
6804 pOleStreamData[1].pData = NULL;
6805 pOleStreamData[1].dwDataLength = 0;
6808 /* Open OlePress000 stream */
6809 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6810 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6811 if(hRes == S_OK)
6813 LARGE_INTEGER iSeekPos;
6814 METAFILEPICT16 MetaFilePict;
6815 char strMetafilePictName[] = "METAFILEPICT";
6817 /* Set the TypeID for a Metafile */
6818 pOleStreamData[1].dwTypeID = 5;
6820 /* Set the OleTypeName to Metafile */
6821 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
6822 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
6824 iSeekPos.s.HighPart = 0;
6825 iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
6827 /* Get Presentation Data */
6828 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6829 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
6830 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
6831 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
6833 /*Set width and Height */
6834 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
6835 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
6836 if(olePress.dwSize > 0)
6838 /* Set Length */
6839 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
6841 /* Set MetaFilePict struct */
6842 MetaFilePict.mm = 8;
6843 MetaFilePict.xExt = olePress.dwExtentX;
6844 MetaFilePict.yExt = olePress.dwExtentY;
6845 MetaFilePict.hMF = 0;
6847 /* Get Metafile Data */
6848 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
6849 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
6850 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
6852 IStream_Release(pStream);
6856 /*************************************************************************
6857 * OleConvertOLESTREAMToIStorage [OLE32.87]
6859 * Read info on MSDN
6861 * TODO
6862 * DVTARGETDEVICE paramenter is not handled
6863 * Still unsure of some mem fields for OLE 10 Stream
6864 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6865 * and "\001OLE" streams
6868 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
6869 LPOLESTREAM pOleStream,
6870 LPSTORAGE pstg,
6871 const DVTARGETDEVICE* ptd)
6873 int i;
6874 HRESULT hRes=S_OK;
6875 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6877 memset(pOleStreamData, 0, sizeof(pOleStreamData));
6879 if(ptd != NULL)
6881 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
6884 if(pstg == NULL || pOleStream == NULL)
6886 hRes = E_INVALIDARG;
6889 if(hRes == S_OK)
6891 /* Load the OLESTREAM to Memory */
6892 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
6895 if(hRes == S_OK)
6897 /* Load the OLESTREAM to Memory (part 2)*/
6898 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
6901 if(hRes == S_OK)
6904 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
6906 /* Do we have the IStorage Data in the OLESTREAM */
6907 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
6909 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6910 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
6912 else
6914 /* It must be an original OLE 1.0 source */
6915 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6918 else
6920 /* It must be an original OLE 1.0 source */
6921 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6924 /* Create CompObj Stream if necessary */
6925 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
6926 if(hRes == S_OK)
6928 /*Create the Ole Stream if necessary */
6929 OLECONVERT_CreateOleStream(pstg);
6934 /* Free allocated memory */
6935 for(i=0; i < 2; i++)
6937 if(pOleStreamData[i].pData != NULL)
6939 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
6941 if(pOleStreamData[i].pstrOleObjFileName != NULL)
6943 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
6944 pOleStreamData[i].pstrOleObjFileName = NULL;
6947 return hRes;
6950 /*************************************************************************
6951 * OleConvertIStorageToOLESTREAM [OLE32.85]
6953 * Read info on MSDN
6955 * Read info on MSDN
6957 * TODO
6958 * Still unsure of some mem fields for OLE 10 Stream
6959 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6960 * and "\001OLE" streams.
6963 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
6964 LPSTORAGE pstg,
6965 LPOLESTREAM pOleStream)
6967 int i;
6968 HRESULT hRes = S_OK;
6969 IStream *pStream;
6970 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6971 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6974 memset(pOleStreamData, 0, sizeof(pOleStreamData));
6976 if(pstg == NULL || pOleStream == NULL)
6978 hRes = E_INVALIDARG;
6980 if(hRes == S_OK)
6982 /* Get the ProgID */
6983 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
6984 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
6986 if(hRes == S_OK)
6988 /*Was it originaly Ole10 */
6989 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
6990 if(hRes == S_OK)
6992 IStream_Release(pStream);
6993 /*Get Presentation Data for Ole10Native */
6994 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
6996 else
6998 /*Get Presentation Data (OLE20)*/
6999 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7002 /* Save OLESTREAM */
7003 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7004 if(hRes == S_OK)
7006 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7011 /* Free allocated memory */
7012 for(i=0; i < 2; i++)
7014 if(pOleStreamData[i].pData != NULL)
7016 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7020 return hRes;
7023 /***********************************************************************
7024 * GetConvertStg (OLE32.68)
7026 HRESULT WINAPI GetConvertStg(LPGUID guid) {
7027 FIXME("(%s), unimplemented stub!\n",debugstr_guid(guid));
7028 return E_FAIL;