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"
30 DEFAULT_DEBUG_CHANNEL(ole
)
34 static const char rootPropertyName
[] = "Root Entry";
36 /***********************************************************************
37 * Forward declaration of internal functions used by the method DestroyElement
39 static HRESULT
deleteStorageProperty(
40 StorageImpl
*parentStorage
,
41 OLECHAR
*propertyToDeleteName
);
43 static HRESULT
deleteStreamProperty(
44 StorageImpl
*parentStorage
,
45 ULONG foundPropertyIndexToDelete
,
46 StgProperty propertyToDelete
);
48 static HRESULT
findPlaceholder(
50 ULONG propertyIndexToStore
,
51 ULONG storagePropertyIndex
,
54 static HRESULT
adjustPropertyChain(
56 StgProperty propertyToDelete
,
57 StgProperty parentProperty
,
58 ULONG parentPropertyId
,
61 /***********************************************************************
62 * Declaration of the functions used to manipulate StgProperty
65 static ULONG
getFreeProperty(
66 StorageImpl
*storage
);
68 static void updatePropertyChain(
70 ULONG newPropertyIndex
,
71 StgProperty newProperty
);
73 static LONG
propertyNameCmp(
75 OLECHAR
*currentProperty
);
78 /***********************************************************************
79 * Declaration of miscellaneous functions...
81 static HRESULT
validateSTGM(DWORD stgmValue
);
83 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
84 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
85 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
88 * Virtual function table for the IStorage32Impl class.
90 static ICOM_VTABLE(IStorage
) Storage32Impl_Vtbl
=
92 StorageBaseImpl_QueryInterface
,
93 StorageBaseImpl_AddRef
,
94 StorageBaseImpl_Release
,
95 StorageBaseImpl_CreateStream
,
96 StorageBaseImpl_OpenStream
,
97 StorageImpl_CreateStorage
,
98 StorageBaseImpl_OpenStorage
,
100 StorageImpl_MoveElementTo
,
103 StorageBaseImpl_EnumElements
,
104 StorageImpl_DestroyElement
,
105 StorageBaseImpl_RenameElement
,
106 StorageImpl_SetElementTimes
,
107 StorageBaseImpl_SetClass
,
108 StorageImpl_SetStateBits
,
113 * Virtual function table for the Storage32InternalImpl class.
115 static ICOM_VTABLE(IStorage
) Storage32InternalImpl_Vtbl
=
117 StorageBaseImpl_QueryInterface
,
118 StorageBaseImpl_AddRef
,
119 StorageBaseImpl_Release
,
120 StorageBaseImpl_CreateStream
,
121 StorageBaseImpl_OpenStream
,
122 StorageImpl_CreateStorage
,
123 StorageBaseImpl_OpenStorage
,
125 StorageImpl_MoveElementTo
,
126 StorageInternalImpl_Commit
,
127 StorageInternalImpl_Revert
,
128 StorageBaseImpl_EnumElements
,
129 StorageImpl_DestroyElement
,
130 StorageBaseImpl_RenameElement
,
131 StorageImpl_SetElementTimes
,
132 StorageBaseImpl_SetClass
,
133 StorageImpl_SetStateBits
,
138 * Virtual function table for the IEnumSTATSTGImpl class.
140 static ICOM_VTABLE(IEnumSTATSTG
) IEnumSTATSTGImpl_Vtbl
=
142 IEnumSTATSTGImpl_QueryInterface
,
143 IEnumSTATSTGImpl_AddRef
,
144 IEnumSTATSTGImpl_Release
,
145 IEnumSTATSTGImpl_Next
,
146 IEnumSTATSTGImpl_Skip
,
147 IEnumSTATSTGImpl_Reset
,
148 IEnumSTATSTGImpl_Clone
155 /************************************************************************
156 ** Storage32BaseImpl implementatiion
159 /************************************************************************
160 * Storage32BaseImpl_QueryInterface (IUnknown)
162 * This method implements the common QueryInterface for all IStorage32
163 * implementations contained in this file.
165 * See Windows documentation for more details on IUnknown methods.
167 HRESULT WINAPI
StorageBaseImpl_QueryInterface(
172 ICOM_THIS(StorageBaseImpl
,iface
);
174 * Perform a sanity check on the parameters.
176 if ( (This
==0) || (ppvObject
==0) )
180 * Initialize the return parameter.
185 * Compare the riid with the interface IDs implemented by this object.
187 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
189 *ppvObject
= (IStorage
*)This
;
191 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IStorage
)) == 0)
193 *ppvObject
= (IStorage
*)This
;
197 * Check that we obtained an interface.
200 return E_NOINTERFACE
;
203 * Query Interface always increases the reference count by one when it is
206 StorageBaseImpl_AddRef(iface
);
211 /************************************************************************
212 * Storage32BaseImpl_AddRef (IUnknown)
214 * This method implements the common AddRef for all IStorage32
215 * implementations contained in this file.
217 * See Windows documentation for more details on IUnknown methods.
219 ULONG WINAPI
StorageBaseImpl_AddRef(
222 ICOM_THIS(StorageBaseImpl
,iface
);
228 /************************************************************************
229 * Storage32BaseImpl_Release (IUnknown)
231 * This method implements the common Release for all IStorage32
232 * implementations contained in this file.
234 * See Windows documentation for more details on IUnknown methods.
236 ULONG WINAPI
StorageBaseImpl_Release(
239 ICOM_THIS(StorageBaseImpl
,iface
);
241 * Decrease the reference count on this object.
246 * If the reference count goes down to 0, perform suicide.
251 * Since we are using a system of base-classes, we want to call the
252 * destructor of the appropriate derived class. To do this, we are
253 * using virtual functions to implement the destructor.
255 This
->v_destructor(This
);
263 /************************************************************************
264 * Storage32BaseImpl_OpenStream (IStorage)
266 * This method will open the specified stream object from the current storage.
268 * See Windows documentation for more details on IStorage methods.
270 HRESULT WINAPI
StorageBaseImpl_OpenStream(
272 const OLECHAR
* pwcsName
, /* [string][in] */
273 void* reserved1
, /* [unique][in] */
274 DWORD grfMode
, /* [in] */
275 DWORD reserved2
, /* [in] */
276 IStream
** ppstm
) /* [out] */
278 ICOM_THIS(StorageBaseImpl
,iface
);
279 IEnumSTATSTGImpl
* propertyEnumeration
;
280 StgStreamImpl
* newStream
;
281 StgProperty currentProperty
;
282 ULONG foundPropertyIndex
;
285 * Perform a sanity check on the parameters.
287 if ( (pwcsName
==NULL
) || (ppstm
==0) )
291 * Initialize the out parameter
296 * Validate the STGM flags
298 if ( FAILED( validateSTGM(grfMode
) ))
299 return STG_E_INVALIDFLAG
;
304 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
305 (grfMode
& STGM_DELETEONRELEASE
) ||
306 (grfMode
& STGM_TRANSACTED
) )
307 return STG_E_INVALIDFUNCTION
;
310 * Create a property enumeration to search the properties
312 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
313 This
->ancestorStorage
,
314 This
->rootPropertySetIndex
);
317 * Search the enumeration for the property with the given name
319 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
325 * Delete the property enumeration since we don't need it anymore
327 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
330 * If it was found, construct the stream object and return a pointer to it.
332 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
333 (currentProperty
.propertyType
==PROPTYPE_STREAM
) )
335 newStream
= StgStreamImpl_Construct(This
, foundPropertyIndex
);
339 *ppstm
= (IStream
*)newStream
;
342 * Since we are returning a pointer to the interface, we have to
343 * nail down the reference.
345 StgStreamImpl_AddRef(*ppstm
);
350 return E_OUTOFMEMORY
;
353 return STG_E_FILENOTFOUND
;
356 /************************************************************************
357 * Storage32BaseImpl_OpenStorage (IStorage)
359 * This method will open a new storage object from the current storage.
361 * See Windows documentation for more details on IStorage methods.
363 HRESULT WINAPI
StorageBaseImpl_OpenStorage(
365 const OLECHAR
* pwcsName
, /* [string][unique][in] */
366 IStorage
* pstgPriority
, /* [unique][in] */
367 DWORD grfMode
, /* [in] */
368 SNB snbExclude
, /* [unique][in] */
369 DWORD reserved
, /* [in] */
370 IStorage
** ppstg
) /* [out] */
372 ICOM_THIS(StorageBaseImpl
,iface
);
373 StorageInternalImpl
* newStorage
;
374 IEnumSTATSTGImpl
* propertyEnumeration
;
375 StgProperty currentProperty
;
376 ULONG foundPropertyIndex
;
379 * Perform a sanity check on the parameters.
381 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
385 * Validate the STGM flags
387 if ( FAILED( validateSTGM(grfMode
) ))
388 return STG_E_INVALIDFLAG
;
393 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
394 (grfMode
& STGM_DELETEONRELEASE
) ||
395 (grfMode
& STGM_PRIORITY
) )
396 return STG_E_INVALIDFUNCTION
;
399 * Initialize the out parameter
404 * Create a property enumeration to search the properties
406 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
407 This
->ancestorStorage
,
408 This
->rootPropertySetIndex
);
411 * Search the enumeration for the property with the given name
413 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
419 * Delete the property enumeration since we don't need it anymore
421 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
424 * If it was found, construct the stream object and return a pointer to it.
426 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
427 (currentProperty
.propertyType
==PROPTYPE_STORAGE
) )
430 * Construct a new Storage object
432 newStorage
= StorageInternalImpl_Construct(
433 This
->ancestorStorage
,
438 *ppstg
= (IStorage
*)newStorage
;
441 * Since we are returning a pointer to the interface,
442 * we have to nail down the reference.
444 StorageBaseImpl_AddRef(*ppstg
);
449 return STG_E_INSUFFICIENTMEMORY
;
452 return STG_E_FILENOTFOUND
;
455 /************************************************************************
456 * Storage32BaseImpl_EnumElements (IStorage)
458 * This method will create an enumerator object that can be used to
459 * retrieve informatino about all the properties in the storage object.
461 * See Windows documentation for more details on IStorage methods.
463 HRESULT WINAPI
StorageBaseImpl_EnumElements(
465 DWORD reserved1
, /* [in] */
466 void* reserved2
, /* [size_is][unique][in] */
467 DWORD reserved3
, /* [in] */
468 IEnumSTATSTG
** ppenum
) /* [out] */
470 ICOM_THIS(StorageBaseImpl
,iface
);
471 IEnumSTATSTGImpl
* newEnum
;
474 * Perform a sanity check on the parameters.
476 if ( (This
==0) || (ppenum
==0))
480 * Construct the enumerator.
482 newEnum
= IEnumSTATSTGImpl_Construct(
483 This
->ancestorStorage
,
484 This
->rootPropertySetIndex
);
488 *ppenum
= (IEnumSTATSTG
*)newEnum
;
491 * Don't forget to nail down a reference to the new object before
494 IEnumSTATSTGImpl_AddRef(*ppenum
);
499 return E_OUTOFMEMORY
;
502 /************************************************************************
503 * Storage32BaseImpl_Stat (IStorage)
505 * This method will retrieve information about this storage object.
507 * See Windows documentation for more details on IStorage methods.
509 HRESULT WINAPI
StorageBaseImpl_Stat(
511 STATSTG
* pstatstg
, /* [out] */
512 DWORD grfStatFlag
) /* [in] */
514 ICOM_THIS(StorageBaseImpl
,iface
);
515 StgProperty curProperty
;
519 * Perform a sanity check on the parameters.
521 if ( (This
==0) || (pstatstg
==0))
525 * Read the information from the property.
527 readSucessful
= StorageImpl_ReadProperty(
528 This
->ancestorStorage
,
529 This
->rootPropertySetIndex
,
534 StorageUtl_CopyPropertyToSTATSTG(
545 /************************************************************************
546 * Storage32BaseImpl_RenameElement (IStorage)
548 * This method will rename the specified element.
550 * See Windows documentation for more details on IStorage methods.
552 * Implementation notes: The method used to rename consists of creating a clone
553 * of the deleted StgProperty object setting it with the new name and to
554 * perform a DestroyElement of the old StgProperty.
556 HRESULT WINAPI
StorageBaseImpl_RenameElement(
558 const OLECHAR
* pwcsOldName
, /* [in] */
559 const OLECHAR
* pwcsNewName
) /* [in] */
561 ICOM_THIS(StorageBaseImpl
,iface
);
562 IEnumSTATSTGImpl
* propertyEnumeration
;
563 StgProperty currentProperty
;
564 ULONG foundPropertyIndex
;
567 * Create a property enumeration to search the properties
569 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
570 This
->rootPropertySetIndex
);
573 * Search the enumeration for the new property name
575 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
579 if (foundPropertyIndex
!= PROPERTY_NULL
)
582 * There is already a property with the new name
584 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
585 return STG_E_FILEALREADYEXISTS
;
588 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)propertyEnumeration
);
591 * Search the enumeration for the old property name
593 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
598 * Delete the property enumeration since we don't need it anymore
600 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
602 if (foundPropertyIndex
!= PROPERTY_NULL
)
604 StgProperty renamedProperty
;
605 ULONG renamedPropertyIndex
;
608 * Setup a new property for the renamed property
610 renamedProperty
.sizeOfNameString
=
611 ( lstrlenW(pwcsNewName
)+1 ) * sizeof(WCHAR
);
613 if (renamedProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
614 return STG_E_INVALIDNAME
;
616 lstrcpyW(renamedProperty
.name
, pwcsNewName
);
618 renamedProperty
.propertyType
= currentProperty
.propertyType
;
619 renamedProperty
.startingBlock
= currentProperty
.startingBlock
;
620 renamedProperty
.size
.LowPart
= currentProperty
.size
.LowPart
;
621 renamedProperty
.size
.HighPart
= currentProperty
.size
.HighPart
;
623 renamedProperty
.previousProperty
= PROPERTY_NULL
;
624 renamedProperty
.nextProperty
= PROPERTY_NULL
;
627 * Bring the dirProperty link in case it is a storage and in which
628 * case the renamed storage elements don't require to be reorganized.
630 renamedProperty
.dirProperty
= currentProperty
.dirProperty
;
632 /* call CoFileTime to get the current time
633 renamedProperty.timeStampS1
634 renamedProperty.timeStampD1
635 renamedProperty.timeStampS2
636 renamedProperty.timeStampD2
637 renamedProperty.propertyUniqueID
641 * Obtain a free property in the property chain
643 renamedPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
646 * Save the new property into the new property spot
648 StorageImpl_WriteProperty(
649 This
->ancestorStorage
,
650 renamedPropertyIndex
,
654 * Find a spot in the property chain for our newly created property.
658 renamedPropertyIndex
,
662 * At this point the renamed property has been inserted in the tree,
663 * now, before to Destroy the old property we must zeroed it's dirProperty
664 * otherwise the DestroyProperty below will zap it all and we do not want
666 * Also, we fake that the old property is a storage so the DestroyProperty
667 * will not do a SetSize(0) on the stream data.
669 * This means that we need to tweek the StgProperty if it is a stream or a
672 currentProperty
.dirProperty
= PROPERTY_NULL
;
673 currentProperty
.propertyType
= PROPTYPE_STORAGE
;
674 StorageImpl_WriteProperty(
675 This
->ancestorStorage
,
680 * Invoke Destroy to get rid of the ole property and automatically redo
681 * the linking of it's previous and next members...
683 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsOldName
);
689 * There is no property with the old name
691 return STG_E_FILENOTFOUND
;
697 /************************************************************************
698 * Storage32BaseImpl_CreateStream (IStorage)
700 * This method will create a stream object within this storage
702 * See Windows documentation for more details on IStorage methods.
704 HRESULT WINAPI
StorageBaseImpl_CreateStream(
706 const OLECHAR
* pwcsName
, /* [string][in] */
707 DWORD grfMode
, /* [in] */
708 DWORD reserved1
, /* [in] */
709 DWORD reserved2
, /* [in] */
710 IStream
** ppstm
) /* [out] */
712 ICOM_THIS(StorageBaseImpl
,iface
);
713 IEnumSTATSTGImpl
* propertyEnumeration
;
714 StgStreamImpl
* newStream
;
715 StgProperty currentProperty
, newStreamProperty
;
716 ULONG foundPropertyIndex
, newPropertyIndex
;
719 * Validate parameters
722 return STG_E_INVALIDPOINTER
;
725 return STG_E_INVALIDNAME
;
728 * Validate the STGM flags
730 if ( FAILED( validateSTGM(grfMode
) ))
731 return STG_E_INVALIDFLAG
;
736 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
737 (grfMode
& STGM_DELETEONRELEASE
) ||
738 (grfMode
& STGM_TRANSACTED
) )
739 return STG_E_INVALIDFUNCTION
;
742 * Initialize the out parameter
747 * Create a property enumeration to search the properties
749 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
750 This
->rootPropertySetIndex
);
752 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
756 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
758 if (foundPropertyIndex
!= PROPERTY_NULL
)
761 * An element with this name already exists
763 if (grfMode
& STGM_CREATE
)
764 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsName
);
766 return STG_E_FILEALREADYEXISTS
;
770 * memset the empty property
772 memset(&newStreamProperty
, 0, sizeof(StgProperty
));
774 newStreamProperty
.sizeOfNameString
=
775 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
777 if (newStreamProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
778 return STG_E_INVALIDNAME
;
780 lstrcpyW(newStreamProperty
.name
, pwcsName
);
782 newStreamProperty
.propertyType
= PROPTYPE_STREAM
;
783 newStreamProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
784 newStreamProperty
.size
.LowPart
= 0;
785 newStreamProperty
.size
.HighPart
= 0;
787 newStreamProperty
.previousProperty
= PROPERTY_NULL
;
788 newStreamProperty
.nextProperty
= PROPERTY_NULL
;
789 newStreamProperty
.dirProperty
= PROPERTY_NULL
;
791 /* call CoFileTime to get the current time
792 newStreamProperty.timeStampS1
793 newStreamProperty.timeStampD1
794 newStreamProperty.timeStampS2
795 newStreamProperty.timeStampD2
798 /* newStreamProperty.propertyUniqueID */
801 * Get a free property or create a new one
803 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
806 * Save the new property into the new property spot
808 StorageImpl_WriteProperty(
809 This
->ancestorStorage
,
814 * Find a spot in the property chain for our newly created property.
822 * Open the stream to return it.
824 newStream
= StgStreamImpl_Construct(This
, newPropertyIndex
);
828 *ppstm
= (IStream
*)newStream
;
831 * Since we are returning a pointer to the interface, we have to nail down
834 StgStreamImpl_AddRef(*ppstm
);
838 return STG_E_INSUFFICIENTMEMORY
;
844 /************************************************************************
845 * Storage32BaseImpl_SetClass (IStorage)
847 * This method will write the specified CLSID in the property of this
850 * See Windows documentation for more details on IStorage methods.
852 HRESULT WINAPI
StorageBaseImpl_SetClass(
854 REFCLSID clsid
) /* [in] */
856 ICOM_THIS(StorageBaseImpl
,iface
);
857 HRESULT hRes
= E_FAIL
;
858 StgProperty curProperty
;
861 success
= StorageImpl_ReadProperty(This
->ancestorStorage
,
862 This
->rootPropertySetIndex
,
866 curProperty
.propertyUniqueID
= *clsid
;
868 success
= StorageImpl_WriteProperty(This
->ancestorStorage
,
869 This
->rootPropertySetIndex
,
878 /************************************************************************
879 ** Storage32Impl implementation
882 /************************************************************************
883 * Storage32Impl_CreateStorage (IStorage)
885 * This method will create the storage object within the provided storage.
887 * See Windows documentation for more details on IStorage methods.
889 HRESULT WINAPI
StorageImpl_CreateStorage(
891 const OLECHAR
*pwcsName
, /* [string][in] */
892 DWORD grfMode
, /* [in] */
893 DWORD reserved1
, /* [in] */
894 DWORD reserved2
, /* [in] */
895 IStorage
**ppstg
) /* [out] */
897 StorageImpl
* const This
=(StorageImpl
*)iface
;
899 IEnumSTATSTGImpl
*propertyEnumeration
;
900 StgProperty currentProperty
;
901 StgProperty newProperty
;
902 ULONG foundPropertyIndex
;
903 ULONG newPropertyIndex
;
908 * Validate parameters
911 return STG_E_INVALIDPOINTER
;
914 return STG_E_INVALIDNAME
;
917 * Validate the STGM flags
919 if ( FAILED( validateSTGM(grfMode
) ) ||
920 (grfMode
& STGM_DELETEONRELEASE
) )
921 return STG_E_INVALIDFLAG
;
924 * Initialize the out parameter
929 * Create a property enumeration and search the properties
931 propertyEnumeration
= IEnumSTATSTGImpl_Construct( This
->ancestorStorage
,
932 This
->rootPropertySetIndex
);
934 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
937 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
939 if (foundPropertyIndex
!= PROPERTY_NULL
)
942 * An element with this name already exists
944 if (grfMode
& STGM_CREATE
)
945 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsName
);
947 return STG_E_FILEALREADYEXISTS
;
951 * memset the empty property
953 memset(&newProperty
, 0, sizeof(StgProperty
));
955 newProperty
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
957 if (newProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
958 return STG_E_INVALIDNAME
;
960 lstrcpyW(newProperty
.name
, pwcsName
);
962 newProperty
.propertyType
= PROPTYPE_STORAGE
;
963 newProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
964 newProperty
.size
.LowPart
= 0;
965 newProperty
.size
.HighPart
= 0;
967 newProperty
.previousProperty
= PROPERTY_NULL
;
968 newProperty
.nextProperty
= PROPERTY_NULL
;
969 newProperty
.dirProperty
= PROPERTY_NULL
;
971 /* call CoFileTime to get the current time
972 newProperty.timeStampS1
973 newProperty.timeStampD1
974 newProperty.timeStampS2
975 newProperty.timeStampD2
978 /* newStorageProperty.propertyUniqueID */
981 * Obtain a free property in the property chain
983 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
986 * Save the new property into the new property spot
988 StorageImpl_WriteProperty(
989 This
->ancestorStorage
,
994 * Find a spot in the property chain for our newly created property.
1002 * Open it to get a pointer to return.
1004 hr
= StorageBaseImpl_OpenStorage(
1013 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1023 /***************************************************************************
1027 * Get a free property or create a new one.
1029 static ULONG
getFreeProperty(
1030 StorageImpl
*storage
)
1032 ULONG currentPropertyIndex
= 0;
1033 ULONG newPropertyIndex
= PROPERTY_NULL
;
1034 BOOL readSucessful
= TRUE
;
1035 StgProperty currentProperty
;
1040 * Start by reading the root property
1042 readSucessful
= StorageImpl_ReadProperty(storage
->ancestorStorage
,
1043 currentPropertyIndex
,
1047 if (currentProperty
.sizeOfNameString
== 0)
1050 * The property existis and is available, we found it.
1052 newPropertyIndex
= currentPropertyIndex
;
1058 * We exhausted the property list, we will create more space below
1060 newPropertyIndex
= currentPropertyIndex
;
1062 currentPropertyIndex
++;
1064 } while (newPropertyIndex
== PROPERTY_NULL
);
1067 * grow the property chain
1069 if (! readSucessful
)
1071 StgProperty emptyProperty
;
1072 ULARGE_INTEGER newSize
;
1073 ULONG propertyIndex
;
1074 ULONG lastProperty
= 0;
1075 ULONG blockCount
= 0;
1078 * obtain the new count of property blocks
1080 blockCount
= BlockChainStream_GetCount(
1081 storage
->ancestorStorage
->rootBlockChain
)+1;
1084 * initialize the size used by the property stream
1086 newSize
.HighPart
= 0;
1087 newSize
.LowPart
= storage
->bigBlockSize
* blockCount
;
1090 * add a property block to the property chain
1092 BlockChainStream_SetSize(storage
->ancestorStorage
->rootBlockChain
, newSize
);
1095 * memset the empty property in order to initialize the unused newly
1098 memset(&emptyProperty
, 0, sizeof(StgProperty
));
1103 lastProperty
= storage
->bigBlockSize
/ PROPSET_BLOCK_SIZE
* blockCount
;
1106 propertyIndex
= newPropertyIndex
;
1107 propertyIndex
< lastProperty
;
1110 StorageImpl_WriteProperty(
1111 storage
->ancestorStorage
,
1117 return newPropertyIndex
;
1120 /****************************************************************************
1124 * Case insensitive comparaison of StgProperty.name by first considering
1127 * Returns <0 when newPrpoerty < currentProperty
1128 * >0 when newPrpoerty > currentProperty
1129 * 0 when newPrpoerty == currentProperty
1131 static LONG
propertyNameCmp(
1132 OLECHAR
*newProperty
,
1133 OLECHAR
*currentProperty
)
1135 LONG diff
= lstrlenW(newProperty
) - lstrlenW(currentProperty
);
1140 * We compare the string themselves only when they are of the same lenght
1142 diff
= lstrcmpiW( newProperty
, currentProperty
);
1148 /****************************************************************************
1152 * Properly link this new element in the property chain.
1154 static void updatePropertyChain(
1155 StorageImpl
*storage
,
1156 ULONG newPropertyIndex
,
1157 StgProperty newProperty
)
1159 StgProperty currentProperty
;
1162 * Read the root property
1164 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1165 storage
->rootPropertySetIndex
,
1168 if (currentProperty
.dirProperty
!= PROPERTY_NULL
)
1171 * The root storage contains some element, therefore, start the research
1172 * for the appropriate location.
1175 ULONG current
, next
, previous
, currentPropertyId
;
1178 * Keep the StgProperty sequence number of the storage first property
1180 currentPropertyId
= currentProperty
.dirProperty
;
1185 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1186 currentProperty
.dirProperty
,
1189 previous
= currentProperty
.previousProperty
;
1190 next
= currentProperty
.nextProperty
;
1191 current
= currentPropertyId
;
1195 LONG diff
= propertyNameCmp( newProperty
.name
, currentProperty
.name
);
1199 if (previous
!= PROPERTY_NULL
)
1201 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1208 currentProperty
.previousProperty
= newPropertyIndex
;
1209 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1217 if (next
!= PROPERTY_NULL
)
1219 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1226 currentProperty
.nextProperty
= newPropertyIndex
;
1227 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1234 previous
= currentProperty
.previousProperty
;
1235 next
= currentProperty
.nextProperty
;
1241 * The root storage is empty, link the new property to it's dir property
1243 currentProperty
.dirProperty
= newPropertyIndex
;
1244 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1245 storage
->rootPropertySetIndex
,
1251 /*************************************************************************
1254 HRESULT WINAPI
StorageImpl_CopyTo(
1256 DWORD ciidExclude
, /* [in] */
1257 const IID
*rgiidExclude
,/* [size_is][unique][in] */
1258 SNB snbExclude
, /* [unique][in] */
1259 IStorage
*pstgDest
) /* [unique][in] */
1261 IEnumSTATSTG
*elements
= 0;
1262 STATSTG curElement
, strStat
;
1264 IStorage
*pstgTmp
, *pstgChild
;
1265 IStream
*pstrTmp
, *pstrChild
;
1267 if ((ciidExclude
!= 0) || (rgiidExclude
!= NULL
) || (snbExclude
!= NULL
))
1268 FIXME( ole
, "Exclude option not implemented\n");
1271 * Perform a sanity check
1273 if ( pstgDest
== 0 )
1274 return STG_E_INVALIDPOINTER
;
1277 * Enumerate the elements
1279 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1287 StorageBaseImpl_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1288 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1293 * Obtain the next element
1295 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1297 if ( hr
== S_FALSE
)
1299 hr
= S_OK
; /* done, every element has been copied */
1303 if (curElement
.type
== STGTY_STORAGE
)
1306 * open child source storage
1308 hr
= StorageBaseImpl_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1309 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1310 NULL
, 0, &pstgChild
);
1316 * Check if destination storage is not a child of the source
1317 * storage, which will cause an infinite loop
1319 if (pstgChild
== pstgDest
)
1321 IEnumSTATSTG_Release(elements
);
1323 return STG_E_ACCESSDENIED
;
1327 * create a new storage in destination storage
1329 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1330 STGM_FAILIFTHERE
|STGM_WRITE
, 0, 0,
1333 * if it already exist, don't create a new one use this one
1335 if (hr
== STG_E_FILEALREADYEXISTS
)
1337 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1338 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1339 NULL
, 0, &pstgTmp
);
1347 * do the copy recursively
1349 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1350 snbExclude
, pstgTmp
);
1352 IStorage_Release( pstgTmp
);
1353 IStorage_Release( pstgChild
);
1355 else if (curElement
.type
== STGTY_STREAM
)
1358 * create a new stream in destination storage. If the stream already
1359 * exist, it will be deleted and a new one will be created.
1361 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1362 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1369 * open child stream storage
1371 hr
= StorageBaseImpl_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1372 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1379 * Get the size of the stream
1381 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1386 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1389 IStream_Release( pstrTmp
);
1390 IStream_Release( pstrChild
);
1394 WARN(ole
, "unknown element type: %ld\n", curElement
.type
);
1397 } while (hr
== S_OK
);
1402 IEnumSTATSTG_Release(elements
);
1407 /*************************************************************************
1408 * MoveElementTo (IStorage)
1410 HRESULT WINAPI
StorageImpl_MoveElementTo(
1412 const OLECHAR
*pwcsName
, /* [string][in] */
1413 IStorage
*pstgDest
, /* [unique][in] */
1414 const OLECHAR
*pwcsNewName
,/* [string][in] */
1415 DWORD grfFlags
) /* [in] */
1417 FIXME(ole
, "not implemented!\n");
1421 /*************************************************************************
1424 HRESULT WINAPI
StorageImpl_Commit(
1426 DWORD grfCommitFlags
)/* [in] */
1428 FIXME(ole
, "(%ld): stub!\n", grfCommitFlags
);
1432 /*************************************************************************
1435 HRESULT WINAPI
StorageImpl_Revert(
1438 FIXME(ole
, "not implemented!\n");
1442 /*************************************************************************
1443 * DestroyElement (IStorage)
1445 * Stategy: This implementation is build this way for simplicity not for speed.
1446 * I always delete the top most element of the enumeration and adjust
1447 * the deleted element pointer all the time. This takes longer to
1448 * do but allow to reinvoke DestroyElement whenever we encounter a
1449 * storage object. The optimisation reside in the usage of another
1450 * enumeration stategy that would give all the leaves of a storage
1451 * first. (postfix order)
1453 HRESULT WINAPI
StorageImpl_DestroyElement(
1455 const OLECHAR
*pwcsName
)/* [string][in] */
1457 StorageImpl
* const This
=(StorageImpl
*)iface
;
1459 IEnumSTATSTGImpl
* propertyEnumeration
;
1462 StgProperty propertyToDelete
;
1463 StgProperty parentProperty
;
1464 ULONG foundPropertyIndexToDelete
;
1465 ULONG typeOfRelation
;
1466 ULONG parentPropertyId
;
1469 * Perform a sanity check on the parameters.
1472 return STG_E_INVALIDPOINTER
;
1475 * Create a property enumeration to search the property with the given name
1477 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
1478 This
->ancestorStorage
,
1479 This
->rootPropertySetIndex
);
1481 foundPropertyIndexToDelete
= IEnumSTATSTGImpl_FindProperty(
1482 propertyEnumeration
,
1486 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1488 if ( foundPropertyIndexToDelete
== PROPERTY_NULL
)
1490 return STG_E_FILENOTFOUND
;
1494 * Find the parent property of the property to delete (the one that
1495 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1496 * the parent is This. Otherwise, the parent is one of it's sibling...
1500 * First, read This's StgProperty..
1502 res
= StorageImpl_ReadProperty(
1503 This
->ancestorStorage
,
1504 This
->rootPropertySetIndex
,
1510 * Second, check to see if by any chance the actual storage (This) is not
1511 * the parent of the property to delete... We never know...
1513 if ( parentProperty
.dirProperty
== foundPropertyIndexToDelete
)
1516 * Set data as it would have been done in the else part...
1518 typeOfRelation
= PROPERTY_RELATION_DIR
;
1519 parentPropertyId
= This
->rootPropertySetIndex
;
1524 * Create a property enumeration to search the parent properties, and
1525 * delete it once done.
1527 IEnumSTATSTGImpl
* propertyEnumeration2
;
1529 propertyEnumeration2
= IEnumSTATSTGImpl_Construct(
1530 This
->ancestorStorage
,
1531 This
->rootPropertySetIndex
);
1533 typeOfRelation
= IEnumSTATSTGImpl_FindParentProperty(
1534 propertyEnumeration2
,
1535 foundPropertyIndexToDelete
,
1539 IEnumSTATSTGImpl_Destroy(propertyEnumeration2
);
1542 if ( propertyToDelete
.propertyType
== PROPTYPE_STORAGE
)
1544 hr
= deleteStorageProperty(
1546 propertyToDelete
.name
);
1548 else if ( propertyToDelete
.propertyType
== PROPTYPE_STREAM
)
1550 hr
= deleteStreamProperty(
1552 foundPropertyIndexToDelete
,
1560 * Adjust the property chain
1562 hr
= adjustPropertyChain(
1573 /*********************************************************************
1577 * Perform the deletion of a complete storage node
1580 static HRESULT
deleteStorageProperty(
1581 StorageImpl
*parentStorage
,
1582 OLECHAR
*propertyToDeleteName
)
1584 IEnumSTATSTG
*elements
= 0;
1585 IStorage
*childStorage
= 0;
1586 STATSTG currentElement
;
1588 HRESULT destroyHr
= S_OK
;
1591 * Open the storage and enumerate it
1593 hr
= StorageBaseImpl_OpenStorage(
1594 (IStorage
*)parentStorage
,
1595 propertyToDeleteName
,
1597 STGM_SHARE_EXCLUSIVE
,
1608 * Enumerate the elements
1610 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1615 * Obtain the next element
1617 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
1620 destroyHr
= StorageImpl_DestroyElement(
1621 (IStorage
*)childStorage
,
1622 (OLECHAR
*)currentElement
.pwcsName
);
1624 CoTaskMemFree(currentElement
.pwcsName
);
1628 * We need to Reset the enumeration every time because we delete elements
1629 * and the enumeration could be invalid
1631 IEnumSTATSTG_Reset(elements
);
1633 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
1635 IStorage_Release(childStorage
);
1636 IEnumSTATSTG_Release(elements
);
1641 /*********************************************************************
1645 * Perform the deletion of a stream node
1648 static HRESULT
deleteStreamProperty(
1649 StorageImpl
*parentStorage
,
1650 ULONG indexOfPropertyToDelete
,
1651 StgProperty propertyToDelete
)
1655 ULARGE_INTEGER size
;
1660 hr
= StorageBaseImpl_OpenStream(
1661 (IStorage
*)parentStorage
,
1662 (OLECHAR
*)propertyToDelete
.name
,
1664 STGM_SHARE_EXCLUSIVE
,
1676 hr
= IStream_SetSize(pis
, size
);
1684 * Invalidate the property by zeroing it's name member.
1686 propertyToDelete
.sizeOfNameString
= 0;
1689 * Here we should re-read the property so we get the updated pointer
1690 * but since we are here to zap it, I don't do it...
1693 StorageImpl_WriteProperty(
1694 parentStorage
->ancestorStorage
,
1695 indexOfPropertyToDelete
,
1701 /*********************************************************************
1705 * Finds a placeholder for the StgProperty within the Storage
1708 static HRESULT
findPlaceholder(
1709 StorageImpl
*storage
,
1710 ULONG propertyIndexToStore
,
1711 ULONG storePropertyIndex
,
1714 StgProperty storeProperty
;
1719 * Read the storage property
1721 res
= StorageImpl_ReadProperty(
1722 storage
->ancestorStorage
,
1731 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1733 if (storeProperty
.previousProperty
!= PROPERTY_NULL
)
1735 return findPlaceholder(
1737 propertyIndexToStore
,
1738 storeProperty
.previousProperty
,
1743 storeProperty
.previousProperty
= propertyIndexToStore
;
1746 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
1748 if (storeProperty
.nextProperty
!= PROPERTY_NULL
)
1750 return findPlaceholder(
1752 propertyIndexToStore
,
1753 storeProperty
.nextProperty
,
1758 storeProperty
.nextProperty
= propertyIndexToStore
;
1761 else if (typeOfRelation
== PROPERTY_RELATION_DIR
)
1763 if (storeProperty
.dirProperty
!= PROPERTY_NULL
)
1765 return findPlaceholder(
1767 propertyIndexToStore
,
1768 storeProperty
.dirProperty
,
1773 storeProperty
.dirProperty
= propertyIndexToStore
;
1777 hr
= StorageImpl_WriteProperty(
1778 storage
->ancestorStorage
,
1790 /*************************************************************************
1794 * This method takes the previous and the next property link of a property
1795 * to be deleted and find them a place in the Storage.
1797 static HRESULT
adjustPropertyChain(
1799 StgProperty propertyToDelete
,
1800 StgProperty parentProperty
,
1801 ULONG parentPropertyId
,
1804 ULONG newLinkProperty
= PROPERTY_NULL
;
1805 BOOL needToFindAPlaceholder
= FALSE
;
1806 ULONG storeNode
= PROPERTY_NULL
;
1807 ULONG toStoreNode
= PROPERTY_NULL
;
1808 INT relationType
= 0;
1812 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1814 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1817 * Set the parent previous to the property to delete previous
1819 newLinkProperty
= propertyToDelete
.previousProperty
;
1821 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1824 * We also need to find a storage for the other link, setup variables
1825 * to do this at the end...
1827 needToFindAPlaceholder
= TRUE
;
1828 storeNode
= propertyToDelete
.previousProperty
;
1829 toStoreNode
= propertyToDelete
.nextProperty
;
1830 relationType
= PROPERTY_RELATION_NEXT
;
1833 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1836 * Set the parent previous to the property to delete next
1838 newLinkProperty
= propertyToDelete
.nextProperty
;
1842 * Link it for real...
1844 parentProperty
.previousProperty
= newLinkProperty
;
1847 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
1849 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1852 * Set the parent next to the property to delete next previous
1854 newLinkProperty
= propertyToDelete
.previousProperty
;
1856 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1859 * We also need to find a storage for the other link, setup variables
1860 * to do this at the end...
1862 needToFindAPlaceholder
= TRUE
;
1863 storeNode
= propertyToDelete
.previousProperty
;
1864 toStoreNode
= propertyToDelete
.nextProperty
;
1865 relationType
= PROPERTY_RELATION_NEXT
;
1868 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1871 * Set the parent next to the property to delete next
1873 newLinkProperty
= propertyToDelete
.nextProperty
;
1877 * Link it for real...
1879 parentProperty
.nextProperty
= newLinkProperty
;
1881 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
1883 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1886 * Set the parent dir to the property to delete previous
1888 newLinkProperty
= propertyToDelete
.previousProperty
;
1890 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1893 * We also need to find a storage for the other link, setup variables
1894 * to do this at the end...
1896 needToFindAPlaceholder
= TRUE
;
1897 storeNode
= propertyToDelete
.previousProperty
;
1898 toStoreNode
= propertyToDelete
.nextProperty
;
1899 relationType
= PROPERTY_RELATION_NEXT
;
1902 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1905 * Set the parent dir to the property to delete next
1907 newLinkProperty
= propertyToDelete
.nextProperty
;
1911 * Link it for real...
1913 parentProperty
.dirProperty
= newLinkProperty
;
1917 * Write back the parent property
1919 res
= StorageImpl_WriteProperty(
1920 This
->ancestorStorage
,
1929 * If a placeholder is required for the other link, then, find one and
1930 * get out of here...
1932 if (needToFindAPlaceholder
)
1934 hr
= findPlaceholder(
1945 /******************************************************************************
1946 * SetElementTimes (IStorage)
1948 HRESULT WINAPI
StorageImpl_SetElementTimes(
1950 const OLECHAR
*pwcsName
,/* [string][in] */
1951 const FILETIME
*pctime
, /* [in] */
1952 const FILETIME
*patime
, /* [in] */
1953 const FILETIME
*pmtime
) /* [in] */
1955 FIXME(ole
, "not implemented!\n");
1959 /******************************************************************************
1960 * SetStateBits (IStorage)
1962 HRESULT WINAPI
StorageImpl_SetStateBits(
1964 DWORD grfStateBits
,/* [in] */
1965 DWORD grfMask
) /* [in] */
1967 FIXME(ole
, "not implemented!\n");
1971 HRESULT
StorageImpl_Construct(
1977 StgProperty currentProperty
;
1979 ULONG currentPropertyIndex
;
1981 if ( FAILED( validateSTGM(openFlags
) ))
1982 return STG_E_INVALIDFLAG
;
1984 memset(This
, 0, sizeof(StorageImpl
));
1987 * Initialize the virtual fgunction table.
1989 This
->lpvtbl
= &Storage32Impl_Vtbl
;
1990 This
->v_destructor
= &StorageImpl_Destroy
;
1993 * This is the top-level storage so initialize the ancester pointer
1996 This
->ancestorStorage
= This
;
1999 * Initialize the physical support of the storage.
2001 This
->hFile
= hFile
;
2004 * Initialize the big block cache.
2006 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2007 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2008 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2010 This
->bigBlockSize
);
2012 if (This
->bigBlockFile
== 0)
2015 if (openFlags
& STGM_CREATE
)
2017 ULARGE_INTEGER size
;
2018 BYTE
* bigBlockBuffer
;
2021 * Initialize all header variables:
2022 * - The big block depot consists of one block and it is at block 0
2023 * - The properties start at block 1
2024 * - There is no small block depot
2026 memset( This
->bigBlockDepotStart
,
2028 sizeof(This
->bigBlockDepotStart
));
2030 This
->bigBlockDepotCount
= 1;
2031 This
->bigBlockDepotStart
[0] = 0;
2032 This
->rootStartBlock
= 1;
2033 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2034 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2035 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2036 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2037 This
->extBigBlockDepotCount
= 0;
2039 StorageImpl_SaveFileHeader(This
);
2042 * Add one block for the big block depot and one block for the properties
2045 size
.LowPart
= This
->bigBlockSize
* 3;
2046 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2049 * Initialize the big block depot
2051 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, 0);
2052 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2053 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2054 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2055 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
2060 * Load the header for the file.
2062 hr
= StorageImpl_LoadFileHeader(This
);
2066 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2073 * There is no block depot cached yet.
2075 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2078 * Start searching for free blocks with block 0.
2080 This
->prevFreeBlock
= 0;
2083 * Create the block chain abstractions.
2085 This
->rootBlockChain
=
2086 BlockChainStream_Construct(This
, &This
->rootStartBlock
, PROPERTY_NULL
);
2088 This
->smallBlockDepotChain
= BlockChainStream_Construct(
2090 &This
->smallBlockDepotStart
,
2094 * Write the root property
2096 if (openFlags
& STGM_CREATE
)
2098 StgProperty rootProp
;
2100 * Initialize the property chain
2102 memset(&rootProp
, 0, sizeof(rootProp
));
2103 lstrcpyAtoW(rootProp
.name
, rootPropertyName
);
2105 rootProp
.sizeOfNameString
= (lstrlenW(rootProp
.name
)+1) * sizeof(WCHAR
);
2106 rootProp
.propertyType
= PROPTYPE_ROOT
;
2107 rootProp
.previousProperty
= PROPERTY_NULL
;
2108 rootProp
.nextProperty
= PROPERTY_NULL
;
2109 rootProp
.dirProperty
= PROPERTY_NULL
;
2110 rootProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
2111 rootProp
.size
.HighPart
= 0;
2112 rootProp
.size
.LowPart
= 0;
2114 StorageImpl_WriteProperty(This
, 0, &rootProp
);
2118 * Find the ID of the root int he property sets.
2120 currentPropertyIndex
= 0;
2124 readSucessful
= StorageImpl_ReadProperty(
2126 currentPropertyIndex
,
2131 if ( (currentProperty
.sizeOfNameString
!= 0 ) &&
2132 (currentProperty
.propertyType
== PROPTYPE_ROOT
) )
2134 This
->rootPropertySetIndex
= currentPropertyIndex
;
2138 currentPropertyIndex
++;
2140 } while (readSucessful
&& (This
->rootPropertySetIndex
== PROPERTY_NULL
) );
2149 * Create the block chain abstraction for the small block root chain.
2151 This
->smallBlockRootChain
= BlockChainStream_Construct(
2154 This
->rootPropertySetIndex
);
2159 void StorageImpl_Destroy(
2162 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2163 BlockChainStream_Destroy(This
->rootBlockChain
);
2164 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2166 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2170 /******************************************************************************
2171 * Storage32Impl_GetNextFreeBigBlock
2173 * Returns the index of the next free big block.
2174 * If the big block depot is filled, this method will enlarge it.
2177 ULONG
StorageImpl_GetNextFreeBigBlock(
2180 ULONG depotBlockIndexPos
;
2182 ULONG depotBlockOffset
;
2183 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2184 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2186 ULONG freeBlock
= BLOCK_UNUSED
;
2188 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2189 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2192 * Scan the entire big block depot until we find a block marked free
2194 while (nextBlockIndex
!= BLOCK_UNUSED
)
2196 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2198 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2201 * Grow the primary depot.
2203 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2205 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2208 * Add a block depot.
2210 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2211 This
->bigBlockDepotCount
++;
2212 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2215 * Flag it as a block depot.
2217 StorageImpl_SetNextBlockInChain(This
,
2221 /* Save new header information.
2223 StorageImpl_SaveFileHeader(This
);
2228 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2230 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2233 * Grow the extended depot.
2235 ULONG extIndex
= BLOCK_UNUSED
;
2236 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2237 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2239 if (extBlockOffset
== 0)
2241 /* We need an extended block.
2243 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2244 This
->extBigBlockDepotCount
++;
2245 depotBlockIndexPos
= extIndex
+ 1;
2248 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2251 * Add a block depot and mark it in the extended block.
2253 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2254 This
->bigBlockDepotCount
++;
2255 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2257 /* Flag the block depot.
2259 StorageImpl_SetNextBlockInChain(This
,
2263 /* If necessary, flag the extended depot block.
2265 if (extIndex
!= BLOCK_UNUSED
)
2266 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2268 /* Save header information.
2270 StorageImpl_SaveFileHeader(This
);
2274 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2276 if (depotBuffer
!= 0)
2278 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2279 ( nextBlockIndex
!= BLOCK_UNUSED
))
2281 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2283 if (nextBlockIndex
== BLOCK_UNUSED
)
2285 freeBlock
= (depotIndex
* blocksPerDepot
) +
2286 (depotBlockOffset
/sizeof(ULONG
));
2289 depotBlockOffset
+= sizeof(ULONG
);
2292 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2296 depotBlockOffset
= 0;
2299 This
->prevFreeBlock
= freeBlock
;
2304 /******************************************************************************
2305 * Storage32Impl_AddBlockDepot
2307 * This will create a depot block, essentially it is a block initialized
2310 void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2314 blockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
2317 * Initialize blocks as free
2319 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2321 StorageImpl_ReleaseBigBlock(This
, blockBuffer
);
2324 /******************************************************************************
2325 * Storage32Impl_GetExtDepotBlock
2327 * Returns the index of the block that corresponds to the specified depot
2328 * index. This method is only for depot indexes equal or greater than
2329 * COUNT_BBDEPOTINHEADER.
2331 ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2333 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2334 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2335 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2336 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2337 ULONG blockIndex
= BLOCK_UNUSED
;
2338 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2340 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2342 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2343 return BLOCK_UNUSED
;
2345 while (extBlockCount
> 0)
2347 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2351 if (extBlockIndex
!= BLOCK_UNUSED
)
2355 depotBuffer
= StorageImpl_GetROBigBlock(This
, extBlockIndex
);
2357 if (depotBuffer
!= 0)
2359 StorageUtl_ReadDWord(depotBuffer
,
2360 extBlockOffset
* sizeof(ULONG
),
2363 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2370 /******************************************************************************
2371 * Storage32Impl_SetExtDepotBlock
2373 * Associates the specified block index to the specified depot index.
2374 * This method is only for depot indexes equal or greater than
2375 * COUNT_BBDEPOTINHEADER.
2377 void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
,
2381 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2382 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2383 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2384 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2385 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2387 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2389 while (extBlockCount
> 0)
2391 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2395 if (extBlockIndex
!= BLOCK_UNUSED
)
2399 depotBuffer
= StorageImpl_GetBigBlock(This
, extBlockIndex
);
2401 if (depotBuffer
!= 0)
2403 StorageUtl_WriteDWord(depotBuffer
,
2404 extBlockOffset
* sizeof(ULONG
),
2407 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2412 /******************************************************************************
2413 * Storage32Impl_AddExtBlockDepot
2415 * Creates an extended depot block.
2417 ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2419 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2420 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2421 BYTE
* depotBuffer
= NULL
;
2422 ULONG index
= BLOCK_UNUSED
;
2423 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2424 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2425 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2427 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2428 blocksPerDepotBlock
;
2430 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2433 * The first extended block.
2435 This
->extBigBlockDepotStart
= index
;
2441 * Follow the chain to the last one.
2443 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2445 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2449 * Add the new extended block to the chain.
2451 depotBuffer
= StorageImpl_GetBigBlock(This
, nextExtBlock
);
2452 StorageUtl_WriteDWord(depotBuffer
, nextBlockOffset
, index
);
2453 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2457 * Initialize this block.
2459 depotBuffer
= StorageImpl_GetBigBlock(This
, index
);
2460 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2461 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2466 /******************************************************************************
2467 * Storage32Impl_FreeBigBlock
2469 * This method will flag the specified block as free in the big block depot.
2471 void StorageImpl_FreeBigBlock(
2475 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2477 if (blockIndex
< This
->prevFreeBlock
)
2478 This
->prevFreeBlock
= blockIndex
;
2481 /************************************************************************
2482 * Storage32Impl_GetNextBlockInChain
2484 * This method will retrieve the block index of the next big block in
2487 * Params: This - Pointer to the Storage object.
2488 * blockIndex - Index of the block to retrieve the chain
2491 * Returns: This method returns the index of the next block in the chain.
2492 * It will return the constants:
2493 * BLOCK_SPECIAL - If the block given was not part of a
2495 * BLOCK_END_OF_CHAIN - If the block given was the last in
2497 * BLOCK_UNUSED - If the block given was not past of a chain
2499 * BLOCK_EXTBBDEPOT - This block is part of the extended
2502 * See Windows documentation for more details on IStorage methods.
2504 ULONG
StorageImpl_GetNextBlockInChain(
2508 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2509 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2510 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2511 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2513 ULONG depotBlockIndexPos
;
2515 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2518 * Cache the currently accessed depot block.
2520 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2522 This
->indexBlockDepotCached
= depotBlockCount
;
2524 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2526 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2531 * We have to look in the extended depot.
2533 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2536 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2542 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2544 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &nextBlockIndex
);
2545 This
->blockDepotCached
[index
] = nextBlockIndex
;
2548 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2552 nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2554 return nextBlockIndex
;
2557 /******************************************************************************
2558 * Storage32Impl_GetNextExtendedBlock
2560 * Given an extended block this method will return the next extended block.
2563 * The last ULONG of an extended block is the block index of the next
2564 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2568 * - The index of the next extended block
2569 * - BLOCK_UNUSED: there is no next extended block.
2570 * - Any other return values denotes failure.
2572 ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
2574 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2575 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2578 depotBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
2582 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2584 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2587 return nextBlockIndex
;
2590 /******************************************************************************
2591 * Storage32Impl_SetNextBlockInChain
2593 * This method will write the index of the specified block's next block
2594 * in the big block depot.
2596 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2599 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2600 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2601 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2604 void StorageImpl_SetNextBlockInChain(
2609 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2610 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2611 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2612 ULONG depotBlockIndexPos
;
2615 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2616 assert(blockIndex
!= nextBlock
);
2618 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2620 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2625 * We have to look in the extended depot.
2627 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2630 depotBuffer
= StorageImpl_GetBigBlock(This
, depotBlockIndexPos
);
2634 StorageUtl_WriteDWord(depotBuffer
, depotBlockOffset
, nextBlock
);
2635 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2639 * Update the cached block depot, if necessary.
2641 if (depotBlockCount
== This
->indexBlockDepotCached
)
2643 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
2647 /******************************************************************************
2648 * Storage32Impl_LoadFileHeader
2650 * This method will read in the file header, i.e. big block index -1.
2652 HRESULT
StorageImpl_LoadFileHeader(
2655 HRESULT hr
= STG_E_FILENOTFOUND
;
2656 void* headerBigBlock
= NULL
;
2660 * Get a pointer to the big block of data containing the header.
2662 headerBigBlock
= StorageImpl_GetROBigBlock(This
, -1);
2665 * Extract the information from the header.
2667 if (headerBigBlock
!=0)
2670 * Check for the "magic number" signature and return an error if it is not
2673 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2675 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2676 return STG_E_OLDFORMAT
;
2679 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2681 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2682 return STG_E_INVALIDHEADER
;
2685 StorageUtl_ReadWord(
2687 OFFSET_BIGBLOCKSIZEBITS
,
2688 &This
->bigBlockSizeBits
);
2690 StorageUtl_ReadWord(
2692 OFFSET_SMALLBLOCKSIZEBITS
,
2693 &This
->smallBlockSizeBits
);
2695 StorageUtl_ReadDWord(
2697 OFFSET_BBDEPOTCOUNT
,
2698 &This
->bigBlockDepotCount
);
2700 StorageUtl_ReadDWord(
2702 OFFSET_ROOTSTARTBLOCK
,
2703 &This
->rootStartBlock
);
2705 StorageUtl_ReadDWord(
2707 OFFSET_SBDEPOTSTART
,
2708 &This
->smallBlockDepotStart
);
2710 StorageUtl_ReadDWord(
2712 OFFSET_EXTBBDEPOTSTART
,
2713 &This
->extBigBlockDepotStart
);
2715 StorageUtl_ReadDWord(
2717 OFFSET_EXTBBDEPOTCOUNT
,
2718 &This
->extBigBlockDepotCount
);
2720 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2722 StorageUtl_ReadDWord(
2724 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2725 &(This
->bigBlockDepotStart
[index
]));
2729 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2733 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
2734 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
2738 This
->bigBlockSize
= 0x000000001 >> (DWORD
)This
->bigBlockSizeBits
;
2739 This
->smallBlockSize
= 0x000000001 >> (DWORD
)This
->smallBlockSizeBits
;
2743 * Right now, the code is making some assumptions about the size of the
2744 * blocks, just make sure they are what we're expecting.
2746 assert( (This
->bigBlockSize
==DEF_BIG_BLOCK_SIZE
) &&
2747 (This
->smallBlockSize
==DEF_SMALL_BLOCK_SIZE
));
2750 * Release the block.
2752 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2760 /******************************************************************************
2761 * Storage32Impl_SaveFileHeader
2763 * This method will save to the file the header, i.e. big block -1.
2765 void StorageImpl_SaveFileHeader(
2768 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
2773 * Get a pointer to the big block of data containing the header.
2775 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
2778 * If the block read failed, the file is probably new.
2783 * Initialize for all unknown fields.
2785 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
2788 * Initialize the magic number.
2790 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
2793 * And a bunch of things we don't know what they mean
2795 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
2796 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
2797 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
2798 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
2799 StorageUtl_WriteDWord(headerBigBlock
, 0x40, (DWORD
)0x0001);
2803 * Write the information to the header.
2805 if (headerBigBlock
!=0)
2807 StorageUtl_WriteWord(
2809 OFFSET_BIGBLOCKSIZEBITS
,
2810 This
->bigBlockSizeBits
);
2812 StorageUtl_WriteWord(
2814 OFFSET_SMALLBLOCKSIZEBITS
,
2815 This
->smallBlockSizeBits
);
2817 StorageUtl_WriteDWord(
2819 OFFSET_BBDEPOTCOUNT
,
2820 This
->bigBlockDepotCount
);
2822 StorageUtl_WriteDWord(
2824 OFFSET_ROOTSTARTBLOCK
,
2825 This
->rootStartBlock
);
2827 StorageUtl_WriteDWord(
2829 OFFSET_SBDEPOTSTART
,
2830 This
->smallBlockDepotStart
);
2832 StorageUtl_WriteDWord(
2834 OFFSET_EXTBBDEPOTSTART
,
2835 This
->extBigBlockDepotStart
);
2837 StorageUtl_WriteDWord(
2839 OFFSET_EXTBBDEPOTCOUNT
,
2840 This
->extBigBlockDepotCount
);
2842 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2844 StorageUtl_WriteDWord(
2846 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2847 (This
->bigBlockDepotStart
[index
]));
2852 * Write the big block back to the file.
2854 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
2857 /******************************************************************************
2858 * Storage32Impl_ReadProperty
2860 * This method will read the specified property from the property chain.
2862 BOOL
StorageImpl_ReadProperty(
2865 StgProperty
* buffer
)
2867 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
2868 ULARGE_INTEGER offsetInPropSet
;
2872 offsetInPropSet
.HighPart
= 0;
2873 offsetInPropSet
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
2875 readSucessful
= BlockChainStream_ReadAt(
2876 This
->rootBlockChain
,
2884 memset(buffer
->name
, 0, sizeof(buffer
->name
));
2887 currentProperty
+OFFSET_PS_NAME
,
2888 PROPERTY_NAME_BUFFER_LEN
);
2890 memcpy(&buffer
->propertyType
, currentProperty
+ OFFSET_PS_PROPERTYTYPE
, 1);
2892 StorageUtl_ReadWord(
2894 OFFSET_PS_NAMELENGTH
,
2895 &buffer
->sizeOfNameString
);
2897 StorageUtl_ReadDWord(
2899 OFFSET_PS_PREVIOUSPROP
,
2900 &buffer
->previousProperty
);
2902 StorageUtl_ReadDWord(
2905 &buffer
->nextProperty
);
2907 StorageUtl_ReadDWord(
2910 &buffer
->dirProperty
);
2912 StorageUtl_ReadGUID(
2915 &buffer
->propertyUniqueID
);
2917 StorageUtl_ReadDWord(
2920 &buffer
->timeStampS1
);
2922 StorageUtl_ReadDWord(
2925 &buffer
->timeStampD1
);
2927 StorageUtl_ReadDWord(
2930 &buffer
->timeStampS2
);
2932 StorageUtl_ReadDWord(
2935 &buffer
->timeStampD2
);
2937 StorageUtl_ReadDWord(
2939 OFFSET_PS_STARTBLOCK
,
2940 &buffer
->startingBlock
);
2942 StorageUtl_ReadDWord(
2945 &buffer
->size
.LowPart
);
2947 buffer
->size
.HighPart
= 0;
2950 return readSucessful
;
2953 /*********************************************************************
2954 * Write the specified property into the property chain
2956 BOOL
StorageImpl_WriteProperty(
2959 StgProperty
* buffer
)
2961 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
2962 ULARGE_INTEGER offsetInPropSet
;
2963 BOOL writeSucessful
;
2966 offsetInPropSet
.HighPart
= 0;
2967 offsetInPropSet
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
2969 memset(currentProperty
, 0, PROPSET_BLOCK_SIZE
);
2972 currentProperty
+ OFFSET_PS_NAME
,
2974 PROPERTY_NAME_BUFFER_LEN
);
2976 memcpy(currentProperty
+ OFFSET_PS_PROPERTYTYPE
, &buffer
->propertyType
, 1);
2979 * Reassign the size in case of mistake....
2981 buffer
->sizeOfNameString
= (lstrlenW(buffer
->name
)+1) * sizeof(WCHAR
);
2983 StorageUtl_WriteWord(
2985 OFFSET_PS_NAMELENGTH
,
2986 buffer
->sizeOfNameString
);
2988 StorageUtl_WriteDWord(
2990 OFFSET_PS_PREVIOUSPROP
,
2991 buffer
->previousProperty
);
2993 StorageUtl_WriteDWord(
2996 buffer
->nextProperty
);
2998 StorageUtl_WriteDWord(
3001 buffer
->dirProperty
);
3003 StorageUtl_WriteGUID(
3006 &buffer
->propertyUniqueID
);
3008 StorageUtl_WriteDWord(
3011 buffer
->timeStampS1
);
3013 StorageUtl_WriteDWord(
3016 buffer
->timeStampD1
);
3018 StorageUtl_WriteDWord(
3021 buffer
->timeStampS2
);
3023 StorageUtl_WriteDWord(
3026 buffer
->timeStampD2
);
3028 StorageUtl_WriteDWord(
3030 OFFSET_PS_STARTBLOCK
,
3031 buffer
->startingBlock
);
3033 StorageUtl_WriteDWord(
3036 buffer
->size
.LowPart
);
3038 writeSucessful
= BlockChainStream_WriteAt(This
->rootBlockChain
,
3043 return writeSucessful
;
3046 BOOL
StorageImpl_ReadBigBlock(
3051 void* bigBlockBuffer
;
3053 bigBlockBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
3055 if (bigBlockBuffer
!=0)
3057 memcpy(buffer
, bigBlockBuffer
, This
->bigBlockSize
);
3059 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
3067 BOOL
StorageImpl_WriteBigBlock(
3072 void* bigBlockBuffer
;
3074 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
3076 if (bigBlockBuffer
!=0)
3078 memcpy(bigBlockBuffer
, buffer
, This
->bigBlockSize
);
3080 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
3088 void* StorageImpl_GetROBigBlock(
3092 return BIGBLOCKFILE_GetROBigBlock(This
->bigBlockFile
, blockIndex
);
3095 void* StorageImpl_GetBigBlock(
3099 return BIGBLOCKFILE_GetBigBlock(This
->bigBlockFile
, blockIndex
);
3102 void StorageImpl_ReleaseBigBlock(
3106 BIGBLOCKFILE_ReleaseBigBlock(This
->bigBlockFile
, pBigBlock
);
3109 /******************************************************************************
3110 * Storage32Impl_SmallBlocksToBigBlocks
3112 * This method will convert a small block chain to a big block chain.
3113 * The small block chain will be destroyed.
3115 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3117 SmallBlockChainStream
** ppsbChain
)
3119 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3120 ULARGE_INTEGER size
, offset
;
3121 ULONG cbRead
, cbWritten
, cbTotalRead
, cbTotalWritten
;
3122 ULONG propertyIndex
;
3123 BOOL successRead
, successWrite
;
3124 StgProperty chainProperty
;
3125 BYTE buffer
[DEF_SMALL_BLOCK_SIZE
];
3126 BlockChainStream
*bbTempChain
= NULL
;
3127 BlockChainStream
*bigBlockChain
= NULL
;
3130 * Create a temporary big block chain that doesn't have
3131 * an associated property. This temporary chain will be
3132 * used to copy data from small blocks to big blocks.
3134 bbTempChain
= BlockChainStream_Construct(This
,
3139 * Grow the big block chain.
3141 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3142 BlockChainStream_SetSize(bbTempChain
, size
);
3145 * Copy the contents of the small block chain to the big block chain
3146 * by small block size increments.
3149 offset
.HighPart
= 0;
3155 successRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3160 cbTotalRead
+= cbRead
;
3162 successWrite
= BlockChainStream_WriteAt(bbTempChain
,
3167 cbTotalWritten
+= cbWritten
;
3169 offset
.LowPart
+= This
->smallBlockSize
;
3171 } while (successRead
&& successWrite
);
3173 assert(cbTotalRead
== cbTotalWritten
);
3176 * Destroy the small block chain.
3178 propertyIndex
= (*ppsbChain
)->ownerPropertyIndex
;
3181 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3182 SmallBlockChainStream_Destroy(*ppsbChain
);
3186 * Change the property information. This chain is now a big block chain
3187 * and it doesn't reside in the small blocks chain anymore.
3189 StorageImpl_ReadProperty(This
, propertyIndex
, &chainProperty
);
3191 chainProperty
.startingBlock
= bbHeadOfChain
;
3193 StorageImpl_WriteProperty(This
, propertyIndex
, &chainProperty
);
3196 * Destroy the temporary propertyless big block chain.
3197 * Create a new big block chain associated with this property.
3199 BlockChainStream_Destroy(bbTempChain
);
3200 bigBlockChain
= BlockChainStream_Construct(This
,
3204 return bigBlockChain
;
3207 /******************************************************************************
3208 ** Storage32InternalImpl implementation
3211 StorageInternalImpl
* StorageInternalImpl_Construct(
3212 StorageImpl
* ancestorStorage
,
3213 ULONG rootPropertyIndex
)
3215 StorageInternalImpl
* newStorage
;
3218 * Allocate space for the new storage object
3220 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl
));
3224 memset(newStorage
, 0, sizeof(StorageInternalImpl
));
3227 * Initialize the virtual function table.
3229 newStorage
->lpvtbl
= &Storage32InternalImpl_Vtbl
;
3230 newStorage
->v_destructor
= &StorageInternalImpl_Destroy
;
3233 * Keep the ancestor storage pointer and nail a reference to it.
3235 newStorage
->ancestorStorage
= ancestorStorage
;
3236 StorageBaseImpl_AddRef((IStorage
*)(newStorage
->ancestorStorage
));
3239 * Keep the index of the root property set for this storage,
3241 newStorage
->rootPropertySetIndex
= rootPropertyIndex
;
3249 void StorageInternalImpl_Destroy(
3250 StorageInternalImpl
* This
)
3252 StorageBaseImpl_Release((IStorage
*)This
->ancestorStorage
);
3253 HeapFree(GetProcessHeap(), 0, This
);
3256 /******************************************************************************
3258 ** Storage32InternalImpl_Commit
3260 ** The non-root storages cannot be opened in transacted mode thus this function
3263 HRESULT WINAPI
StorageInternalImpl_Commit(
3265 DWORD grfCommitFlags
) /* [in] */
3270 /******************************************************************************
3272 ** Storage32InternalImpl_Revert
3274 ** The non-root storages cannot be opened in transacted mode thus this function
3277 HRESULT WINAPI
StorageInternalImpl_Revert(
3283 /******************************************************************************
3284 ** IEnumSTATSTGImpl implementation
3287 IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
3288 StorageImpl
* parentStorage
,
3289 ULONG firstPropertyNode
)
3291 IEnumSTATSTGImpl
* newEnumeration
;
3293 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
3295 if (newEnumeration
!=0)
3298 * Set-up the virtual function table and reference count.
3300 newEnumeration
->lpvtbl
= &IEnumSTATSTGImpl_Vtbl
;
3301 newEnumeration
->ref
= 0;
3304 * We want to nail-down the reference to the storage in case the
3305 * enumeration out-lives the storage in the client application.
3307 newEnumeration
->parentStorage
= parentStorage
;
3308 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
3310 newEnumeration
->firstPropertyNode
= firstPropertyNode
;
3313 * Initialize the search stack
3315 newEnumeration
->stackSize
= 0;
3316 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
3317 newEnumeration
->stackToVisit
=
3318 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
)*ENUMSTATSGT_SIZE_INCREMENT
);
3321 * Make sure the current node of the iterator is the first one.
3323 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
3326 return newEnumeration
;
3329 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3331 IStorage_Release((IStorage
*)This
->parentStorage
);
3332 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3333 HeapFree(GetProcessHeap(), 0, This
);
3336 HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3337 IEnumSTATSTG
* iface
,
3341 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3344 * Perform a sanity check on the parameters.
3347 return E_INVALIDARG
;
3350 * Initialize the return parameter.
3355 * Compare the riid with the interface IDs implemented by this object.
3357 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
3359 *ppvObject
= (IEnumSTATSTG
*)This
;
3361 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IEnumSTATSTG
)) == 0)
3363 *ppvObject
= (IEnumSTATSTG
*)This
;
3367 * Check that we obtained an interface.
3369 if ((*ppvObject
)==0)
3370 return E_NOINTERFACE
;
3373 * Query Interface always increases the reference count by one when it is
3376 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG
*)This
);
3381 ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3382 IEnumSTATSTG
* iface
)
3384 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3390 ULONG WINAPI
IEnumSTATSTGImpl_Release(
3391 IEnumSTATSTG
* iface
)
3393 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3401 * If the reference count goes down to 0, perform suicide.
3405 IEnumSTATSTGImpl_Destroy(This
);
3411 HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3412 IEnumSTATSTG
* iface
,
3415 ULONG
* pceltFetched
)
3417 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3419 StgProperty currentProperty
;
3420 STATSTG
* currentReturnStruct
= rgelt
;
3421 ULONG objectFetched
= 0;
3422 ULONG currentSearchNode
;
3425 * Perform a sanity check on the parameters.
3427 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3428 return E_INVALIDARG
;
3431 * To avoid the special case, get another pointer to a ULONG value if
3432 * the caller didn't supply one.
3434 if (pceltFetched
==0)
3435 pceltFetched
= &objectFetched
;
3438 * Start the iteration, we will iterate until we hit the end of the
3439 * linked list or until we hit the number of items to iterate through
3444 * Start with the node at the top of the stack.
3446 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3448 while ( ( *pceltFetched
< celt
) &&
3449 ( currentSearchNode
!=PROPERTY_NULL
) )
3452 * Remove the top node from the stack
3454 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3457 * Read the property from the storage.
3459 StorageImpl_ReadProperty(This
->parentStorage
,
3464 * Copy the information to the return buffer.
3466 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct
,
3471 * Step to the next item in the iteration
3474 currentReturnStruct
++;
3477 * Push the next search node in the search stack.
3479 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3482 * continue the iteration.
3484 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3487 if (*pceltFetched
== celt
)
3494 HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3495 IEnumSTATSTG
* iface
,
3498 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3500 StgProperty currentProperty
;
3501 ULONG objectFetched
= 0;
3502 ULONG currentSearchNode
;
3505 * Start with the node at the top of the stack.
3507 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3509 while ( (objectFetched
< celt
) &&
3510 (currentSearchNode
!=PROPERTY_NULL
) )
3513 * Remove the top node from the stack
3515 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3518 * Read the property from the storage.
3520 StorageImpl_ReadProperty(This
->parentStorage
,
3525 * Step to the next item in the iteration
3530 * Push the next search node in the search stack.
3532 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3535 * continue the iteration.
3537 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3540 if (objectFetched
== celt
)
3546 HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3547 IEnumSTATSTG
* iface
)
3549 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3551 StgProperty rootProperty
;
3555 * Re-initialize the search stack to an empty stack
3557 This
->stackSize
= 0;
3560 * Read the root property from the storage.
3562 readSucessful
= StorageImpl_ReadProperty(
3563 This
->parentStorage
,
3564 This
->firstPropertyNode
,
3569 assert(rootProperty
.sizeOfNameString
!=0);
3572 * Push the search node in the search stack.
3574 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.dirProperty
);
3580 HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3581 IEnumSTATSTG
* iface
,
3582 IEnumSTATSTG
** ppenum
)
3584 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3586 IEnumSTATSTGImpl
* newClone
;
3589 * Perform a sanity check on the parameters.
3592 return E_INVALIDARG
;
3594 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3595 This
->firstPropertyNode
);
3599 * The new clone enumeration must point to the same current node as
3602 newClone
->stackSize
= This
->stackSize
;
3603 newClone
->stackMaxSize
= This
->stackMaxSize
;
3604 newClone
->stackToVisit
=
3605 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3608 newClone
->stackToVisit
,
3610 sizeof(ULONG
) * newClone
->stackSize
);
3612 *ppenum
= (IEnumSTATSTG
*)newClone
;
3615 * Don't forget to nail down a reference to the clone before
3618 IEnumSTATSTGImpl_AddRef(*ppenum
);
3623 INT
IEnumSTATSTGImpl_FindParentProperty(
3624 IEnumSTATSTGImpl
*This
,
3625 ULONG childProperty
,
3626 StgProperty
*currentProperty
,
3629 ULONG currentSearchNode
;
3633 * To avoid the special case, get another pointer to a ULONG value if
3634 * the caller didn't supply one.
3637 thisNodeId
= &foundNode
;
3640 * Start with the node at the top of the stack.
3642 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3645 while (currentSearchNode
!=PROPERTY_NULL
)
3648 * Store the current node in the returned parameters
3650 *thisNodeId
= currentSearchNode
;
3653 * Remove the top node from the stack
3655 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3658 * Read the property from the storage.
3660 StorageImpl_ReadProperty(
3661 This
->parentStorage
,
3665 if (currentProperty
->previousProperty
== childProperty
)
3666 return PROPERTY_RELATION_PREVIOUS
;
3668 else if (currentProperty
->nextProperty
== childProperty
)
3669 return PROPERTY_RELATION_NEXT
;
3671 else if (currentProperty
->dirProperty
== childProperty
)
3672 return PROPERTY_RELATION_DIR
;
3675 * Push the next search node in the search stack.
3677 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3680 * continue the iteration.
3682 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3685 return PROPERTY_NULL
;
3688 ULONG
IEnumSTATSTGImpl_FindProperty(
3689 IEnumSTATSTGImpl
* This
,
3690 const OLECHAR
* lpszPropName
,
3691 StgProperty
* currentProperty
)
3693 ULONG currentSearchNode
;
3696 * Start with the node at the top of the stack.
3698 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3700 while (currentSearchNode
!=PROPERTY_NULL
)
3703 * Remove the top node from the stack
3705 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3708 * Read the property from the storage.
3710 StorageImpl_ReadProperty(This
->parentStorage
,
3714 if ( propertyNameCmp(
3715 (OLECHAR
*)currentProperty
->name
,
3716 (OLECHAR
*)lpszPropName
) == 0)
3717 return currentSearchNode
;
3720 * Push the next search node in the search stack.
3722 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3725 * continue the iteration.
3727 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3730 return PROPERTY_NULL
;
3733 void IEnumSTATSTGImpl_PushSearchNode(
3734 IEnumSTATSTGImpl
* This
,
3737 StgProperty rootProperty
;
3741 * First, make sure we're not trying to push an unexisting node.
3743 if (nodeToPush
==PROPERTY_NULL
)
3747 * First push the node to the stack
3749 if (This
->stackSize
== This
->stackMaxSize
)
3751 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
3753 This
->stackToVisit
= HeapReAlloc(
3757 sizeof(ULONG
) * This
->stackMaxSize
);
3760 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
3764 * Read the root property from the storage.
3766 readSucessful
= StorageImpl_ReadProperty(
3767 This
->parentStorage
,
3773 assert(rootProperty
.sizeOfNameString
!=0);
3776 * Push the previous search node in the search stack.
3778 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.previousProperty
);
3782 ULONG
IEnumSTATSTGImpl_PopSearchNode(
3783 IEnumSTATSTGImpl
* This
,
3788 if (This
->stackSize
== 0)
3789 return PROPERTY_NULL
;
3791 topNode
= This
->stackToVisit
[This
->stackSize
-1];
3799 /******************************************************************************
3800 ** StorageUtl implementation
3803 void StorageUtl_ReadWord(void* buffer
, ULONG offset
, WORD
* value
)
3805 memcpy(value
, (BYTE
*)buffer
+offset
, sizeof(WORD
));
3808 void StorageUtl_WriteWord(void* buffer
, ULONG offset
, WORD value
)
3810 memcpy((BYTE
*)buffer
+offset
, &value
, sizeof(WORD
));
3813 void StorageUtl_ReadDWord(void* buffer
, ULONG offset
, DWORD
* value
)
3815 memcpy(value
, (BYTE
*)buffer
+offset
, sizeof(DWORD
));
3818 void StorageUtl_WriteDWord(void* buffer
, ULONG offset
, DWORD value
)
3820 memcpy((BYTE
*)buffer
+offset
, &value
, sizeof(DWORD
));
3823 void StorageUtl_ReadGUID(void* buffer
, ULONG offset
, GUID
* value
)
3825 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
3826 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
3827 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
3829 memcpy(value
->Data4
, (BYTE
*)buffer
+offset
+8, sizeof(value
->Data4
));
3832 void StorageUtl_WriteGUID(void* buffer
, ULONG offset
, GUID
* value
)
3834 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
3835 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
3836 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
3838 memcpy((BYTE
*)buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
3841 void StorageUtl_CopyPropertyToSTATSTG(
3842 STATSTG
* destination
,
3843 StgProperty
* source
,
3847 * The copy of the string occurs only when the flag is not set
3849 if ((statFlags
& STATFLAG_NONAME
) != 0)
3851 destination
->pwcsName
= 0;
3855 destination
->pwcsName
=
3856 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
3858 lstrcpyW((LPWSTR
)destination
->pwcsName
, source
->name
);
3861 switch (source
->propertyType
)
3863 case PROPTYPE_STORAGE
:
3865 destination
->type
= STGTY_STORAGE
;
3867 case PROPTYPE_STREAM
:
3868 destination
->type
= STGTY_STREAM
;
3871 destination
->type
= STGTY_STREAM
;
3875 destination
->cbSize
= source
->size
;
3877 currentReturnStruct->mtime = {0}; TODO
3878 currentReturnStruct->ctime = {0};
3879 currentReturnStruct->atime = {0};
3881 destination
->grfMode
= 0;
3882 destination
->grfLocksSupported
= 0;
3883 destination
->clsid
= source
->propertyUniqueID
;
3884 destination
->grfStateBits
= 0;
3885 destination
->reserved
= 0;
3888 /******************************************************************************
3889 ** BlockChainStream implementation
3892 BlockChainStream
* BlockChainStream_Construct(
3893 StorageImpl
* parentStorage
,
3894 ULONG
* headOfStreamPlaceHolder
,
3895 ULONG propertyIndex
)
3897 BlockChainStream
* newStream
;
3900 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
3902 newStream
->parentStorage
= parentStorage
;
3903 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
3904 newStream
->ownerPropertyIndex
= propertyIndex
;
3905 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
3906 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
3907 newStream
->numBlocks
= 0;
3909 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
3911 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
3913 newStream
->numBlocks
++;
3914 newStream
->tailIndex
= blockIndex
;
3916 blockIndex
= StorageImpl_GetNextBlockInChain(
3924 void BlockChainStream_Destroy(BlockChainStream
* This
)
3926 HeapFree(GetProcessHeap(), 0, This
);
3929 /******************************************************************************
3930 * BlockChainStream_GetHeadOfChain
3932 * Returns the head of this stream chain.
3933 * Some special chains don't have properties, their heads are kept in
3934 * This->headOfStreamPlaceHolder.
3937 ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
3939 StgProperty chainProperty
;
3942 if (This
->headOfStreamPlaceHolder
!= 0)
3943 return *(This
->headOfStreamPlaceHolder
);
3945 if (This
->ownerPropertyIndex
!= PROPERTY_NULL
)
3947 readSucessful
= StorageImpl_ReadProperty(
3948 This
->parentStorage
,
3949 This
->ownerPropertyIndex
,
3954 return chainProperty
.startingBlock
;
3958 return BLOCK_END_OF_CHAIN
;
3961 /******************************************************************************
3962 * BlockChainStream_GetCount
3964 * Returns the number of blocks that comprises this chain.
3965 * This is not the size of the stream as the last block may not be full!
3968 ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
3973 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
3975 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
3979 blockIndex
= StorageImpl_GetNextBlockInChain(
3980 This
->parentStorage
,
3987 /******************************************************************************
3988 * BlockChainStream_ReadAt
3990 * Reads a specified number of bytes from this chain at the specified offset.
3991 * bytesRead may be NULL.
3992 * Failure will be returned if the specified number of bytes has not been read.
3994 BOOL
BlockChainStream_ReadAt(BlockChainStream
* This
,
3995 ULARGE_INTEGER offset
,
4000 ULONG blockNoInSequence
= offset
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4001 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->bigBlockSize
;
4002 ULONG bytesToReadInBuffer
;
4005 BYTE
* bigBlockBuffer
;
4007 if (This
->lastBlockNoInSequence
== 0xFFFFFFFF)
4008 This
->lastBlockNoInSequence
= blockNoInSequence
;
4010 * Find the first block in the stream that contains part of the buffer.
4012 if (blockNoInSequence
> This
->lastBlockNoInSequence
)
4014 ULONG temp
= blockNoInSequence
;
4016 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4017 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4018 This
->lastBlockNoInSequence
= temp
;
4022 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4023 This
->lastBlockNoInSequence
= blockNoInSequence
;
4026 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4029 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4031 blockNoInSequence
--;
4034 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4037 * Start reading the buffer.
4040 bufferWalker
= buffer
;
4042 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4045 * Calculate how many bytes we can copy from this big block.
4047 bytesToReadInBuffer
=
4048 MIN(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4051 * Copy those bytes to the buffer
4054 StorageImpl_GetROBigBlock(This
->parentStorage
, blockIndex
);
4056 memcpy(bufferWalker
, bigBlockBuffer
+ offsetInBlock
, bytesToReadInBuffer
);
4058 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4061 * Step to the next big block.
4064 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4066 bufferWalker
+= bytesToReadInBuffer
;
4067 size
-= bytesToReadInBuffer
;
4068 *bytesRead
+= bytesToReadInBuffer
;
4069 offsetInBlock
= 0; /* There is no offset on the next block */
4076 /******************************************************************************
4077 * BlockChainStream_WriteAt
4079 * Writes the specified number of bytes to this chain at the specified offset.
4080 * bytesWritten may be NULL.
4081 * Will fail if not all specified number of bytes have been written.
4083 BOOL
BlockChainStream_WriteAt(BlockChainStream
* This
,
4084 ULARGE_INTEGER offset
,
4087 ULONG
* bytesWritten
)
4089 ULONG blockNoInSequence
= offset
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4090 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->bigBlockSize
;
4094 BYTE
* bigBlockBuffer
;
4096 if (This
->lastBlockNoInSequence
== 0xFFFFFFFF)
4097 This
->lastBlockNoInSequence
= blockNoInSequence
;
4100 * Find the first block in the stream that contains part of the buffer.
4102 if (blockNoInSequence
> This
->lastBlockNoInSequence
)
4104 ULONG temp
= blockNoInSequence
;
4106 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4107 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4108 This
->lastBlockNoInSequence
= temp
;
4112 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4113 This
->lastBlockNoInSequence
= blockNoInSequence
;
4116 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4119 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4121 blockNoInSequence
--;
4124 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4127 * Here, I'm casting away the constness on the buffer variable
4128 * This is OK since we don't intend to modify that buffer.
4131 bufferWalker
= (BYTE
*)buffer
;
4133 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4136 * Calculate how many bytes we can copy from this big block.
4139 MIN(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4142 * Copy those bytes to the buffer
4144 bigBlockBuffer
= StorageImpl_GetBigBlock(This
->parentStorage
, blockIndex
);
4146 memcpy(bigBlockBuffer
+ offsetInBlock
, bufferWalker
, bytesToWrite
);
4148 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4151 * Step to the next big block.
4154 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4156 bufferWalker
+= bytesToWrite
;
4157 size
-= bytesToWrite
;
4158 *bytesWritten
+= bytesToWrite
;
4159 offsetInBlock
= 0; /* There is no offset on the next block */
4165 /******************************************************************************
4166 * BlockChainStream_Shrink
4168 * Shrinks this chain in the big block depot.
4170 BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4171 ULARGE_INTEGER newSize
)
4173 ULONG blockIndex
, extraBlock
;
4178 * Figure out how many blocks are needed to contain the new size
4180 numBlocks
= newSize
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4182 if ((newSize
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4185 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4188 * Go to the new end of chain
4190 while (count
< numBlocks
)
4193 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4198 /* Get the next block before marking the new end */
4200 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4202 /* Mark the new end of chain */
4203 StorageImpl_SetNextBlockInChain(
4204 This
->parentStorage
,
4206 BLOCK_END_OF_CHAIN
);
4208 This
->tailIndex
= blockIndex
;
4209 This
->numBlocks
= numBlocks
;
4212 * Mark the extra blocks as free
4214 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4217 StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
);
4219 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4220 extraBlock
= blockIndex
;
4226 /******************************************************************************
4227 * BlockChainStream_Enlarge
4229 * Grows this chain in the big block depot.
4231 BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4232 ULARGE_INTEGER newSize
)
4234 ULONG blockIndex
, currentBlock
;
4236 ULONG oldNumBlocks
= 0;
4238 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4241 * Empty chain. Create the head.
4243 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4245 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4246 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4248 BLOCK_END_OF_CHAIN
);
4250 if (This
->headOfStreamPlaceHolder
!= 0)
4252 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4256 StgProperty chainProp
;
4257 assert(This
->ownerPropertyIndex
!= PROPERTY_NULL
);
4259 StorageImpl_ReadProperty(
4260 This
->parentStorage
,
4261 This
->ownerPropertyIndex
,
4264 chainProp
.startingBlock
= blockIndex
;
4266 StorageImpl_WriteProperty(
4267 This
->parentStorage
,
4268 This
->ownerPropertyIndex
,
4272 This
->tailIndex
= blockIndex
;
4273 This
->numBlocks
= 1;
4277 * Figure out how many blocks are needed to contain this stream
4279 newNumBlocks
= newSize
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4281 if ((newSize
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4285 * Go to the current end of chain
4287 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4289 currentBlock
= blockIndex
;
4291 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4294 currentBlock
= blockIndex
;
4297 StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
);
4300 This
->tailIndex
= currentBlock
;
4303 currentBlock
= This
->tailIndex
;
4304 oldNumBlocks
= This
->numBlocks
;
4307 * Add new blocks to the chain
4309 while (oldNumBlocks
< newNumBlocks
)
4311 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4313 StorageImpl_SetNextBlockInChain(
4314 This
->parentStorage
,
4318 StorageImpl_SetNextBlockInChain(
4319 This
->parentStorage
,
4321 BLOCK_END_OF_CHAIN
);
4323 currentBlock
= blockIndex
;
4327 This
->tailIndex
= blockIndex
;
4328 This
->numBlocks
= newNumBlocks
;
4333 /******************************************************************************
4334 * BlockChainStream_SetSize
4336 * Sets the size of this stream. The big block depot will be updated.
4337 * The file will grow if we grow the chain.
4339 * TODO: Free the actual blocks in the file when we shrink the chain.
4340 * Currently, the blocks are still in the file. So the file size
4341 * doesn't shrink even if we shrink streams.
4343 BOOL
BlockChainStream_SetSize(
4344 BlockChainStream
* This
,
4345 ULARGE_INTEGER newSize
)
4347 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4349 if (newSize
.LowPart
== size
.LowPart
)
4352 if (newSize
.LowPart
< size
.LowPart
)
4354 BlockChainStream_Shrink(This
, newSize
);
4358 ULARGE_INTEGER fileSize
=
4359 BIGBLOCKFILE_GetSize(This
->parentStorage
->bigBlockFile
);
4361 ULONG diff
= newSize
.LowPart
- size
.LowPart
;
4364 * Make sure the file stays a multiple of blocksize
4366 if ((diff
% This
->parentStorage
->bigBlockSize
) != 0)
4367 diff
+= (This
->parentStorage
->bigBlockSize
-
4368 (diff
% This
->parentStorage
->bigBlockSize
) );
4370 fileSize
.LowPart
+= diff
;
4371 BIGBLOCKFILE_SetSize(This
->parentStorage
->bigBlockFile
, fileSize
);
4373 BlockChainStream_Enlarge(This
, newSize
);
4379 /******************************************************************************
4380 * BlockChainStream_GetSize
4382 * Returns the size of this chain.
4383 * Will return the block count if this chain doesn't have a property.
4385 ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4387 StgProperty chainProperty
;
4389 if(This
->headOfStreamPlaceHolder
== NULL
)
4392 * This chain is a data stream read the property and return
4393 * the appropriate size
4395 StorageImpl_ReadProperty(
4396 This
->parentStorage
,
4397 This
->ownerPropertyIndex
,
4400 return chainProperty
.size
;
4405 * this chain is a chain that does not have a property, figure out the
4406 * size by making the product number of used blocks times the
4409 ULARGE_INTEGER result
;
4410 result
.HighPart
= 0;
4413 BlockChainStream_GetCount(This
) *
4414 This
->parentStorage
->bigBlockSize
;
4420 /******************************************************************************
4421 ** SmallBlockChainStream implementation
4424 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4425 StorageImpl
* parentStorage
,
4426 ULONG propertyIndex
)
4428 SmallBlockChainStream
* newStream
;
4430 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4432 newStream
->parentStorage
= parentStorage
;
4433 newStream
->ownerPropertyIndex
= propertyIndex
;
4438 void SmallBlockChainStream_Destroy(
4439 SmallBlockChainStream
* This
)
4441 HeapFree(GetProcessHeap(), 0, This
);
4444 /******************************************************************************
4445 * SmallBlockChainStream_GetHeadOfChain
4447 * Returns the head of this chain of small blocks.
4449 ULONG
SmallBlockChainStream_GetHeadOfChain(
4450 SmallBlockChainStream
* This
)
4452 StgProperty chainProperty
;
4455 if (This
->ownerPropertyIndex
)
4457 readSucessful
= StorageImpl_ReadProperty(
4458 This
->parentStorage
,
4459 This
->ownerPropertyIndex
,
4464 return chainProperty
.startingBlock
;
4469 return BLOCK_END_OF_CHAIN
;
4472 /******************************************************************************
4473 * SmallBlockChainStream_GetNextBlockInChain
4475 * Returns the index of the next small block in this chain.
4478 * - BLOCK_END_OF_CHAIN: end of this chain
4479 * - BLOCK_UNUSED: small block 'blockIndex' is free
4481 ULONG
SmallBlockChainStream_GetNextBlockInChain(
4482 SmallBlockChainStream
* This
,
4485 ULARGE_INTEGER offsetOfBlockInDepot
;
4487 ULONG nextBlockInChain
= BLOCK_END_OF_CHAIN
;
4491 offsetOfBlockInDepot
.HighPart
= 0;
4492 offsetOfBlockInDepot
.LowPart
= blockIndex
* sizeof(ULONG
);
4495 * Read those bytes in the buffer from the small block file.
4497 success
= BlockChainStream_ReadAt(
4498 This
->parentStorage
->smallBlockDepotChain
,
4499 offsetOfBlockInDepot
,
4506 StorageUtl_ReadDWord(&buffer
, 0, &nextBlockInChain
);
4509 return nextBlockInChain
;
4512 /******************************************************************************
4513 * SmallBlockChainStream_SetNextBlockInChain
4515 * Writes the index of the next block of the specified block in the small
4517 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4518 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4520 void SmallBlockChainStream_SetNextBlockInChain(
4521 SmallBlockChainStream
* This
,
4525 ULARGE_INTEGER offsetOfBlockInDepot
;
4529 offsetOfBlockInDepot
.HighPart
= 0;
4530 offsetOfBlockInDepot
.LowPart
= blockIndex
* sizeof(ULONG
);
4532 StorageUtl_WriteDWord(&buffer
, 0, nextBlock
);
4535 * Read those bytes in the buffer from the small block file.
4537 BlockChainStream_WriteAt(
4538 This
->parentStorage
->smallBlockDepotChain
,
4539 offsetOfBlockInDepot
,
4545 /******************************************************************************
4546 * SmallBlockChainStream_FreeBlock
4548 * Flag small block 'blockIndex' as free in the small block depot.
4550 void SmallBlockChainStream_FreeBlock(
4551 SmallBlockChainStream
* This
,
4554 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4557 /******************************************************************************
4558 * SmallBlockChainStream_GetNextFreeBlock
4560 * Returns the index of a free small block. The small block depot will be
4561 * enlarged if necessary. The small block chain will also be enlarged if
4564 ULONG
SmallBlockChainStream_GetNextFreeBlock(
4565 SmallBlockChainStream
* This
)
4567 ULARGE_INTEGER offsetOfBlockInDepot
;
4570 ULONG blockIndex
= 0;
4571 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
4572 BOOL success
= TRUE
;
4573 ULONG smallBlocksPerBigBlock
;
4575 offsetOfBlockInDepot
.HighPart
= 0;
4578 * Scan the small block depot for a free block
4580 while (nextBlockIndex
!= BLOCK_UNUSED
)
4582 offsetOfBlockInDepot
.LowPart
= blockIndex
* sizeof(ULONG
);
4584 success
= BlockChainStream_ReadAt(
4585 This
->parentStorage
->smallBlockDepotChain
,
4586 offsetOfBlockInDepot
,
4592 * If we run out of space for the small block depot, enlarge it
4596 StorageUtl_ReadDWord(&buffer
, 0, &nextBlockIndex
);
4598 if (nextBlockIndex
!= BLOCK_UNUSED
)
4604 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
4606 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
4607 ULONG nextBlock
, newsbdIndex
;
4608 BYTE
* smallBlockDepot
;
4610 nextBlock
= sbdIndex
;
4611 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
4613 sbdIndex
= nextBlock
;
4615 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
);
4618 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4619 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
4620 StorageImpl_SetNextBlockInChain(
4621 This
->parentStorage
,
4625 StorageImpl_SetNextBlockInChain(
4626 This
->parentStorage
,
4628 BLOCK_END_OF_CHAIN
);
4631 * Initialize all the small blocks to free
4634 StorageImpl_GetBigBlock(This
->parentStorage
, newsbdIndex
);
4636 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
4637 StorageImpl_ReleaseBigBlock(This
->parentStorage
, smallBlockDepot
);
4642 * We have just created the small block depot.
4644 StgProperty rootProp
;
4648 * Save it in the header
4650 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
4651 StorageImpl_SaveFileHeader(This
->parentStorage
);
4654 * And allocate the first big block that will contain small blocks
4657 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4659 StorageImpl_SetNextBlockInChain(
4660 This
->parentStorage
,
4662 BLOCK_END_OF_CHAIN
);
4664 StorageImpl_ReadProperty(
4665 This
->parentStorage
,
4666 This
->parentStorage
->rootPropertySetIndex
,
4669 rootProp
.startingBlock
= sbStartIndex
;
4670 rootProp
.size
.HighPart
= 0;
4671 rootProp
.size
.LowPart
= This
->parentStorage
->bigBlockSize
;
4673 StorageImpl_WriteProperty(
4674 This
->parentStorage
,
4675 This
->parentStorage
->rootPropertySetIndex
,
4681 smallBlocksPerBigBlock
=
4682 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
4685 * Verify if we have to allocate big blocks to contain small blocks
4687 if (blockIndex
% smallBlocksPerBigBlock
== 0)
4689 StgProperty rootProp
;
4690 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
4692 StorageImpl_ReadProperty(
4693 This
->parentStorage
,
4694 This
->parentStorage
->rootPropertySetIndex
,
4697 if (rootProp
.size
.LowPart
<
4698 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
4700 rootProp
.size
.LowPart
+= This
->parentStorage
->bigBlockSize
;
4702 BlockChainStream_SetSize(
4703 This
->parentStorage
->smallBlockRootChain
,
4706 StorageImpl_WriteProperty(
4707 This
->parentStorage
,
4708 This
->parentStorage
->rootPropertySetIndex
,
4716 /******************************************************************************
4717 * SmallBlockChainStream_ReadAt
4719 * Reads a specified number of bytes from this chain at the specified offset.
4720 * bytesRead may be NULL.
4721 * Failure will be returned if the specified number of bytes has not been read.
4723 BOOL
SmallBlockChainStream_ReadAt(
4724 SmallBlockChainStream
* This
,
4725 ULARGE_INTEGER offset
,
4730 ULARGE_INTEGER offsetInBigBlockFile
;
4731 ULONG blockNoInSequence
=
4732 offset
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4734 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->smallBlockSize
;
4735 ULONG bytesToReadInBuffer
;
4737 ULONG bytesReadFromBigBlockFile
;
4741 * This should never happen on a small block file.
4743 assert(offset
.HighPart
==0);
4746 * Find the first block in the stream that contains part of the buffer.
4748 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4750 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4752 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4754 blockNoInSequence
--;
4758 * Start reading the buffer.
4761 bufferWalker
= buffer
;
4763 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4766 * Calculate how many bytes we can copy from this small block.
4768 bytesToReadInBuffer
=
4769 MIN(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
4772 * Calculate the offset of the small block in the small block file.
4774 offsetInBigBlockFile
.HighPart
= 0;
4775 offsetInBigBlockFile
.LowPart
=
4776 blockIndex
* This
->parentStorage
->smallBlockSize
;
4778 offsetInBigBlockFile
.LowPart
+= offsetInBlock
;
4781 * Read those bytes in the buffer from the small block file.
4783 BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
4784 offsetInBigBlockFile
,
4785 bytesToReadInBuffer
,
4787 &bytesReadFromBigBlockFile
);
4789 assert(bytesReadFromBigBlockFile
== bytesToReadInBuffer
);
4792 * Step to the next big block.
4794 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4795 bufferWalker
+= bytesToReadInBuffer
;
4796 size
-= bytesToReadInBuffer
;
4797 *bytesRead
+= bytesToReadInBuffer
;
4798 offsetInBlock
= 0; /* There is no offset on the next block */
4804 /******************************************************************************
4805 * SmallBlockChainStream_WriteAt
4807 * Writes the specified number of bytes to this chain at the specified offset.
4808 * bytesWritten may be NULL.
4809 * Will fail if not all specified number of bytes have been written.
4811 BOOL
SmallBlockChainStream_WriteAt(
4812 SmallBlockChainStream
* This
,
4813 ULARGE_INTEGER offset
,
4816 ULONG
* bytesWritten
)
4818 ULARGE_INTEGER offsetInBigBlockFile
;
4819 ULONG blockNoInSequence
=
4820 offset
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4822 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->smallBlockSize
;
4823 ULONG bytesToWriteInBuffer
;
4825 ULONG bytesWrittenFromBigBlockFile
;
4829 * This should never happen on a small block file.
4831 assert(offset
.HighPart
==0);
4834 * Find the first block in the stream that contains part of the buffer.
4836 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4838 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4840 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4842 blockNoInSequence
--;
4846 * Start writing the buffer.
4848 * Here, I'm casting away the constness on the buffer variable
4849 * This is OK since we don't intend to modify that buffer.
4852 bufferWalker
= (BYTE
*)buffer
;
4853 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4856 * Calculate how many bytes we can copy to this small block.
4858 bytesToWriteInBuffer
=
4859 MIN(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
4862 * Calculate the offset of the small block in the small block file.
4864 offsetInBigBlockFile
.HighPart
= 0;
4865 offsetInBigBlockFile
.LowPart
=
4866 blockIndex
* This
->parentStorage
->smallBlockSize
;
4868 offsetInBigBlockFile
.LowPart
+= offsetInBlock
;
4871 * Write those bytes in the buffer to the small block file.
4873 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockRootChain
,
4874 offsetInBigBlockFile
,
4875 bytesToWriteInBuffer
,
4877 &bytesWrittenFromBigBlockFile
);
4879 assert(bytesWrittenFromBigBlockFile
== bytesToWriteInBuffer
);
4882 * Step to the next big block.
4884 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4885 bufferWalker
+= bytesToWriteInBuffer
;
4886 size
-= bytesToWriteInBuffer
;
4887 *bytesWritten
+= bytesToWriteInBuffer
;
4888 offsetInBlock
= 0; /* There is no offset on the next block */
4894 /******************************************************************************
4895 * SmallBlockChainStream_Shrink
4897 * Shrinks this chain in the small block depot.
4899 BOOL
SmallBlockChainStream_Shrink(
4900 SmallBlockChainStream
* This
,
4901 ULARGE_INTEGER newSize
)
4903 ULONG blockIndex
, extraBlock
;
4907 numBlocks
= newSize
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4909 if ((newSize
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
4912 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4915 * Go to the new end of chain
4917 while (count
< numBlocks
)
4919 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4923 /* Get the next block before marking the new end */
4924 extraBlock
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4926 /* Mark the new end of chain */
4927 SmallBlockChainStream_SetNextBlockInChain(
4930 BLOCK_END_OF_CHAIN
);
4933 * Mark the extra blocks as free
4935 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4937 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
);
4938 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
4939 extraBlock
= blockIndex
;
4945 /******************************************************************************
4946 * SmallBlockChainStream_Enlarge
4948 * Grows this chain in the small block depot.
4950 BOOL
SmallBlockChainStream_Enlarge(
4951 SmallBlockChainStream
* This
,
4952 ULARGE_INTEGER newSize
)
4954 ULONG blockIndex
, currentBlock
;
4956 ULONG oldNumBlocks
= 0;
4958 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4963 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4965 StgProperty chainProp
;
4967 StorageImpl_ReadProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
4970 chainProp
.startingBlock
= SmallBlockChainStream_GetNextFreeBlock(This
);
4972 StorageImpl_WriteProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
4975 blockIndex
= chainProp
.startingBlock
;
4976 SmallBlockChainStream_SetNextBlockInChain(
4979 BLOCK_END_OF_CHAIN
);
4982 currentBlock
= blockIndex
;
4985 * Figure out how many blocks are needed to contain this stream
4987 newNumBlocks
= newSize
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4989 if ((newSize
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
4993 * Go to the current end of chain
4995 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4998 currentBlock
= blockIndex
;
4999 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
);
5003 * Add new blocks to the chain
5005 while (oldNumBlocks
< newNumBlocks
)
5007 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5008 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
5010 SmallBlockChainStream_SetNextBlockInChain(
5013 BLOCK_END_OF_CHAIN
);
5015 currentBlock
= blockIndex
;
5022 /******************************************************************************
5023 * SmallBlockChainStream_GetCount
5025 * Returns the number of blocks that comprises this chain.
5026 * This is not the size of this chain as the last block may not be full!
5028 ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
5033 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5035 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5039 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
5045 /******************************************************************************
5046 * SmallBlockChainStream_SetSize
5048 * Sets the size of this stream.
5049 * The file will grow if we grow the chain.
5051 * TODO: Free the actual blocks in the file when we shrink the chain.
5052 * Currently, the blocks are still in the file. So the file size
5053 * doesn't shrink even if we shrink streams.
5055 BOOL
SmallBlockChainStream_SetSize(
5056 SmallBlockChainStream
* This
,
5057 ULARGE_INTEGER newSize
)
5059 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
5061 if (newSize
.LowPart
== size
.LowPart
)
5064 if (newSize
.LowPart
< size
.LowPart
)
5066 SmallBlockChainStream_Shrink(This
, newSize
);
5070 SmallBlockChainStream_Enlarge(This
, newSize
);
5076 /******************************************************************************
5077 * SmallBlockChainStream_GetSize
5079 * Returns the size of this chain.
5081 ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
5083 StgProperty chainProperty
;
5085 StorageImpl_ReadProperty(
5086 This
->parentStorage
,
5087 This
->ownerPropertyIndex
,
5090 return chainProperty
.size
;
5093 /******************************************************************************
5094 * StgCreateDocfile32 [OLE32.144]
5096 HRESULT WINAPI
StgCreateDocfile(
5100 IStorage
**ppstgOpen
)
5102 StorageImpl
* newStorage
= 0;
5103 HANDLE hFile
= INVALID_HANDLE_VALUE
;
5108 DWORD fileAttributes
;
5109 WCHAR tempFileName
[MAX_PATH
];
5112 * Validate the parameters
5115 return STG_E_INVALIDPOINTER
;
5118 * Validate the STGM flags
5120 if ( FAILED( validateSTGM(grfMode
) ))
5121 return STG_E_INVALIDFLAG
;
5124 * Generate a unique name.
5128 WCHAR tempPath
[MAX_PATH
];
5129 WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
5131 memset(tempPath
, 0, sizeof(tempPath
));
5132 memset(tempFileName
, 0, sizeof(tempFileName
));
5134 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
5137 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
5138 pwcsName
= tempFileName
;
5140 return STG_E_INSUFFICIENTMEMORY
;
5144 * Interpret the STGM value grfMode
5146 shareMode
= GetShareModeFromSTGM(grfMode
);
5147 accessMode
= GetAccessModeFromSTGM(grfMode
);
5148 creationMode
= GetCreationModeFromSTGM(grfMode
);
5150 if (grfMode
& STGM_DELETEONRELEASE
)
5151 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5153 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5155 if (grfMode
& STGM_TRANSACTED
)
5156 FIXME(ole
, "Transacted mode not implemented.\n");
5159 * Initialize the "out" parameter.
5163 hFile
= CreateFileW(pwcsName
,
5171 if (hFile
== INVALID_HANDLE_VALUE
)
5177 * Allocate and initialize the new IStorage32object.
5179 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5181 if (newStorage
== 0)
5182 return STG_E_INSUFFICIENTMEMORY
;
5184 hr
= StorageImpl_Construct(
5191 HeapFree(GetProcessHeap(), 0, newStorage
);
5196 * Get an "out" pointer for the caller.
5198 hr
= StorageBaseImpl_QueryInterface(
5199 (IStorage
*)newStorage
,
5200 (REFIID
)&IID_IStorage
,
5206 /******************************************************************************
5207 * StgOpenStorage32 [OLE32.148]
5209 HRESULT WINAPI
StgOpenStorage(
5210 const OLECHAR
*pwcsName
,
5211 IStorage
*pstgPriority
,
5215 IStorage
**ppstgOpen
)
5217 StorageImpl
* newStorage
= 0;
5224 * Perform a sanity check
5226 if (( pwcsName
== 0) || (ppstgOpen
== 0) )
5227 return STG_E_INVALIDPOINTER
;
5230 * Validate the STGM flags
5232 if ( FAILED( validateSTGM(grfMode
) ))
5233 return STG_E_INVALIDFLAG
;
5236 * Interpret the STGM value grfMode
5238 shareMode
= GetShareModeFromSTGM(grfMode
);
5239 accessMode
= GetAccessModeFromSTGM(grfMode
);
5242 * Initialize the "out" parameter.
5246 hFile
= CreateFileW( pwcsName
,
5251 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
5255 if (hFile
==INVALID_HANDLE_VALUE
)
5261 * Allocate and initialize the new IStorage32object.
5263 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5265 if (newStorage
== 0)
5266 return STG_E_INSUFFICIENTMEMORY
;
5268 hr
= StorageImpl_Construct(
5275 HeapFree(GetProcessHeap(), 0, newStorage
);
5280 * Get an "out" pointer for the caller.
5282 hr
= StorageBaseImpl_QueryInterface(
5283 (IStorage
*)newStorage
,
5284 (REFIID
)&IID_IStorage
,
5290 /******************************************************************************
5291 * WriteClassStg32 [OLE32.148]
5293 * This method will store the specified CLSID in the specified storage object
5295 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
5301 hRes
= IStorage_SetClass(pStg
, rclsid
);
5306 /*******************************************************************************************
5309 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5311 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
5321 * read a STATSTG structure (contains the clsid) from the storage
5323 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_DEFAULT
);
5326 *pclsid
=pstatstg
.clsid
;
5331 /*************************************************************************************
5334 * This function loads an object from stream
5336 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
5341 FIXME(ole
,"(),stub!\n");
5343 res
=ReadClassStm(pStm
,&clsid
);
5345 if (SUCCEEDED(res
)){
5347 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
5351 res
=IPersistStream_Load((IPersistStream
*)ppvObj
,pStm
);
5357 /************************************************************************************************
5360 * This function saves an object with the IPersistStream interface on it to the specified stream
5362 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
5368 TRACE(ole
,"(%p,%p)\n",pPStm
,pStm
);
5370 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
5372 if (SUCCEEDED(res
)){
5374 res
=WriteClassStm(pStm
,&clsid
);
5378 res
=IPersistStream_Save(pPStm
,pStm
,FALSE
);
5384 /****************************************************************************
5385 * This method validate a STGM parameter that can contain the values below
5387 * STGM_DIRECT 0x00000000
5388 * STGM_TRANSACTED 0x00010000
5389 * STGM_SIMPLE 0x08000000
5391 * STGM_READ 0x00000000
5392 * STGM_WRITE 0x00000001
5393 * STGM_READWRITE 0x00000002
5395 * STGM_SHARE_DENY_NONE 0x00000040
5396 * STGM_SHARE_DENY_READ 0x00000030
5397 * STGM_SHARE_DENY_WRITE 0x00000020
5398 * STGM_SHARE_EXCLUSIVE 0x00000010
5400 * STGM_PRIORITY 0x00040000
5401 * STGM_DELETEONRELEASE 0x04000000
5403 * STGM_CREATE 0x00001000
5404 * STGM_CONVERT 0x00020000
5405 * STGM_FAILIFTHERE 0x00000000
5407 * STGM_NOSCRATCH 0x00100000
5408 * STGM_NOSNAPSHOT 0x00200000
5410 static HRESULT
validateSTGM(DWORD stgm
)
5412 BOOL bSTGM_TRANSACTED
= ((stgm
& STGM_TRANSACTED
) == STGM_TRANSACTED
);
5413 BOOL bSTGM_SIMPLE
= ((stgm
& STGM_SIMPLE
) == STGM_SIMPLE
);
5414 BOOL bSTGM_DIRECT
= ! (bSTGM_TRANSACTED
|| bSTGM_SIMPLE
);
5416 BOOL bSTGM_WRITE
= ((stgm
& STGM_WRITE
) == STGM_WRITE
);
5417 BOOL bSTGM_READWRITE
= ((stgm
& STGM_READWRITE
) == STGM_READWRITE
);
5418 BOOL bSTGM_READ
= ! (bSTGM_WRITE
|| bSTGM_READWRITE
);
5420 BOOL bSTGM_SHARE_DENY_NONE
=
5421 ((stgm
& STGM_SHARE_DENY_NONE
) == STGM_SHARE_DENY_NONE
);
5423 BOOL bSTGM_SHARE_DENY_READ
=
5424 ((stgm
& STGM_SHARE_DENY_READ
) == STGM_SHARE_DENY_READ
);
5426 BOOL bSTGM_SHARE_DENY_WRITE
=
5427 ((stgm
& STGM_SHARE_DENY_WRITE
) == STGM_SHARE_DENY_WRITE
);
5429 BOOL bSTGM_SHARE_EXCLUSIVE
=
5430 ((stgm
& STGM_SHARE_EXCLUSIVE
) == STGM_SHARE_EXCLUSIVE
);
5432 BOOL bSTGM_CREATE
= ((stgm
& STGM_CREATE
) == STGM_CREATE
);
5433 BOOL bSTGM_CONVERT
= ((stgm
& STGM_CONVERT
) == STGM_CONVERT
);
5435 BOOL bSTGM_NOSCRATCH
= ((stgm
& STGM_NOSCRATCH
) == STGM_NOSCRATCH
);
5436 BOOL bSTGM_NOSNAPSHOT
= ((stgm
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
);
5439 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5441 if ( ! bSTGM_DIRECT
)
5442 if( bSTGM_TRANSACTED
&& bSTGM_SIMPLE
)
5446 * STGM_WRITE | STGM_READWRITE | STGM_READ
5449 if( bSTGM_WRITE
&& bSTGM_READWRITE
)
5453 * STGM_SHARE_DENY_NONE | others
5454 * (I assume here that DENY_READ implies DENY_WRITE)
5456 if ( bSTGM_SHARE_DENY_NONE
)
5457 if ( bSTGM_SHARE_DENY_READ
||
5458 bSTGM_SHARE_DENY_WRITE
||
5459 bSTGM_SHARE_EXCLUSIVE
)
5463 * STGM_CREATE | STGM_CONVERT
5464 * if both are false, STGM_FAILIFTHERE is set to TRUE
5466 if ( bSTGM_CREATE
&& bSTGM_CONVERT
)
5470 * STGM_NOSCRATCH requires STGM_TRANSACTED
5472 if ( bSTGM_NOSCRATCH
&& ! bSTGM_TRANSACTED
)
5476 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5477 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5479 if (bSTGM_NOSNAPSHOT
)
5481 if ( ! ( bSTGM_TRANSACTED
&&
5482 !(bSTGM_SHARE_EXCLUSIVE
|| bSTGM_SHARE_DENY_WRITE
)) )
5489 /****************************************************************************
5490 * GetShareModeFromSTGM
5492 * This method will return a share mode flag from a STGM value.
5493 * The STGM value is assumed valid.
5495 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
5497 DWORD dwShareMode
= 0;
5498 BOOL bSTGM_SHARE_DENY_NONE
=
5499 ((stgm
& STGM_SHARE_DENY_NONE
) == STGM_SHARE_DENY_NONE
);
5501 BOOL bSTGM_SHARE_DENY_READ
=
5502 ((stgm
& STGM_SHARE_DENY_READ
) == STGM_SHARE_DENY_READ
);
5504 BOOL bSTGM_SHARE_DENY_WRITE
=
5505 ((stgm
& STGM_SHARE_DENY_WRITE
) == STGM_SHARE_DENY_WRITE
);
5507 BOOL bSTGM_SHARE_EXCLUSIVE
=
5508 ((stgm
& STGM_SHARE_EXCLUSIVE
) == STGM_SHARE_EXCLUSIVE
);
5510 if ((bSTGM_SHARE_EXCLUSIVE
) || (bSTGM_SHARE_DENY_READ
))
5513 if (bSTGM_SHARE_DENY_NONE
)
5514 dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
5516 if (bSTGM_SHARE_DENY_WRITE
)
5517 dwShareMode
= FILE_SHARE_READ
;
5522 /****************************************************************************
5523 * GetAccessModeFromSTGM
5525 * This method will return an access mode flag from a STGM value.
5526 * The STGM value is assumed valid.
5528 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
5530 DWORD dwDesiredAccess
= GENERIC_READ
;
5531 BOOL bSTGM_WRITE
= ((stgm
& STGM_WRITE
) == STGM_WRITE
);
5532 BOOL bSTGM_READWRITE
= ((stgm
& STGM_READWRITE
) == STGM_READWRITE
);
5533 BOOL bSTGM_READ
= ! (bSTGM_WRITE
|| bSTGM_READWRITE
);
5536 dwDesiredAccess
= GENERIC_READ
;
5539 dwDesiredAccess
|= GENERIC_WRITE
;
5541 if (bSTGM_READWRITE
)
5542 dwDesiredAccess
= GENERIC_READ
| GENERIC_WRITE
;
5544 return dwDesiredAccess
;
5547 /****************************************************************************
5548 * GetCreationModeFromSTGM
5550 * This method will return a creation mode flag from a STGM value.
5551 * The STGM value is assumed valid.
5553 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
5555 if ( stgm
& STGM_CREATE
)
5556 return CREATE_ALWAYS
;
5557 if (stgm
& STGM_CONVERT
) {
5558 FIXME(ole
, "STGM_CONVERT not implemented!\n");
5561 /* All other cases */
5562 if (stgm
& ~ (STGM_CREATE
|STGM_CONVERT
))
5563 FIXME(ole
,"unhandled storage mode : 0x%08lx\n",stgm
& ~ (STGM_CREATE
|STGM_CONVERT
));