2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
20 #include "wine/obj_storage.h"
21 #include "wine/winestring.h"
27 #include "storage32.h"
32 static const char rootPropertyName
[] = "Root Entry";
34 /***********************************************************************
35 * Forward declaration of internal functions used by the method DestroyElement
37 static HRESULT
deleteStorageProperty(
38 StorageImpl
*parentStorage
,
39 OLECHAR
*propertyToDeleteName
);
41 static HRESULT
deleteStreamProperty(
42 StorageImpl
*parentStorage
,
43 ULONG foundPropertyIndexToDelete
,
44 StgProperty propertyToDelete
);
46 static HRESULT
findPlaceholder(
48 ULONG propertyIndexToStore
,
49 ULONG storagePropertyIndex
,
52 static HRESULT
adjustPropertyChain(
54 StgProperty propertyToDelete
,
55 StgProperty parentProperty
,
56 ULONG parentPropertyId
,
59 /***********************************************************************
60 * Declaration of the functions used to manipulate StgProperty
63 static ULONG
getFreeProperty(
64 StorageImpl
*storage
);
66 static void updatePropertyChain(
68 ULONG newPropertyIndex
,
69 StgProperty newProperty
);
71 static LONG
propertyNameCmp(
73 OLECHAR
*currentProperty
);
76 /***********************************************************************
77 * Declaration of miscellaneous functions...
79 static HRESULT
validateSTGM(DWORD stgmValue
);
81 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
82 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
83 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
86 * Virtual function table for the IStorage32Impl class.
88 static ICOM_VTABLE(IStorage
) Storage32Impl_Vtbl
=
90 StorageBaseImpl_QueryInterface
,
91 StorageBaseImpl_AddRef
,
92 StorageBaseImpl_Release
,
93 StorageBaseImpl_CreateStream
,
94 StorageBaseImpl_OpenStream
,
95 StorageImpl_CreateStorage
,
96 StorageBaseImpl_OpenStorage
,
98 StorageImpl_MoveElementTo
,
101 StorageBaseImpl_EnumElements
,
102 StorageImpl_DestroyElement
,
103 StorageBaseImpl_RenameElement
,
104 StorageImpl_SetElementTimes
,
105 StorageBaseImpl_SetClass
,
106 StorageImpl_SetStateBits
,
111 * Virtual function table for the Storage32InternalImpl class.
113 static ICOM_VTABLE(IStorage
) Storage32InternalImpl_Vtbl
=
115 StorageBaseImpl_QueryInterface
,
116 StorageBaseImpl_AddRef
,
117 StorageBaseImpl_Release
,
118 StorageBaseImpl_CreateStream
,
119 StorageBaseImpl_OpenStream
,
120 StorageImpl_CreateStorage
,
121 StorageBaseImpl_OpenStorage
,
123 StorageImpl_MoveElementTo
,
124 StorageInternalImpl_Commit
,
125 StorageInternalImpl_Revert
,
126 StorageBaseImpl_EnumElements
,
127 StorageImpl_DestroyElement
,
128 StorageBaseImpl_RenameElement
,
129 StorageImpl_SetElementTimes
,
130 StorageBaseImpl_SetClass
,
131 StorageImpl_SetStateBits
,
136 * Virtual function table for the IEnumSTATSTGImpl class.
138 static ICOM_VTABLE(IEnumSTATSTG
) IEnumSTATSTGImpl_Vtbl
=
140 IEnumSTATSTGImpl_QueryInterface
,
141 IEnumSTATSTGImpl_AddRef
,
142 IEnumSTATSTGImpl_Release
,
143 IEnumSTATSTGImpl_Next
,
144 IEnumSTATSTGImpl_Skip
,
145 IEnumSTATSTGImpl_Reset
,
146 IEnumSTATSTGImpl_Clone
153 /************************************************************************
154 ** Storage32BaseImpl implementatiion
157 /************************************************************************
158 * Storage32BaseImpl_QueryInterface (IUnknown)
160 * This method implements the common QueryInterface for all IStorage32
161 * implementations contained in this file.
163 * See Windows documentation for more details on IUnknown methods.
165 HRESULT WINAPI
StorageBaseImpl_QueryInterface(
170 ICOM_THIS(StorageBaseImpl
,iface
);
172 * Perform a sanity check on the parameters.
174 if ( (This
==0) || (ppvObject
==0) )
178 * Initialize the return parameter.
183 * Compare the riid with the interface IDs implemented by this object.
185 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
187 *ppvObject
= (IStorage
*)This
;
189 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IStorage
)) == 0)
191 *ppvObject
= (IStorage
*)This
;
195 * Check that we obtained an interface.
198 return E_NOINTERFACE
;
201 * Query Interface always increases the reference count by one when it is
204 StorageBaseImpl_AddRef(iface
);
209 /************************************************************************
210 * Storage32BaseImpl_AddRef (IUnknown)
212 * This method implements the common AddRef for all IStorage32
213 * implementations contained in this file.
215 * See Windows documentation for more details on IUnknown methods.
217 ULONG WINAPI
StorageBaseImpl_AddRef(
220 ICOM_THIS(StorageBaseImpl
,iface
);
226 /************************************************************************
227 * Storage32BaseImpl_Release (IUnknown)
229 * This method implements the common Release for all IStorage32
230 * implementations contained in this file.
232 * See Windows documentation for more details on IUnknown methods.
234 ULONG WINAPI
StorageBaseImpl_Release(
237 ICOM_THIS(StorageBaseImpl
,iface
);
239 * Decrease the reference count on this object.
244 * If the reference count goes down to 0, perform suicide.
249 * Since we are using a system of base-classes, we want to call the
250 * destructor of the appropriate derived class. To do this, we are
251 * using virtual functions to implement the destructor.
253 This
->v_destructor(This
);
261 /************************************************************************
262 * Storage32BaseImpl_OpenStream (IStorage)
264 * This method will open the specified stream object from the current storage.
266 * See Windows documentation for more details on IStorage methods.
268 HRESULT WINAPI
StorageBaseImpl_OpenStream(
270 const OLECHAR
* pwcsName
, /* [string][in] */
271 void* reserved1
, /* [unique][in] */
272 DWORD grfMode
, /* [in] */
273 DWORD reserved2
, /* [in] */
274 IStream
** ppstm
) /* [out] */
276 ICOM_THIS(StorageBaseImpl
,iface
);
277 IEnumSTATSTGImpl
* propertyEnumeration
;
278 StgStreamImpl
* newStream
;
279 StgProperty currentProperty
;
280 ULONG foundPropertyIndex
;
283 * Perform a sanity check on the parameters.
285 if ( (pwcsName
==NULL
) || (ppstm
==0) )
289 * Initialize the out parameter
294 * Validate the STGM flags
296 if ( FAILED( validateSTGM(grfMode
) ))
297 return STG_E_INVALIDFLAG
;
302 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
303 (grfMode
& STGM_DELETEONRELEASE
) ||
304 (grfMode
& STGM_TRANSACTED
) )
305 return STG_E_INVALIDFUNCTION
;
308 * Create a property enumeration to search the properties
310 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
311 This
->ancestorStorage
,
312 This
->rootPropertySetIndex
);
315 * Search the enumeration for the property with the given name
317 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
323 * Delete the property enumeration since we don't need it anymore
325 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
328 * If it was found, construct the stream object and return a pointer to it.
330 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
331 (currentProperty
.propertyType
==PROPTYPE_STREAM
) )
333 newStream
= StgStreamImpl_Construct(This
, foundPropertyIndex
);
337 *ppstm
= (IStream
*)newStream
;
340 * Since we are returning a pointer to the interface, we have to
341 * nail down the reference.
343 StgStreamImpl_AddRef(*ppstm
);
348 return E_OUTOFMEMORY
;
351 return STG_E_FILENOTFOUND
;
354 /************************************************************************
355 * Storage32BaseImpl_OpenStorage (IStorage)
357 * This method will open a new storage object from the current storage.
359 * See Windows documentation for more details on IStorage methods.
361 HRESULT WINAPI
StorageBaseImpl_OpenStorage(
363 const OLECHAR
* pwcsName
, /* [string][unique][in] */
364 IStorage
* pstgPriority
, /* [unique][in] */
365 DWORD grfMode
, /* [in] */
366 SNB snbExclude
, /* [unique][in] */
367 DWORD reserved
, /* [in] */
368 IStorage
** ppstg
) /* [out] */
370 ICOM_THIS(StorageBaseImpl
,iface
);
371 StorageInternalImpl
* newStorage
;
372 IEnumSTATSTGImpl
* propertyEnumeration
;
373 StgProperty currentProperty
;
374 ULONG foundPropertyIndex
;
377 * Perform a sanity check on the parameters.
379 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
383 * Validate the STGM flags
385 if ( FAILED( validateSTGM(grfMode
) ))
386 return STG_E_INVALIDFLAG
;
391 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
392 (grfMode
& STGM_DELETEONRELEASE
) ||
393 (grfMode
& STGM_PRIORITY
) )
394 return STG_E_INVALIDFUNCTION
;
397 * Initialize the out parameter
402 * Create a property enumeration to search the properties
404 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
405 This
->ancestorStorage
,
406 This
->rootPropertySetIndex
);
409 * Search the enumeration for the property with the given name
411 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
417 * Delete the property enumeration since we don't need it anymore
419 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
422 * If it was found, construct the stream object and return a pointer to it.
424 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
425 (currentProperty
.propertyType
==PROPTYPE_STORAGE
) )
428 * Construct a new Storage object
430 newStorage
= StorageInternalImpl_Construct(
431 This
->ancestorStorage
,
436 *ppstg
= (IStorage
*)newStorage
;
439 * Since we are returning a pointer to the interface,
440 * we have to nail down the reference.
442 StorageBaseImpl_AddRef(*ppstg
);
447 return STG_E_INSUFFICIENTMEMORY
;
450 return STG_E_FILENOTFOUND
;
453 /************************************************************************
454 * Storage32BaseImpl_EnumElements (IStorage)
456 * This method will create an enumerator object that can be used to
457 * retrieve informatino about all the properties in the storage object.
459 * See Windows documentation for more details on IStorage methods.
461 HRESULT WINAPI
StorageBaseImpl_EnumElements(
463 DWORD reserved1
, /* [in] */
464 void* reserved2
, /* [size_is][unique][in] */
465 DWORD reserved3
, /* [in] */
466 IEnumSTATSTG
** ppenum
) /* [out] */
468 ICOM_THIS(StorageBaseImpl
,iface
);
469 IEnumSTATSTGImpl
* newEnum
;
472 * Perform a sanity check on the parameters.
474 if ( (This
==0) || (ppenum
==0))
478 * Construct the enumerator.
480 newEnum
= IEnumSTATSTGImpl_Construct(
481 This
->ancestorStorage
,
482 This
->rootPropertySetIndex
);
486 *ppenum
= (IEnumSTATSTG
*)newEnum
;
489 * Don't forget to nail down a reference to the new object before
492 IEnumSTATSTGImpl_AddRef(*ppenum
);
497 return E_OUTOFMEMORY
;
500 /************************************************************************
501 * Storage32BaseImpl_Stat (IStorage)
503 * This method will retrieve information about this storage object.
505 * See Windows documentation for more details on IStorage methods.
507 HRESULT WINAPI
StorageBaseImpl_Stat(
509 STATSTG
* pstatstg
, /* [out] */
510 DWORD grfStatFlag
) /* [in] */
512 ICOM_THIS(StorageBaseImpl
,iface
);
513 StgProperty curProperty
;
517 * Perform a sanity check on the parameters.
519 if ( (This
==0) || (pstatstg
==0))
523 * Read the information from the property.
525 readSucessful
= StorageImpl_ReadProperty(
526 This
->ancestorStorage
,
527 This
->rootPropertySetIndex
,
532 StorageUtl_CopyPropertyToSTATSTG(
543 /************************************************************************
544 * Storage32BaseImpl_RenameElement (IStorage)
546 * This method will rename the specified element.
548 * See Windows documentation for more details on IStorage methods.
550 * Implementation notes: The method used to rename consists of creating a clone
551 * of the deleted StgProperty object setting it with the new name and to
552 * perform a DestroyElement of the old StgProperty.
554 HRESULT WINAPI
StorageBaseImpl_RenameElement(
556 const OLECHAR
* pwcsOldName
, /* [in] */
557 const OLECHAR
* pwcsNewName
) /* [in] */
559 ICOM_THIS(StorageBaseImpl
,iface
);
560 IEnumSTATSTGImpl
* propertyEnumeration
;
561 StgProperty currentProperty
;
562 ULONG foundPropertyIndex
;
565 * Create a property enumeration to search the properties
567 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
568 This
->rootPropertySetIndex
);
571 * Search the enumeration for the new property name
573 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
577 if (foundPropertyIndex
!= PROPERTY_NULL
)
580 * There is already a property with the new name
582 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
583 return STG_E_FILEALREADYEXISTS
;
586 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)propertyEnumeration
);
589 * Search the enumeration for the old property name
591 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
596 * Delete the property enumeration since we don't need it anymore
598 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
600 if (foundPropertyIndex
!= PROPERTY_NULL
)
602 StgProperty renamedProperty
;
603 ULONG renamedPropertyIndex
;
606 * Setup a new property for the renamed property
608 renamedProperty
.sizeOfNameString
=
609 ( lstrlenW(pwcsNewName
)+1 ) * sizeof(WCHAR
);
611 if (renamedProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
612 return STG_E_INVALIDNAME
;
614 lstrcpyW(renamedProperty
.name
, pwcsNewName
);
616 renamedProperty
.propertyType
= currentProperty
.propertyType
;
617 renamedProperty
.startingBlock
= currentProperty
.startingBlock
;
618 renamedProperty
.size
.LowPart
= currentProperty
.size
.LowPart
;
619 renamedProperty
.size
.HighPart
= currentProperty
.size
.HighPart
;
621 renamedProperty
.previousProperty
= PROPERTY_NULL
;
622 renamedProperty
.nextProperty
= PROPERTY_NULL
;
625 * Bring the dirProperty link in case it is a storage and in which
626 * case the renamed storage elements don't require to be reorganized.
628 renamedProperty
.dirProperty
= currentProperty
.dirProperty
;
630 /* call CoFileTime to get the current time
631 renamedProperty.timeStampS1
632 renamedProperty.timeStampD1
633 renamedProperty.timeStampS2
634 renamedProperty.timeStampD2
635 renamedProperty.propertyUniqueID
639 * Obtain a free property in the property chain
641 renamedPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
644 * Save the new property into the new property spot
646 StorageImpl_WriteProperty(
647 This
->ancestorStorage
,
648 renamedPropertyIndex
,
652 * Find a spot in the property chain for our newly created property.
656 renamedPropertyIndex
,
660 * At this point the renamed property has been inserted in the tree,
661 * now, before to Destroy the old property we must zeroed it's dirProperty
662 * otherwise the DestroyProperty below will zap it all and we do not want
664 * Also, we fake that the old property is a storage so the DestroyProperty
665 * will not do a SetSize(0) on the stream data.
667 * This means that we need to tweek the StgProperty if it is a stream or a
670 currentProperty
.dirProperty
= PROPERTY_NULL
;
671 currentProperty
.propertyType
= PROPTYPE_STORAGE
;
672 StorageImpl_WriteProperty(
673 This
->ancestorStorage
,
678 * Invoke Destroy to get rid of the ole property and automatically redo
679 * the linking of it's previous and next members...
681 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsOldName
);
687 * There is no property with the old name
689 return STG_E_FILENOTFOUND
;
695 /************************************************************************
696 * Storage32BaseImpl_CreateStream (IStorage)
698 * This method will create a stream object within this storage
700 * See Windows documentation for more details on IStorage methods.
702 HRESULT WINAPI
StorageBaseImpl_CreateStream(
704 const OLECHAR
* pwcsName
, /* [string][in] */
705 DWORD grfMode
, /* [in] */
706 DWORD reserved1
, /* [in] */
707 DWORD reserved2
, /* [in] */
708 IStream
** ppstm
) /* [out] */
710 ICOM_THIS(StorageBaseImpl
,iface
);
711 IEnumSTATSTGImpl
* propertyEnumeration
;
712 StgStreamImpl
* newStream
;
713 StgProperty currentProperty
, newStreamProperty
;
714 ULONG foundPropertyIndex
, newPropertyIndex
;
717 * Validate parameters
720 return STG_E_INVALIDPOINTER
;
723 return STG_E_INVALIDNAME
;
726 * Validate the STGM flags
728 if ( FAILED( validateSTGM(grfMode
) ))
729 return STG_E_INVALIDFLAG
;
734 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
735 (grfMode
& STGM_DELETEONRELEASE
) ||
736 (grfMode
& STGM_TRANSACTED
) )
737 return STG_E_INVALIDFUNCTION
;
740 * Initialize the out parameter
745 * Create a property enumeration to search the properties
747 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
748 This
->rootPropertySetIndex
);
750 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
754 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
756 if (foundPropertyIndex
!= PROPERTY_NULL
)
759 * An element with this name already exists
761 if (grfMode
& STGM_CREATE
)
762 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsName
);
764 return STG_E_FILEALREADYEXISTS
;
768 * memset the empty property
770 memset(&newStreamProperty
, 0, sizeof(StgProperty
));
772 newStreamProperty
.sizeOfNameString
=
773 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
775 if (newStreamProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
776 return STG_E_INVALIDNAME
;
778 lstrcpyW(newStreamProperty
.name
, pwcsName
);
780 newStreamProperty
.propertyType
= PROPTYPE_STREAM
;
781 newStreamProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
782 newStreamProperty
.size
.LowPart
= 0;
783 newStreamProperty
.size
.HighPart
= 0;
785 newStreamProperty
.previousProperty
= PROPERTY_NULL
;
786 newStreamProperty
.nextProperty
= PROPERTY_NULL
;
787 newStreamProperty
.dirProperty
= PROPERTY_NULL
;
789 /* call CoFileTime to get the current time
790 newStreamProperty.timeStampS1
791 newStreamProperty.timeStampD1
792 newStreamProperty.timeStampS2
793 newStreamProperty.timeStampD2
796 /* newStreamProperty.propertyUniqueID */
799 * Get a free property or create a new one
801 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
804 * Save the new property into the new property spot
806 StorageImpl_WriteProperty(
807 This
->ancestorStorage
,
812 * Find a spot in the property chain for our newly created property.
820 * Open the stream to return it.
822 newStream
= StgStreamImpl_Construct(This
, newPropertyIndex
);
826 *ppstm
= (IStream
*)newStream
;
829 * Since we are returning a pointer to the interface, we have to nail down
832 StgStreamImpl_AddRef(*ppstm
);
836 return STG_E_INSUFFICIENTMEMORY
;
842 /************************************************************************
843 * Storage32BaseImpl_SetClass (IStorage)
845 * This method will write the specified CLSID in the property of this
848 * See Windows documentation for more details on IStorage methods.
850 HRESULT WINAPI
StorageBaseImpl_SetClass(
852 REFCLSID clsid
) /* [in] */
854 ICOM_THIS(StorageBaseImpl
,iface
);
855 HRESULT hRes
= E_FAIL
;
856 StgProperty curProperty
;
859 success
= StorageImpl_ReadProperty(This
->ancestorStorage
,
860 This
->rootPropertySetIndex
,
864 curProperty
.propertyUniqueID
= *clsid
;
866 success
= StorageImpl_WriteProperty(This
->ancestorStorage
,
867 This
->rootPropertySetIndex
,
876 /************************************************************************
877 ** Storage32Impl implementation
880 /************************************************************************
881 * Storage32Impl_CreateStorage (IStorage)
883 * This method will create the storage object within the provided storage.
885 * See Windows documentation for more details on IStorage methods.
887 HRESULT WINAPI
StorageImpl_CreateStorage(
889 const OLECHAR
*pwcsName
, /* [string][in] */
890 DWORD grfMode
, /* [in] */
891 DWORD reserved1
, /* [in] */
892 DWORD reserved2
, /* [in] */
893 IStorage
**ppstg
) /* [out] */
895 StorageImpl
* const This
=(StorageImpl
*)iface
;
897 IEnumSTATSTGImpl
*propertyEnumeration
;
898 StgProperty currentProperty
;
899 StgProperty newProperty
;
900 ULONG foundPropertyIndex
;
901 ULONG newPropertyIndex
;
905 * Validate parameters
908 return STG_E_INVALIDPOINTER
;
911 return STG_E_INVALIDNAME
;
914 * Validate the STGM flags
916 if ( FAILED( validateSTGM(grfMode
) ) ||
917 (grfMode
& STGM_DELETEONRELEASE
) )
918 return STG_E_INVALIDFLAG
;
921 * Initialize the out parameter
926 * Create a property enumeration and search the properties
928 propertyEnumeration
= IEnumSTATSTGImpl_Construct( This
->ancestorStorage
,
929 This
->rootPropertySetIndex
);
931 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
934 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
936 if (foundPropertyIndex
!= PROPERTY_NULL
)
939 * An element with this name already exists
941 if (grfMode
& STGM_CREATE
)
942 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsName
);
944 return STG_E_FILEALREADYEXISTS
;
948 * memset the empty property
950 memset(&newProperty
, 0, sizeof(StgProperty
));
952 newProperty
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
954 if (newProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
955 return STG_E_INVALIDNAME
;
957 lstrcpyW(newProperty
.name
, pwcsName
);
959 newProperty
.propertyType
= PROPTYPE_STORAGE
;
960 newProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
961 newProperty
.size
.LowPart
= 0;
962 newProperty
.size
.HighPart
= 0;
964 newProperty
.previousProperty
= PROPERTY_NULL
;
965 newProperty
.nextProperty
= PROPERTY_NULL
;
966 newProperty
.dirProperty
= PROPERTY_NULL
;
968 /* call CoFileTime to get the current time
969 newProperty.timeStampS1
970 newProperty.timeStampD1
971 newProperty.timeStampS2
972 newProperty.timeStampD2
975 /* newStorageProperty.propertyUniqueID */
978 * Obtain a free property in the property chain
980 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
983 * Save the new property into the new property spot
985 StorageImpl_WriteProperty(
986 This
->ancestorStorage
,
991 * Find a spot in the property chain for our newly created property.
999 * Open it to get a pointer to return.
1001 hr
= StorageBaseImpl_OpenStorage(
1010 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1019 /***************************************************************************
1023 * Get a free property or create a new one.
1025 static ULONG
getFreeProperty(
1026 StorageImpl
*storage
)
1028 ULONG currentPropertyIndex
= 0;
1029 ULONG newPropertyIndex
= PROPERTY_NULL
;
1030 BOOL readSucessful
= TRUE
;
1031 StgProperty currentProperty
;
1036 * Start by reading the root property
1038 readSucessful
= StorageImpl_ReadProperty(storage
->ancestorStorage
,
1039 currentPropertyIndex
,
1043 if (currentProperty
.sizeOfNameString
== 0)
1046 * The property existis and is available, we found it.
1048 newPropertyIndex
= currentPropertyIndex
;
1054 * We exhausted the property list, we will create more space below
1056 newPropertyIndex
= currentPropertyIndex
;
1058 currentPropertyIndex
++;
1060 } while (newPropertyIndex
== PROPERTY_NULL
);
1063 * grow the property chain
1065 if (! readSucessful
)
1067 StgProperty emptyProperty
;
1068 ULARGE_INTEGER newSize
;
1069 ULONG propertyIndex
;
1070 ULONG lastProperty
= 0;
1071 ULONG blockCount
= 0;
1074 * obtain the new count of property blocks
1076 blockCount
= BlockChainStream_GetCount(
1077 storage
->ancestorStorage
->rootBlockChain
)+1;
1080 * initialize the size used by the property stream
1082 newSize
.HighPart
= 0;
1083 newSize
.LowPart
= storage
->bigBlockSize
* blockCount
;
1086 * add a property block to the property chain
1088 BlockChainStream_SetSize(storage
->ancestorStorage
->rootBlockChain
, newSize
);
1091 * memset the empty property in order to initialize the unused newly
1094 memset(&emptyProperty
, 0, sizeof(StgProperty
));
1099 lastProperty
= storage
->bigBlockSize
/ PROPSET_BLOCK_SIZE
* blockCount
;
1102 propertyIndex
= newPropertyIndex
;
1103 propertyIndex
< lastProperty
;
1106 StorageImpl_WriteProperty(
1107 storage
->ancestorStorage
,
1113 return newPropertyIndex
;
1116 /****************************************************************************
1120 * Case insensitive comparaison of StgProperty.name by first considering
1123 * Returns <0 when newPrpoerty < currentProperty
1124 * >0 when newPrpoerty > currentProperty
1125 * 0 when newPrpoerty == currentProperty
1127 static LONG
propertyNameCmp(
1128 OLECHAR
*newProperty
,
1129 OLECHAR
*currentProperty
)
1131 LONG sizeOfNew
= (lstrlenW(newProperty
) +1) * sizeof(WCHAR
);
1132 LONG sizeOfCur
= (lstrlenW(currentProperty
)+1) * sizeof(WCHAR
);
1133 LONG diff
= sizeOfNew
- sizeOfCur
;
1138 * We compare the string themselves only when they are of the same lenght
1140 WCHAR wsnew
[PROPERTY_NAME_MAX_LEN
];
1141 WCHAR wscur
[PROPERTY_NAME_MAX_LEN
];
1143 diff
= lstrcmpW( (LPCWSTR
)CRTDLL__wcsupr(
1144 lstrcpynW(wsnew
, newProperty
, sizeOfNew
)),
1145 (LPCWSTR
)CRTDLL__wcsupr(
1146 lstrcpynW(wscur
, currentProperty
, sizeOfCur
)));
1152 /****************************************************************************
1156 * Properly link this new element in the property chain.
1158 static void updatePropertyChain(
1159 StorageImpl
*storage
,
1160 ULONG newPropertyIndex
,
1161 StgProperty newProperty
)
1163 StgProperty currentProperty
;
1166 * Read the root property
1168 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1169 storage
->rootPropertySetIndex
,
1172 if (currentProperty
.dirProperty
!= PROPERTY_NULL
)
1175 * The root storage contains some element, therefore, start the research
1176 * for the appropriate location.
1179 ULONG current
, next
, previous
, currentPropertyId
;
1182 * Keep the StgProperty sequence number of the storage first property
1184 currentPropertyId
= currentProperty
.dirProperty
;
1189 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1190 currentProperty
.dirProperty
,
1193 previous
= currentProperty
.previousProperty
;
1194 next
= currentProperty
.nextProperty
;
1195 current
= currentPropertyId
;
1199 LONG diff
= propertyNameCmp( newProperty
.name
, currentProperty
.name
);
1203 if (previous
!= PROPERTY_NULL
)
1205 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1212 currentProperty
.previousProperty
= newPropertyIndex
;
1213 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1221 if (next
!= PROPERTY_NULL
)
1223 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1230 currentProperty
.nextProperty
= newPropertyIndex
;
1231 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1238 previous
= currentProperty
.previousProperty
;
1239 next
= currentProperty
.nextProperty
;
1245 * The root storage is empty, link the new property to it's dir property
1247 currentProperty
.dirProperty
= newPropertyIndex
;
1248 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1249 storage
->rootPropertySetIndex
,
1255 /*************************************************************************
1258 HRESULT WINAPI
StorageImpl_CopyTo(
1260 DWORD ciidExclude
, /* [in] */
1261 const IID
*rgiidExclude
,/* [size_is][unique][in] */
1262 SNB snbExclude
, /* [unique][in] */
1263 IStorage
*pstgDest
) /* [unique][in] */
1268 /*************************************************************************
1269 * MoveElementTo (IStorage)
1271 HRESULT WINAPI
StorageImpl_MoveElementTo(
1273 const OLECHAR
*pwcsName
, /* [string][in] */
1274 IStorage
*pstgDest
, /* [unique][in] */
1275 const OLECHAR
*pwcsNewName
,/* [string][in] */
1276 DWORD grfFlags
) /* [in] */
1281 /*************************************************************************
1284 HRESULT WINAPI
StorageImpl_Commit(
1286 DWORD grfCommitFlags
)/* [in] */
1288 FIXME(ole
, "(%ld): stub!\n", grfCommitFlags
);
1292 /*************************************************************************
1295 HRESULT WINAPI
StorageImpl_Revert(
1301 /*************************************************************************
1302 * DestroyElement (IStorage)
1304 * Stategy: This implementation is build this way for simplicity not for speed.
1305 * I always delete the top most element of the enumeration and adjust
1306 * the deleted element pointer all the time. This takes longer to
1307 * do but allow to reinvoke DestroyElement whenever we encounter a
1308 * storage object. The optimisation reside in the usage of another
1309 * enumeration stategy that would give all the leaves of a storage
1310 * first. (postfix order)
1312 HRESULT WINAPI
StorageImpl_DestroyElement(
1314 const OLECHAR
*pwcsName
)/* [string][in] */
1316 StorageImpl
* const This
=(StorageImpl
*)iface
;
1318 IEnumSTATSTGImpl
* propertyEnumeration
;
1321 StgProperty propertyToDelete
;
1322 StgProperty parentProperty
;
1323 ULONG foundPropertyIndexToDelete
;
1324 ULONG typeOfRelation
;
1325 ULONG parentPropertyId
;
1328 * Perform a sanity check on the parameters.
1331 return STG_E_INVALIDPOINTER
;
1334 * Create a property enumeration to search the property with the given name
1336 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
1337 This
->ancestorStorage
,
1338 This
->rootPropertySetIndex
);
1340 foundPropertyIndexToDelete
= IEnumSTATSTGImpl_FindProperty(
1341 propertyEnumeration
,
1345 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1347 if ( foundPropertyIndexToDelete
== PROPERTY_NULL
)
1349 return STG_E_FILENOTFOUND
;
1353 * Find the parent property of the property to delete (the one that
1354 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1355 * the parent is This. Otherwise, the parent is one of it's sibling...
1359 * First, read This's StgProperty..
1361 res
= StorageImpl_ReadProperty(
1362 This
->ancestorStorage
,
1363 This
->rootPropertySetIndex
,
1369 * Second, check to see if by any chance the actual storage (This) is not
1370 * the parent of the property to delete... We never know...
1372 if ( parentProperty
.dirProperty
== foundPropertyIndexToDelete
)
1375 * Set data as it would have been done in the else part...
1377 typeOfRelation
= PROPERTY_RELATION_DIR
;
1378 parentPropertyId
= This
->rootPropertySetIndex
;
1383 * Create a property enumeration to search the parent properties, and
1384 * delete it once done.
1386 IEnumSTATSTGImpl
* propertyEnumeration2
;
1388 propertyEnumeration2
= IEnumSTATSTGImpl_Construct(
1389 This
->ancestorStorage
,
1390 This
->rootPropertySetIndex
);
1392 typeOfRelation
= IEnumSTATSTGImpl_FindParentProperty(
1393 propertyEnumeration2
,
1394 foundPropertyIndexToDelete
,
1398 IEnumSTATSTGImpl_Destroy(propertyEnumeration2
);
1401 if ( propertyToDelete
.propertyType
== PROPTYPE_STORAGE
)
1403 hr
= deleteStorageProperty(
1405 propertyToDelete
.name
);
1407 else if ( propertyToDelete
.propertyType
== PROPTYPE_STREAM
)
1409 hr
= deleteStreamProperty(
1411 foundPropertyIndexToDelete
,
1419 * Adjust the property chain
1421 hr
= adjustPropertyChain(
1432 /*********************************************************************
1436 * Perform the deletion of a complete storage node
1439 static HRESULT
deleteStorageProperty(
1440 StorageImpl
*parentStorage
,
1441 OLECHAR
*propertyToDeleteName
)
1443 IEnumSTATSTG
*elements
= 0;
1444 IStorage
*childStorage
= 0;
1445 STATSTG currentElement
;
1447 HRESULT destroyHr
= S_OK
;
1450 * Open the storage and enumerate it
1452 hr
= StorageBaseImpl_OpenStorage(
1453 (IStorage
*)parentStorage
,
1454 propertyToDeleteName
,
1456 STGM_SHARE_EXCLUSIVE
,
1467 * Enumerate the elements
1469 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1474 * Obtain the next element
1476 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
1479 destroyHr
= StorageImpl_DestroyElement(
1480 (IStorage
*)childStorage
,
1481 (OLECHAR
*)currentElement
.pwcsName
);
1483 CoTaskMemFree(currentElement
.pwcsName
);
1487 * We need to Reset the enumeration every time because we delete elements
1488 * and the enumeration could be invalid
1490 IEnumSTATSTG_Reset(elements
);
1492 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
1494 IStorage_Release(childStorage
);
1495 IEnumSTATSTG_Release(elements
);
1500 /*********************************************************************
1504 * Perform the deletion of a stream node
1507 static HRESULT
deleteStreamProperty(
1508 StorageImpl
*parentStorage
,
1509 ULONG indexOfPropertyToDelete
,
1510 StgProperty propertyToDelete
)
1514 ULARGE_INTEGER size
;
1519 hr
= StorageBaseImpl_OpenStream(
1520 (IStorage
*)parentStorage
,
1521 (OLECHAR
*)propertyToDelete
.name
,
1523 STGM_SHARE_EXCLUSIVE
,
1535 hr
= IStream_SetSize(pis
, size
);
1543 * Invalidate the property by zeroing it's name member.
1545 propertyToDelete
.sizeOfNameString
= 0;
1548 * Here we should re-read the property so we get the updated pointer
1549 * but since we are here to zap it, I don't do it...
1552 StorageImpl_WriteProperty(
1553 parentStorage
->ancestorStorage
,
1554 indexOfPropertyToDelete
,
1560 /*********************************************************************
1564 * Finds a placeholder for the StgProperty within the Storage
1567 static HRESULT
findPlaceholder(
1568 StorageImpl
*storage
,
1569 ULONG propertyIndexToStore
,
1570 ULONG storePropertyIndex
,
1573 StgProperty storeProperty
;
1578 * Read the storage property
1580 res
= StorageImpl_ReadProperty(
1581 storage
->ancestorStorage
,
1590 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1592 if (storeProperty
.previousProperty
!= PROPERTY_NULL
)
1594 return findPlaceholder(
1596 propertyIndexToStore
,
1597 storeProperty
.previousProperty
,
1602 storeProperty
.previousProperty
= propertyIndexToStore
;
1605 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
1607 if (storeProperty
.nextProperty
!= PROPERTY_NULL
)
1609 return findPlaceholder(
1611 propertyIndexToStore
,
1612 storeProperty
.nextProperty
,
1617 storeProperty
.nextProperty
= propertyIndexToStore
;
1620 else if (typeOfRelation
== PROPERTY_RELATION_DIR
)
1622 if (storeProperty
.dirProperty
!= PROPERTY_NULL
)
1624 return findPlaceholder(
1626 propertyIndexToStore
,
1627 storeProperty
.dirProperty
,
1632 storeProperty
.dirProperty
= propertyIndexToStore
;
1636 hr
= StorageImpl_WriteProperty(
1637 storage
->ancestorStorage
,
1649 /*************************************************************************
1653 * This method takes the previous and the next property link of a property
1654 * to be deleted and find them a place in the Storage.
1656 static HRESULT
adjustPropertyChain(
1658 StgProperty propertyToDelete
,
1659 StgProperty parentProperty
,
1660 ULONG parentPropertyId
,
1663 ULONG newLinkProperty
= PROPERTY_NULL
;
1664 BOOL needToFindAPlaceholder
= FALSE
;
1665 ULONG storeNode
= PROPERTY_NULL
;
1666 ULONG toStoreNode
= PROPERTY_NULL
;
1667 INT relationType
= 0;
1671 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1673 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1676 * Set the parent previous to the property to delete previous
1678 newLinkProperty
= propertyToDelete
.previousProperty
;
1680 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1683 * We also need to find a storage for the other link, setup variables
1684 * to do this at the end...
1686 needToFindAPlaceholder
= TRUE
;
1687 storeNode
= propertyToDelete
.previousProperty
;
1688 toStoreNode
= propertyToDelete
.nextProperty
;
1689 relationType
= PROPERTY_RELATION_NEXT
;
1692 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1695 * Set the parent previous to the property to delete next
1697 newLinkProperty
= propertyToDelete
.nextProperty
;
1701 * Link it for real...
1703 parentProperty
.previousProperty
= newLinkProperty
;
1706 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
1708 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1711 * Set the parent next to the property to delete next previous
1713 newLinkProperty
= propertyToDelete
.previousProperty
;
1715 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1718 * We also need to find a storage for the other link, setup variables
1719 * to do this at the end...
1721 needToFindAPlaceholder
= TRUE
;
1722 storeNode
= propertyToDelete
.previousProperty
;
1723 toStoreNode
= propertyToDelete
.nextProperty
;
1724 relationType
= PROPERTY_RELATION_NEXT
;
1727 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1730 * Set the parent next to the property to delete next
1732 newLinkProperty
= propertyToDelete
.nextProperty
;
1736 * Link it for real...
1738 parentProperty
.nextProperty
= newLinkProperty
;
1740 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
1742 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1745 * Set the parent dir to the property to delete previous
1747 newLinkProperty
= propertyToDelete
.previousProperty
;
1749 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1752 * We also need to find a storage for the other link, setup variables
1753 * to do this at the end...
1755 needToFindAPlaceholder
= TRUE
;
1756 storeNode
= propertyToDelete
.previousProperty
;
1757 toStoreNode
= propertyToDelete
.nextProperty
;
1758 relationType
= PROPERTY_RELATION_NEXT
;
1761 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1764 * Set the parent dir to the property to delete next
1766 newLinkProperty
= propertyToDelete
.nextProperty
;
1770 * Link it for real...
1772 parentProperty
.dirProperty
= newLinkProperty
;
1776 * Write back the parent property
1778 res
= StorageImpl_WriteProperty(
1779 This
->ancestorStorage
,
1788 * If a placeholder is required for the other link, then, find one and
1789 * get out of here...
1791 if (needToFindAPlaceholder
)
1793 hr
= findPlaceholder(
1804 /******************************************************************************
1805 * SetElementTimes (IStorage)
1807 HRESULT WINAPI
StorageImpl_SetElementTimes(
1809 const OLECHAR
*pwcsName
,/* [string][in] */
1810 const FILETIME
*pctime
, /* [in] */
1811 const FILETIME
*patime
, /* [in] */
1812 const FILETIME
*pmtime
) /* [in] */
1817 /******************************************************************************
1818 * SetStateBits (IStorage)
1820 HRESULT WINAPI
StorageImpl_SetStateBits(
1822 DWORD grfStateBits
,/* [in] */
1823 DWORD grfMask
) /* [in] */
1828 HRESULT
StorageImpl_Construct(
1834 StgProperty currentProperty
;
1836 ULONG currentPropertyIndex
;
1838 if ( FAILED( validateSTGM(openFlags
) ))
1839 return STG_E_INVALIDFLAG
;
1841 memset(This
, 0, sizeof(StorageImpl
));
1844 * Initialize the virtual fgunction table.
1846 This
->lpvtbl
= &Storage32Impl_Vtbl
;
1847 This
->v_destructor
= &StorageImpl_Destroy
;
1850 * This is the top-level storage so initialize the ancester pointer
1853 This
->ancestorStorage
= This
;
1856 * Initialize the physical support of the storage.
1858 This
->hFile
= hFile
;
1861 * Initialize the big block cache.
1863 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
1864 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
1865 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
1867 This
->bigBlockSize
);
1869 if (This
->bigBlockFile
== 0)
1872 if (openFlags
& STGM_CREATE
)
1874 ULARGE_INTEGER size
;
1875 BYTE
* bigBlockBuffer
;
1878 * Initialize all header variables:
1879 * - The big block depot consists of one block and it is at block 0
1880 * - The properties start at block 1
1881 * - There is no small block depot
1883 memset( This
->bigBlockDepotStart
,
1885 sizeof(This
->bigBlockDepotStart
));
1887 This
->bigBlockDepotCount
= 1;
1888 This
->bigBlockDepotStart
[0] = 0;
1889 This
->rootStartBlock
= 1;
1890 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
1891 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
1892 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
1893 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
1894 This
->extBigBlockDepotCount
= 0;
1896 StorageImpl_SaveFileHeader(This
);
1899 * Add one block for the big block depot and one block for the properties
1902 size
.LowPart
= This
->bigBlockSize
* 3;
1903 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
1906 * Initialize the big block depot
1908 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, 0);
1909 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
1910 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
1911 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
1912 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
1917 * Load the header for the file.
1919 hr
= StorageImpl_LoadFileHeader(This
);
1923 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
1930 * There is no block depot cached yet.
1932 This
->indexBlockDepotCached
= 0xFFFFFFFF;
1935 * Start searching for free blocks with block 0.
1937 This
->prevFreeBlock
= 0;
1940 * Create the block chain abstractions.
1942 This
->rootBlockChain
=
1943 BlockChainStream_Construct(This
, &This
->rootStartBlock
, PROPERTY_NULL
);
1945 This
->smallBlockDepotChain
= BlockChainStream_Construct(
1947 &This
->smallBlockDepotStart
,
1951 * Write the root property
1953 if (openFlags
& STGM_CREATE
)
1955 StgProperty rootProp
;
1957 * Initialize the property chain
1959 memset(&rootProp
, 0, sizeof(rootProp
));
1960 lstrcpyAtoW(rootProp
.name
, rootPropertyName
);
1962 rootProp
.sizeOfNameString
= (lstrlenW(rootProp
.name
)+1) * sizeof(WCHAR
);
1963 rootProp
.propertyType
= PROPTYPE_ROOT
;
1964 rootProp
.previousProperty
= PROPERTY_NULL
;
1965 rootProp
.nextProperty
= PROPERTY_NULL
;
1966 rootProp
.dirProperty
= PROPERTY_NULL
;
1967 rootProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
1968 rootProp
.size
.HighPart
= 0;
1969 rootProp
.size
.LowPart
= 0;
1971 StorageImpl_WriteProperty(This
, 0, &rootProp
);
1975 * Find the ID of the root int he property sets.
1977 currentPropertyIndex
= 0;
1981 readSucessful
= StorageImpl_ReadProperty(
1983 currentPropertyIndex
,
1988 if ( (currentProperty
.sizeOfNameString
!= 0 ) &&
1989 (currentProperty
.propertyType
== PROPTYPE_ROOT
) )
1991 This
->rootPropertySetIndex
= currentPropertyIndex
;
1995 currentPropertyIndex
++;
1997 } while (readSucessful
&& (This
->rootPropertySetIndex
== PROPERTY_NULL
) );
2006 * Create the block chain abstraction for the small block root chain.
2008 This
->smallBlockRootChain
= BlockChainStream_Construct(
2011 This
->rootPropertySetIndex
);
2016 void StorageImpl_Destroy(
2019 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2020 BlockChainStream_Destroy(This
->rootBlockChain
);
2021 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2023 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2027 /******************************************************************************
2028 * Storage32Impl_GetNextFreeBigBlock
2030 * Returns the index of the next free big block.
2031 * If the big block depot is filled, this method will enlarge it.
2034 ULONG
StorageImpl_GetNextFreeBigBlock(
2037 ULONG depotBlockIndexPos
;
2039 ULONG depotBlockOffset
;
2040 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2041 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2043 ULONG freeBlock
= BLOCK_UNUSED
;
2045 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2046 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2049 * Scan the entire big block depot until we find a block marked free
2051 while (nextBlockIndex
!= BLOCK_UNUSED
)
2053 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2055 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2058 * Grow the primary depot.
2060 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2062 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2065 * Add a block depot.
2067 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2068 This
->bigBlockDepotCount
++;
2069 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2072 * Flag it as a block depot.
2074 StorageImpl_SetNextBlockInChain(This
,
2078 /* Save new header information.
2080 StorageImpl_SaveFileHeader(This
);
2085 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2087 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2090 * Grow the extended depot.
2092 ULONG extIndex
= BLOCK_UNUSED
;
2093 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2094 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2096 if (extBlockOffset
== 0)
2098 /* We need an extended block.
2100 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2101 This
->extBigBlockDepotCount
++;
2102 depotBlockIndexPos
= extIndex
+ 1;
2105 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2108 * Add a block depot and mark it in the extended block.
2110 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2111 This
->bigBlockDepotCount
++;
2112 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2114 /* Flag the block depot.
2116 StorageImpl_SetNextBlockInChain(This
,
2120 /* If necessary, flag the extended depot block.
2122 if (extIndex
!= BLOCK_UNUSED
)
2123 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2125 /* Save header information.
2127 StorageImpl_SaveFileHeader(This
);
2131 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2133 if (depotBuffer
!= 0)
2135 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2136 ( nextBlockIndex
!= BLOCK_UNUSED
))
2138 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2140 if (nextBlockIndex
== BLOCK_UNUSED
)
2142 freeBlock
= (depotIndex
* blocksPerDepot
) +
2143 (depotBlockOffset
/sizeof(ULONG
));
2146 depotBlockOffset
+= sizeof(ULONG
);
2149 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2153 depotBlockOffset
= 0;
2156 This
->prevFreeBlock
= freeBlock
;
2161 /******************************************************************************
2162 * Storage32Impl_AddBlockDepot
2164 * This will create a depot block, essentially it is a block initialized
2167 void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2171 blockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
2174 * Initialize blocks as free
2176 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2178 StorageImpl_ReleaseBigBlock(This
, blockBuffer
);
2181 /******************************************************************************
2182 * Storage32Impl_GetExtDepotBlock
2184 * Returns the index of the block that corresponds to the specified depot
2185 * index. This method is only for depot indexes equal or greater than
2186 * COUNT_BBDEPOTINHEADER.
2188 ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2190 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2191 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2192 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2193 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2194 ULONG blockIndex
= BLOCK_UNUSED
;
2195 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2197 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2199 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2200 return BLOCK_UNUSED
;
2202 while (extBlockCount
> 0)
2204 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2208 if (extBlockIndex
!= BLOCK_UNUSED
)
2212 depotBuffer
= StorageImpl_GetROBigBlock(This
, extBlockIndex
);
2214 if (depotBuffer
!= 0)
2216 StorageUtl_ReadDWord(depotBuffer
,
2217 extBlockOffset
* sizeof(ULONG
),
2220 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2227 /******************************************************************************
2228 * Storage32Impl_SetExtDepotBlock
2230 * Associates the specified block index to the specified depot index.
2231 * This method is only for depot indexes equal or greater than
2232 * COUNT_BBDEPOTINHEADER.
2234 void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
,
2238 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2239 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2240 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2241 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2242 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2244 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2246 while (extBlockCount
> 0)
2248 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2252 if (extBlockIndex
!= BLOCK_UNUSED
)
2256 depotBuffer
= StorageImpl_GetBigBlock(This
, extBlockIndex
);
2258 if (depotBuffer
!= 0)
2260 StorageUtl_WriteDWord(depotBuffer
,
2261 extBlockOffset
* sizeof(ULONG
),
2264 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2269 /******************************************************************************
2270 * Storage32Impl_AddExtBlockDepot
2272 * Creates an extended depot block.
2274 ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2276 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2277 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2278 BYTE
* depotBuffer
= NULL
;
2279 ULONG index
= BLOCK_UNUSED
;
2280 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2281 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2282 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2284 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2285 blocksPerDepotBlock
;
2287 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2290 * The first extended block.
2292 This
->extBigBlockDepotStart
= index
;
2298 * Follow the chain to the last one.
2300 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2302 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2306 * Add the new extended block to the chain.
2308 depotBuffer
= StorageImpl_GetBigBlock(This
, nextExtBlock
);
2309 StorageUtl_WriteDWord(depotBuffer
, nextBlockOffset
, index
);
2310 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2314 * Initialize this block.
2316 depotBuffer
= StorageImpl_GetBigBlock(This
, index
);
2317 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2318 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2323 /******************************************************************************
2324 * Storage32Impl_FreeBigBlock
2326 * This method will flag the specified block as free in the big block depot.
2328 void StorageImpl_FreeBigBlock(
2332 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2334 if (blockIndex
< This
->prevFreeBlock
)
2335 This
->prevFreeBlock
= blockIndex
;
2338 /************************************************************************
2339 * Storage32Impl_GetNextBlockInChain
2341 * This method will retrieve the block index of the next big block in
2344 * Params: This - Pointer to the Storage object.
2345 * blockIndex - Index of the block to retrieve the chain
2348 * Returns: This method returns the index of the next block in the chain.
2349 * It will return the constants:
2350 * BLOCK_SPECIAL - If the block given was not part of a
2352 * BLOCK_END_OF_CHAIN - If the block given was the last in
2354 * BLOCK_UNUSED - If the block given was not past of a chain
2356 * BLOCK_EXTBBDEPOT - This block is part of the extended
2359 * See Windows documentation for more details on IStorage methods.
2361 ULONG
StorageImpl_GetNextBlockInChain(
2365 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2366 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2367 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2368 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2370 ULONG depotBlockIndexPos
;
2372 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2375 * Cache the currently accessed depot block.
2377 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2379 This
->indexBlockDepotCached
= depotBlockCount
;
2381 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2383 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2388 * We have to look in the extended depot.
2390 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2393 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2399 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2401 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &nextBlockIndex
);
2402 This
->blockDepotCached
[index
] = nextBlockIndex
;
2405 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2409 nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2411 return nextBlockIndex
;
2414 /******************************************************************************
2415 * Storage32Impl_GetNextExtendedBlock
2417 * Given an extended block this method will return the next extended block.
2420 * The last ULONG of an extended block is the block index of the next
2421 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2425 * - The index of the next extended block
2426 * - BLOCK_UNUSED: there is no next extended block.
2427 * - Any other return values denotes failure.
2429 ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
2431 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2432 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2435 depotBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
2439 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2441 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2444 return nextBlockIndex
;
2447 /******************************************************************************
2448 * Storage32Impl_SetNextBlockInChain
2450 * This method will write the index of the specified block's next block
2451 * in the big block depot.
2453 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2456 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2457 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2458 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2461 void StorageImpl_SetNextBlockInChain(
2466 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2467 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2468 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2469 ULONG depotBlockIndexPos
;
2472 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2473 assert(blockIndex
!= nextBlock
);
2475 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2477 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2482 * We have to look in the extended depot.
2484 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2487 depotBuffer
= StorageImpl_GetBigBlock(This
, depotBlockIndexPos
);
2491 StorageUtl_WriteDWord(depotBuffer
, depotBlockOffset
, nextBlock
);
2492 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2496 * Update the cached block depot, if necessary.
2498 if (depotBlockCount
== This
->indexBlockDepotCached
)
2500 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
2504 /******************************************************************************
2505 * Storage32Impl_LoadFileHeader
2507 * This method will read in the file header, i.e. big block index -1.
2509 HRESULT
StorageImpl_LoadFileHeader(
2512 HRESULT hr
= STG_E_FILENOTFOUND
;
2513 void* headerBigBlock
= NULL
;
2517 * Get a pointer to the big block of data containing the header.
2519 headerBigBlock
= StorageImpl_GetROBigBlock(This
, -1);
2522 * Extract the information from the header.
2524 if (headerBigBlock
!=0)
2527 * Check for the "magic number" signature and return an error if it is not
2530 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2532 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2533 return STG_E_OLDFORMAT
;
2536 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2538 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2539 return STG_E_INVALIDHEADER
;
2542 StorageUtl_ReadWord(
2544 OFFSET_BIGBLOCKSIZEBITS
,
2545 &This
->bigBlockSizeBits
);
2547 StorageUtl_ReadWord(
2549 OFFSET_SMALLBLOCKSIZEBITS
,
2550 &This
->smallBlockSizeBits
);
2552 StorageUtl_ReadDWord(
2554 OFFSET_BBDEPOTCOUNT
,
2555 &This
->bigBlockDepotCount
);
2557 StorageUtl_ReadDWord(
2559 OFFSET_ROOTSTARTBLOCK
,
2560 &This
->rootStartBlock
);
2562 StorageUtl_ReadDWord(
2564 OFFSET_SBDEPOTSTART
,
2565 &This
->smallBlockDepotStart
);
2567 StorageUtl_ReadDWord(
2569 OFFSET_EXTBBDEPOTSTART
,
2570 &This
->extBigBlockDepotStart
);
2572 StorageUtl_ReadDWord(
2574 OFFSET_EXTBBDEPOTCOUNT
,
2575 &This
->extBigBlockDepotCount
);
2577 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2579 StorageUtl_ReadDWord(
2581 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2582 &(This
->bigBlockDepotStart
[index
]));
2586 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2590 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
2591 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
2595 This
->bigBlockSize
= 0x000000001 >> (DWORD
)This
->bigBlockSizeBits
;
2596 This
->smallBlockSize
= 0x000000001 >> (DWORD
)This
->smallBlockSizeBits
;
2600 * Right now, the code is making some assumptions about the size of the
2601 * blocks, just make sure they are what we're expecting.
2603 assert( (This
->bigBlockSize
==DEF_BIG_BLOCK_SIZE
) &&
2604 (This
->smallBlockSize
==DEF_SMALL_BLOCK_SIZE
));
2607 * Release the block.
2609 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2617 /******************************************************************************
2618 * Storage32Impl_SaveFileHeader
2620 * This method will save to the file the header, i.e. big block -1.
2622 void StorageImpl_SaveFileHeader(
2625 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
2630 * Get a pointer to the big block of data containing the header.
2632 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
2635 * If the block read failed, the file is probably new.
2640 * Initialize for all unknown fields.
2642 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
2645 * Initialize the magic number.
2647 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
2650 * And a bunch of things we don't know what they mean
2652 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
2653 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
2654 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
2655 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
2656 StorageUtl_WriteDWord(headerBigBlock
, 0x40, (DWORD
)0x0001);
2660 * Write the information to the header.
2662 if (headerBigBlock
!=0)
2664 StorageUtl_WriteWord(
2666 OFFSET_BIGBLOCKSIZEBITS
,
2667 This
->bigBlockSizeBits
);
2669 StorageUtl_WriteWord(
2671 OFFSET_SMALLBLOCKSIZEBITS
,
2672 This
->smallBlockSizeBits
);
2674 StorageUtl_WriteDWord(
2676 OFFSET_BBDEPOTCOUNT
,
2677 This
->bigBlockDepotCount
);
2679 StorageUtl_WriteDWord(
2681 OFFSET_ROOTSTARTBLOCK
,
2682 This
->rootStartBlock
);
2684 StorageUtl_WriteDWord(
2686 OFFSET_SBDEPOTSTART
,
2687 This
->smallBlockDepotStart
);
2689 StorageUtl_WriteDWord(
2691 OFFSET_EXTBBDEPOTSTART
,
2692 This
->extBigBlockDepotStart
);
2694 StorageUtl_WriteDWord(
2696 OFFSET_EXTBBDEPOTCOUNT
,
2697 This
->extBigBlockDepotCount
);
2699 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2701 StorageUtl_WriteDWord(
2703 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2704 (This
->bigBlockDepotStart
[index
]));
2709 * Write the big block back to the file.
2711 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
2714 /******************************************************************************
2715 * Storage32Impl_ReadProperty
2717 * This method will read the specified property from the property chain.
2719 BOOL
StorageImpl_ReadProperty(
2722 StgProperty
* buffer
)
2724 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
2725 ULARGE_INTEGER offsetInPropSet
;
2729 offsetInPropSet
.HighPart
= 0;
2730 offsetInPropSet
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
2732 readSucessful
= BlockChainStream_ReadAt(
2733 This
->rootBlockChain
,
2741 memset(buffer
->name
, 0, sizeof(buffer
->name
));
2744 currentProperty
+OFFSET_PS_NAME
,
2745 PROPERTY_NAME_BUFFER_LEN
);
2747 memcpy(&buffer
->propertyType
, currentProperty
+ OFFSET_PS_PROPERTYTYPE
, 1);
2749 StorageUtl_ReadWord(
2751 OFFSET_PS_NAMELENGTH
,
2752 &buffer
->sizeOfNameString
);
2754 StorageUtl_ReadDWord(
2756 OFFSET_PS_PREVIOUSPROP
,
2757 &buffer
->previousProperty
);
2759 StorageUtl_ReadDWord(
2762 &buffer
->nextProperty
);
2764 StorageUtl_ReadDWord(
2767 &buffer
->dirProperty
);
2769 StorageUtl_ReadGUID(
2772 &buffer
->propertyUniqueID
);
2774 StorageUtl_ReadDWord(
2777 &buffer
->timeStampS1
);
2779 StorageUtl_ReadDWord(
2782 &buffer
->timeStampD1
);
2784 StorageUtl_ReadDWord(
2787 &buffer
->timeStampS2
);
2789 StorageUtl_ReadDWord(
2792 &buffer
->timeStampD2
);
2794 StorageUtl_ReadDWord(
2796 OFFSET_PS_STARTBLOCK
,
2797 &buffer
->startingBlock
);
2799 StorageUtl_ReadDWord(
2802 &buffer
->size
.LowPart
);
2804 buffer
->size
.HighPart
= 0;
2807 return readSucessful
;
2810 /*********************************************************************
2811 * Write the specified property into the property chain
2813 BOOL
StorageImpl_WriteProperty(
2816 StgProperty
* buffer
)
2818 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
2819 ULARGE_INTEGER offsetInPropSet
;
2820 BOOL writeSucessful
;
2823 offsetInPropSet
.HighPart
= 0;
2824 offsetInPropSet
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
2826 memset(currentProperty
, 0, PROPSET_BLOCK_SIZE
);
2829 currentProperty
+ OFFSET_PS_NAME
,
2831 PROPERTY_NAME_BUFFER_LEN
);
2833 memcpy(currentProperty
+ OFFSET_PS_PROPERTYTYPE
, &buffer
->propertyType
, 1);
2836 * Reassign the size in case of mistake....
2838 buffer
->sizeOfNameString
= (lstrlenW(buffer
->name
)+1) * sizeof(WCHAR
);
2840 StorageUtl_WriteWord(
2842 OFFSET_PS_NAMELENGTH
,
2843 buffer
->sizeOfNameString
);
2845 StorageUtl_WriteDWord(
2847 OFFSET_PS_PREVIOUSPROP
,
2848 buffer
->previousProperty
);
2850 StorageUtl_WriteDWord(
2853 buffer
->nextProperty
);
2855 StorageUtl_WriteDWord(
2858 buffer
->dirProperty
);
2860 StorageUtl_WriteGUID(
2863 &buffer
->propertyUniqueID
);
2865 StorageUtl_WriteDWord(
2868 buffer
->timeStampS1
);
2870 StorageUtl_WriteDWord(
2873 buffer
->timeStampD1
);
2875 StorageUtl_WriteDWord(
2878 buffer
->timeStampS2
);
2880 StorageUtl_WriteDWord(
2883 buffer
->timeStampD2
);
2885 StorageUtl_WriteDWord(
2887 OFFSET_PS_STARTBLOCK
,
2888 buffer
->startingBlock
);
2890 StorageUtl_WriteDWord(
2893 buffer
->size
.LowPart
);
2895 writeSucessful
= BlockChainStream_WriteAt(This
->rootBlockChain
,
2900 return writeSucessful
;
2903 BOOL
StorageImpl_ReadBigBlock(
2908 void* bigBlockBuffer
;
2910 bigBlockBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
2912 if (bigBlockBuffer
!=0)
2914 memcpy(buffer
, bigBlockBuffer
, This
->bigBlockSize
);
2916 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
2924 BOOL
StorageImpl_WriteBigBlock(
2929 void* bigBlockBuffer
;
2931 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
2933 if (bigBlockBuffer
!=0)
2935 memcpy(bigBlockBuffer
, buffer
, This
->bigBlockSize
);
2937 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
2945 void* StorageImpl_GetROBigBlock(
2949 return BIGBLOCKFILE_GetROBigBlock(This
->bigBlockFile
, blockIndex
);
2952 void* StorageImpl_GetBigBlock(
2956 return BIGBLOCKFILE_GetBigBlock(This
->bigBlockFile
, blockIndex
);
2959 void StorageImpl_ReleaseBigBlock(
2963 BIGBLOCKFILE_ReleaseBigBlock(This
->bigBlockFile
, pBigBlock
);
2966 /******************************************************************************
2967 * Storage32Impl_SmallBlocksToBigBlocks
2969 * This method will convert a small block chain to a big block chain.
2970 * The small block chain will be destroyed.
2972 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
2974 SmallBlockChainStream
** ppsbChain
)
2976 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
2977 ULARGE_INTEGER size
, offset
;
2978 ULONG cbRead
, cbWritten
, cbTotalRead
, cbTotalWritten
;
2979 ULONG propertyIndex
;
2980 BOOL successRead
, successWrite
;
2981 StgProperty chainProperty
;
2982 BYTE buffer
[DEF_SMALL_BLOCK_SIZE
];
2983 BlockChainStream
*bbTempChain
= NULL
;
2984 BlockChainStream
*bigBlockChain
= NULL
;
2987 * Create a temporary big block chain that doesn't have
2988 * an associated property. This temporary chain will be
2989 * used to copy data from small blocks to big blocks.
2991 bbTempChain
= BlockChainStream_Construct(This
,
2996 * Grow the big block chain.
2998 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
2999 BlockChainStream_SetSize(bbTempChain
, size
);
3002 * Copy the contents of the small block chain to the big block chain
3003 * by small block size increments.
3006 offset
.HighPart
= 0;
3012 successRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3017 cbTotalRead
+= cbRead
;
3019 successWrite
= BlockChainStream_WriteAt(bbTempChain
,
3024 cbTotalWritten
+= cbWritten
;
3026 offset
.LowPart
+= This
->smallBlockSize
;
3028 } while (successRead
&& successWrite
);
3030 assert(cbTotalRead
== cbTotalWritten
);
3033 * Destroy the small block chain.
3035 propertyIndex
= (*ppsbChain
)->ownerPropertyIndex
;
3038 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3039 SmallBlockChainStream_Destroy(*ppsbChain
);
3043 * Change the property information. This chain is now a big block chain
3044 * and it doesn't reside in the small blocks chain anymore.
3046 StorageImpl_ReadProperty(This
, propertyIndex
, &chainProperty
);
3048 chainProperty
.startingBlock
= bbHeadOfChain
;
3050 StorageImpl_WriteProperty(This
, propertyIndex
, &chainProperty
);
3053 * Destroy the temporary propertyless big block chain.
3054 * Create a new big block chain associated with this property.
3056 BlockChainStream_Destroy(bbTempChain
);
3057 bigBlockChain
= BlockChainStream_Construct(This
,
3061 return bigBlockChain
;
3064 /******************************************************************************
3065 ** Storage32InternalImpl implementation
3068 StorageInternalImpl
* StorageInternalImpl_Construct(
3069 StorageImpl
* ancestorStorage
,
3070 ULONG rootPropertyIndex
)
3072 StorageInternalImpl
* newStorage
;
3075 * Allocate space for the new storage object
3077 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl
));
3081 memset(newStorage
, 0, sizeof(StorageInternalImpl
));
3084 * Initialize the virtual function table.
3086 newStorage
->lpvtbl
= &Storage32InternalImpl_Vtbl
;
3087 newStorage
->v_destructor
= &StorageInternalImpl_Destroy
;
3090 * Keep the ancestor storage pointer and nail a reference to it.
3092 newStorage
->ancestorStorage
= ancestorStorage
;
3093 StorageBaseImpl_AddRef((IStorage
*)(newStorage
->ancestorStorage
));
3096 * Keep the index of the root property set for this storage,
3098 newStorage
->rootPropertySetIndex
= rootPropertyIndex
;
3106 void StorageInternalImpl_Destroy(
3107 StorageInternalImpl
* This
)
3109 StorageBaseImpl_Release((IStorage
*)This
->ancestorStorage
);
3110 HeapFree(GetProcessHeap(), 0, This
);
3113 /******************************************************************************
3115 ** Storage32InternalImpl_Commit
3117 ** The non-root storages cannot be opened in transacted mode thus this function
3120 HRESULT WINAPI
StorageInternalImpl_Commit(
3122 DWORD grfCommitFlags
) /* [in] */
3127 /******************************************************************************
3129 ** Storage32InternalImpl_Revert
3131 ** The non-root storages cannot be opened in transacted mode thus this function
3134 HRESULT WINAPI
StorageInternalImpl_Revert(
3140 /******************************************************************************
3141 ** IEnumSTATSTGImpl implementation
3144 IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
3145 StorageImpl
* parentStorage
,
3146 ULONG firstPropertyNode
)
3148 IEnumSTATSTGImpl
* newEnumeration
;
3150 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
3152 if (newEnumeration
!=0)
3155 * Set-up the virtual function table and reference count.
3157 newEnumeration
->lpvtbl
= &IEnumSTATSTGImpl_Vtbl
;
3158 newEnumeration
->ref
= 0;
3161 * We want to nail-down the reference to the storage in case the
3162 * enumeration out-lives the storage in the client application.
3164 newEnumeration
->parentStorage
= parentStorage
;
3165 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
3167 newEnumeration
->firstPropertyNode
= firstPropertyNode
;
3170 * Initialize the search stack
3172 newEnumeration
->stackSize
= 0;
3173 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
3174 newEnumeration
->stackToVisit
=
3175 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
)*ENUMSTATSGT_SIZE_INCREMENT
);
3178 * Make sure the current node of the iterator is the first one.
3180 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
3183 return newEnumeration
;
3186 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3188 IStorage_Release((IStorage
*)This
->parentStorage
);
3189 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3190 HeapFree(GetProcessHeap(), 0, This
);
3193 HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3194 IEnumSTATSTG
* iface
,
3198 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3201 * Perform a sanity check on the parameters.
3204 return E_INVALIDARG
;
3207 * Initialize the return parameter.
3212 * Compare the riid with the interface IDs implemented by this object.
3214 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
3216 *ppvObject
= (IEnumSTATSTG
*)This
;
3218 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IEnumSTATSTG
)) == 0)
3220 *ppvObject
= (IEnumSTATSTG
*)This
;
3224 * Check that we obtained an interface.
3226 if ((*ppvObject
)==0)
3227 return E_NOINTERFACE
;
3230 * Query Interface always increases the reference count by one when it is
3233 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG
*)This
);
3238 ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3239 IEnumSTATSTG
* iface
)
3241 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3247 ULONG WINAPI
IEnumSTATSTGImpl_Release(
3248 IEnumSTATSTG
* iface
)
3250 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3258 * If the reference count goes down to 0, perform suicide.
3262 IEnumSTATSTGImpl_Destroy(This
);
3268 HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3269 IEnumSTATSTG
* iface
,
3272 ULONG
* pceltFetched
)
3274 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3276 StgProperty currentProperty
;
3277 STATSTG
* currentReturnStruct
= rgelt
;
3278 ULONG objectFetched
= 0;
3279 ULONG currentSearchNode
;
3282 * Perform a sanity check on the parameters.
3284 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3285 return E_INVALIDARG
;
3288 * To avoid the special case, get another pointer to a ULONG value if
3289 * the caller didn't supply one.
3291 if (pceltFetched
==0)
3292 pceltFetched
= &objectFetched
;
3295 * Start the iteration, we will iterate until we hit the end of the
3296 * linked list or until we hit the number of items to iterate through
3301 * Start with the node at the top of the stack.
3303 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3305 while ( ( *pceltFetched
< celt
) &&
3306 ( currentSearchNode
!=PROPERTY_NULL
) )
3309 * Remove the top node from the stack
3311 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3314 * Read the property from the storage.
3316 StorageImpl_ReadProperty(This
->parentStorage
,
3321 * Copy the information to the return buffer.
3323 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct
,
3328 * Step to the next item in the iteration
3331 currentReturnStruct
++;
3334 * Push the next search node in the search stack.
3336 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3339 * continue the iteration.
3341 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3344 if (*pceltFetched
== celt
)
3351 HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3352 IEnumSTATSTG
* iface
,
3355 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3357 StgProperty currentProperty
;
3358 ULONG objectFetched
= 0;
3359 ULONG currentSearchNode
;
3362 * Start with the node at the top of the stack.
3364 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3366 while ( (objectFetched
< celt
) &&
3367 (currentSearchNode
!=PROPERTY_NULL
) )
3370 * Remove the top node from the stack
3372 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3375 * Read the property from the storage.
3377 StorageImpl_ReadProperty(This
->parentStorage
,
3382 * Step to the next item in the iteration
3387 * Push the next search node in the search stack.
3389 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3392 * continue the iteration.
3394 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3397 if (objectFetched
== celt
)
3403 HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3404 IEnumSTATSTG
* iface
)
3406 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3408 StgProperty rootProperty
;
3412 * Re-initialize the search stack to an empty stack
3414 This
->stackSize
= 0;
3417 * Read the root property from the storage.
3419 readSucessful
= StorageImpl_ReadProperty(
3420 This
->parentStorage
,
3421 This
->firstPropertyNode
,
3426 assert(rootProperty
.sizeOfNameString
!=0);
3429 * Push the search node in the search stack.
3431 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.dirProperty
);
3437 HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3438 IEnumSTATSTG
* iface
,
3439 IEnumSTATSTG
** ppenum
)
3441 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3443 IEnumSTATSTGImpl
* newClone
;
3446 * Perform a sanity check on the parameters.
3449 return E_INVALIDARG
;
3451 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3452 This
->firstPropertyNode
);
3456 * The new clone enumeration must point to the same current node as
3459 newClone
->stackSize
= This
->stackSize
;
3460 newClone
->stackMaxSize
= This
->stackMaxSize
;
3461 newClone
->stackToVisit
=
3462 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3465 newClone
->stackToVisit
,
3467 sizeof(ULONG
) * newClone
->stackSize
);
3469 *ppenum
= (IEnumSTATSTG
*)newClone
;
3472 * Don't forget to nail down a reference to the clone before
3475 IEnumSTATSTGImpl_AddRef(*ppenum
);
3480 INT
IEnumSTATSTGImpl_FindParentProperty(
3481 IEnumSTATSTGImpl
*This
,
3482 ULONG childProperty
,
3483 StgProperty
*currentProperty
,
3486 ULONG currentSearchNode
;
3490 * To avoid the special case, get another pointer to a ULONG value if
3491 * the caller didn't supply one.
3494 thisNodeId
= &foundNode
;
3497 * Start with the node at the top of the stack.
3499 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3502 while (currentSearchNode
!=PROPERTY_NULL
)
3505 * Store the current node in the returned parameters
3507 *thisNodeId
= currentSearchNode
;
3510 * Remove the top node from the stack
3512 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3515 * Read the property from the storage.
3517 StorageImpl_ReadProperty(
3518 This
->parentStorage
,
3522 if (currentProperty
->previousProperty
== childProperty
)
3523 return PROPERTY_RELATION_PREVIOUS
;
3525 else if (currentProperty
->nextProperty
== childProperty
)
3526 return PROPERTY_RELATION_NEXT
;
3528 else if (currentProperty
->dirProperty
== childProperty
)
3529 return PROPERTY_RELATION_DIR
;
3532 * Push the next search node in the search stack.
3534 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3537 * continue the iteration.
3539 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3542 return PROPERTY_NULL
;
3545 ULONG
IEnumSTATSTGImpl_FindProperty(
3546 IEnumSTATSTGImpl
* This
,
3547 const OLECHAR
* lpszPropName
,
3548 StgProperty
* currentProperty
)
3550 ULONG currentSearchNode
;
3553 * Start with the node at the top of the stack.
3555 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3557 while (currentSearchNode
!=PROPERTY_NULL
)
3560 * Remove the top node from the stack
3562 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3565 * Read the property from the storage.
3567 StorageImpl_ReadProperty(This
->parentStorage
,
3571 if ( propertyNameCmp(
3572 (OLECHAR
*)currentProperty
->name
,
3573 (OLECHAR
*)lpszPropName
) == 0)
3574 return currentSearchNode
;
3577 * Push the next search node in the search stack.
3579 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3582 * continue the iteration.
3584 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3587 return PROPERTY_NULL
;
3590 void IEnumSTATSTGImpl_PushSearchNode(
3591 IEnumSTATSTGImpl
* This
,
3594 StgProperty rootProperty
;
3598 * First, make sure we're not trying to push an unexisting node.
3600 if (nodeToPush
==PROPERTY_NULL
)
3604 * First push the node to the stack
3606 if (This
->stackSize
== This
->stackMaxSize
)
3608 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
3610 This
->stackToVisit
= HeapReAlloc(
3614 sizeof(ULONG
) * This
->stackMaxSize
);
3617 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
3621 * Read the root property from the storage.
3623 readSucessful
= StorageImpl_ReadProperty(
3624 This
->parentStorage
,
3630 assert(rootProperty
.sizeOfNameString
!=0);
3633 * Push the previous search node in the search stack.
3635 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.previousProperty
);
3639 ULONG
IEnumSTATSTGImpl_PopSearchNode(
3640 IEnumSTATSTGImpl
* This
,
3645 if (This
->stackSize
== 0)
3646 return PROPERTY_NULL
;
3648 topNode
= This
->stackToVisit
[This
->stackSize
-1];
3656 /******************************************************************************
3657 ** StorageUtl implementation
3660 void StorageUtl_ReadWord(void* buffer
, ULONG offset
, WORD
* value
)
3662 memcpy(value
, (BYTE
*)buffer
+offset
, sizeof(WORD
));
3665 void StorageUtl_WriteWord(void* buffer
, ULONG offset
, WORD value
)
3667 memcpy((BYTE
*)buffer
+offset
, &value
, sizeof(WORD
));
3670 void StorageUtl_ReadDWord(void* buffer
, ULONG offset
, DWORD
* value
)
3672 memcpy(value
, (BYTE
*)buffer
+offset
, sizeof(DWORD
));
3675 void StorageUtl_WriteDWord(void* buffer
, ULONG offset
, DWORD value
)
3677 memcpy((BYTE
*)buffer
+offset
, &value
, sizeof(DWORD
));
3680 void StorageUtl_ReadGUID(void* buffer
, ULONG offset
, GUID
* value
)
3682 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
3683 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
3684 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
3686 memcpy(value
->Data4
, (BYTE
*)buffer
+offset
+8, sizeof(value
->Data4
));
3689 void StorageUtl_WriteGUID(void* buffer
, ULONG offset
, GUID
* value
)
3691 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
3692 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
3693 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
3695 memcpy((BYTE
*)buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
3698 void StorageUtl_CopyPropertyToSTATSTG(
3699 STATSTG
* destination
,
3700 StgProperty
* source
,
3704 * The copy of the string occurs only when the flag is not set
3706 if ((statFlags
& STATFLAG_NONAME
) != 0)
3708 destination
->pwcsName
= 0;
3712 destination
->pwcsName
=
3713 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
3715 lstrcpyW((LPWSTR
)destination
->pwcsName
, source
->name
);
3718 switch (source
->propertyType
)
3720 case PROPTYPE_STORAGE
:
3722 destination
->type
= STGTY_STORAGE
;
3724 case PROPTYPE_STREAM
:
3725 destination
->type
= STGTY_STREAM
;
3728 destination
->type
= STGTY_STREAM
;
3732 destination
->cbSize
= source
->size
;
3734 currentReturnStruct->mtime = {0}; TODO
3735 currentReturnStruct->ctime = {0};
3736 currentReturnStruct->atime = {0};
3738 destination
->grfMode
= 0;
3739 destination
->grfLocksSupported
= 0;
3740 destination
->clsid
= source
->propertyUniqueID
;
3741 destination
->grfStateBits
= 0;
3742 destination
->reserved
= 0;
3745 /******************************************************************************
3746 ** BlockChainStream implementation
3749 BlockChainStream
* BlockChainStream_Construct(
3750 StorageImpl
* parentStorage
,
3751 ULONG
* headOfStreamPlaceHolder
,
3752 ULONG propertyIndex
)
3754 BlockChainStream
* newStream
;
3757 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
3759 newStream
->parentStorage
= parentStorage
;
3760 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
3761 newStream
->ownerPropertyIndex
= propertyIndex
;
3762 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
3763 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
3764 newStream
->numBlocks
= 0;
3766 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
3768 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
3770 newStream
->numBlocks
++;
3771 newStream
->tailIndex
= blockIndex
;
3773 blockIndex
= StorageImpl_GetNextBlockInChain(
3781 void BlockChainStream_Destroy(BlockChainStream
* This
)
3783 HeapFree(GetProcessHeap(), 0, This
);
3786 /******************************************************************************
3787 * BlockChainStream_GetHeadOfChain
3789 * Returns the head of this stream chain.
3790 * Some special chains don't have properties, their heads are kept in
3791 * This->headOfStreamPlaceHolder.
3794 ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
3796 StgProperty chainProperty
;
3799 if (This
->headOfStreamPlaceHolder
!= 0)
3800 return *(This
->headOfStreamPlaceHolder
);
3802 if (This
->ownerPropertyIndex
!= PROPERTY_NULL
)
3804 readSucessful
= StorageImpl_ReadProperty(
3805 This
->parentStorage
,
3806 This
->ownerPropertyIndex
,
3811 return chainProperty
.startingBlock
;
3815 return BLOCK_END_OF_CHAIN
;
3818 /******************************************************************************
3819 * BlockChainStream_GetCount
3821 * Returns the number of blocks that comprises this chain.
3822 * This is not the size of the stream as the last block may not be full!
3825 ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
3830 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
3832 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
3836 blockIndex
= StorageImpl_GetNextBlockInChain(
3837 This
->parentStorage
,
3844 /******************************************************************************
3845 * BlockChainStream_ReadAt
3847 * Reads a specified number of bytes from this chain at the specified offset.
3848 * bytesRead may be NULL.
3849 * Failure will be returned if the specified number of bytes has not been read.
3851 BOOL
BlockChainStream_ReadAt(BlockChainStream
* This
,
3852 ULARGE_INTEGER offset
,
3857 ULONG blockNoInSequence
= offset
.LowPart
/ This
->parentStorage
->bigBlockSize
;
3858 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->bigBlockSize
;
3859 ULONG bytesToReadInBuffer
;
3862 BYTE
* bigBlockBuffer
;
3864 if (This
->lastBlockNoInSequence
== 0xFFFFFFFF)
3865 This
->lastBlockNoInSequence
= blockNoInSequence
;
3867 * Find the first block in the stream that contains part of the buffer.
3869 if (blockNoInSequence
> This
->lastBlockNoInSequence
)
3871 ULONG temp
= blockNoInSequence
;
3873 blockIndex
= This
->lastBlockNoInSequenceIndex
;
3874 blockNoInSequence
-= This
->lastBlockNoInSequence
;
3875 This
->lastBlockNoInSequence
= temp
;
3879 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
3880 This
->lastBlockNoInSequence
= blockNoInSequence
;
3883 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
3886 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
3888 blockNoInSequence
--;
3891 This
->lastBlockNoInSequenceIndex
= blockIndex
;
3894 * Start reading the buffer.
3897 bufferWalker
= buffer
;
3899 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
3902 * Calculate how many bytes we can copy from this big block.
3904 bytesToReadInBuffer
=
3905 MIN(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
3908 * Copy those bytes to the buffer
3911 StorageImpl_GetROBigBlock(This
->parentStorage
, blockIndex
);
3913 memcpy(bufferWalker
, bigBlockBuffer
+ offsetInBlock
, bytesToReadInBuffer
);
3915 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
3918 * Step to the next big block.
3921 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
3923 bufferWalker
+= bytesToReadInBuffer
;
3924 size
-= bytesToReadInBuffer
;
3925 *bytesRead
+= bytesToReadInBuffer
;
3926 offsetInBlock
= 0; /* There is no offset on the next block */
3933 /******************************************************************************
3934 * BlockChainStream_WriteAt
3936 * Writes the specified number of bytes to this chain at the specified offset.
3937 * bytesWritten may be NULL.
3938 * Will fail if not all specified number of bytes have been written.
3940 BOOL
BlockChainStream_WriteAt(BlockChainStream
* This
,
3941 ULARGE_INTEGER offset
,
3944 ULONG
* bytesWritten
)
3946 ULONG blockNoInSequence
= offset
.LowPart
/ This
->parentStorage
->bigBlockSize
;
3947 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->bigBlockSize
;
3951 BYTE
* bigBlockBuffer
;
3953 if (This
->lastBlockNoInSequence
== 0xFFFFFFFF)
3954 This
->lastBlockNoInSequence
= blockNoInSequence
;
3957 * Find the first block in the stream that contains part of the buffer.
3959 if (blockNoInSequence
> This
->lastBlockNoInSequence
)
3961 ULONG temp
= blockNoInSequence
;
3963 blockIndex
= This
->lastBlockNoInSequenceIndex
;
3964 blockNoInSequence
-= This
->lastBlockNoInSequence
;
3965 This
->lastBlockNoInSequence
= temp
;
3969 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
3970 This
->lastBlockNoInSequence
= blockNoInSequence
;
3973 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
3976 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
3978 blockNoInSequence
--;
3981 This
->lastBlockNoInSequenceIndex
= blockIndex
;
3984 * Here, I'm casting away the constness on the buffer variable
3985 * This is OK since we don't intend to modify that buffer.
3988 bufferWalker
= (BYTE
*)buffer
;
3990 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
3993 * Calculate how many bytes we can copy from this big block.
3996 MIN(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
3999 * Copy those bytes to the buffer
4001 bigBlockBuffer
= StorageImpl_GetBigBlock(This
->parentStorage
, blockIndex
);
4003 memcpy(bigBlockBuffer
+ offsetInBlock
, bufferWalker
, bytesToWrite
);
4005 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4008 * Step to the next big block.
4011 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4013 bufferWalker
+= bytesToWrite
;
4014 size
-= bytesToWrite
;
4015 *bytesWritten
+= bytesToWrite
;
4016 offsetInBlock
= 0; /* There is no offset on the next block */
4022 /******************************************************************************
4023 * BlockChainStream_Shrink
4025 * Shrinks this chain in the big block depot.
4027 BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4028 ULARGE_INTEGER newSize
)
4030 ULONG blockIndex
, extraBlock
;
4035 * Figure out how many blocks are needed to contain the new size
4037 numBlocks
= newSize
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4039 if ((newSize
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4042 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4045 * Go to the new end of chain
4047 while (count
< numBlocks
)
4050 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4055 /* Get the next block before marking the new end */
4057 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4059 /* Mark the new end of chain */
4060 StorageImpl_SetNextBlockInChain(
4061 This
->parentStorage
,
4063 BLOCK_END_OF_CHAIN
);
4065 This
->tailIndex
= blockIndex
;
4066 This
->numBlocks
= numBlocks
;
4069 * Mark the extra blocks as free
4071 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4074 StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
);
4076 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4077 extraBlock
= blockIndex
;
4083 /******************************************************************************
4084 * BlockChainStream_Enlarge
4086 * Grows this chain in the big block depot.
4088 BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4089 ULARGE_INTEGER newSize
)
4091 ULONG blockIndex
, currentBlock
;
4093 ULONG oldNumBlocks
= 0;
4095 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4098 * Empty chain. Create the head.
4100 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4102 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4103 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4105 BLOCK_END_OF_CHAIN
);
4107 if (This
->headOfStreamPlaceHolder
!= 0)
4109 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4113 StgProperty chainProp
;
4114 assert(This
->ownerPropertyIndex
!= PROPERTY_NULL
);
4116 StorageImpl_ReadProperty(
4117 This
->parentStorage
,
4118 This
->ownerPropertyIndex
,
4121 chainProp
.startingBlock
= blockIndex
;
4123 StorageImpl_WriteProperty(
4124 This
->parentStorage
,
4125 This
->ownerPropertyIndex
,
4129 This
->tailIndex
= blockIndex
;
4130 This
->numBlocks
= 1;
4134 * Figure out how many blocks are needed to contain this stream
4136 newNumBlocks
= newSize
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4138 if ((newSize
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4142 * Go to the current end of chain
4144 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4146 currentBlock
= blockIndex
;
4148 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4151 currentBlock
= blockIndex
;
4154 StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
);
4157 This
->tailIndex
= currentBlock
;
4160 currentBlock
= This
->tailIndex
;
4161 oldNumBlocks
= This
->numBlocks
;
4164 * Add new blocks to the chain
4166 while (oldNumBlocks
< newNumBlocks
)
4168 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4170 StorageImpl_SetNextBlockInChain(
4171 This
->parentStorage
,
4175 StorageImpl_SetNextBlockInChain(
4176 This
->parentStorage
,
4178 BLOCK_END_OF_CHAIN
);
4180 currentBlock
= blockIndex
;
4184 This
->tailIndex
= blockIndex
;
4185 This
->numBlocks
= newNumBlocks
;
4190 /******************************************************************************
4191 * BlockChainStream_SetSize
4193 * Sets the size of this stream. The big block depot will be updated.
4194 * The file will grow if we grow the chain.
4196 * TODO: Free the actual blocks in the file when we shrink the chain.
4197 * Currently, the blocks are still in the file. So the file size
4198 * doesn't shrink even if we shrink streams.
4200 BOOL
BlockChainStream_SetSize(
4201 BlockChainStream
* This
,
4202 ULARGE_INTEGER newSize
)
4204 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4206 if (newSize
.LowPart
== size
.LowPart
)
4209 if (newSize
.LowPart
< size
.LowPart
)
4211 BlockChainStream_Shrink(This
, newSize
);
4215 ULARGE_INTEGER fileSize
=
4216 BIGBLOCKFILE_GetSize(This
->parentStorage
->bigBlockFile
);
4218 ULONG diff
= newSize
.LowPart
- size
.LowPart
;
4221 * Make sure the file stays a multiple of blocksize
4223 if ((diff
% This
->parentStorage
->bigBlockSize
) != 0)
4224 diff
+= (This
->parentStorage
->bigBlockSize
-
4225 (diff
% This
->parentStorage
->bigBlockSize
) );
4227 fileSize
.LowPart
+= diff
;
4228 BIGBLOCKFILE_SetSize(This
->parentStorage
->bigBlockFile
, fileSize
);
4230 BlockChainStream_Enlarge(This
, newSize
);
4236 /******************************************************************************
4237 * BlockChainStream_GetSize
4239 * Returns the size of this chain.
4240 * Will return the block count if this chain doesn't have a property.
4242 ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4244 StgProperty chainProperty
;
4246 if(This
->headOfStreamPlaceHolder
== NULL
)
4249 * This chain is a data stream read the property and return
4250 * the appropriate size
4252 StorageImpl_ReadProperty(
4253 This
->parentStorage
,
4254 This
->ownerPropertyIndex
,
4257 return chainProperty
.size
;
4262 * this chain is a chain that does not have a property, figure out the
4263 * size by making the product number of used blocks times the
4266 ULARGE_INTEGER result
;
4267 result
.HighPart
= 0;
4270 BlockChainStream_GetCount(This
) *
4271 This
->parentStorage
->bigBlockSize
;
4277 /******************************************************************************
4278 ** SmallBlockChainStream implementation
4281 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4282 StorageImpl
* parentStorage
,
4283 ULONG propertyIndex
)
4285 SmallBlockChainStream
* newStream
;
4287 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4289 newStream
->parentStorage
= parentStorage
;
4290 newStream
->ownerPropertyIndex
= propertyIndex
;
4295 void SmallBlockChainStream_Destroy(
4296 SmallBlockChainStream
* This
)
4298 HeapFree(GetProcessHeap(), 0, This
);
4301 /******************************************************************************
4302 * SmallBlockChainStream_GetHeadOfChain
4304 * Returns the head of this chain of small blocks.
4306 ULONG
SmallBlockChainStream_GetHeadOfChain(
4307 SmallBlockChainStream
* This
)
4309 StgProperty chainProperty
;
4312 if (This
->ownerPropertyIndex
)
4314 readSucessful
= StorageImpl_ReadProperty(
4315 This
->parentStorage
,
4316 This
->ownerPropertyIndex
,
4321 return chainProperty
.startingBlock
;
4326 return BLOCK_END_OF_CHAIN
;
4329 /******************************************************************************
4330 * SmallBlockChainStream_GetNextBlockInChain
4332 * Returns the index of the next small block in this chain.
4335 * - BLOCK_END_OF_CHAIN: end of this chain
4336 * - BLOCK_UNUSED: small block 'blockIndex' is free
4338 ULONG
SmallBlockChainStream_GetNextBlockInChain(
4339 SmallBlockChainStream
* This
,
4342 ULARGE_INTEGER offsetOfBlockInDepot
;
4344 ULONG nextBlockInChain
= BLOCK_END_OF_CHAIN
;
4348 offsetOfBlockInDepot
.HighPart
= 0;
4349 offsetOfBlockInDepot
.LowPart
= blockIndex
* sizeof(ULONG
);
4352 * Read those bytes in the buffer from the small block file.
4354 success
= BlockChainStream_ReadAt(
4355 This
->parentStorage
->smallBlockDepotChain
,
4356 offsetOfBlockInDepot
,
4363 StorageUtl_ReadDWord(&buffer
, 0, &nextBlockInChain
);
4366 return nextBlockInChain
;
4369 /******************************************************************************
4370 * SmallBlockChainStream_SetNextBlockInChain
4372 * Writes the index of the next block of the specified block in the small
4374 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4375 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4377 void SmallBlockChainStream_SetNextBlockInChain(
4378 SmallBlockChainStream
* This
,
4382 ULARGE_INTEGER offsetOfBlockInDepot
;
4386 offsetOfBlockInDepot
.HighPart
= 0;
4387 offsetOfBlockInDepot
.LowPart
= blockIndex
* sizeof(ULONG
);
4389 StorageUtl_WriteDWord(&buffer
, 0, nextBlock
);
4392 * Read those bytes in the buffer from the small block file.
4394 BlockChainStream_WriteAt(
4395 This
->parentStorage
->smallBlockDepotChain
,
4396 offsetOfBlockInDepot
,
4402 /******************************************************************************
4403 * SmallBlockChainStream_FreeBlock
4405 * Flag small block 'blockIndex' as free in the small block depot.
4407 void SmallBlockChainStream_FreeBlock(
4408 SmallBlockChainStream
* This
,
4411 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4414 /******************************************************************************
4415 * SmallBlockChainStream_GetNextFreeBlock
4417 * Returns the index of a free small block. The small block depot will be
4418 * enlarged if necessary. The small block chain will also be enlarged if
4421 ULONG
SmallBlockChainStream_GetNextFreeBlock(
4422 SmallBlockChainStream
* This
)
4424 ULARGE_INTEGER offsetOfBlockInDepot
;
4427 ULONG blockIndex
= 0;
4428 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
4429 BOOL success
= TRUE
;
4430 ULONG smallBlocksPerBigBlock
;
4432 offsetOfBlockInDepot
.HighPart
= 0;
4435 * Scan the small block depot for a free block
4437 while (nextBlockIndex
!= BLOCK_UNUSED
)
4439 offsetOfBlockInDepot
.LowPart
= blockIndex
* sizeof(ULONG
);
4441 success
= BlockChainStream_ReadAt(
4442 This
->parentStorage
->smallBlockDepotChain
,
4443 offsetOfBlockInDepot
,
4449 * If we run out of space for the small block depot, enlarge it
4453 StorageUtl_ReadDWord(&buffer
, 0, &nextBlockIndex
);
4455 if (nextBlockIndex
!= BLOCK_UNUSED
)
4461 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
4463 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
4464 ULONG nextBlock
, newsbdIndex
;
4465 BYTE
* smallBlockDepot
;
4467 nextBlock
= sbdIndex
;
4468 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
4470 sbdIndex
= nextBlock
;
4472 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
);
4475 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4476 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
4477 StorageImpl_SetNextBlockInChain(
4478 This
->parentStorage
,
4482 StorageImpl_SetNextBlockInChain(
4483 This
->parentStorage
,
4485 BLOCK_END_OF_CHAIN
);
4488 * Initialize all the small blocks to free
4491 StorageImpl_GetBigBlock(This
->parentStorage
, newsbdIndex
);
4493 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
4494 StorageImpl_ReleaseBigBlock(This
->parentStorage
, smallBlockDepot
);
4499 * We have just created the small block depot.
4501 StgProperty rootProp
;
4505 * Save it in the header
4507 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
4508 StorageImpl_SaveFileHeader(This
->parentStorage
);
4511 * And allocate the first big block that will contain small blocks
4514 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4516 StorageImpl_SetNextBlockInChain(
4517 This
->parentStorage
,
4519 BLOCK_END_OF_CHAIN
);
4521 StorageImpl_ReadProperty(
4522 This
->parentStorage
,
4523 This
->parentStorage
->rootPropertySetIndex
,
4526 rootProp
.startingBlock
= sbStartIndex
;
4527 rootProp
.size
.HighPart
= 0;
4528 rootProp
.size
.LowPart
= This
->parentStorage
->bigBlockSize
;
4530 StorageImpl_WriteProperty(
4531 This
->parentStorage
,
4532 This
->parentStorage
->rootPropertySetIndex
,
4538 smallBlocksPerBigBlock
=
4539 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
4542 * Verify if we have to allocate big blocks to contain small blocks
4544 if (blockIndex
% smallBlocksPerBigBlock
== 0)
4546 StgProperty rootProp
;
4547 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
4549 StorageImpl_ReadProperty(
4550 This
->parentStorage
,
4551 This
->parentStorage
->rootPropertySetIndex
,
4554 if (rootProp
.size
.LowPart
<
4555 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
4557 rootProp
.size
.LowPart
+= This
->parentStorage
->bigBlockSize
;
4559 BlockChainStream_SetSize(
4560 This
->parentStorage
->smallBlockRootChain
,
4563 StorageImpl_WriteProperty(
4564 This
->parentStorage
,
4565 This
->parentStorage
->rootPropertySetIndex
,
4573 /******************************************************************************
4574 * SmallBlockChainStream_ReadAt
4576 * Reads a specified number of bytes from this chain at the specified offset.
4577 * bytesRead may be NULL.
4578 * Failure will be returned if the specified number of bytes has not been read.
4580 BOOL
SmallBlockChainStream_ReadAt(
4581 SmallBlockChainStream
* This
,
4582 ULARGE_INTEGER offset
,
4587 ULARGE_INTEGER offsetInBigBlockFile
;
4588 ULONG blockNoInSequence
=
4589 offset
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4591 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->smallBlockSize
;
4592 ULONG bytesToReadInBuffer
;
4594 ULONG bytesReadFromBigBlockFile
;
4598 * This should never happen on a small block file.
4600 assert(offset
.HighPart
==0);
4603 * Find the first block in the stream that contains part of the buffer.
4605 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4607 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4609 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4611 blockNoInSequence
--;
4615 * Start reading the buffer.
4618 bufferWalker
= buffer
;
4620 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4623 * Calculate how many bytes we can copy from this small block.
4625 bytesToReadInBuffer
=
4626 MIN(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
4629 * Calculate the offset of the small block in the small block file.
4631 offsetInBigBlockFile
.HighPart
= 0;
4632 offsetInBigBlockFile
.LowPart
=
4633 blockIndex
* This
->parentStorage
->smallBlockSize
;
4635 offsetInBigBlockFile
.LowPart
+= offsetInBlock
;
4638 * Read those bytes in the buffer from the small block file.
4640 BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
4641 offsetInBigBlockFile
,
4642 bytesToReadInBuffer
,
4644 &bytesReadFromBigBlockFile
);
4646 assert(bytesReadFromBigBlockFile
== bytesToReadInBuffer
);
4649 * Step to the next big block.
4651 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4652 bufferWalker
+= bytesToReadInBuffer
;
4653 size
-= bytesToReadInBuffer
;
4654 *bytesRead
+= bytesToReadInBuffer
;
4655 offsetInBlock
= 0; /* There is no offset on the next block */
4661 /******************************************************************************
4662 * SmallBlockChainStream_WriteAt
4664 * Writes the specified number of bytes to this chain at the specified offset.
4665 * bytesWritten may be NULL.
4666 * Will fail if not all specified number of bytes have been written.
4668 BOOL
SmallBlockChainStream_WriteAt(
4669 SmallBlockChainStream
* This
,
4670 ULARGE_INTEGER offset
,
4673 ULONG
* bytesWritten
)
4675 ULARGE_INTEGER offsetInBigBlockFile
;
4676 ULONG blockNoInSequence
=
4677 offset
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4679 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->smallBlockSize
;
4680 ULONG bytesToWriteInBuffer
;
4682 ULONG bytesWrittenFromBigBlockFile
;
4686 * This should never happen on a small block file.
4688 assert(offset
.HighPart
==0);
4691 * Find the first block in the stream that contains part of the buffer.
4693 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4695 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4697 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4699 blockNoInSequence
--;
4703 * Start writing the buffer.
4705 * Here, I'm casting away the constness on the buffer variable
4706 * This is OK since we don't intend to modify that buffer.
4709 bufferWalker
= (BYTE
*)buffer
;
4710 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4713 * Calculate how many bytes we can copy to this small block.
4715 bytesToWriteInBuffer
=
4716 MIN(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
4719 * Calculate the offset of the small block in the small block file.
4721 offsetInBigBlockFile
.HighPart
= 0;
4722 offsetInBigBlockFile
.LowPart
=
4723 blockIndex
* This
->parentStorage
->smallBlockSize
;
4725 offsetInBigBlockFile
.LowPart
+= offsetInBlock
;
4728 * Write those bytes in the buffer to the small block file.
4730 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockRootChain
,
4731 offsetInBigBlockFile
,
4732 bytesToWriteInBuffer
,
4734 &bytesWrittenFromBigBlockFile
);
4736 assert(bytesWrittenFromBigBlockFile
== bytesToWriteInBuffer
);
4739 * Step to the next big block.
4741 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4742 bufferWalker
+= bytesToWriteInBuffer
;
4743 size
-= bytesToWriteInBuffer
;
4744 *bytesWritten
+= bytesToWriteInBuffer
;
4745 offsetInBlock
= 0; /* There is no offset on the next block */
4751 /******************************************************************************
4752 * SmallBlockChainStream_Shrink
4754 * Shrinks this chain in the small block depot.
4756 BOOL
SmallBlockChainStream_Shrink(
4757 SmallBlockChainStream
* This
,
4758 ULARGE_INTEGER newSize
)
4760 ULONG blockIndex
, extraBlock
;
4764 numBlocks
= newSize
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4766 if ((newSize
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
4769 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4772 * Go to the new end of chain
4774 while (count
< numBlocks
)
4776 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4780 /* Get the next block before marking the new end */
4781 extraBlock
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4783 /* Mark the new end of chain */
4784 SmallBlockChainStream_SetNextBlockInChain(
4787 BLOCK_END_OF_CHAIN
);
4790 * Mark the extra blocks as free
4792 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4794 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
);
4795 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
4796 extraBlock
= blockIndex
;
4802 /******************************************************************************
4803 * SmallBlockChainStream_Enlarge
4805 * Grows this chain in the small block depot.
4807 BOOL
SmallBlockChainStream_Enlarge(
4808 SmallBlockChainStream
* This
,
4809 ULARGE_INTEGER newSize
)
4811 ULONG blockIndex
, currentBlock
;
4813 ULONG oldNumBlocks
= 0;
4815 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4820 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4822 StgProperty chainProp
;
4824 StorageImpl_ReadProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
4827 chainProp
.startingBlock
= SmallBlockChainStream_GetNextFreeBlock(This
);
4829 StorageImpl_WriteProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
4832 blockIndex
= chainProp
.startingBlock
;
4833 SmallBlockChainStream_SetNextBlockInChain(
4836 BLOCK_END_OF_CHAIN
);
4839 currentBlock
= blockIndex
;
4842 * Figure out how many blocks are needed to contain this stream
4844 newNumBlocks
= newSize
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4846 if ((newSize
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
4850 * Go to the current end of chain
4852 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4855 currentBlock
= blockIndex
;
4856 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
);
4860 * Add new blocks to the chain
4862 while (oldNumBlocks
< newNumBlocks
)
4864 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
4865 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
4867 SmallBlockChainStream_SetNextBlockInChain(
4870 BLOCK_END_OF_CHAIN
);
4872 currentBlock
= blockIndex
;
4879 /******************************************************************************
4880 * SmallBlockChainStream_GetCount
4882 * Returns the number of blocks that comprises this chain.
4883 * This is not the size of this chain as the last block may not be full!
4885 ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
4890 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4892 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4896 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4902 /******************************************************************************
4903 * SmallBlockChainStream_SetSize
4905 * Sets the size of this stream.
4906 * The file will grow if we grow the chain.
4908 * TODO: Free the actual blocks in the file when we shrink the chain.
4909 * Currently, the blocks are still in the file. So the file size
4910 * doesn't shrink even if we shrink streams.
4912 BOOL
SmallBlockChainStream_SetSize(
4913 SmallBlockChainStream
* This
,
4914 ULARGE_INTEGER newSize
)
4916 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
4918 if (newSize
.LowPart
== size
.LowPart
)
4921 if (newSize
.LowPart
< size
.LowPart
)
4923 SmallBlockChainStream_Shrink(This
, newSize
);
4927 SmallBlockChainStream_Enlarge(This
, newSize
);
4933 /******************************************************************************
4934 * SmallBlockChainStream_GetSize
4936 * Returns the size of this chain.
4938 ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
4940 StgProperty chainProperty
;
4942 StorageImpl_ReadProperty(
4943 This
->parentStorage
,
4944 This
->ownerPropertyIndex
,
4947 return chainProperty
.size
;
4950 /******************************************************************************
4951 * StgCreateDocfile32 [OLE32.144]
4953 HRESULT WINAPI
StgCreateDocfile(
4957 IStorage
**ppstgOpen
)
4959 StorageImpl
* newStorage
= 0;
4960 HANDLE hFile
= INVALID_HANDLE_VALUE
;
4965 DWORD fileAttributes
;
4966 WCHAR tempFileName
[MAX_PATH
];
4969 * Validate the parameters
4972 return STG_E_INVALIDPOINTER
;
4975 * Validate the STGM flags
4977 if ( FAILED( validateSTGM(grfMode
) ))
4978 return STG_E_INVALIDFLAG
;
4981 * Generate a unique name.
4985 WCHAR tempPath
[MAX_PATH
];
4986 WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
4988 memset(tempPath
, 0, sizeof(tempPath
));
4989 memset(tempFileName
, 0, sizeof(tempFileName
));
4991 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
4994 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
4995 pwcsName
= tempFileName
;
4997 return STG_E_INSUFFICIENTMEMORY
;
5001 * Interpret the STGM value grfMode
5003 shareMode
= GetShareModeFromSTGM(grfMode
);
5004 accessMode
= GetAccessModeFromSTGM(grfMode
);
5005 creationMode
= GetCreationModeFromSTGM(grfMode
);
5007 if (grfMode
& STGM_DELETEONRELEASE
)
5008 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5010 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5012 if (grfMode
& STGM_TRANSACTED
)
5013 FIXME(ole
, "Transacted mode not implemented.\n");
5016 * Initialize the "out" parameter.
5020 hFile
= CreateFileW(pwcsName
,
5028 if (hFile
== INVALID_HANDLE_VALUE
)
5034 * Allocate and initialize the new IStorage32object.
5036 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5038 if (newStorage
== 0)
5039 return STG_E_INSUFFICIENTMEMORY
;
5041 hr
= StorageImpl_Construct(
5048 HeapFree(GetProcessHeap(), 0, newStorage
);
5053 * Get an "out" pointer for the caller.
5055 hr
= StorageBaseImpl_QueryInterface(
5056 (IStorage
*)newStorage
,
5057 (REFIID
)&IID_IStorage
,
5063 /******************************************************************************
5064 * StgOpenStorage32 [OLE32.148]
5066 HRESULT WINAPI
StgOpenStorage(
5067 const OLECHAR
*pwcsName
,
5068 IStorage
*pstgPriority
,
5072 IStorage
**ppstgOpen
)
5074 StorageImpl
* newStorage
= 0;
5081 * Perform a sanity check
5083 if (( pwcsName
== 0) || (ppstgOpen
== 0) )
5084 return STG_E_INVALIDPOINTER
;
5087 * Validate the STGM flags
5089 if ( FAILED( validateSTGM(grfMode
) ))
5090 return STG_E_INVALIDFLAG
;
5093 * Interpret the STGM value grfMode
5095 shareMode
= GetShareModeFromSTGM(grfMode
);
5096 accessMode
= GetAccessModeFromSTGM(grfMode
);
5099 * Initialize the "out" parameter.
5103 hFile
= CreateFileW( pwcsName
,
5108 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
5112 if (hFile
==INVALID_HANDLE_VALUE
)
5118 * Allocate and initialize the new IStorage32object.
5120 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5122 if (newStorage
== 0)
5123 return STG_E_INSUFFICIENTMEMORY
;
5125 hr
= StorageImpl_Construct(
5132 HeapFree(GetProcessHeap(), 0, newStorage
);
5137 * Get an "out" pointer for the caller.
5139 hr
= StorageBaseImpl_QueryInterface(
5140 (IStorage
*)newStorage
,
5141 (REFIID
)&IID_IStorage
,
5147 /******************************************************************************
5148 * WriteClassStg32 [OLE32.148]
5150 * This method will store the specified CLSID in the specified storage object
5152 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
5158 hRes
= IStorage_SetClass(pStg
, rclsid
);
5163 /*******************************************************************************************
5166 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5168 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
5178 * read a STATSTG structure (contains the clsid) from the storage
5180 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_DEFAULT
);
5183 *pclsid
=pstatstg
.clsid
;
5188 /*************************************************************************************
5191 * This function loads an object from stream
5193 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
5198 FIXME(ole
,"(),stub!\n");
5200 res
=ReadClassStm(pStm
,&clsid
);
5202 if (SUCCEEDED(res
)){
5204 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
5208 res
=IPersistStream_Load((IPersistStream
*)ppvObj
,pStm
);
5214 /************************************************************************************************
5217 * This function saves an object with the IPersistStream interface on it to the specified stream
5219 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
5225 TRACE(ole
,"(%p,%p)\n",pPStm
,pStm
);
5227 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
5229 if (SUCCEEDED(res
)){
5231 res
=WriteClassStm(pStm
,&clsid
);
5235 res
=IPersistStream_Save(pPStm
,pStm
,FALSE
);
5241 /****************************************************************************
5242 * This method validate a STGM parameter that can contain the values below
5244 * STGM_DIRECT 0x00000000
5245 * STGM_TRANSACTED 0x00010000
5246 * STGM_SIMPLE 0x08000000
5248 * STGM_READ 0x00000000
5249 * STGM_WRITE 0x00000001
5250 * STGM_READWRITE 0x00000002
5252 * STGM_SHARE_DENY_NONE 0x00000040
5253 * STGM_SHARE_DENY_READ 0x00000030
5254 * STGM_SHARE_DENY_WRITE 0x00000020
5255 * STGM_SHARE_EXCLUSIVE 0x00000010
5257 * STGM_PRIORITY 0x00040000
5258 * STGM_DELETEONRELEASE 0x04000000
5260 * STGM_CREATE 0x00001000
5261 * STGM_CONVERT 0x00020000
5262 * STGM_FAILIFTHERE 0x00000000
5264 * STGM_NOSCRATCH 0x00100000
5265 * STGM_NOSNAPSHOT 0x00200000
5267 static HRESULT
validateSTGM(DWORD stgm
)
5269 BOOL bSTGM_TRANSACTED
= ((stgm
& STGM_TRANSACTED
) == STGM_TRANSACTED
);
5270 BOOL bSTGM_SIMPLE
= ((stgm
& STGM_SIMPLE
) == STGM_SIMPLE
);
5271 BOOL bSTGM_DIRECT
= ! (bSTGM_TRANSACTED
|| bSTGM_SIMPLE
);
5273 BOOL bSTGM_WRITE
= ((stgm
& STGM_WRITE
) == STGM_WRITE
);
5274 BOOL bSTGM_READWRITE
= ((stgm
& STGM_READWRITE
) == STGM_READWRITE
);
5275 BOOL bSTGM_READ
= ! (bSTGM_WRITE
|| bSTGM_READWRITE
);
5277 BOOL bSTGM_SHARE_DENY_NONE
=
5278 ((stgm
& STGM_SHARE_DENY_NONE
) == STGM_SHARE_DENY_NONE
);
5280 BOOL bSTGM_SHARE_DENY_READ
=
5281 ((stgm
& STGM_SHARE_DENY_READ
) == STGM_SHARE_DENY_READ
);
5283 BOOL bSTGM_SHARE_DENY_WRITE
=
5284 ((stgm
& STGM_SHARE_DENY_WRITE
) == STGM_SHARE_DENY_WRITE
);
5286 BOOL bSTGM_SHARE_EXCLUSIVE
=
5287 ((stgm
& STGM_SHARE_EXCLUSIVE
) == STGM_SHARE_EXCLUSIVE
);
5289 BOOL bSTGM_CREATE
= ((stgm
& STGM_CREATE
) == STGM_CREATE
);
5290 BOOL bSTGM_CONVERT
= ((stgm
& STGM_CONVERT
) == STGM_CONVERT
);
5292 BOOL bSTGM_NOSCRATCH
= ((stgm
& STGM_NOSCRATCH
) == STGM_NOSCRATCH
);
5293 BOOL bSTGM_NOSNAPSHOT
= ((stgm
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
);
5296 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5298 if ( ! bSTGM_DIRECT
)
5299 if( bSTGM_TRANSACTED
&& bSTGM_SIMPLE
)
5303 * STGM_WRITE | STGM_READWRITE | STGM_READ
5306 if( bSTGM_WRITE
&& bSTGM_READWRITE
)
5310 * STGM_SHARE_DENY_NONE | others
5311 * (I assume here that DENY_READ implies DENY_WRITE)
5313 if ( bSTGM_SHARE_DENY_NONE
)
5314 if ( bSTGM_SHARE_DENY_READ
||
5315 bSTGM_SHARE_DENY_WRITE
||
5316 bSTGM_SHARE_EXCLUSIVE
)
5320 * STGM_CREATE | STGM_CONVERT
5321 * if both are false, STGM_FAILIFTHERE is set to TRUE
5323 if ( bSTGM_CREATE
&& bSTGM_CONVERT
)
5327 * STGM_NOSCRATCH requires STGM_TRANSACTED
5329 if ( bSTGM_NOSCRATCH
&& ! bSTGM_TRANSACTED
)
5333 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5334 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5336 if (bSTGM_NOSNAPSHOT
)
5338 if ( ! ( bSTGM_TRANSACTED
&&
5339 !(bSTGM_SHARE_EXCLUSIVE
|| bSTGM_SHARE_DENY_WRITE
)) )
5346 /****************************************************************************
5347 * GetShareModeFromSTGM
5349 * This method will return a share mode flag from a STGM value.
5350 * The STGM value is assumed valid.
5352 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
5354 DWORD dwShareMode
= 0;
5355 BOOL bSTGM_SHARE_DENY_NONE
=
5356 ((stgm
& STGM_SHARE_DENY_NONE
) == STGM_SHARE_DENY_NONE
);
5358 BOOL bSTGM_SHARE_DENY_READ
=
5359 ((stgm
& STGM_SHARE_DENY_READ
) == STGM_SHARE_DENY_READ
);
5361 BOOL bSTGM_SHARE_DENY_WRITE
=
5362 ((stgm
& STGM_SHARE_DENY_WRITE
) == STGM_SHARE_DENY_WRITE
);
5364 BOOL bSTGM_SHARE_EXCLUSIVE
=
5365 ((stgm
& STGM_SHARE_EXCLUSIVE
) == STGM_SHARE_EXCLUSIVE
);
5367 if ((bSTGM_SHARE_EXCLUSIVE
) || (bSTGM_SHARE_DENY_READ
))
5370 if (bSTGM_SHARE_DENY_NONE
)
5371 dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
5373 if (bSTGM_SHARE_DENY_WRITE
)
5374 dwShareMode
= FILE_SHARE_READ
;
5379 /****************************************************************************
5380 * GetAccessModeFromSTGM
5382 * This method will return an access mode flag from a STGM value.
5383 * The STGM value is assumed valid.
5385 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
5387 DWORD dwDesiredAccess
= GENERIC_READ
;
5388 BOOL bSTGM_WRITE
= ((stgm
& STGM_WRITE
) == STGM_WRITE
);
5389 BOOL bSTGM_READWRITE
= ((stgm
& STGM_READWRITE
) == STGM_READWRITE
);
5390 BOOL bSTGM_READ
= ! (bSTGM_WRITE
|| bSTGM_READWRITE
);
5393 dwDesiredAccess
= GENERIC_READ
;
5396 dwDesiredAccess
|= GENERIC_WRITE
;
5398 if (bSTGM_READWRITE
)
5399 dwDesiredAccess
= GENERIC_READ
| GENERIC_WRITE
;
5401 return dwDesiredAccess
;
5404 /****************************************************************************
5405 * GetCreationModeFromSTGM
5407 * This method will return a creation mode flag from a STGM value.
5408 * The STGM value is assumed valid.
5410 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
5412 if ( stgm
& STGM_CREATE
)
5413 return CREATE_ALWAYS
;
5414 if (stgm
& STGM_CONVERT
) {
5415 FIXME(ole
, "STGM_CONVERT not implemented!\n");
5418 /* All other cases */
5419 if (stgm
& ~ (STGM_CREATE
|STGM_CONVERT
))
5420 FIXME(ole
,"unhandled storage mode : 0x%08lx\n",stgm
& ~ (STGM_CREATE
|STGM_CONVERT
));