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
20 #include "wine/obj_storage.h"
21 #include "wine/winestring.h"
27 #include "storage32.h"
32 static const char rootPropertyName
[] = "Root Entry";
34 /***********************************************************************
35 * Forward declaration of internal functions used by the method DestroyElement
37 static HRESULT
deleteStorageProperty(
38 StorageImpl
*parentStorage
,
39 OLECHAR
*propertyToDeleteName
);
41 static HRESULT
deleteStreamProperty(
42 StorageImpl
*parentStorage
,
43 ULONG foundPropertyIndexToDelete
,
44 StgProperty propertyToDelete
);
46 static HRESULT
findPlaceholder(
48 ULONG propertyIndexToStore
,
49 ULONG storagePropertyIndex
,
52 static HRESULT
adjustPropertyChain(
54 StgProperty propertyToDelete
,
55 StgProperty parentProperty
,
56 ULONG parentPropertyId
,
59 /***********************************************************************
60 * Declaration of the functions used to manipulate StgProperty
63 static ULONG
getFreeProperty(
64 StorageImpl
*storage
);
66 static void updatePropertyChain(
68 ULONG newPropertyIndex
,
69 StgProperty newProperty
);
71 static LONG
propertyNameCmp(
73 OLECHAR
*currentProperty
);
76 /***********************************************************************
77 * Declaration of miscellaneous functions...
79 static HRESULT
validateSTGM(DWORD stgmValue
);
81 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
82 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
83 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
86 * Virtual function table for the IStorage32Impl class.
88 static ICOM_VTABLE(IStorage
) Storage32Impl_Vtbl
=
90 StorageBaseImpl_QueryInterface
,
91 StorageBaseImpl_AddRef
,
92 StorageBaseImpl_Release
,
93 StorageBaseImpl_CreateStream
,
94 StorageBaseImpl_OpenStream
,
95 StorageImpl_CreateStorage
,
96 StorageBaseImpl_OpenStorage
,
98 StorageImpl_MoveElementTo
,
101 StorageBaseImpl_EnumElements
,
102 StorageImpl_DestroyElement
,
103 StorageBaseImpl_RenameElement
,
104 StorageImpl_SetElementTimes
,
105 StorageBaseImpl_SetClass
,
106 StorageImpl_SetStateBits
,
111 * Virtual function table for the Storage32InternalImpl class.
113 static ICOM_VTABLE(IStorage
) Storage32InternalImpl_Vtbl
=
115 StorageBaseImpl_QueryInterface
,
116 StorageBaseImpl_AddRef
,
117 StorageBaseImpl_Release
,
118 StorageBaseImpl_CreateStream
,
119 StorageBaseImpl_OpenStream
,
120 StorageImpl_CreateStorage
,
121 StorageBaseImpl_OpenStorage
,
123 StorageImpl_MoveElementTo
,
124 StorageInternalImpl_Commit
,
125 StorageInternalImpl_Revert
,
126 StorageBaseImpl_EnumElements
,
127 StorageImpl_DestroyElement
,
128 StorageBaseImpl_RenameElement
,
129 StorageImpl_SetElementTimes
,
130 StorageBaseImpl_SetClass
,
131 StorageImpl_SetStateBits
,
136 * Virtual function table for the IEnumSTATSTGImpl class.
138 static ICOM_VTABLE(IEnumSTATSTG
) IEnumSTATSTGImpl_Vtbl
=
140 IEnumSTATSTGImpl_QueryInterface
,
141 IEnumSTATSTGImpl_AddRef
,
142 IEnumSTATSTGImpl_Release
,
143 IEnumSTATSTGImpl_Next
,
144 IEnumSTATSTGImpl_Skip
,
145 IEnumSTATSTGImpl_Reset
,
146 IEnumSTATSTGImpl_Clone
153 /************************************************************************
154 ** Storage32BaseImpl implementatiion
157 /************************************************************************
158 * Storage32BaseImpl_QueryInterface (IUnknown)
160 * This method implements the common QueryInterface for all IStorage32
161 * implementations contained in this file.
163 * See Windows documentation for more details on IUnknown methods.
165 HRESULT WINAPI
StorageBaseImpl_QueryInterface(
170 ICOM_THIS(StorageBaseImpl
,iface
);
172 * Perform a sanity check on the parameters.
174 if ( (This
==0) || (ppvObject
==0) )
178 * Initialize the return parameter.
183 * Compare the riid with the interface IDs implemented by this object.
185 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
187 *ppvObject
= (IStorage
*)This
;
189 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IStorage
)) == 0)
191 *ppvObject
= (IStorage
*)This
;
195 * Check that we obtained an interface.
198 return E_NOINTERFACE
;
201 * Query Interface always increases the reference count by one when it is
204 StorageBaseImpl_AddRef(iface
);
209 /************************************************************************
210 * Storage32BaseImpl_AddRef (IUnknown)
212 * This method implements the common AddRef for all IStorage32
213 * implementations contained in this file.
215 * See Windows documentation for more details on IUnknown methods.
217 ULONG WINAPI
StorageBaseImpl_AddRef(
220 ICOM_THIS(StorageBaseImpl
,iface
);
226 /************************************************************************
227 * Storage32BaseImpl_Release (IUnknown)
229 * This method implements the common Release for all IStorage32
230 * implementations contained in this file.
232 * See Windows documentation for more details on IUnknown methods.
234 ULONG WINAPI
StorageBaseImpl_Release(
237 ICOM_THIS(StorageBaseImpl
,iface
);
239 * Decrease the reference count on this object.
244 * If the reference count goes down to 0, perform suicide.
249 * Since we are using a system of base-classes, we want to call the
250 * destructor of the appropriate derived class. To do this, we are
251 * using virtual functions to implement the destructor.
253 This
->v_destructor(This
);
261 /************************************************************************
262 * Storage32BaseImpl_OpenStream (IStorage)
264 * This method will open the specified stream object from the current storage.
266 * See Windows documentation for more details on IStorage methods.
268 HRESULT WINAPI
StorageBaseImpl_OpenStream(
270 const OLECHAR
* pwcsName
, /* [string][in] */
271 void* reserved1
, /* [unique][in] */
272 DWORD grfMode
, /* [in] */
273 DWORD reserved2
, /* [in] */
274 IStream
** ppstm
) /* [out] */
276 ICOM_THIS(StorageBaseImpl
,iface
);
277 IEnumSTATSTGImpl
* propertyEnumeration
;
278 StgStreamImpl
* newStream
;
279 StgProperty currentProperty
;
280 ULONG foundPropertyIndex
;
283 * Perform a sanity check on the parameters.
285 if ( (pwcsName
==NULL
) || (ppstm
==0) )
289 * Initialize the out parameter
294 * Validate the STGM flags
296 if ( FAILED( validateSTGM(grfMode
) ))
297 return STG_E_INVALIDFLAG
;
302 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
303 (grfMode
& STGM_DELETEONRELEASE
) ||
304 (grfMode
& STGM_TRANSACTED
) )
305 return STG_E_INVALIDFUNCTION
;
308 * Create a property enumeration to search the properties
310 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
311 This
->ancestorStorage
,
312 This
->rootPropertySetIndex
);
315 * Search the enumeration for the property with the given name
317 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
323 * Delete the property enumeration since we don't need it anymore
325 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
328 * If it was found, construct the stream object and return a pointer to it.
330 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
331 (currentProperty
.propertyType
==PROPTYPE_STREAM
) )
333 newStream
= StgStreamImpl_Construct(This
, foundPropertyIndex
);
337 *ppstm
= (IStream
*)newStream
;
340 * Since we are returning a pointer to the interface, we have to
341 * nail down the reference.
343 StgStreamImpl_AddRef(*ppstm
);
348 return E_OUTOFMEMORY
;
351 return STG_E_FILENOTFOUND
;
354 /************************************************************************
355 * Storage32BaseImpl_OpenStorage (IStorage)
357 * This method will open a new storage object from the current storage.
359 * See Windows documentation for more details on IStorage methods.
361 HRESULT WINAPI
StorageBaseImpl_OpenStorage(
363 const OLECHAR
* pwcsName
, /* [string][unique][in] */
364 IStorage
* pstgPriority
, /* [unique][in] */
365 DWORD grfMode
, /* [in] */
366 SNB snbExclude
, /* [unique][in] */
367 DWORD reserved
, /* [in] */
368 IStorage
** ppstg
) /* [out] */
370 ICOM_THIS(StorageBaseImpl
,iface
);
371 StorageInternalImpl
* newStorage
;
372 IEnumSTATSTGImpl
* propertyEnumeration
;
373 StgProperty currentProperty
;
374 ULONG foundPropertyIndex
;
377 * Perform a sanity check on the parameters.
379 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
383 * Validate the STGM flags
385 if ( FAILED( validateSTGM(grfMode
) ))
386 return STG_E_INVALIDFLAG
;
391 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
392 (grfMode
& STGM_DELETEONRELEASE
) ||
393 (grfMode
& STGM_PRIORITY
) )
394 return STG_E_INVALIDFUNCTION
;
397 * Initialize the out parameter
402 * Create a property enumeration to search the properties
404 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
405 This
->ancestorStorage
,
406 This
->rootPropertySetIndex
);
409 * Search the enumeration for the property with the given name
411 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
417 * Delete the property enumeration since we don't need it anymore
419 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
422 * If it was found, construct the stream object and return a pointer to it.
424 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
425 (currentProperty
.propertyType
==PROPTYPE_STORAGE
) )
428 * Construct a new Storage object
430 newStorage
= StorageInternalImpl_Construct(
431 This
->ancestorStorage
,
436 *ppstg
= (IStorage
*)newStorage
;
439 * Since we are returning a pointer to the interface,
440 * we have to nail down the reference.
442 StorageBaseImpl_AddRef(*ppstg
);
447 return STG_E_INSUFFICIENTMEMORY
;
450 return STG_E_FILENOTFOUND
;
453 /************************************************************************
454 * Storage32BaseImpl_EnumElements (IStorage)
456 * This method will create an enumerator object that can be used to
457 * retrieve informatino about all the properties in the storage object.
459 * See Windows documentation for more details on IStorage methods.
461 HRESULT WINAPI
StorageBaseImpl_EnumElements(
463 DWORD reserved1
, /* [in] */
464 void* reserved2
, /* [size_is][unique][in] */
465 DWORD reserved3
, /* [in] */
466 IEnumSTATSTG
** ppenum
) /* [out] */
468 ICOM_THIS(StorageBaseImpl
,iface
);
469 IEnumSTATSTGImpl
* newEnum
;
472 * Perform a sanity check on the parameters.
474 if ( (This
==0) || (ppenum
==0))
478 * Construct the enumerator.
480 newEnum
= IEnumSTATSTGImpl_Construct(
481 This
->ancestorStorage
,
482 This
->rootPropertySetIndex
);
486 *ppenum
= (IEnumSTATSTG
*)newEnum
;
489 * Don't forget to nail down a reference to the new object before
492 IEnumSTATSTGImpl_AddRef(*ppenum
);
497 return E_OUTOFMEMORY
;
500 /************************************************************************
501 * Storage32BaseImpl_Stat (IStorage)
503 * This method will retrieve information about this storage object.
505 * See Windows documentation for more details on IStorage methods.
507 HRESULT WINAPI
StorageBaseImpl_Stat(
509 STATSTG
* pstatstg
, /* [out] */
510 DWORD grfStatFlag
) /* [in] */
512 ICOM_THIS(StorageBaseImpl
,iface
);
513 StgProperty curProperty
;
517 * Perform a sanity check on the parameters.
519 if ( (This
==0) || (pstatstg
==0))
523 * Read the information from the property.
525 readSucessful
= StorageImpl_ReadProperty(
526 This
->ancestorStorage
,
527 This
->rootPropertySetIndex
,
532 StorageUtl_CopyPropertyToSTATSTG(
543 /************************************************************************
544 * Storage32BaseImpl_RenameElement (IStorage)
546 * This method will rename the specified element.
548 * See Windows documentation for more details on IStorage methods.
550 * Implementation notes: The method used to rename consists of creating a clone
551 * of the deleted StgProperty object setting it with the new name and to
552 * perform a DestroyElement of the old StgProperty.
554 HRESULT WINAPI
StorageBaseImpl_RenameElement(
556 const OLECHAR
* pwcsOldName
, /* [in] */
557 const OLECHAR
* pwcsNewName
) /* [in] */
559 ICOM_THIS(StorageBaseImpl
,iface
);
560 IEnumSTATSTGImpl
* propertyEnumeration
;
561 StgProperty currentProperty
;
562 ULONG foundPropertyIndex
;
565 * Create a property enumeration to search the properties
567 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
568 This
->rootPropertySetIndex
);
571 * Search the enumeration for the new property name
573 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
577 if (foundPropertyIndex
!= PROPERTY_NULL
)
580 * There is already a property with the new name
582 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
583 return STG_E_FILEALREADYEXISTS
;
586 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)propertyEnumeration
);
589 * Search the enumeration for the old property name
591 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
596 * Delete the property enumeration since we don't need it anymore
598 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
600 if (foundPropertyIndex
!= PROPERTY_NULL
)
602 StgProperty renamedProperty
;
603 ULONG renamedPropertyIndex
;
606 * Setup a new property for the renamed property
608 renamedProperty
.sizeOfNameString
=
609 ( lstrlenW(pwcsNewName
)+1 ) * sizeof(WCHAR
);
611 if (renamedProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
612 return STG_E_INVALIDNAME
;
614 lstrcpyW(renamedProperty
.name
, pwcsNewName
);
616 renamedProperty
.propertyType
= currentProperty
.propertyType
;
617 renamedProperty
.startingBlock
= currentProperty
.startingBlock
;
618 renamedProperty
.size
.LowPart
= currentProperty
.size
.LowPart
;
619 renamedProperty
.size
.HighPart
= currentProperty
.size
.HighPart
;
621 renamedProperty
.previousProperty
= PROPERTY_NULL
;
622 renamedProperty
.nextProperty
= PROPERTY_NULL
;
625 * Bring the dirProperty link in case it is a storage and in which
626 * case the renamed storage elements don't require to be reorganized.
628 renamedProperty
.dirProperty
= currentProperty
.dirProperty
;
630 /* call CoFileTime to get the current time
631 renamedProperty.timeStampS1
632 renamedProperty.timeStampD1
633 renamedProperty.timeStampS2
634 renamedProperty.timeStampD2
635 renamedProperty.propertyUniqueID
639 * Obtain a free property in the property chain
641 renamedPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
644 * Save the new property into the new property spot
646 StorageImpl_WriteProperty(
647 This
->ancestorStorage
,
648 renamedPropertyIndex
,
652 * Find a spot in the property chain for our newly created property.
656 renamedPropertyIndex
,
660 * At this point the renamed property has been inserted in the tree,
661 * now, before to Destroy the old property we must zeroed it's dirProperty
662 * otherwise the DestroyProperty below will zap it all and we do not want
664 * Also, we fake that the old property is a storage so the DestroyProperty
665 * will not do a SetSize(0) on the stream data.
667 * This means that we need to tweek the StgProperty if it is a stream or a
670 currentProperty
.dirProperty
= PROPERTY_NULL
;
671 currentProperty
.propertyType
= PROPTYPE_STORAGE
;
672 StorageImpl_WriteProperty(
673 This
->ancestorStorage
,
678 * Invoke Destroy to get rid of the ole property and automatically redo
679 * the linking of it's previous and next members...
681 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsOldName
);
687 * There is no property with the old name
689 return STG_E_FILENOTFOUND
;
695 /************************************************************************
696 * Storage32BaseImpl_CreateStream (IStorage)
698 * This method will create a stream object within this storage
700 * See Windows documentation for more details on IStorage methods.
702 HRESULT WINAPI
StorageBaseImpl_CreateStream(
704 const OLECHAR
* pwcsName
, /* [string][in] */
705 DWORD grfMode
, /* [in] */
706 DWORD reserved1
, /* [in] */
707 DWORD reserved2
, /* [in] */
708 IStream
** ppstm
) /* [out] */
710 ICOM_THIS(StorageBaseImpl
,iface
);
711 IEnumSTATSTGImpl
* propertyEnumeration
;
712 StgStreamImpl
* newStream
;
713 StgProperty currentProperty
, newStreamProperty
;
714 ULONG foundPropertyIndex
, newPropertyIndex
;
717 * Validate parameters
720 return STG_E_INVALIDPOINTER
;
723 return STG_E_INVALIDNAME
;
726 * Validate the STGM flags
728 if ( FAILED( validateSTGM(grfMode
) ))
729 return STG_E_INVALIDFLAG
;
734 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
735 (grfMode
& STGM_DELETEONRELEASE
) ||
736 (grfMode
& STGM_TRANSACTED
) )
737 return STG_E_INVALIDFUNCTION
;
740 * Initialize the out parameter
745 * Create a property enumeration to search the properties
747 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
748 This
->rootPropertySetIndex
);
750 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
754 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
756 if (foundPropertyIndex
!= PROPERTY_NULL
)
759 * An element with this name already exists
761 if (grfMode
& STGM_CREATE
)
762 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsName
);
764 return STG_E_FILEALREADYEXISTS
;
768 * memset the empty property
770 memset(&newStreamProperty
, 0, sizeof(StgProperty
));
772 newStreamProperty
.sizeOfNameString
=
773 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
775 if (newStreamProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
776 return STG_E_INVALIDNAME
;
778 lstrcpyW(newStreamProperty
.name
, pwcsName
);
780 newStreamProperty
.propertyType
= PROPTYPE_STREAM
;
781 newStreamProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
782 newStreamProperty
.size
.LowPart
= 0;
783 newStreamProperty
.size
.HighPart
= 0;
785 newStreamProperty
.previousProperty
= PROPERTY_NULL
;
786 newStreamProperty
.nextProperty
= PROPERTY_NULL
;
787 newStreamProperty
.dirProperty
= PROPERTY_NULL
;
789 /* call CoFileTime to get the current time
790 newStreamProperty.timeStampS1
791 newStreamProperty.timeStampD1
792 newStreamProperty.timeStampS2
793 newStreamProperty.timeStampD2
796 /* newStreamProperty.propertyUniqueID */
799 * Get a free property or create a new one
801 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
804 * Save the new property into the new property spot
806 StorageImpl_WriteProperty(
807 This
->ancestorStorage
,
812 * Find a spot in the property chain for our newly created property.
820 * Open the stream to return it.
822 newStream
= StgStreamImpl_Construct(This
, newPropertyIndex
);
826 *ppstm
= (IStream
*)newStream
;
829 * Since we are returning a pointer to the interface, we have to nail down
832 StgStreamImpl_AddRef(*ppstm
);
836 return STG_E_INSUFFICIENTMEMORY
;
842 /************************************************************************
843 * Storage32BaseImpl_SetClass (IStorage)
845 * This method will write the specified CLSID in the property of this
848 * See Windows documentation for more details on IStorage methods.
850 HRESULT WINAPI
StorageBaseImpl_SetClass(
852 REFCLSID clsid
) /* [in] */
854 ICOM_THIS(StorageBaseImpl
,iface
);
855 HRESULT hRes
= E_FAIL
;
856 StgProperty curProperty
;
859 success
= StorageImpl_ReadProperty(This
->ancestorStorage
,
860 This
->rootPropertySetIndex
,
864 curProperty
.propertyUniqueID
= *clsid
;
866 success
= StorageImpl_WriteProperty(This
->ancestorStorage
,
867 This
->rootPropertySetIndex
,
876 /************************************************************************
877 ** Storage32Impl implementation
880 /************************************************************************
881 * Storage32Impl_CreateStorage (IStorage)
883 * This method will create the storage object within the provided storage.
885 * See Windows documentation for more details on IStorage methods.
887 HRESULT WINAPI
StorageImpl_CreateStorage(
889 const OLECHAR
*pwcsName
, /* [string][in] */
890 DWORD grfMode
, /* [in] */
891 DWORD reserved1
, /* [in] */
892 DWORD reserved2
, /* [in] */
893 IStorage
**ppstg
) /* [out] */
895 StorageImpl
* const This
=(StorageImpl
*)iface
;
897 IEnumSTATSTGImpl
*propertyEnumeration
;
898 StgProperty currentProperty
;
899 StgProperty newProperty
;
900 ULONG foundPropertyIndex
;
901 ULONG newPropertyIndex
;
906 * Validate parameters
909 return STG_E_INVALIDPOINTER
;
912 return STG_E_INVALIDNAME
;
915 * Validate the STGM flags
917 if ( FAILED( validateSTGM(grfMode
) ) ||
918 (grfMode
& STGM_DELETEONRELEASE
) )
919 return STG_E_INVALIDFLAG
;
922 * Initialize the out parameter
927 * Create a property enumeration and search the properties
929 propertyEnumeration
= IEnumSTATSTGImpl_Construct( This
->ancestorStorage
,
930 This
->rootPropertySetIndex
);
932 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
935 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
937 if (foundPropertyIndex
!= PROPERTY_NULL
)
940 * An element with this name already exists
942 if (grfMode
& STGM_CREATE
)
943 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsName
);
945 return STG_E_FILEALREADYEXISTS
;
949 * memset the empty property
951 memset(&newProperty
, 0, sizeof(StgProperty
));
953 newProperty
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
955 if (newProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
956 return STG_E_INVALIDNAME
;
958 lstrcpyW(newProperty
.name
, pwcsName
);
960 newProperty
.propertyType
= PROPTYPE_STORAGE
;
961 newProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
962 newProperty
.size
.LowPart
= 0;
963 newProperty
.size
.HighPart
= 0;
965 newProperty
.previousProperty
= PROPERTY_NULL
;
966 newProperty
.nextProperty
= PROPERTY_NULL
;
967 newProperty
.dirProperty
= PROPERTY_NULL
;
969 /* call CoFileTime to get the current time
970 newProperty.timeStampS1
971 newProperty.timeStampD1
972 newProperty.timeStampS2
973 newProperty.timeStampD2
976 /* newStorageProperty.propertyUniqueID */
979 * Obtain a free property in the property chain
981 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
984 * Save the new property into the new property spot
986 StorageImpl_WriteProperty(
987 This
->ancestorStorage
,
992 * Find a spot in the property chain for our newly created property.
1000 * Open it to get a pointer to return.
1002 hr
= StorageBaseImpl_OpenStorage(
1011 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1021 /***************************************************************************
1025 * Get a free property or create a new one.
1027 static ULONG
getFreeProperty(
1028 StorageImpl
*storage
)
1030 ULONG currentPropertyIndex
= 0;
1031 ULONG newPropertyIndex
= PROPERTY_NULL
;
1032 BOOL readSucessful
= TRUE
;
1033 StgProperty currentProperty
;
1038 * Start by reading the root property
1040 readSucessful
= StorageImpl_ReadProperty(storage
->ancestorStorage
,
1041 currentPropertyIndex
,
1045 if (currentProperty
.sizeOfNameString
== 0)
1048 * The property existis and is available, we found it.
1050 newPropertyIndex
= currentPropertyIndex
;
1056 * We exhausted the property list, we will create more space below
1058 newPropertyIndex
= currentPropertyIndex
;
1060 currentPropertyIndex
++;
1062 } while (newPropertyIndex
== PROPERTY_NULL
);
1065 * grow the property chain
1067 if (! readSucessful
)
1069 StgProperty emptyProperty
;
1070 ULARGE_INTEGER newSize
;
1071 ULONG propertyIndex
;
1072 ULONG lastProperty
= 0;
1073 ULONG blockCount
= 0;
1076 * obtain the new count of property blocks
1078 blockCount
= BlockChainStream_GetCount(
1079 storage
->ancestorStorage
->rootBlockChain
)+1;
1082 * initialize the size used by the property stream
1084 newSize
.HighPart
= 0;
1085 newSize
.LowPart
= storage
->bigBlockSize
* blockCount
;
1088 * add a property block to the property chain
1090 BlockChainStream_SetSize(storage
->ancestorStorage
->rootBlockChain
, newSize
);
1093 * memset the empty property in order to initialize the unused newly
1096 memset(&emptyProperty
, 0, sizeof(StgProperty
));
1101 lastProperty
= storage
->bigBlockSize
/ PROPSET_BLOCK_SIZE
* blockCount
;
1104 propertyIndex
= newPropertyIndex
;
1105 propertyIndex
< lastProperty
;
1108 StorageImpl_WriteProperty(
1109 storage
->ancestorStorage
,
1115 return newPropertyIndex
;
1118 /****************************************************************************
1122 * Case insensitive comparaison of StgProperty.name by first considering
1125 * Returns <0 when newPrpoerty < currentProperty
1126 * >0 when newPrpoerty > currentProperty
1127 * 0 when newPrpoerty == currentProperty
1129 static LONG
propertyNameCmp(
1130 OLECHAR
*newProperty
,
1131 OLECHAR
*currentProperty
)
1133 LONG diff
= lstrlenW(newProperty
) - lstrlenW(currentProperty
);
1138 * We compare the string themselves only when they are of the same lenght
1140 diff
= lstrcmpiW( newProperty
, currentProperty
);
1146 /****************************************************************************
1150 * Properly link this new element in the property chain.
1152 static void updatePropertyChain(
1153 StorageImpl
*storage
,
1154 ULONG newPropertyIndex
,
1155 StgProperty newProperty
)
1157 StgProperty currentProperty
;
1160 * Read the root property
1162 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1163 storage
->rootPropertySetIndex
,
1166 if (currentProperty
.dirProperty
!= PROPERTY_NULL
)
1169 * The root storage contains some element, therefore, start the research
1170 * for the appropriate location.
1173 ULONG current
, next
, previous
, currentPropertyId
;
1176 * Keep the StgProperty sequence number of the storage first property
1178 currentPropertyId
= currentProperty
.dirProperty
;
1183 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1184 currentProperty
.dirProperty
,
1187 previous
= currentProperty
.previousProperty
;
1188 next
= currentProperty
.nextProperty
;
1189 current
= currentPropertyId
;
1193 LONG diff
= propertyNameCmp( newProperty
.name
, currentProperty
.name
);
1197 if (previous
!= PROPERTY_NULL
)
1199 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1206 currentProperty
.previousProperty
= newPropertyIndex
;
1207 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1215 if (next
!= PROPERTY_NULL
)
1217 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1224 currentProperty
.nextProperty
= newPropertyIndex
;
1225 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1232 previous
= currentProperty
.previousProperty
;
1233 next
= currentProperty
.nextProperty
;
1239 * The root storage is empty, link the new property to it's dir property
1241 currentProperty
.dirProperty
= newPropertyIndex
;
1242 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1243 storage
->rootPropertySetIndex
,
1249 /*************************************************************************
1252 HRESULT WINAPI
StorageImpl_CopyTo(
1254 DWORD ciidExclude
, /* [in] */
1255 const IID
*rgiidExclude
,/* [size_is][unique][in] */
1256 SNB snbExclude
, /* [unique][in] */
1257 IStorage
*pstgDest
) /* [unique][in] */
1259 IEnumSTATSTG
*elements
= 0;
1260 STATSTG curElement
, strStat
;
1262 IStorage
*pstgTmp
, *pstgChild
;
1263 IStream
*pstrTmp
, *pstrChild
;
1265 if ((ciidExclude
!= 0) || (rgiidExclude
!= NULL
) || (snbExclude
!= NULL
))
1266 FIXME( ole
, "Exclude option not implemented\n");
1269 * Perform a sanity check
1271 if ( pstgDest
== 0 )
1272 return STG_E_INVALIDPOINTER
;
1275 * Enumerate the elements
1277 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1285 StorageBaseImpl_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1286 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1291 * Obtain the next element
1293 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1295 if ( hr
== S_FALSE
)
1297 hr
= S_OK
; /* done, every element has been copied */
1301 if (curElement
.type
== STGTY_STORAGE
)
1304 * open child source storage
1306 hr
= StorageBaseImpl_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1307 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1308 NULL
, 0, &pstgChild
);
1314 * Check if destination storage is not a child of the source
1315 * storage, which will cause an infinite loop
1317 if (pstgChild
== pstgDest
)
1319 IEnumSTATSTG_Release(elements
);
1321 return STG_E_ACCESSDENIED
;
1325 * create a new storage in destination storage
1327 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1328 STGM_FAILIFTHERE
|STGM_WRITE
, 0, 0,
1331 * if it already exist, don't create a new one use this one
1333 if (hr
== STG_E_FILEALREADYEXISTS
)
1335 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1336 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1337 NULL
, 0, &pstgTmp
);
1345 * do the copy recursively
1347 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1348 snbExclude
, pstgTmp
);
1350 IStorage_Release( pstgTmp
);
1351 IStorage_Release( pstgChild
);
1353 else if (curElement
.type
== STGTY_STREAM
)
1356 * create a new stream in destination storage. If the stream already
1357 * exist, it will be deleted and a new one will be created.
1359 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1360 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1367 * open child stream storage
1369 hr
= StorageBaseImpl_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1370 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1377 * Get the size of the stream
1379 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1384 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1387 IStream_Release( pstrTmp
);
1388 IStream_Release( pstrChild
);
1392 WARN(ole
, "unknown element type: %ld\n", curElement
.type
);
1395 } while (hr
== S_OK
);
1400 IEnumSTATSTG_Release(elements
);
1405 /*************************************************************************
1406 * MoveElementTo (IStorage)
1408 HRESULT WINAPI
StorageImpl_MoveElementTo(
1410 const OLECHAR
*pwcsName
, /* [string][in] */
1411 IStorage
*pstgDest
, /* [unique][in] */
1412 const OLECHAR
*pwcsNewName
,/* [string][in] */
1413 DWORD grfFlags
) /* [in] */
1415 FIXME(ole
, "not implemented!\n");
1419 /*************************************************************************
1422 HRESULT WINAPI
StorageImpl_Commit(
1424 DWORD grfCommitFlags
)/* [in] */
1426 FIXME(ole
, "(%ld): stub!\n", grfCommitFlags
);
1430 /*************************************************************************
1433 HRESULT WINAPI
StorageImpl_Revert(
1436 FIXME(ole
, "not implemented!\n");
1440 /*************************************************************************
1441 * DestroyElement (IStorage)
1443 * Stategy: This implementation is build this way for simplicity not for speed.
1444 * I always delete the top most element of the enumeration and adjust
1445 * the deleted element pointer all the time. This takes longer to
1446 * do but allow to reinvoke DestroyElement whenever we encounter a
1447 * storage object. The optimisation reside in the usage of another
1448 * enumeration stategy that would give all the leaves of a storage
1449 * first. (postfix order)
1451 HRESULT WINAPI
StorageImpl_DestroyElement(
1453 const OLECHAR
*pwcsName
)/* [string][in] */
1455 StorageImpl
* const This
=(StorageImpl
*)iface
;
1457 IEnumSTATSTGImpl
* propertyEnumeration
;
1460 StgProperty propertyToDelete
;
1461 StgProperty parentProperty
;
1462 ULONG foundPropertyIndexToDelete
;
1463 ULONG typeOfRelation
;
1464 ULONG parentPropertyId
;
1467 * Perform a sanity check on the parameters.
1470 return STG_E_INVALIDPOINTER
;
1473 * Create a property enumeration to search the property with the given name
1475 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
1476 This
->ancestorStorage
,
1477 This
->rootPropertySetIndex
);
1479 foundPropertyIndexToDelete
= IEnumSTATSTGImpl_FindProperty(
1480 propertyEnumeration
,
1484 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1486 if ( foundPropertyIndexToDelete
== PROPERTY_NULL
)
1488 return STG_E_FILENOTFOUND
;
1492 * Find the parent property of the property to delete (the one that
1493 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1494 * the parent is This. Otherwise, the parent is one of it's sibling...
1498 * First, read This's StgProperty..
1500 res
= StorageImpl_ReadProperty(
1501 This
->ancestorStorage
,
1502 This
->rootPropertySetIndex
,
1508 * Second, check to see if by any chance the actual storage (This) is not
1509 * the parent of the property to delete... We never know...
1511 if ( parentProperty
.dirProperty
== foundPropertyIndexToDelete
)
1514 * Set data as it would have been done in the else part...
1516 typeOfRelation
= PROPERTY_RELATION_DIR
;
1517 parentPropertyId
= This
->rootPropertySetIndex
;
1522 * Create a property enumeration to search the parent properties, and
1523 * delete it once done.
1525 IEnumSTATSTGImpl
* propertyEnumeration2
;
1527 propertyEnumeration2
= IEnumSTATSTGImpl_Construct(
1528 This
->ancestorStorage
,
1529 This
->rootPropertySetIndex
);
1531 typeOfRelation
= IEnumSTATSTGImpl_FindParentProperty(
1532 propertyEnumeration2
,
1533 foundPropertyIndexToDelete
,
1537 IEnumSTATSTGImpl_Destroy(propertyEnumeration2
);
1540 if ( propertyToDelete
.propertyType
== PROPTYPE_STORAGE
)
1542 hr
= deleteStorageProperty(
1544 propertyToDelete
.name
);
1546 else if ( propertyToDelete
.propertyType
== PROPTYPE_STREAM
)
1548 hr
= deleteStreamProperty(
1550 foundPropertyIndexToDelete
,
1558 * Adjust the property chain
1560 hr
= adjustPropertyChain(
1571 /*********************************************************************
1575 * Perform the deletion of a complete storage node
1578 static HRESULT
deleteStorageProperty(
1579 StorageImpl
*parentStorage
,
1580 OLECHAR
*propertyToDeleteName
)
1582 IEnumSTATSTG
*elements
= 0;
1583 IStorage
*childStorage
= 0;
1584 STATSTG currentElement
;
1586 HRESULT destroyHr
= S_OK
;
1589 * Open the storage and enumerate it
1591 hr
= StorageBaseImpl_OpenStorage(
1592 (IStorage
*)parentStorage
,
1593 propertyToDeleteName
,
1595 STGM_SHARE_EXCLUSIVE
,
1606 * Enumerate the elements
1608 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1613 * Obtain the next element
1615 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
1618 destroyHr
= StorageImpl_DestroyElement(
1619 (IStorage
*)childStorage
,
1620 (OLECHAR
*)currentElement
.pwcsName
);
1622 CoTaskMemFree(currentElement
.pwcsName
);
1626 * We need to Reset the enumeration every time because we delete elements
1627 * and the enumeration could be invalid
1629 IEnumSTATSTG_Reset(elements
);
1631 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
1633 IStorage_Release(childStorage
);
1634 IEnumSTATSTG_Release(elements
);
1639 /*********************************************************************
1643 * Perform the deletion of a stream node
1646 static HRESULT
deleteStreamProperty(
1647 StorageImpl
*parentStorage
,
1648 ULONG indexOfPropertyToDelete
,
1649 StgProperty propertyToDelete
)
1653 ULARGE_INTEGER size
;
1658 hr
= StorageBaseImpl_OpenStream(
1659 (IStorage
*)parentStorage
,
1660 (OLECHAR
*)propertyToDelete
.name
,
1662 STGM_SHARE_EXCLUSIVE
,
1674 hr
= IStream_SetSize(pis
, size
);
1682 * Invalidate the property by zeroing it's name member.
1684 propertyToDelete
.sizeOfNameString
= 0;
1687 * Here we should re-read the property so we get the updated pointer
1688 * but since we are here to zap it, I don't do it...
1691 StorageImpl_WriteProperty(
1692 parentStorage
->ancestorStorage
,
1693 indexOfPropertyToDelete
,
1699 /*********************************************************************
1703 * Finds a placeholder for the StgProperty within the Storage
1706 static HRESULT
findPlaceholder(
1707 StorageImpl
*storage
,
1708 ULONG propertyIndexToStore
,
1709 ULONG storePropertyIndex
,
1712 StgProperty storeProperty
;
1717 * Read the storage property
1719 res
= StorageImpl_ReadProperty(
1720 storage
->ancestorStorage
,
1729 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1731 if (storeProperty
.previousProperty
!= PROPERTY_NULL
)
1733 return findPlaceholder(
1735 propertyIndexToStore
,
1736 storeProperty
.previousProperty
,
1741 storeProperty
.previousProperty
= propertyIndexToStore
;
1744 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
1746 if (storeProperty
.nextProperty
!= PROPERTY_NULL
)
1748 return findPlaceholder(
1750 propertyIndexToStore
,
1751 storeProperty
.nextProperty
,
1756 storeProperty
.nextProperty
= propertyIndexToStore
;
1759 else if (typeOfRelation
== PROPERTY_RELATION_DIR
)
1761 if (storeProperty
.dirProperty
!= PROPERTY_NULL
)
1763 return findPlaceholder(
1765 propertyIndexToStore
,
1766 storeProperty
.dirProperty
,
1771 storeProperty
.dirProperty
= propertyIndexToStore
;
1775 hr
= StorageImpl_WriteProperty(
1776 storage
->ancestorStorage
,
1788 /*************************************************************************
1792 * This method takes the previous and the next property link of a property
1793 * to be deleted and find them a place in the Storage.
1795 static HRESULT
adjustPropertyChain(
1797 StgProperty propertyToDelete
,
1798 StgProperty parentProperty
,
1799 ULONG parentPropertyId
,
1802 ULONG newLinkProperty
= PROPERTY_NULL
;
1803 BOOL needToFindAPlaceholder
= FALSE
;
1804 ULONG storeNode
= PROPERTY_NULL
;
1805 ULONG toStoreNode
= PROPERTY_NULL
;
1806 INT relationType
= 0;
1810 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1812 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1815 * Set the parent previous to the property to delete previous
1817 newLinkProperty
= propertyToDelete
.previousProperty
;
1819 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1822 * We also need to find a storage for the other link, setup variables
1823 * to do this at the end...
1825 needToFindAPlaceholder
= TRUE
;
1826 storeNode
= propertyToDelete
.previousProperty
;
1827 toStoreNode
= propertyToDelete
.nextProperty
;
1828 relationType
= PROPERTY_RELATION_NEXT
;
1831 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1834 * Set the parent previous to the property to delete next
1836 newLinkProperty
= propertyToDelete
.nextProperty
;
1840 * Link it for real...
1842 parentProperty
.previousProperty
= newLinkProperty
;
1845 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
1847 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1850 * Set the parent next to the property to delete next previous
1852 newLinkProperty
= propertyToDelete
.previousProperty
;
1854 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1857 * We also need to find a storage for the other link, setup variables
1858 * to do this at the end...
1860 needToFindAPlaceholder
= TRUE
;
1861 storeNode
= propertyToDelete
.previousProperty
;
1862 toStoreNode
= propertyToDelete
.nextProperty
;
1863 relationType
= PROPERTY_RELATION_NEXT
;
1866 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1869 * Set the parent next to the property to delete next
1871 newLinkProperty
= propertyToDelete
.nextProperty
;
1875 * Link it for real...
1877 parentProperty
.nextProperty
= newLinkProperty
;
1879 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
1881 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1884 * Set the parent dir to the property to delete previous
1886 newLinkProperty
= propertyToDelete
.previousProperty
;
1888 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1891 * We also need to find a storage for the other link, setup variables
1892 * to do this at the end...
1894 needToFindAPlaceholder
= TRUE
;
1895 storeNode
= propertyToDelete
.previousProperty
;
1896 toStoreNode
= propertyToDelete
.nextProperty
;
1897 relationType
= PROPERTY_RELATION_NEXT
;
1900 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1903 * Set the parent dir to the property to delete next
1905 newLinkProperty
= propertyToDelete
.nextProperty
;
1909 * Link it for real...
1911 parentProperty
.dirProperty
= newLinkProperty
;
1915 * Write back the parent property
1917 res
= StorageImpl_WriteProperty(
1918 This
->ancestorStorage
,
1927 * If a placeholder is required for the other link, then, find one and
1928 * get out of here...
1930 if (needToFindAPlaceholder
)
1932 hr
= findPlaceholder(
1943 /******************************************************************************
1944 * SetElementTimes (IStorage)
1946 HRESULT WINAPI
StorageImpl_SetElementTimes(
1948 const OLECHAR
*pwcsName
,/* [string][in] */
1949 const FILETIME
*pctime
, /* [in] */
1950 const FILETIME
*patime
, /* [in] */
1951 const FILETIME
*pmtime
) /* [in] */
1953 FIXME(ole
, "not implemented!\n");
1957 /******************************************************************************
1958 * SetStateBits (IStorage)
1960 HRESULT WINAPI
StorageImpl_SetStateBits(
1962 DWORD grfStateBits
,/* [in] */
1963 DWORD grfMask
) /* [in] */
1965 FIXME(ole
, "not implemented!\n");
1969 HRESULT
StorageImpl_Construct(
1975 StgProperty currentProperty
;
1977 ULONG currentPropertyIndex
;
1979 if ( FAILED( validateSTGM(openFlags
) ))
1980 return STG_E_INVALIDFLAG
;
1982 memset(This
, 0, sizeof(StorageImpl
));
1985 * Initialize the virtual fgunction table.
1987 This
->lpvtbl
= &Storage32Impl_Vtbl
;
1988 This
->v_destructor
= &StorageImpl_Destroy
;
1991 * This is the top-level storage so initialize the ancester pointer
1994 This
->ancestorStorage
= This
;
1997 * Initialize the physical support of the storage.
1999 This
->hFile
= hFile
;
2002 * Initialize the big block cache.
2004 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2005 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2006 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2008 This
->bigBlockSize
);
2010 if (This
->bigBlockFile
== 0)
2013 if (openFlags
& STGM_CREATE
)
2015 ULARGE_INTEGER size
;
2016 BYTE
* bigBlockBuffer
;
2019 * Initialize all header variables:
2020 * - The big block depot consists of one block and it is at block 0
2021 * - The properties start at block 1
2022 * - There is no small block depot
2024 memset( This
->bigBlockDepotStart
,
2026 sizeof(This
->bigBlockDepotStart
));
2028 This
->bigBlockDepotCount
= 1;
2029 This
->bigBlockDepotStart
[0] = 0;
2030 This
->rootStartBlock
= 1;
2031 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2032 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2033 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2034 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2035 This
->extBigBlockDepotCount
= 0;
2037 StorageImpl_SaveFileHeader(This
);
2040 * Add one block for the big block depot and one block for the properties
2043 size
.LowPart
= This
->bigBlockSize
* 3;
2044 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2047 * Initialize the big block depot
2049 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, 0);
2050 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2051 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2052 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2053 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
2058 * Load the header for the file.
2060 hr
= StorageImpl_LoadFileHeader(This
);
2064 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2071 * There is no block depot cached yet.
2073 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2076 * Start searching for free blocks with block 0.
2078 This
->prevFreeBlock
= 0;
2081 * Create the block chain abstractions.
2083 This
->rootBlockChain
=
2084 BlockChainStream_Construct(This
, &This
->rootStartBlock
, PROPERTY_NULL
);
2086 This
->smallBlockDepotChain
= BlockChainStream_Construct(
2088 &This
->smallBlockDepotStart
,
2092 * Write the root property
2094 if (openFlags
& STGM_CREATE
)
2096 StgProperty rootProp
;
2098 * Initialize the property chain
2100 memset(&rootProp
, 0, sizeof(rootProp
));
2101 lstrcpyAtoW(rootProp
.name
, rootPropertyName
);
2103 rootProp
.sizeOfNameString
= (lstrlenW(rootProp
.name
)+1) * sizeof(WCHAR
);
2104 rootProp
.propertyType
= PROPTYPE_ROOT
;
2105 rootProp
.previousProperty
= PROPERTY_NULL
;
2106 rootProp
.nextProperty
= PROPERTY_NULL
;
2107 rootProp
.dirProperty
= PROPERTY_NULL
;
2108 rootProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
2109 rootProp
.size
.HighPart
= 0;
2110 rootProp
.size
.LowPart
= 0;
2112 StorageImpl_WriteProperty(This
, 0, &rootProp
);
2116 * Find the ID of the root int he property sets.
2118 currentPropertyIndex
= 0;
2122 readSucessful
= StorageImpl_ReadProperty(
2124 currentPropertyIndex
,
2129 if ( (currentProperty
.sizeOfNameString
!= 0 ) &&
2130 (currentProperty
.propertyType
== PROPTYPE_ROOT
) )
2132 This
->rootPropertySetIndex
= currentPropertyIndex
;
2136 currentPropertyIndex
++;
2138 } while (readSucessful
&& (This
->rootPropertySetIndex
== PROPERTY_NULL
) );
2147 * Create the block chain abstraction for the small block root chain.
2149 This
->smallBlockRootChain
= BlockChainStream_Construct(
2152 This
->rootPropertySetIndex
);
2157 void StorageImpl_Destroy(
2160 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2161 BlockChainStream_Destroy(This
->rootBlockChain
);
2162 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2164 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2168 /******************************************************************************
2169 * Storage32Impl_GetNextFreeBigBlock
2171 * Returns the index of the next free big block.
2172 * If the big block depot is filled, this method will enlarge it.
2175 ULONG
StorageImpl_GetNextFreeBigBlock(
2178 ULONG depotBlockIndexPos
;
2180 ULONG depotBlockOffset
;
2181 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2182 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2184 ULONG freeBlock
= BLOCK_UNUSED
;
2186 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2187 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2190 * Scan the entire big block depot until we find a block marked free
2192 while (nextBlockIndex
!= BLOCK_UNUSED
)
2194 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2196 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2199 * Grow the primary depot.
2201 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2203 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2206 * Add a block depot.
2208 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2209 This
->bigBlockDepotCount
++;
2210 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2213 * Flag it as a block depot.
2215 StorageImpl_SetNextBlockInChain(This
,
2219 /* Save new header information.
2221 StorageImpl_SaveFileHeader(This
);
2226 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2228 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2231 * Grow the extended depot.
2233 ULONG extIndex
= BLOCK_UNUSED
;
2234 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2235 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2237 if (extBlockOffset
== 0)
2239 /* We need an extended block.
2241 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2242 This
->extBigBlockDepotCount
++;
2243 depotBlockIndexPos
= extIndex
+ 1;
2246 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2249 * Add a block depot and mark it in the extended block.
2251 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2252 This
->bigBlockDepotCount
++;
2253 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2255 /* Flag the block depot.
2257 StorageImpl_SetNextBlockInChain(This
,
2261 /* If necessary, flag the extended depot block.
2263 if (extIndex
!= BLOCK_UNUSED
)
2264 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2266 /* Save header information.
2268 StorageImpl_SaveFileHeader(This
);
2272 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2274 if (depotBuffer
!= 0)
2276 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2277 ( nextBlockIndex
!= BLOCK_UNUSED
))
2279 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2281 if (nextBlockIndex
== BLOCK_UNUSED
)
2283 freeBlock
= (depotIndex
* blocksPerDepot
) +
2284 (depotBlockOffset
/sizeof(ULONG
));
2287 depotBlockOffset
+= sizeof(ULONG
);
2290 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2294 depotBlockOffset
= 0;
2297 This
->prevFreeBlock
= freeBlock
;
2302 /******************************************************************************
2303 * Storage32Impl_AddBlockDepot
2305 * This will create a depot block, essentially it is a block initialized
2308 void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2312 blockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
2315 * Initialize blocks as free
2317 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2319 StorageImpl_ReleaseBigBlock(This
, blockBuffer
);
2322 /******************************************************************************
2323 * Storage32Impl_GetExtDepotBlock
2325 * Returns the index of the block that corresponds to the specified depot
2326 * index. This method is only for depot indexes equal or greater than
2327 * COUNT_BBDEPOTINHEADER.
2329 ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2331 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2332 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2333 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2334 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2335 ULONG blockIndex
= BLOCK_UNUSED
;
2336 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2338 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2340 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2341 return BLOCK_UNUSED
;
2343 while (extBlockCount
> 0)
2345 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2349 if (extBlockIndex
!= BLOCK_UNUSED
)
2353 depotBuffer
= StorageImpl_GetROBigBlock(This
, extBlockIndex
);
2355 if (depotBuffer
!= 0)
2357 StorageUtl_ReadDWord(depotBuffer
,
2358 extBlockOffset
* sizeof(ULONG
),
2361 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2368 /******************************************************************************
2369 * Storage32Impl_SetExtDepotBlock
2371 * Associates the specified block index to the specified depot index.
2372 * This method is only for depot indexes equal or greater than
2373 * COUNT_BBDEPOTINHEADER.
2375 void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
,
2379 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2380 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2381 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2382 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2383 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2385 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2387 while (extBlockCount
> 0)
2389 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2393 if (extBlockIndex
!= BLOCK_UNUSED
)
2397 depotBuffer
= StorageImpl_GetBigBlock(This
, extBlockIndex
);
2399 if (depotBuffer
!= 0)
2401 StorageUtl_WriteDWord(depotBuffer
,
2402 extBlockOffset
* sizeof(ULONG
),
2405 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2410 /******************************************************************************
2411 * Storage32Impl_AddExtBlockDepot
2413 * Creates an extended depot block.
2415 ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2417 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2418 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2419 BYTE
* depotBuffer
= NULL
;
2420 ULONG index
= BLOCK_UNUSED
;
2421 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2422 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2423 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2425 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2426 blocksPerDepotBlock
;
2428 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2431 * The first extended block.
2433 This
->extBigBlockDepotStart
= index
;
2439 * Follow the chain to the last one.
2441 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2443 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2447 * Add the new extended block to the chain.
2449 depotBuffer
= StorageImpl_GetBigBlock(This
, nextExtBlock
);
2450 StorageUtl_WriteDWord(depotBuffer
, nextBlockOffset
, index
);
2451 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2455 * Initialize this block.
2457 depotBuffer
= StorageImpl_GetBigBlock(This
, index
);
2458 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2459 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2464 /******************************************************************************
2465 * Storage32Impl_FreeBigBlock
2467 * This method will flag the specified block as free in the big block depot.
2469 void StorageImpl_FreeBigBlock(
2473 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2475 if (blockIndex
< This
->prevFreeBlock
)
2476 This
->prevFreeBlock
= blockIndex
;
2479 /************************************************************************
2480 * Storage32Impl_GetNextBlockInChain
2482 * This method will retrieve the block index of the next big block in
2485 * Params: This - Pointer to the Storage object.
2486 * blockIndex - Index of the block to retrieve the chain
2489 * Returns: This method returns the index of the next block in the chain.
2490 * It will return the constants:
2491 * BLOCK_SPECIAL - If the block given was not part of a
2493 * BLOCK_END_OF_CHAIN - If the block given was the last in
2495 * BLOCK_UNUSED - If the block given was not past of a chain
2497 * BLOCK_EXTBBDEPOT - This block is part of the extended
2500 * See Windows documentation for more details on IStorage methods.
2502 ULONG
StorageImpl_GetNextBlockInChain(
2506 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2507 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2508 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2509 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2511 ULONG depotBlockIndexPos
;
2513 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2516 * Cache the currently accessed depot block.
2518 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2520 This
->indexBlockDepotCached
= depotBlockCount
;
2522 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2524 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2529 * We have to look in the extended depot.
2531 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2534 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2540 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2542 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &nextBlockIndex
);
2543 This
->blockDepotCached
[index
] = nextBlockIndex
;
2546 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2550 nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2552 return nextBlockIndex
;
2555 /******************************************************************************
2556 * Storage32Impl_GetNextExtendedBlock
2558 * Given an extended block this method will return the next extended block.
2561 * The last ULONG of an extended block is the block index of the next
2562 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2566 * - The index of the next extended block
2567 * - BLOCK_UNUSED: there is no next extended block.
2568 * - Any other return values denotes failure.
2570 ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
2572 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2573 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2576 depotBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
2580 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2582 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2585 return nextBlockIndex
;
2588 /******************************************************************************
2589 * Storage32Impl_SetNextBlockInChain
2591 * This method will write the index of the specified block's next block
2592 * in the big block depot.
2594 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2597 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2598 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2599 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2602 void StorageImpl_SetNextBlockInChain(
2607 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2608 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2609 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2610 ULONG depotBlockIndexPos
;
2613 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2614 assert(blockIndex
!= nextBlock
);
2616 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2618 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2623 * We have to look in the extended depot.
2625 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2628 depotBuffer
= StorageImpl_GetBigBlock(This
, depotBlockIndexPos
);
2632 StorageUtl_WriteDWord(depotBuffer
, depotBlockOffset
, nextBlock
);
2633 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2637 * Update the cached block depot, if necessary.
2639 if (depotBlockCount
== This
->indexBlockDepotCached
)
2641 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
2645 /******************************************************************************
2646 * Storage32Impl_LoadFileHeader
2648 * This method will read in the file header, i.e. big block index -1.
2650 HRESULT
StorageImpl_LoadFileHeader(
2653 HRESULT hr
= STG_E_FILENOTFOUND
;
2654 void* headerBigBlock
= NULL
;
2658 * Get a pointer to the big block of data containing the header.
2660 headerBigBlock
= StorageImpl_GetROBigBlock(This
, -1);
2663 * Extract the information from the header.
2665 if (headerBigBlock
!=0)
2668 * Check for the "magic number" signature and return an error if it is not
2671 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2673 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2674 return STG_E_OLDFORMAT
;
2677 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2679 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2680 return STG_E_INVALIDHEADER
;
2683 StorageUtl_ReadWord(
2685 OFFSET_BIGBLOCKSIZEBITS
,
2686 &This
->bigBlockSizeBits
);
2688 StorageUtl_ReadWord(
2690 OFFSET_SMALLBLOCKSIZEBITS
,
2691 &This
->smallBlockSizeBits
);
2693 StorageUtl_ReadDWord(
2695 OFFSET_BBDEPOTCOUNT
,
2696 &This
->bigBlockDepotCount
);
2698 StorageUtl_ReadDWord(
2700 OFFSET_ROOTSTARTBLOCK
,
2701 &This
->rootStartBlock
);
2703 StorageUtl_ReadDWord(
2705 OFFSET_SBDEPOTSTART
,
2706 &This
->smallBlockDepotStart
);
2708 StorageUtl_ReadDWord(
2710 OFFSET_EXTBBDEPOTSTART
,
2711 &This
->extBigBlockDepotStart
);
2713 StorageUtl_ReadDWord(
2715 OFFSET_EXTBBDEPOTCOUNT
,
2716 &This
->extBigBlockDepotCount
);
2718 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2720 StorageUtl_ReadDWord(
2722 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2723 &(This
->bigBlockDepotStart
[index
]));
2727 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2731 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
2732 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
2736 This
->bigBlockSize
= 0x000000001 >> (DWORD
)This
->bigBlockSizeBits
;
2737 This
->smallBlockSize
= 0x000000001 >> (DWORD
)This
->smallBlockSizeBits
;
2741 * Right now, the code is making some assumptions about the size of the
2742 * blocks, just make sure they are what we're expecting.
2744 assert( (This
->bigBlockSize
==DEF_BIG_BLOCK_SIZE
) &&
2745 (This
->smallBlockSize
==DEF_SMALL_BLOCK_SIZE
));
2748 * Release the block.
2750 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2758 /******************************************************************************
2759 * Storage32Impl_SaveFileHeader
2761 * This method will save to the file the header, i.e. big block -1.
2763 void StorageImpl_SaveFileHeader(
2766 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
2771 * Get a pointer to the big block of data containing the header.
2773 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
2776 * If the block read failed, the file is probably new.
2781 * Initialize for all unknown fields.
2783 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
2786 * Initialize the magic number.
2788 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
2791 * And a bunch of things we don't know what they mean
2793 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
2794 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
2795 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
2796 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
2797 StorageUtl_WriteDWord(headerBigBlock
, 0x40, (DWORD
)0x0001);
2801 * Write the information to the header.
2803 if (headerBigBlock
!=0)
2805 StorageUtl_WriteWord(
2807 OFFSET_BIGBLOCKSIZEBITS
,
2808 This
->bigBlockSizeBits
);
2810 StorageUtl_WriteWord(
2812 OFFSET_SMALLBLOCKSIZEBITS
,
2813 This
->smallBlockSizeBits
);
2815 StorageUtl_WriteDWord(
2817 OFFSET_BBDEPOTCOUNT
,
2818 This
->bigBlockDepotCount
);
2820 StorageUtl_WriteDWord(
2822 OFFSET_ROOTSTARTBLOCK
,
2823 This
->rootStartBlock
);
2825 StorageUtl_WriteDWord(
2827 OFFSET_SBDEPOTSTART
,
2828 This
->smallBlockDepotStart
);
2830 StorageUtl_WriteDWord(
2832 OFFSET_EXTBBDEPOTSTART
,
2833 This
->extBigBlockDepotStart
);
2835 StorageUtl_WriteDWord(
2837 OFFSET_EXTBBDEPOTCOUNT
,
2838 This
->extBigBlockDepotCount
);
2840 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2842 StorageUtl_WriteDWord(
2844 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2845 (This
->bigBlockDepotStart
[index
]));
2850 * Write the big block back to the file.
2852 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
2855 /******************************************************************************
2856 * Storage32Impl_ReadProperty
2858 * This method will read the specified property from the property chain.
2860 BOOL
StorageImpl_ReadProperty(
2863 StgProperty
* buffer
)
2865 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
2866 ULARGE_INTEGER offsetInPropSet
;
2870 offsetInPropSet
.HighPart
= 0;
2871 offsetInPropSet
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
2873 readSucessful
= BlockChainStream_ReadAt(
2874 This
->rootBlockChain
,
2882 memset(buffer
->name
, 0, sizeof(buffer
->name
));
2885 currentProperty
+OFFSET_PS_NAME
,
2886 PROPERTY_NAME_BUFFER_LEN
);
2888 memcpy(&buffer
->propertyType
, currentProperty
+ OFFSET_PS_PROPERTYTYPE
, 1);
2890 StorageUtl_ReadWord(
2892 OFFSET_PS_NAMELENGTH
,
2893 &buffer
->sizeOfNameString
);
2895 StorageUtl_ReadDWord(
2897 OFFSET_PS_PREVIOUSPROP
,
2898 &buffer
->previousProperty
);
2900 StorageUtl_ReadDWord(
2903 &buffer
->nextProperty
);
2905 StorageUtl_ReadDWord(
2908 &buffer
->dirProperty
);
2910 StorageUtl_ReadGUID(
2913 &buffer
->propertyUniqueID
);
2915 StorageUtl_ReadDWord(
2918 &buffer
->timeStampS1
);
2920 StorageUtl_ReadDWord(
2923 &buffer
->timeStampD1
);
2925 StorageUtl_ReadDWord(
2928 &buffer
->timeStampS2
);
2930 StorageUtl_ReadDWord(
2933 &buffer
->timeStampD2
);
2935 StorageUtl_ReadDWord(
2937 OFFSET_PS_STARTBLOCK
,
2938 &buffer
->startingBlock
);
2940 StorageUtl_ReadDWord(
2943 &buffer
->size
.LowPart
);
2945 buffer
->size
.HighPart
= 0;
2948 return readSucessful
;
2951 /*********************************************************************
2952 * Write the specified property into the property chain
2954 BOOL
StorageImpl_WriteProperty(
2957 StgProperty
* buffer
)
2959 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
2960 ULARGE_INTEGER offsetInPropSet
;
2961 BOOL writeSucessful
;
2964 offsetInPropSet
.HighPart
= 0;
2965 offsetInPropSet
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
2967 memset(currentProperty
, 0, PROPSET_BLOCK_SIZE
);
2970 currentProperty
+ OFFSET_PS_NAME
,
2972 PROPERTY_NAME_BUFFER_LEN
);
2974 memcpy(currentProperty
+ OFFSET_PS_PROPERTYTYPE
, &buffer
->propertyType
, 1);
2977 * Reassign the size in case of mistake....
2979 buffer
->sizeOfNameString
= (lstrlenW(buffer
->name
)+1) * sizeof(WCHAR
);
2981 StorageUtl_WriteWord(
2983 OFFSET_PS_NAMELENGTH
,
2984 buffer
->sizeOfNameString
);
2986 StorageUtl_WriteDWord(
2988 OFFSET_PS_PREVIOUSPROP
,
2989 buffer
->previousProperty
);
2991 StorageUtl_WriteDWord(
2994 buffer
->nextProperty
);
2996 StorageUtl_WriteDWord(
2999 buffer
->dirProperty
);
3001 StorageUtl_WriteGUID(
3004 &buffer
->propertyUniqueID
);
3006 StorageUtl_WriteDWord(
3009 buffer
->timeStampS1
);
3011 StorageUtl_WriteDWord(
3014 buffer
->timeStampD1
);
3016 StorageUtl_WriteDWord(
3019 buffer
->timeStampS2
);
3021 StorageUtl_WriteDWord(
3024 buffer
->timeStampD2
);
3026 StorageUtl_WriteDWord(
3028 OFFSET_PS_STARTBLOCK
,
3029 buffer
->startingBlock
);
3031 StorageUtl_WriteDWord(
3034 buffer
->size
.LowPart
);
3036 writeSucessful
= BlockChainStream_WriteAt(This
->rootBlockChain
,
3041 return writeSucessful
;
3044 BOOL
StorageImpl_ReadBigBlock(
3049 void* bigBlockBuffer
;
3051 bigBlockBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
3053 if (bigBlockBuffer
!=0)
3055 memcpy(buffer
, bigBlockBuffer
, This
->bigBlockSize
);
3057 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
3065 BOOL
StorageImpl_WriteBigBlock(
3070 void* bigBlockBuffer
;
3072 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
3074 if (bigBlockBuffer
!=0)
3076 memcpy(bigBlockBuffer
, buffer
, This
->bigBlockSize
);
3078 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
3086 void* StorageImpl_GetROBigBlock(
3090 return BIGBLOCKFILE_GetROBigBlock(This
->bigBlockFile
, blockIndex
);
3093 void* StorageImpl_GetBigBlock(
3097 return BIGBLOCKFILE_GetBigBlock(This
->bigBlockFile
, blockIndex
);
3100 void StorageImpl_ReleaseBigBlock(
3104 BIGBLOCKFILE_ReleaseBigBlock(This
->bigBlockFile
, pBigBlock
);
3107 /******************************************************************************
3108 * Storage32Impl_SmallBlocksToBigBlocks
3110 * This method will convert a small block chain to a big block chain.
3111 * The small block chain will be destroyed.
3113 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3115 SmallBlockChainStream
** ppsbChain
)
3117 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3118 ULARGE_INTEGER size
, offset
;
3119 ULONG cbRead
, cbWritten
, cbTotalRead
, cbTotalWritten
;
3120 ULONG propertyIndex
;
3121 BOOL successRead
, successWrite
;
3122 StgProperty chainProperty
;
3123 BYTE buffer
[DEF_SMALL_BLOCK_SIZE
];
3124 BlockChainStream
*bbTempChain
= NULL
;
3125 BlockChainStream
*bigBlockChain
= NULL
;
3128 * Create a temporary big block chain that doesn't have
3129 * an associated property. This temporary chain will be
3130 * used to copy data from small blocks to big blocks.
3132 bbTempChain
= BlockChainStream_Construct(This
,
3137 * Grow the big block chain.
3139 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3140 BlockChainStream_SetSize(bbTempChain
, size
);
3143 * Copy the contents of the small block chain to the big block chain
3144 * by small block size increments.
3147 offset
.HighPart
= 0;
3153 successRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3158 cbTotalRead
+= cbRead
;
3160 successWrite
= BlockChainStream_WriteAt(bbTempChain
,
3165 cbTotalWritten
+= cbWritten
;
3167 offset
.LowPart
+= This
->smallBlockSize
;
3169 } while (successRead
&& successWrite
);
3171 assert(cbTotalRead
== cbTotalWritten
);
3174 * Destroy the small block chain.
3176 propertyIndex
= (*ppsbChain
)->ownerPropertyIndex
;
3179 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3180 SmallBlockChainStream_Destroy(*ppsbChain
);
3184 * Change the property information. This chain is now a big block chain
3185 * and it doesn't reside in the small blocks chain anymore.
3187 StorageImpl_ReadProperty(This
, propertyIndex
, &chainProperty
);
3189 chainProperty
.startingBlock
= bbHeadOfChain
;
3191 StorageImpl_WriteProperty(This
, propertyIndex
, &chainProperty
);
3194 * Destroy the temporary propertyless big block chain.
3195 * Create a new big block chain associated with this property.
3197 BlockChainStream_Destroy(bbTempChain
);
3198 bigBlockChain
= BlockChainStream_Construct(This
,
3202 return bigBlockChain
;
3205 /******************************************************************************
3206 ** Storage32InternalImpl implementation
3209 StorageInternalImpl
* StorageInternalImpl_Construct(
3210 StorageImpl
* ancestorStorage
,
3211 ULONG rootPropertyIndex
)
3213 StorageInternalImpl
* newStorage
;
3216 * Allocate space for the new storage object
3218 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl
));
3222 memset(newStorage
, 0, sizeof(StorageInternalImpl
));
3225 * Initialize the virtual function table.
3227 newStorage
->lpvtbl
= &Storage32InternalImpl_Vtbl
;
3228 newStorage
->v_destructor
= &StorageInternalImpl_Destroy
;
3231 * Keep the ancestor storage pointer and nail a reference to it.
3233 newStorage
->ancestorStorage
= ancestorStorage
;
3234 StorageBaseImpl_AddRef((IStorage
*)(newStorage
->ancestorStorage
));
3237 * Keep the index of the root property set for this storage,
3239 newStorage
->rootPropertySetIndex
= rootPropertyIndex
;
3247 void StorageInternalImpl_Destroy(
3248 StorageInternalImpl
* This
)
3250 StorageBaseImpl_Release((IStorage
*)This
->ancestorStorage
);
3251 HeapFree(GetProcessHeap(), 0, This
);
3254 /******************************************************************************
3256 ** Storage32InternalImpl_Commit
3258 ** The non-root storages cannot be opened in transacted mode thus this function
3261 HRESULT WINAPI
StorageInternalImpl_Commit(
3263 DWORD grfCommitFlags
) /* [in] */
3268 /******************************************************************************
3270 ** Storage32InternalImpl_Revert
3272 ** The non-root storages cannot be opened in transacted mode thus this function
3275 HRESULT WINAPI
StorageInternalImpl_Revert(
3281 /******************************************************************************
3282 ** IEnumSTATSTGImpl implementation
3285 IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
3286 StorageImpl
* parentStorage
,
3287 ULONG firstPropertyNode
)
3289 IEnumSTATSTGImpl
* newEnumeration
;
3291 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
3293 if (newEnumeration
!=0)
3296 * Set-up the virtual function table and reference count.
3298 newEnumeration
->lpvtbl
= &IEnumSTATSTGImpl_Vtbl
;
3299 newEnumeration
->ref
= 0;
3302 * We want to nail-down the reference to the storage in case the
3303 * enumeration out-lives the storage in the client application.
3305 newEnumeration
->parentStorage
= parentStorage
;
3306 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
3308 newEnumeration
->firstPropertyNode
= firstPropertyNode
;
3311 * Initialize the search stack
3313 newEnumeration
->stackSize
= 0;
3314 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
3315 newEnumeration
->stackToVisit
=
3316 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
)*ENUMSTATSGT_SIZE_INCREMENT
);
3319 * Make sure the current node of the iterator is the first one.
3321 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
3324 return newEnumeration
;
3327 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3329 IStorage_Release((IStorage
*)This
->parentStorage
);
3330 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3331 HeapFree(GetProcessHeap(), 0, This
);
3334 HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3335 IEnumSTATSTG
* iface
,
3339 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3342 * Perform a sanity check on the parameters.
3345 return E_INVALIDARG
;
3348 * Initialize the return parameter.
3353 * Compare the riid with the interface IDs implemented by this object.
3355 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
3357 *ppvObject
= (IEnumSTATSTG
*)This
;
3359 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IEnumSTATSTG
)) == 0)
3361 *ppvObject
= (IEnumSTATSTG
*)This
;
3365 * Check that we obtained an interface.
3367 if ((*ppvObject
)==0)
3368 return E_NOINTERFACE
;
3371 * Query Interface always increases the reference count by one when it is
3374 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG
*)This
);
3379 ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3380 IEnumSTATSTG
* iface
)
3382 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3388 ULONG WINAPI
IEnumSTATSTGImpl_Release(
3389 IEnumSTATSTG
* iface
)
3391 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3399 * If the reference count goes down to 0, perform suicide.
3403 IEnumSTATSTGImpl_Destroy(This
);
3409 HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3410 IEnumSTATSTG
* iface
,
3413 ULONG
* pceltFetched
)
3415 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3417 StgProperty currentProperty
;
3418 STATSTG
* currentReturnStruct
= rgelt
;
3419 ULONG objectFetched
= 0;
3420 ULONG currentSearchNode
;
3423 * Perform a sanity check on the parameters.
3425 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3426 return E_INVALIDARG
;
3429 * To avoid the special case, get another pointer to a ULONG value if
3430 * the caller didn't supply one.
3432 if (pceltFetched
==0)
3433 pceltFetched
= &objectFetched
;
3436 * Start the iteration, we will iterate until we hit the end of the
3437 * linked list or until we hit the number of items to iterate through
3442 * Start with the node at the top of the stack.
3444 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3446 while ( ( *pceltFetched
< celt
) &&
3447 ( currentSearchNode
!=PROPERTY_NULL
) )
3450 * Remove the top node from the stack
3452 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3455 * Read the property from the storage.
3457 StorageImpl_ReadProperty(This
->parentStorage
,
3462 * Copy the information to the return buffer.
3464 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct
,
3469 * Step to the next item in the iteration
3472 currentReturnStruct
++;
3475 * Push the next search node in the search stack.
3477 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3480 * continue the iteration.
3482 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3485 if (*pceltFetched
== celt
)
3492 HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3493 IEnumSTATSTG
* iface
,
3496 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3498 StgProperty currentProperty
;
3499 ULONG objectFetched
= 0;
3500 ULONG currentSearchNode
;
3503 * Start with the node at the top of the stack.
3505 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3507 while ( (objectFetched
< celt
) &&
3508 (currentSearchNode
!=PROPERTY_NULL
) )
3511 * Remove the top node from the stack
3513 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3516 * Read the property from the storage.
3518 StorageImpl_ReadProperty(This
->parentStorage
,
3523 * Step to the next item in the iteration
3528 * Push the next search node in the search stack.
3530 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3533 * continue the iteration.
3535 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3538 if (objectFetched
== celt
)
3544 HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3545 IEnumSTATSTG
* iface
)
3547 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3549 StgProperty rootProperty
;
3553 * Re-initialize the search stack to an empty stack
3555 This
->stackSize
= 0;
3558 * Read the root property from the storage.
3560 readSucessful
= StorageImpl_ReadProperty(
3561 This
->parentStorage
,
3562 This
->firstPropertyNode
,
3567 assert(rootProperty
.sizeOfNameString
!=0);
3570 * Push the search node in the search stack.
3572 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.dirProperty
);
3578 HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3579 IEnumSTATSTG
* iface
,
3580 IEnumSTATSTG
** ppenum
)
3582 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3584 IEnumSTATSTGImpl
* newClone
;
3587 * Perform a sanity check on the parameters.
3590 return E_INVALIDARG
;
3592 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3593 This
->firstPropertyNode
);
3597 * The new clone enumeration must point to the same current node as
3600 newClone
->stackSize
= This
->stackSize
;
3601 newClone
->stackMaxSize
= This
->stackMaxSize
;
3602 newClone
->stackToVisit
=
3603 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3606 newClone
->stackToVisit
,
3608 sizeof(ULONG
) * newClone
->stackSize
);
3610 *ppenum
= (IEnumSTATSTG
*)newClone
;
3613 * Don't forget to nail down a reference to the clone before
3616 IEnumSTATSTGImpl_AddRef(*ppenum
);
3621 INT
IEnumSTATSTGImpl_FindParentProperty(
3622 IEnumSTATSTGImpl
*This
,
3623 ULONG childProperty
,
3624 StgProperty
*currentProperty
,
3627 ULONG currentSearchNode
;
3631 * To avoid the special case, get another pointer to a ULONG value if
3632 * the caller didn't supply one.
3635 thisNodeId
= &foundNode
;
3638 * Start with the node at the top of the stack.
3640 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3643 while (currentSearchNode
!=PROPERTY_NULL
)
3646 * Store the current node in the returned parameters
3648 *thisNodeId
= currentSearchNode
;
3651 * Remove the top node from the stack
3653 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3656 * Read the property from the storage.
3658 StorageImpl_ReadProperty(
3659 This
->parentStorage
,
3663 if (currentProperty
->previousProperty
== childProperty
)
3664 return PROPERTY_RELATION_PREVIOUS
;
3666 else if (currentProperty
->nextProperty
== childProperty
)
3667 return PROPERTY_RELATION_NEXT
;
3669 else if (currentProperty
->dirProperty
== childProperty
)
3670 return PROPERTY_RELATION_DIR
;
3673 * Push the next search node in the search stack.
3675 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3678 * continue the iteration.
3680 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3683 return PROPERTY_NULL
;
3686 ULONG
IEnumSTATSTGImpl_FindProperty(
3687 IEnumSTATSTGImpl
* This
,
3688 const OLECHAR
* lpszPropName
,
3689 StgProperty
* currentProperty
)
3691 ULONG currentSearchNode
;
3694 * Start with the node at the top of the stack.
3696 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3698 while (currentSearchNode
!=PROPERTY_NULL
)
3701 * Remove the top node from the stack
3703 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3706 * Read the property from the storage.
3708 StorageImpl_ReadProperty(This
->parentStorage
,
3712 if ( propertyNameCmp(
3713 (OLECHAR
*)currentProperty
->name
,
3714 (OLECHAR
*)lpszPropName
) == 0)
3715 return currentSearchNode
;
3718 * Push the next search node in the search stack.
3720 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3723 * continue the iteration.
3725 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3728 return PROPERTY_NULL
;
3731 void IEnumSTATSTGImpl_PushSearchNode(
3732 IEnumSTATSTGImpl
* This
,
3735 StgProperty rootProperty
;
3739 * First, make sure we're not trying to push an unexisting node.
3741 if (nodeToPush
==PROPERTY_NULL
)
3745 * First push the node to the stack
3747 if (This
->stackSize
== This
->stackMaxSize
)
3749 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
3751 This
->stackToVisit
= HeapReAlloc(
3755 sizeof(ULONG
) * This
->stackMaxSize
);
3758 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
3762 * Read the root property from the storage.
3764 readSucessful
= StorageImpl_ReadProperty(
3765 This
->parentStorage
,
3771 assert(rootProperty
.sizeOfNameString
!=0);
3774 * Push the previous search node in the search stack.
3776 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.previousProperty
);
3780 ULONG
IEnumSTATSTGImpl_PopSearchNode(
3781 IEnumSTATSTGImpl
* This
,
3786 if (This
->stackSize
== 0)
3787 return PROPERTY_NULL
;
3789 topNode
= This
->stackToVisit
[This
->stackSize
-1];
3797 /******************************************************************************
3798 ** StorageUtl implementation
3801 void StorageUtl_ReadWord(void* buffer
, ULONG offset
, WORD
* value
)
3803 memcpy(value
, (BYTE
*)buffer
+offset
, sizeof(WORD
));
3806 void StorageUtl_WriteWord(void* buffer
, ULONG offset
, WORD value
)
3808 memcpy((BYTE
*)buffer
+offset
, &value
, sizeof(WORD
));
3811 void StorageUtl_ReadDWord(void* buffer
, ULONG offset
, DWORD
* value
)
3813 memcpy(value
, (BYTE
*)buffer
+offset
, sizeof(DWORD
));
3816 void StorageUtl_WriteDWord(void* buffer
, ULONG offset
, DWORD value
)
3818 memcpy((BYTE
*)buffer
+offset
, &value
, sizeof(DWORD
));
3821 void StorageUtl_ReadGUID(void* buffer
, ULONG offset
, GUID
* value
)
3823 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
3824 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
3825 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
3827 memcpy(value
->Data4
, (BYTE
*)buffer
+offset
+8, sizeof(value
->Data4
));
3830 void StorageUtl_WriteGUID(void* buffer
, ULONG offset
, GUID
* value
)
3832 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
3833 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
3834 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
3836 memcpy((BYTE
*)buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
3839 void StorageUtl_CopyPropertyToSTATSTG(
3840 STATSTG
* destination
,
3841 StgProperty
* source
,
3845 * The copy of the string occurs only when the flag is not set
3847 if ((statFlags
& STATFLAG_NONAME
) != 0)
3849 destination
->pwcsName
= 0;
3853 destination
->pwcsName
=
3854 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
3856 lstrcpyW((LPWSTR
)destination
->pwcsName
, source
->name
);
3859 switch (source
->propertyType
)
3861 case PROPTYPE_STORAGE
:
3863 destination
->type
= STGTY_STORAGE
;
3865 case PROPTYPE_STREAM
:
3866 destination
->type
= STGTY_STREAM
;
3869 destination
->type
= STGTY_STREAM
;
3873 destination
->cbSize
= source
->size
;
3875 currentReturnStruct->mtime = {0}; TODO
3876 currentReturnStruct->ctime = {0};
3877 currentReturnStruct->atime = {0};
3879 destination
->grfMode
= 0;
3880 destination
->grfLocksSupported
= 0;
3881 destination
->clsid
= source
->propertyUniqueID
;
3882 destination
->grfStateBits
= 0;
3883 destination
->reserved
= 0;
3886 /******************************************************************************
3887 ** BlockChainStream implementation
3890 BlockChainStream
* BlockChainStream_Construct(
3891 StorageImpl
* parentStorage
,
3892 ULONG
* headOfStreamPlaceHolder
,
3893 ULONG propertyIndex
)
3895 BlockChainStream
* newStream
;
3898 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
3900 newStream
->parentStorage
= parentStorage
;
3901 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
3902 newStream
->ownerPropertyIndex
= propertyIndex
;
3903 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
3904 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
3905 newStream
->numBlocks
= 0;
3907 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
3909 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
3911 newStream
->numBlocks
++;
3912 newStream
->tailIndex
= blockIndex
;
3914 blockIndex
= StorageImpl_GetNextBlockInChain(
3922 void BlockChainStream_Destroy(BlockChainStream
* This
)
3924 HeapFree(GetProcessHeap(), 0, This
);
3927 /******************************************************************************
3928 * BlockChainStream_GetHeadOfChain
3930 * Returns the head of this stream chain.
3931 * Some special chains don't have properties, their heads are kept in
3932 * This->headOfStreamPlaceHolder.
3935 ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
3937 StgProperty chainProperty
;
3940 if (This
->headOfStreamPlaceHolder
!= 0)
3941 return *(This
->headOfStreamPlaceHolder
);
3943 if (This
->ownerPropertyIndex
!= PROPERTY_NULL
)
3945 readSucessful
= StorageImpl_ReadProperty(
3946 This
->parentStorage
,
3947 This
->ownerPropertyIndex
,
3952 return chainProperty
.startingBlock
;
3956 return BLOCK_END_OF_CHAIN
;
3959 /******************************************************************************
3960 * BlockChainStream_GetCount
3962 * Returns the number of blocks that comprises this chain.
3963 * This is not the size of the stream as the last block may not be full!
3966 ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
3971 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
3973 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
3977 blockIndex
= StorageImpl_GetNextBlockInChain(
3978 This
->parentStorage
,
3985 /******************************************************************************
3986 * BlockChainStream_ReadAt
3988 * Reads a specified number of bytes from this chain at the specified offset.
3989 * bytesRead may be NULL.
3990 * Failure will be returned if the specified number of bytes has not been read.
3992 BOOL
BlockChainStream_ReadAt(BlockChainStream
* This
,
3993 ULARGE_INTEGER offset
,
3998 ULONG blockNoInSequence
= offset
.LowPart
/ This
->parentStorage
->bigBlockSize
;
3999 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->bigBlockSize
;
4000 ULONG bytesToReadInBuffer
;
4003 BYTE
* bigBlockBuffer
;
4005 if (This
->lastBlockNoInSequence
== 0xFFFFFFFF)
4006 This
->lastBlockNoInSequence
= blockNoInSequence
;
4008 * Find the first block in the stream that contains part of the buffer.
4010 if (blockNoInSequence
> This
->lastBlockNoInSequence
)
4012 ULONG temp
= blockNoInSequence
;
4014 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4015 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4016 This
->lastBlockNoInSequence
= temp
;
4020 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4021 This
->lastBlockNoInSequence
= blockNoInSequence
;
4024 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4027 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4029 blockNoInSequence
--;
4032 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4035 * Start reading the buffer.
4038 bufferWalker
= buffer
;
4040 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4043 * Calculate how many bytes we can copy from this big block.
4045 bytesToReadInBuffer
=
4046 MIN(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4049 * Copy those bytes to the buffer
4052 StorageImpl_GetROBigBlock(This
->parentStorage
, blockIndex
);
4054 memcpy(bufferWalker
, bigBlockBuffer
+ offsetInBlock
, bytesToReadInBuffer
);
4056 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4059 * Step to the next big block.
4062 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4064 bufferWalker
+= bytesToReadInBuffer
;
4065 size
-= bytesToReadInBuffer
;
4066 *bytesRead
+= bytesToReadInBuffer
;
4067 offsetInBlock
= 0; /* There is no offset on the next block */
4074 /******************************************************************************
4075 * BlockChainStream_WriteAt
4077 * Writes the specified number of bytes to this chain at the specified offset.
4078 * bytesWritten may be NULL.
4079 * Will fail if not all specified number of bytes have been written.
4081 BOOL
BlockChainStream_WriteAt(BlockChainStream
* This
,
4082 ULARGE_INTEGER offset
,
4085 ULONG
* bytesWritten
)
4087 ULONG blockNoInSequence
= offset
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4088 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->bigBlockSize
;
4092 BYTE
* bigBlockBuffer
;
4094 if (This
->lastBlockNoInSequence
== 0xFFFFFFFF)
4095 This
->lastBlockNoInSequence
= blockNoInSequence
;
4098 * Find the first block in the stream that contains part of the buffer.
4100 if (blockNoInSequence
> This
->lastBlockNoInSequence
)
4102 ULONG temp
= blockNoInSequence
;
4104 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4105 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4106 This
->lastBlockNoInSequence
= temp
;
4110 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4111 This
->lastBlockNoInSequence
= blockNoInSequence
;
4114 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4117 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4119 blockNoInSequence
--;
4122 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4125 * Here, I'm casting away the constness on the buffer variable
4126 * This is OK since we don't intend to modify that buffer.
4129 bufferWalker
= (BYTE
*)buffer
;
4131 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4134 * Calculate how many bytes we can copy from this big block.
4137 MIN(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4140 * Copy those bytes to the buffer
4142 bigBlockBuffer
= StorageImpl_GetBigBlock(This
->parentStorage
, blockIndex
);
4144 memcpy(bigBlockBuffer
+ offsetInBlock
, bufferWalker
, bytesToWrite
);
4146 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4149 * Step to the next big block.
4152 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4154 bufferWalker
+= bytesToWrite
;
4155 size
-= bytesToWrite
;
4156 *bytesWritten
+= bytesToWrite
;
4157 offsetInBlock
= 0; /* There is no offset on the next block */
4163 /******************************************************************************
4164 * BlockChainStream_Shrink
4166 * Shrinks this chain in the big block depot.
4168 BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4169 ULARGE_INTEGER newSize
)
4171 ULONG blockIndex
, extraBlock
;
4176 * Figure out how many blocks are needed to contain the new size
4178 numBlocks
= newSize
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4180 if ((newSize
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4183 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4186 * Go to the new end of chain
4188 while (count
< numBlocks
)
4191 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4196 /* Get the next block before marking the new end */
4198 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4200 /* Mark the new end of chain */
4201 StorageImpl_SetNextBlockInChain(
4202 This
->parentStorage
,
4204 BLOCK_END_OF_CHAIN
);
4206 This
->tailIndex
= blockIndex
;
4207 This
->numBlocks
= numBlocks
;
4210 * Mark the extra blocks as free
4212 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4215 StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
);
4217 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4218 extraBlock
= blockIndex
;
4224 /******************************************************************************
4225 * BlockChainStream_Enlarge
4227 * Grows this chain in the big block depot.
4229 BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4230 ULARGE_INTEGER newSize
)
4232 ULONG blockIndex
, currentBlock
;
4234 ULONG oldNumBlocks
= 0;
4236 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4239 * Empty chain. Create the head.
4241 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4243 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4244 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4246 BLOCK_END_OF_CHAIN
);
4248 if (This
->headOfStreamPlaceHolder
!= 0)
4250 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4254 StgProperty chainProp
;
4255 assert(This
->ownerPropertyIndex
!= PROPERTY_NULL
);
4257 StorageImpl_ReadProperty(
4258 This
->parentStorage
,
4259 This
->ownerPropertyIndex
,
4262 chainProp
.startingBlock
= blockIndex
;
4264 StorageImpl_WriteProperty(
4265 This
->parentStorage
,
4266 This
->ownerPropertyIndex
,
4270 This
->tailIndex
= blockIndex
;
4271 This
->numBlocks
= 1;
4275 * Figure out how many blocks are needed to contain this stream
4277 newNumBlocks
= newSize
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4279 if ((newSize
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4283 * Go to the current end of chain
4285 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4287 currentBlock
= blockIndex
;
4289 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4292 currentBlock
= blockIndex
;
4295 StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
);
4298 This
->tailIndex
= currentBlock
;
4301 currentBlock
= This
->tailIndex
;
4302 oldNumBlocks
= This
->numBlocks
;
4305 * Add new blocks to the chain
4307 while (oldNumBlocks
< newNumBlocks
)
4309 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4311 StorageImpl_SetNextBlockInChain(
4312 This
->parentStorage
,
4316 StorageImpl_SetNextBlockInChain(
4317 This
->parentStorage
,
4319 BLOCK_END_OF_CHAIN
);
4321 currentBlock
= blockIndex
;
4325 This
->tailIndex
= blockIndex
;
4326 This
->numBlocks
= newNumBlocks
;
4331 /******************************************************************************
4332 * BlockChainStream_SetSize
4334 * Sets the size of this stream. The big block depot will be updated.
4335 * The file will grow if we grow the chain.
4337 * TODO: Free the actual blocks in the file when we shrink the chain.
4338 * Currently, the blocks are still in the file. So the file size
4339 * doesn't shrink even if we shrink streams.
4341 BOOL
BlockChainStream_SetSize(
4342 BlockChainStream
* This
,
4343 ULARGE_INTEGER newSize
)
4345 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4347 if (newSize
.LowPart
== size
.LowPart
)
4350 if (newSize
.LowPart
< size
.LowPart
)
4352 BlockChainStream_Shrink(This
, newSize
);
4356 ULARGE_INTEGER fileSize
=
4357 BIGBLOCKFILE_GetSize(This
->parentStorage
->bigBlockFile
);
4359 ULONG diff
= newSize
.LowPart
- size
.LowPart
;
4362 * Make sure the file stays a multiple of blocksize
4364 if ((diff
% This
->parentStorage
->bigBlockSize
) != 0)
4365 diff
+= (This
->parentStorage
->bigBlockSize
-
4366 (diff
% This
->parentStorage
->bigBlockSize
) );
4368 fileSize
.LowPart
+= diff
;
4369 BIGBLOCKFILE_SetSize(This
->parentStorage
->bigBlockFile
, fileSize
);
4371 BlockChainStream_Enlarge(This
, newSize
);
4377 /******************************************************************************
4378 * BlockChainStream_GetSize
4380 * Returns the size of this chain.
4381 * Will return the block count if this chain doesn't have a property.
4383 ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4385 StgProperty chainProperty
;
4387 if(This
->headOfStreamPlaceHolder
== NULL
)
4390 * This chain is a data stream read the property and return
4391 * the appropriate size
4393 StorageImpl_ReadProperty(
4394 This
->parentStorage
,
4395 This
->ownerPropertyIndex
,
4398 return chainProperty
.size
;
4403 * this chain is a chain that does not have a property, figure out the
4404 * size by making the product number of used blocks times the
4407 ULARGE_INTEGER result
;
4408 result
.HighPart
= 0;
4411 BlockChainStream_GetCount(This
) *
4412 This
->parentStorage
->bigBlockSize
;
4418 /******************************************************************************
4419 ** SmallBlockChainStream implementation
4422 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4423 StorageImpl
* parentStorage
,
4424 ULONG propertyIndex
)
4426 SmallBlockChainStream
* newStream
;
4428 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4430 newStream
->parentStorage
= parentStorage
;
4431 newStream
->ownerPropertyIndex
= propertyIndex
;
4436 void SmallBlockChainStream_Destroy(
4437 SmallBlockChainStream
* This
)
4439 HeapFree(GetProcessHeap(), 0, This
);
4442 /******************************************************************************
4443 * SmallBlockChainStream_GetHeadOfChain
4445 * Returns the head of this chain of small blocks.
4447 ULONG
SmallBlockChainStream_GetHeadOfChain(
4448 SmallBlockChainStream
* This
)
4450 StgProperty chainProperty
;
4453 if (This
->ownerPropertyIndex
)
4455 readSucessful
= StorageImpl_ReadProperty(
4456 This
->parentStorage
,
4457 This
->ownerPropertyIndex
,
4462 return chainProperty
.startingBlock
;
4467 return BLOCK_END_OF_CHAIN
;
4470 /******************************************************************************
4471 * SmallBlockChainStream_GetNextBlockInChain
4473 * Returns the index of the next small block in this chain.
4476 * - BLOCK_END_OF_CHAIN: end of this chain
4477 * - BLOCK_UNUSED: small block 'blockIndex' is free
4479 ULONG
SmallBlockChainStream_GetNextBlockInChain(
4480 SmallBlockChainStream
* This
,
4483 ULARGE_INTEGER offsetOfBlockInDepot
;
4485 ULONG nextBlockInChain
= BLOCK_END_OF_CHAIN
;
4489 offsetOfBlockInDepot
.HighPart
= 0;
4490 offsetOfBlockInDepot
.LowPart
= blockIndex
* sizeof(ULONG
);
4493 * Read those bytes in the buffer from the small block file.
4495 success
= BlockChainStream_ReadAt(
4496 This
->parentStorage
->smallBlockDepotChain
,
4497 offsetOfBlockInDepot
,
4504 StorageUtl_ReadDWord(&buffer
, 0, &nextBlockInChain
);
4507 return nextBlockInChain
;
4510 /******************************************************************************
4511 * SmallBlockChainStream_SetNextBlockInChain
4513 * Writes the index of the next block of the specified block in the small
4515 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4516 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4518 void SmallBlockChainStream_SetNextBlockInChain(
4519 SmallBlockChainStream
* This
,
4523 ULARGE_INTEGER offsetOfBlockInDepot
;
4527 offsetOfBlockInDepot
.HighPart
= 0;
4528 offsetOfBlockInDepot
.LowPart
= blockIndex
* sizeof(ULONG
);
4530 StorageUtl_WriteDWord(&buffer
, 0, nextBlock
);
4533 * Read those bytes in the buffer from the small block file.
4535 BlockChainStream_WriteAt(
4536 This
->parentStorage
->smallBlockDepotChain
,
4537 offsetOfBlockInDepot
,
4543 /******************************************************************************
4544 * SmallBlockChainStream_FreeBlock
4546 * Flag small block 'blockIndex' as free in the small block depot.
4548 void SmallBlockChainStream_FreeBlock(
4549 SmallBlockChainStream
* This
,
4552 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4555 /******************************************************************************
4556 * SmallBlockChainStream_GetNextFreeBlock
4558 * Returns the index of a free small block. The small block depot will be
4559 * enlarged if necessary. The small block chain will also be enlarged if
4562 ULONG
SmallBlockChainStream_GetNextFreeBlock(
4563 SmallBlockChainStream
* This
)
4565 ULARGE_INTEGER offsetOfBlockInDepot
;
4568 ULONG blockIndex
= 0;
4569 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
4570 BOOL success
= TRUE
;
4571 ULONG smallBlocksPerBigBlock
;
4573 offsetOfBlockInDepot
.HighPart
= 0;
4576 * Scan the small block depot for a free block
4578 while (nextBlockIndex
!= BLOCK_UNUSED
)
4580 offsetOfBlockInDepot
.LowPart
= blockIndex
* sizeof(ULONG
);
4582 success
= BlockChainStream_ReadAt(
4583 This
->parentStorage
->smallBlockDepotChain
,
4584 offsetOfBlockInDepot
,
4590 * If we run out of space for the small block depot, enlarge it
4594 StorageUtl_ReadDWord(&buffer
, 0, &nextBlockIndex
);
4596 if (nextBlockIndex
!= BLOCK_UNUSED
)
4602 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
4604 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
4605 ULONG nextBlock
, newsbdIndex
;
4606 BYTE
* smallBlockDepot
;
4608 nextBlock
= sbdIndex
;
4609 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
4611 sbdIndex
= nextBlock
;
4613 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
);
4616 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4617 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
4618 StorageImpl_SetNextBlockInChain(
4619 This
->parentStorage
,
4623 StorageImpl_SetNextBlockInChain(
4624 This
->parentStorage
,
4626 BLOCK_END_OF_CHAIN
);
4629 * Initialize all the small blocks to free
4632 StorageImpl_GetBigBlock(This
->parentStorage
, newsbdIndex
);
4634 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
4635 StorageImpl_ReleaseBigBlock(This
->parentStorage
, smallBlockDepot
);
4640 * We have just created the small block depot.
4642 StgProperty rootProp
;
4646 * Save it in the header
4648 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
4649 StorageImpl_SaveFileHeader(This
->parentStorage
);
4652 * And allocate the first big block that will contain small blocks
4655 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4657 StorageImpl_SetNextBlockInChain(
4658 This
->parentStorage
,
4660 BLOCK_END_OF_CHAIN
);
4662 StorageImpl_ReadProperty(
4663 This
->parentStorage
,
4664 This
->parentStorage
->rootPropertySetIndex
,
4667 rootProp
.startingBlock
= sbStartIndex
;
4668 rootProp
.size
.HighPart
= 0;
4669 rootProp
.size
.LowPart
= This
->parentStorage
->bigBlockSize
;
4671 StorageImpl_WriteProperty(
4672 This
->parentStorage
,
4673 This
->parentStorage
->rootPropertySetIndex
,
4679 smallBlocksPerBigBlock
=
4680 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
4683 * Verify if we have to allocate big blocks to contain small blocks
4685 if (blockIndex
% smallBlocksPerBigBlock
== 0)
4687 StgProperty rootProp
;
4688 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
4690 StorageImpl_ReadProperty(
4691 This
->parentStorage
,
4692 This
->parentStorage
->rootPropertySetIndex
,
4695 if (rootProp
.size
.LowPart
<
4696 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
4698 rootProp
.size
.LowPart
+= This
->parentStorage
->bigBlockSize
;
4700 BlockChainStream_SetSize(
4701 This
->parentStorage
->smallBlockRootChain
,
4704 StorageImpl_WriteProperty(
4705 This
->parentStorage
,
4706 This
->parentStorage
->rootPropertySetIndex
,
4714 /******************************************************************************
4715 * SmallBlockChainStream_ReadAt
4717 * Reads a specified number of bytes from this chain at the specified offset.
4718 * bytesRead may be NULL.
4719 * Failure will be returned if the specified number of bytes has not been read.
4721 BOOL
SmallBlockChainStream_ReadAt(
4722 SmallBlockChainStream
* This
,
4723 ULARGE_INTEGER offset
,
4728 ULARGE_INTEGER offsetInBigBlockFile
;
4729 ULONG blockNoInSequence
=
4730 offset
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4732 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->smallBlockSize
;
4733 ULONG bytesToReadInBuffer
;
4735 ULONG bytesReadFromBigBlockFile
;
4739 * This should never happen on a small block file.
4741 assert(offset
.HighPart
==0);
4744 * Find the first block in the stream that contains part of the buffer.
4746 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4748 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4750 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4752 blockNoInSequence
--;
4756 * Start reading the buffer.
4759 bufferWalker
= buffer
;
4761 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4764 * Calculate how many bytes we can copy from this small block.
4766 bytesToReadInBuffer
=
4767 MIN(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
4770 * Calculate the offset of the small block in the small block file.
4772 offsetInBigBlockFile
.HighPart
= 0;
4773 offsetInBigBlockFile
.LowPart
=
4774 blockIndex
* This
->parentStorage
->smallBlockSize
;
4776 offsetInBigBlockFile
.LowPart
+= offsetInBlock
;
4779 * Read those bytes in the buffer from the small block file.
4781 BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
4782 offsetInBigBlockFile
,
4783 bytesToReadInBuffer
,
4785 &bytesReadFromBigBlockFile
);
4787 assert(bytesReadFromBigBlockFile
== bytesToReadInBuffer
);
4790 * Step to the next big block.
4792 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4793 bufferWalker
+= bytesToReadInBuffer
;
4794 size
-= bytesToReadInBuffer
;
4795 *bytesRead
+= bytesToReadInBuffer
;
4796 offsetInBlock
= 0; /* There is no offset on the next block */
4802 /******************************************************************************
4803 * SmallBlockChainStream_WriteAt
4805 * Writes the specified number of bytes to this chain at the specified offset.
4806 * bytesWritten may be NULL.
4807 * Will fail if not all specified number of bytes have been written.
4809 BOOL
SmallBlockChainStream_WriteAt(
4810 SmallBlockChainStream
* This
,
4811 ULARGE_INTEGER offset
,
4814 ULONG
* bytesWritten
)
4816 ULARGE_INTEGER offsetInBigBlockFile
;
4817 ULONG blockNoInSequence
=
4818 offset
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4820 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->smallBlockSize
;
4821 ULONG bytesToWriteInBuffer
;
4823 ULONG bytesWrittenFromBigBlockFile
;
4827 * This should never happen on a small block file.
4829 assert(offset
.HighPart
==0);
4832 * Find the first block in the stream that contains part of the buffer.
4834 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4836 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4838 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4840 blockNoInSequence
--;
4844 * Start writing the buffer.
4846 * Here, I'm casting away the constness on the buffer variable
4847 * This is OK since we don't intend to modify that buffer.
4850 bufferWalker
= (BYTE
*)buffer
;
4851 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4854 * Calculate how many bytes we can copy to this small block.
4856 bytesToWriteInBuffer
=
4857 MIN(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
4860 * Calculate the offset of the small block in the small block file.
4862 offsetInBigBlockFile
.HighPart
= 0;
4863 offsetInBigBlockFile
.LowPart
=
4864 blockIndex
* This
->parentStorage
->smallBlockSize
;
4866 offsetInBigBlockFile
.LowPart
+= offsetInBlock
;
4869 * Write those bytes in the buffer to the small block file.
4871 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockRootChain
,
4872 offsetInBigBlockFile
,
4873 bytesToWriteInBuffer
,
4875 &bytesWrittenFromBigBlockFile
);
4877 assert(bytesWrittenFromBigBlockFile
== bytesToWriteInBuffer
);
4880 * Step to the next big block.
4882 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4883 bufferWalker
+= bytesToWriteInBuffer
;
4884 size
-= bytesToWriteInBuffer
;
4885 *bytesWritten
+= bytesToWriteInBuffer
;
4886 offsetInBlock
= 0; /* There is no offset on the next block */
4892 /******************************************************************************
4893 * SmallBlockChainStream_Shrink
4895 * Shrinks this chain in the small block depot.
4897 BOOL
SmallBlockChainStream_Shrink(
4898 SmallBlockChainStream
* This
,
4899 ULARGE_INTEGER newSize
)
4901 ULONG blockIndex
, extraBlock
;
4905 numBlocks
= newSize
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4907 if ((newSize
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
4910 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4913 * Go to the new end of chain
4915 while (count
< numBlocks
)
4917 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4921 /* Get the next block before marking the new end */
4922 extraBlock
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4924 /* Mark the new end of chain */
4925 SmallBlockChainStream_SetNextBlockInChain(
4928 BLOCK_END_OF_CHAIN
);
4931 * Mark the extra blocks as free
4933 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4935 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
);
4936 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
4937 extraBlock
= blockIndex
;
4943 /******************************************************************************
4944 * SmallBlockChainStream_Enlarge
4946 * Grows this chain in the small block depot.
4948 BOOL
SmallBlockChainStream_Enlarge(
4949 SmallBlockChainStream
* This
,
4950 ULARGE_INTEGER newSize
)
4952 ULONG blockIndex
, currentBlock
;
4954 ULONG oldNumBlocks
= 0;
4956 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4961 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4963 StgProperty chainProp
;
4965 StorageImpl_ReadProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
4968 chainProp
.startingBlock
= SmallBlockChainStream_GetNextFreeBlock(This
);
4970 StorageImpl_WriteProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
4973 blockIndex
= chainProp
.startingBlock
;
4974 SmallBlockChainStream_SetNextBlockInChain(
4977 BLOCK_END_OF_CHAIN
);
4980 currentBlock
= blockIndex
;
4983 * Figure out how many blocks are needed to contain this stream
4985 newNumBlocks
= newSize
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4987 if ((newSize
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
4991 * Go to the current end of chain
4993 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4996 currentBlock
= blockIndex
;
4997 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
);
5001 * Add new blocks to the chain
5003 while (oldNumBlocks
< newNumBlocks
)
5005 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5006 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
5008 SmallBlockChainStream_SetNextBlockInChain(
5011 BLOCK_END_OF_CHAIN
);
5013 currentBlock
= blockIndex
;
5020 /******************************************************************************
5021 * SmallBlockChainStream_GetCount
5023 * Returns the number of blocks that comprises this chain.
5024 * This is not the size of this chain as the last block may not be full!
5026 ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
5031 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5033 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5037 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
5043 /******************************************************************************
5044 * SmallBlockChainStream_SetSize
5046 * Sets the size of this stream.
5047 * The file will grow if we grow the chain.
5049 * TODO: Free the actual blocks in the file when we shrink the chain.
5050 * Currently, the blocks are still in the file. So the file size
5051 * doesn't shrink even if we shrink streams.
5053 BOOL
SmallBlockChainStream_SetSize(
5054 SmallBlockChainStream
* This
,
5055 ULARGE_INTEGER newSize
)
5057 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
5059 if (newSize
.LowPart
== size
.LowPart
)
5062 if (newSize
.LowPart
< size
.LowPart
)
5064 SmallBlockChainStream_Shrink(This
, newSize
);
5068 SmallBlockChainStream_Enlarge(This
, newSize
);
5074 /******************************************************************************
5075 * SmallBlockChainStream_GetSize
5077 * Returns the size of this chain.
5079 ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
5081 StgProperty chainProperty
;
5083 StorageImpl_ReadProperty(
5084 This
->parentStorage
,
5085 This
->ownerPropertyIndex
,
5088 return chainProperty
.size
;
5091 /******************************************************************************
5092 * StgCreateDocfile32 [OLE32.144]
5094 HRESULT WINAPI
StgCreateDocfile(
5098 IStorage
**ppstgOpen
)
5100 StorageImpl
* newStorage
= 0;
5101 HANDLE hFile
= INVALID_HANDLE_VALUE
;
5106 DWORD fileAttributes
;
5107 WCHAR tempFileName
[MAX_PATH
];
5110 * Validate the parameters
5113 return STG_E_INVALIDPOINTER
;
5116 * Validate the STGM flags
5118 if ( FAILED( validateSTGM(grfMode
) ))
5119 return STG_E_INVALIDFLAG
;
5122 * Generate a unique name.
5126 WCHAR tempPath
[MAX_PATH
];
5127 WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
5129 memset(tempPath
, 0, sizeof(tempPath
));
5130 memset(tempFileName
, 0, sizeof(tempFileName
));
5132 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
5135 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
5136 pwcsName
= tempFileName
;
5138 return STG_E_INSUFFICIENTMEMORY
;
5142 * Interpret the STGM value grfMode
5144 shareMode
= GetShareModeFromSTGM(grfMode
);
5145 accessMode
= GetAccessModeFromSTGM(grfMode
);
5146 creationMode
= GetCreationModeFromSTGM(grfMode
);
5148 if (grfMode
& STGM_DELETEONRELEASE
)
5149 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5151 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5153 if (grfMode
& STGM_TRANSACTED
)
5154 FIXME(ole
, "Transacted mode not implemented.\n");
5157 * Initialize the "out" parameter.
5161 hFile
= CreateFileW(pwcsName
,
5169 if (hFile
== INVALID_HANDLE_VALUE
)
5175 * Allocate and initialize the new IStorage32object.
5177 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5179 if (newStorage
== 0)
5180 return STG_E_INSUFFICIENTMEMORY
;
5182 hr
= StorageImpl_Construct(
5189 HeapFree(GetProcessHeap(), 0, newStorage
);
5194 * Get an "out" pointer for the caller.
5196 hr
= StorageBaseImpl_QueryInterface(
5197 (IStorage
*)newStorage
,
5198 (REFIID
)&IID_IStorage
,
5204 /******************************************************************************
5205 * StgOpenStorage32 [OLE32.148]
5207 HRESULT WINAPI
StgOpenStorage(
5208 const OLECHAR
*pwcsName
,
5209 IStorage
*pstgPriority
,
5213 IStorage
**ppstgOpen
)
5215 StorageImpl
* newStorage
= 0;
5222 * Perform a sanity check
5224 if (( pwcsName
== 0) || (ppstgOpen
== 0) )
5225 return STG_E_INVALIDPOINTER
;
5228 * Validate the STGM flags
5230 if ( FAILED( validateSTGM(grfMode
) ))
5231 return STG_E_INVALIDFLAG
;
5234 * Interpret the STGM value grfMode
5236 shareMode
= GetShareModeFromSTGM(grfMode
);
5237 accessMode
= GetAccessModeFromSTGM(grfMode
);
5240 * Initialize the "out" parameter.
5244 hFile
= CreateFileW( pwcsName
,
5249 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
5253 if (hFile
==INVALID_HANDLE_VALUE
)
5259 * Allocate and initialize the new IStorage32object.
5261 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5263 if (newStorage
== 0)
5264 return STG_E_INSUFFICIENTMEMORY
;
5266 hr
= StorageImpl_Construct(
5273 HeapFree(GetProcessHeap(), 0, newStorage
);
5278 * Get an "out" pointer for the caller.
5280 hr
= StorageBaseImpl_QueryInterface(
5281 (IStorage
*)newStorage
,
5282 (REFIID
)&IID_IStorage
,
5288 /******************************************************************************
5289 * WriteClassStg32 [OLE32.148]
5291 * This method will store the specified CLSID in the specified storage object
5293 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
5299 hRes
= IStorage_SetClass(pStg
, rclsid
);
5304 /*******************************************************************************************
5307 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5309 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
5319 * read a STATSTG structure (contains the clsid) from the storage
5321 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_DEFAULT
);
5324 *pclsid
=pstatstg
.clsid
;
5329 /*************************************************************************************
5332 * This function loads an object from stream
5334 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
5339 FIXME(ole
,"(),stub!\n");
5341 res
=ReadClassStm(pStm
,&clsid
);
5343 if (SUCCEEDED(res
)){
5345 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
5349 res
=IPersistStream_Load((IPersistStream
*)ppvObj
,pStm
);
5355 /************************************************************************************************
5358 * This function saves an object with the IPersistStream interface on it to the specified stream
5360 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
5366 TRACE(ole
,"(%p,%p)\n",pPStm
,pStm
);
5368 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
5370 if (SUCCEEDED(res
)){
5372 res
=WriteClassStm(pStm
,&clsid
);
5376 res
=IPersistStream_Save(pPStm
,pStm
,FALSE
);
5382 /****************************************************************************
5383 * This method validate a STGM parameter that can contain the values below
5385 * STGM_DIRECT 0x00000000
5386 * STGM_TRANSACTED 0x00010000
5387 * STGM_SIMPLE 0x08000000
5389 * STGM_READ 0x00000000
5390 * STGM_WRITE 0x00000001
5391 * STGM_READWRITE 0x00000002
5393 * STGM_SHARE_DENY_NONE 0x00000040
5394 * STGM_SHARE_DENY_READ 0x00000030
5395 * STGM_SHARE_DENY_WRITE 0x00000020
5396 * STGM_SHARE_EXCLUSIVE 0x00000010
5398 * STGM_PRIORITY 0x00040000
5399 * STGM_DELETEONRELEASE 0x04000000
5401 * STGM_CREATE 0x00001000
5402 * STGM_CONVERT 0x00020000
5403 * STGM_FAILIFTHERE 0x00000000
5405 * STGM_NOSCRATCH 0x00100000
5406 * STGM_NOSNAPSHOT 0x00200000
5408 static HRESULT
validateSTGM(DWORD stgm
)
5410 BOOL bSTGM_TRANSACTED
= ((stgm
& STGM_TRANSACTED
) == STGM_TRANSACTED
);
5411 BOOL bSTGM_SIMPLE
= ((stgm
& STGM_SIMPLE
) == STGM_SIMPLE
);
5412 BOOL bSTGM_DIRECT
= ! (bSTGM_TRANSACTED
|| bSTGM_SIMPLE
);
5414 BOOL bSTGM_WRITE
= ((stgm
& STGM_WRITE
) == STGM_WRITE
);
5415 BOOL bSTGM_READWRITE
= ((stgm
& STGM_READWRITE
) == STGM_READWRITE
);
5416 BOOL bSTGM_READ
= ! (bSTGM_WRITE
|| bSTGM_READWRITE
);
5418 BOOL bSTGM_SHARE_DENY_NONE
=
5419 ((stgm
& STGM_SHARE_DENY_NONE
) == STGM_SHARE_DENY_NONE
);
5421 BOOL bSTGM_SHARE_DENY_READ
=
5422 ((stgm
& STGM_SHARE_DENY_READ
) == STGM_SHARE_DENY_READ
);
5424 BOOL bSTGM_SHARE_DENY_WRITE
=
5425 ((stgm
& STGM_SHARE_DENY_WRITE
) == STGM_SHARE_DENY_WRITE
);
5427 BOOL bSTGM_SHARE_EXCLUSIVE
=
5428 ((stgm
& STGM_SHARE_EXCLUSIVE
) == STGM_SHARE_EXCLUSIVE
);
5430 BOOL bSTGM_CREATE
= ((stgm
& STGM_CREATE
) == STGM_CREATE
);
5431 BOOL bSTGM_CONVERT
= ((stgm
& STGM_CONVERT
) == STGM_CONVERT
);
5433 BOOL bSTGM_NOSCRATCH
= ((stgm
& STGM_NOSCRATCH
) == STGM_NOSCRATCH
);
5434 BOOL bSTGM_NOSNAPSHOT
= ((stgm
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
);
5437 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5439 if ( ! bSTGM_DIRECT
)
5440 if( bSTGM_TRANSACTED
&& bSTGM_SIMPLE
)
5444 * STGM_WRITE | STGM_READWRITE | STGM_READ
5447 if( bSTGM_WRITE
&& bSTGM_READWRITE
)
5451 * STGM_SHARE_DENY_NONE | others
5452 * (I assume here that DENY_READ implies DENY_WRITE)
5454 if ( bSTGM_SHARE_DENY_NONE
)
5455 if ( bSTGM_SHARE_DENY_READ
||
5456 bSTGM_SHARE_DENY_WRITE
||
5457 bSTGM_SHARE_EXCLUSIVE
)
5461 * STGM_CREATE | STGM_CONVERT
5462 * if both are false, STGM_FAILIFTHERE is set to TRUE
5464 if ( bSTGM_CREATE
&& bSTGM_CONVERT
)
5468 * STGM_NOSCRATCH requires STGM_TRANSACTED
5470 if ( bSTGM_NOSCRATCH
&& ! bSTGM_TRANSACTED
)
5474 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5475 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5477 if (bSTGM_NOSNAPSHOT
)
5479 if ( ! ( bSTGM_TRANSACTED
&&
5480 !(bSTGM_SHARE_EXCLUSIVE
|| bSTGM_SHARE_DENY_WRITE
)) )
5487 /****************************************************************************
5488 * GetShareModeFromSTGM
5490 * This method will return a share mode flag from a STGM value.
5491 * The STGM value is assumed valid.
5493 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
5495 DWORD dwShareMode
= 0;
5496 BOOL bSTGM_SHARE_DENY_NONE
=
5497 ((stgm
& STGM_SHARE_DENY_NONE
) == STGM_SHARE_DENY_NONE
);
5499 BOOL bSTGM_SHARE_DENY_READ
=
5500 ((stgm
& STGM_SHARE_DENY_READ
) == STGM_SHARE_DENY_READ
);
5502 BOOL bSTGM_SHARE_DENY_WRITE
=
5503 ((stgm
& STGM_SHARE_DENY_WRITE
) == STGM_SHARE_DENY_WRITE
);
5505 BOOL bSTGM_SHARE_EXCLUSIVE
=
5506 ((stgm
& STGM_SHARE_EXCLUSIVE
) == STGM_SHARE_EXCLUSIVE
);
5508 if ((bSTGM_SHARE_EXCLUSIVE
) || (bSTGM_SHARE_DENY_READ
))
5511 if (bSTGM_SHARE_DENY_NONE
)
5512 dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
5514 if (bSTGM_SHARE_DENY_WRITE
)
5515 dwShareMode
= FILE_SHARE_READ
;
5520 /****************************************************************************
5521 * GetAccessModeFromSTGM
5523 * This method will return an access mode flag from a STGM value.
5524 * The STGM value is assumed valid.
5526 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
5528 DWORD dwDesiredAccess
= GENERIC_READ
;
5529 BOOL bSTGM_WRITE
= ((stgm
& STGM_WRITE
) == STGM_WRITE
);
5530 BOOL bSTGM_READWRITE
= ((stgm
& STGM_READWRITE
) == STGM_READWRITE
);
5531 BOOL bSTGM_READ
= ! (bSTGM_WRITE
|| bSTGM_READWRITE
);
5534 dwDesiredAccess
= GENERIC_READ
;
5537 dwDesiredAccess
|= GENERIC_WRITE
;
5539 if (bSTGM_READWRITE
)
5540 dwDesiredAccess
= GENERIC_READ
| GENERIC_WRITE
;
5542 return dwDesiredAccess
;
5545 /****************************************************************************
5546 * GetCreationModeFromSTGM
5548 * This method will return a creation mode flag from a STGM value.
5549 * The STGM value is assumed valid.
5551 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
5553 if ( stgm
& STGM_CREATE
)
5554 return CREATE_ALWAYS
;
5555 if (stgm
& STGM_CONVERT
) {
5556 FIXME(ole
, "STGM_CONVERT not implemented!\n");
5559 /* All other cases */
5560 if (stgm
& ~ (STGM_CREATE
|STGM_CONVERT
))
5561 FIXME(ole
,"unhandled storage mode : 0x%08lx\n",stgm
& ~ (STGM_CREATE
|STGM_CONVERT
));