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"
31 static const char rootPropertyName
[] = "Root Entry";
33 /***********************************************************************
34 * Forward declaration of internal functions used by the method DestroyElement
36 static HRESULT
deleteStorageProperty(
37 StorageImpl
*parentStorage
,
38 OLECHAR
*propertyToDeleteName
);
40 static HRESULT
deleteStreamProperty(
41 StorageImpl
*parentStorage
,
42 ULONG foundPropertyIndexToDelete
,
43 StgProperty propertyToDelete
);
45 static HRESULT
findPlaceholder(
47 ULONG propertyIndexToStore
,
48 ULONG storagePropertyIndex
,
51 static HRESULT
adjustPropertyChain(
53 StgProperty propertyToDelete
,
54 StgProperty parentProperty
,
55 ULONG parentPropertyId
,
58 /***********************************************************************
59 * Declaration of the functions used to manipulate StgProperty
62 static ULONG
getFreeProperty(
63 StorageImpl
*storage
);
65 static void updatePropertyChain(
67 ULONG newPropertyIndex
,
68 StgProperty newProperty
);
70 static LONG
propertyNameCmp(
72 OLECHAR
*currentProperty
);
75 /***********************************************************************
76 * Declaration of miscellaneous functions...
78 static HRESULT
validateSTGM(DWORD stgmValue
);
80 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
81 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
82 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
85 * Virtual function table for the IStorage32Impl class.
87 static ICOM_VTABLE(IStorage
) Storage32Impl_Vtbl
=
89 StorageBaseImpl_QueryInterface
,
90 StorageBaseImpl_AddRef
,
91 StorageBaseImpl_Release
,
92 StorageBaseImpl_CreateStream
,
93 StorageBaseImpl_OpenStream
,
94 StorageImpl_CreateStorage
,
95 StorageBaseImpl_OpenStorage
,
97 StorageImpl_MoveElementTo
,
100 StorageBaseImpl_EnumElements
,
101 StorageImpl_DestroyElement
,
102 StorageBaseImpl_RenameElement
,
103 StorageImpl_SetElementTimes
,
104 StorageBaseImpl_SetClass
,
105 StorageImpl_SetStateBits
,
110 * Virtual function table for the Storage32InternalImpl class.
112 static ICOM_VTABLE(IStorage
) Storage32InternalImpl_Vtbl
=
114 StorageBaseImpl_QueryInterface
,
115 StorageBaseImpl_AddRef
,
116 StorageBaseImpl_Release
,
117 StorageBaseImpl_CreateStream
,
118 StorageBaseImpl_OpenStream
,
119 StorageImpl_CreateStorage
,
120 StorageBaseImpl_OpenStorage
,
122 StorageImpl_MoveElementTo
,
123 StorageInternalImpl_Commit
,
124 StorageInternalImpl_Revert
,
125 StorageBaseImpl_EnumElements
,
126 StorageImpl_DestroyElement
,
127 StorageBaseImpl_RenameElement
,
128 StorageImpl_SetElementTimes
,
129 StorageBaseImpl_SetClass
,
130 StorageImpl_SetStateBits
,
135 * Virtual function table for the IEnumSTATSTGImpl class.
137 static ICOM_VTABLE(IEnumSTATSTG
) IEnumSTATSTGImpl_Vtbl
=
139 IEnumSTATSTGImpl_QueryInterface
,
140 IEnumSTATSTGImpl_AddRef
,
141 IEnumSTATSTGImpl_Release
,
142 IEnumSTATSTGImpl_Next
,
143 IEnumSTATSTGImpl_Skip
,
144 IEnumSTATSTGImpl_Reset
,
145 IEnumSTATSTGImpl_Clone
152 /************************************************************************
153 ** Storage32BaseImpl implementatiion
156 /************************************************************************
157 * Storage32BaseImpl_QueryInterface (IUnknown)
159 * This method implements the common QueryInterface for all IStorage32
160 * implementations contained in this file.
162 * See Windows documentation for more details on IUnknown methods.
164 HRESULT WINAPI
StorageBaseImpl_QueryInterface(
169 ICOM_THIS(StorageBaseImpl
,iface
);
171 * Perform a sanity check on the parameters.
173 if ( (This
==0) || (ppvObject
==0) )
177 * Initialize the return parameter.
182 * Compare the riid with the interface IDs implemented by this object.
184 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
186 *ppvObject
= (IStorage
*)This
;
188 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IStorage
)) == 0)
190 *ppvObject
= (IStorage
*)This
;
194 * Check that we obtained an interface.
197 return E_NOINTERFACE
;
200 * Query Interface always increases the reference count by one when it is
203 StorageBaseImpl_AddRef(iface
);
208 /************************************************************************
209 * Storage32BaseImpl_AddRef (IUnknown)
211 * This method implements the common AddRef for all IStorage32
212 * implementations contained in this file.
214 * See Windows documentation for more details on IUnknown methods.
216 ULONG WINAPI
StorageBaseImpl_AddRef(
219 ICOM_THIS(StorageBaseImpl
,iface
);
225 /************************************************************************
226 * Storage32BaseImpl_Release (IUnknown)
228 * This method implements the common Release for all IStorage32
229 * implementations contained in this file.
231 * See Windows documentation for more details on IUnknown methods.
233 ULONG WINAPI
StorageBaseImpl_Release(
236 ICOM_THIS(StorageBaseImpl
,iface
);
238 * Decrease the reference count on this object.
243 * If the reference count goes down to 0, perform suicide.
248 * Since we are using a system of base-classes, we want to call the
249 * destructor of the appropriate derived class. To do this, we are
250 * using virtual functions to implement the destructor.
252 This
->v_destructor(This
);
260 /************************************************************************
261 * Storage32BaseImpl_OpenStream (IStorage)
263 * This method will open the specified stream object from the current storage.
265 * See Windows documentation for more details on IStorage methods.
267 HRESULT WINAPI
StorageBaseImpl_OpenStream(
269 const OLECHAR
* pwcsName
, /* [string][in] */
270 void* reserved1
, /* [unique][in] */
271 DWORD grfMode
, /* [in] */
272 DWORD reserved2
, /* [in] */
273 IStream
** ppstm
) /* [out] */
275 ICOM_THIS(StorageBaseImpl
,iface
);
276 IEnumSTATSTGImpl
* propertyEnumeration
;
277 StgStreamImpl
* newStream
;
278 StgProperty currentProperty
;
279 ULONG foundPropertyIndex
;
282 * Perform a sanity check on the parameters.
284 if ( (pwcsName
==NULL
) || (ppstm
==0) )
288 * Initialize the out parameter
293 * Validate the STGM flags
295 if ( FAILED( validateSTGM(grfMode
) ))
296 return STG_E_INVALIDFLAG
;
301 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
302 (grfMode
& STGM_DELETEONRELEASE
) ||
303 (grfMode
& STGM_TRANSACTED
) )
304 return STG_E_INVALIDFUNCTION
;
307 * Create a property enumeration to search the properties
309 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
310 This
->ancestorStorage
,
311 This
->rootPropertySetIndex
);
314 * Search the enumeration for the property with the given name
316 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
322 * Delete the property enumeration since we don't need it anymore
324 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
327 * If it was found, construct the stream object and return a pointer to it.
329 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
330 (currentProperty
.propertyType
==PROPTYPE_STREAM
) )
332 newStream
= StgStreamImpl_Construct(This
, foundPropertyIndex
);
336 *ppstm
= (IStream
*)newStream
;
339 * Since we are returning a pointer to the interface, we have to
340 * nail down the reference.
342 StgStreamImpl_AddRef(*ppstm
);
347 return E_OUTOFMEMORY
;
350 return STG_E_FILENOTFOUND
;
353 /************************************************************************
354 * Storage32BaseImpl_OpenStorage (IStorage)
356 * This method will open a new storage object from the current storage.
358 * See Windows documentation for more details on IStorage methods.
360 HRESULT WINAPI
StorageBaseImpl_OpenStorage(
362 const OLECHAR
* pwcsName
, /* [string][unique][in] */
363 IStorage
* pstgPriority
, /* [unique][in] */
364 DWORD grfMode
, /* [in] */
365 SNB snbExclude
, /* [unique][in] */
366 DWORD reserved
, /* [in] */
367 IStorage
** ppstg
) /* [out] */
369 ICOM_THIS(StorageBaseImpl
,iface
);
370 StorageInternalImpl
* newStorage
;
371 IEnumSTATSTGImpl
* propertyEnumeration
;
372 StgProperty currentProperty
;
373 ULONG foundPropertyIndex
;
376 * Perform a sanity check on the parameters.
378 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
382 * Validate the STGM flags
384 if ( FAILED( validateSTGM(grfMode
) ))
385 return STG_E_INVALIDFLAG
;
390 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
391 (grfMode
& STGM_DELETEONRELEASE
) ||
392 (grfMode
& STGM_PRIORITY
) )
393 return STG_E_INVALIDFUNCTION
;
396 * Initialize the out parameter
401 * Create a property enumeration to search the properties
403 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
404 This
->ancestorStorage
,
405 This
->rootPropertySetIndex
);
408 * Search the enumeration for the property with the given name
410 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
416 * Delete the property enumeration since we don't need it anymore
418 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
421 * If it was found, construct the stream object and return a pointer to it.
423 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
424 (currentProperty
.propertyType
==PROPTYPE_STORAGE
) )
427 * Construct a new Storage object
429 newStorage
= StorageInternalImpl_Construct(
430 This
->ancestorStorage
,
435 *ppstg
= (IStorage
*)newStorage
;
438 * Since we are returning a pointer to the interface,
439 * we have to nail down the reference.
441 StorageBaseImpl_AddRef(*ppstg
);
446 return STG_E_INSUFFICIENTMEMORY
;
449 return STG_E_FILENOTFOUND
;
452 /************************************************************************
453 * Storage32BaseImpl_EnumElements (IStorage)
455 * This method will create an enumerator object that can be used to
456 * retrieve informatino about all the properties in the storage object.
458 * See Windows documentation for more details on IStorage methods.
460 HRESULT WINAPI
StorageBaseImpl_EnumElements(
462 DWORD reserved1
, /* [in] */
463 void* reserved2
, /* [size_is][unique][in] */
464 DWORD reserved3
, /* [in] */
465 IEnumSTATSTG
** ppenum
) /* [out] */
467 ICOM_THIS(StorageBaseImpl
,iface
);
468 IEnumSTATSTGImpl
* newEnum
;
471 * Perform a sanity check on the parameters.
473 if ( (This
==0) || (ppenum
==0))
477 * Construct the enumerator.
479 newEnum
= IEnumSTATSTGImpl_Construct(
480 This
->ancestorStorage
,
481 This
->rootPropertySetIndex
);
485 *ppenum
= (IEnumSTATSTG
*)newEnum
;
488 * Don't forget to nail down a reference to the new object before
491 IEnumSTATSTGImpl_AddRef(*ppenum
);
496 return E_OUTOFMEMORY
;
499 /************************************************************************
500 * Storage32BaseImpl_Stat (IStorage)
502 * This method will retrieve information about this storage object.
504 * See Windows documentation for more details on IStorage methods.
506 HRESULT WINAPI
StorageBaseImpl_Stat(
508 STATSTG
* pstatstg
, /* [out] */
509 DWORD grfStatFlag
) /* [in] */
511 ICOM_THIS(StorageBaseImpl
,iface
);
512 StgProperty curProperty
;
516 * Perform a sanity check on the parameters.
518 if ( (This
==0) || (pstatstg
==0))
522 * Read the information from the property.
524 readSucessful
= StorageImpl_ReadProperty(
525 This
->ancestorStorage
,
526 This
->rootPropertySetIndex
,
531 StorageUtl_CopyPropertyToSTATSTG(
542 /************************************************************************
543 * Storage32BaseImpl_RenameElement (IStorage)
545 * This method will rename the specified element.
547 * See Windows documentation for more details on IStorage methods.
549 * Implementation notes: The method used to rename consists of creating a clone
550 * of the deleted StgProperty object setting it with the new name and to
551 * perform a DestroyElement of the old StgProperty.
553 HRESULT WINAPI
StorageBaseImpl_RenameElement(
555 const OLECHAR
* pwcsOldName
, /* [in] */
556 const OLECHAR
* pwcsNewName
) /* [in] */
558 ICOM_THIS(StorageBaseImpl
,iface
);
559 IEnumSTATSTGImpl
* propertyEnumeration
;
560 StgProperty currentProperty
;
561 ULONG foundPropertyIndex
;
564 * Create a property enumeration to search the properties
566 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
567 This
->rootPropertySetIndex
);
570 * Search the enumeration for the new property name
572 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
576 if (foundPropertyIndex
!= PROPERTY_NULL
)
579 * There is already a property with the new name
581 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
582 return STG_E_FILEALREADYEXISTS
;
585 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)propertyEnumeration
);
588 * Search the enumeration for the old property name
590 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
595 * Delete the property enumeration since we don't need it anymore
597 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
599 if (foundPropertyIndex
!= PROPERTY_NULL
)
601 StgProperty renamedProperty
;
602 ULONG renamedPropertyIndex
;
605 * Setup a new property for the renamed property
607 renamedProperty
.sizeOfNameString
=
608 ( lstrlenW(pwcsNewName
)+1 ) * sizeof(WCHAR
);
610 if (renamedProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
611 return STG_E_INVALIDNAME
;
613 lstrcpyW(renamedProperty
.name
, pwcsNewName
);
615 renamedProperty
.propertyType
= currentProperty
.propertyType
;
616 renamedProperty
.startingBlock
= currentProperty
.startingBlock
;
617 renamedProperty
.size
.LowPart
= currentProperty
.size
.LowPart
;
618 renamedProperty
.size
.HighPart
= currentProperty
.size
.HighPart
;
620 renamedProperty
.previousProperty
= PROPERTY_NULL
;
621 renamedProperty
.nextProperty
= PROPERTY_NULL
;
624 * Bring the dirProperty link in case it is a storage and in which
625 * case the renamed storage elements don't require to be reorganized.
627 renamedProperty
.dirProperty
= currentProperty
.dirProperty
;
629 /* call CoFileTime to get the current time
630 renamedProperty.timeStampS1
631 renamedProperty.timeStampD1
632 renamedProperty.timeStampS2
633 renamedProperty.timeStampD2
634 renamedProperty.propertyUniqueID
638 * Obtain a free property in the property chain
640 renamedPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
643 * Save the new property into the new property spot
645 StorageImpl_WriteProperty(
646 This
->ancestorStorage
,
647 renamedPropertyIndex
,
651 * Find a spot in the property chain for our newly created property.
655 renamedPropertyIndex
,
659 * At this point the renamed property has been inserted in the tree,
660 * now, before to Destroy the old property we must zeroed it's dirProperty
661 * otherwise the DestroyProperty below will zap it all and we do not want
663 * Also, we fake that the old property is a storage so the DestroyProperty
664 * will not do a SetSize(0) on the stream data.
666 * This means that we need to tweek the StgProperty if it is a stream or a
669 currentProperty
.dirProperty
= PROPERTY_NULL
;
670 currentProperty
.propertyType
= PROPTYPE_STORAGE
;
671 StorageImpl_WriteProperty(
672 This
->ancestorStorage
,
677 * Invoke Destroy to get rid of the ole property and automatically redo
678 * the linking of it's previous and next members...
680 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsOldName
);
686 * There is no property with the old name
688 return STG_E_FILENOTFOUND
;
694 /************************************************************************
695 * Storage32BaseImpl_CreateStream (IStorage)
697 * This method will create a stream object within this storage
699 * See Windows documentation for more details on IStorage methods.
701 HRESULT WINAPI
StorageBaseImpl_CreateStream(
703 const OLECHAR
* pwcsName
, /* [string][in] */
704 DWORD grfMode
, /* [in] */
705 DWORD reserved1
, /* [in] */
706 DWORD reserved2
, /* [in] */
707 IStream
** ppstm
) /* [out] */
709 ICOM_THIS(StorageBaseImpl
,iface
);
710 IEnumSTATSTGImpl
* propertyEnumeration
;
711 StgStreamImpl
* newStream
;
712 StgProperty currentProperty
, newStreamProperty
;
713 ULONG foundPropertyIndex
, newPropertyIndex
;
716 * Validate parameters
719 return STG_E_INVALIDPOINTER
;
722 return STG_E_INVALIDNAME
;
725 * Validate the STGM flags
727 if ( FAILED( validateSTGM(grfMode
) ))
728 return STG_E_INVALIDFLAG
;
733 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
734 (grfMode
& STGM_DELETEONRELEASE
) ||
735 (grfMode
& STGM_TRANSACTED
) )
736 return STG_E_INVALIDFUNCTION
;
739 * Initialize the out parameter
744 * Create a property enumeration to search the properties
746 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
747 This
->rootPropertySetIndex
);
749 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
753 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
755 if (foundPropertyIndex
!= PROPERTY_NULL
)
758 * An element with this name already exists
760 if (grfMode
& STGM_CREATE
)
761 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsName
);
763 return STG_E_FILEALREADYEXISTS
;
767 * memset the empty property
769 memset(&newStreamProperty
, 0, sizeof(StgProperty
));
771 newStreamProperty
.sizeOfNameString
=
772 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
774 if (newStreamProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
775 return STG_E_INVALIDNAME
;
777 lstrcpyW(newStreamProperty
.name
, pwcsName
);
779 newStreamProperty
.propertyType
= PROPTYPE_STREAM
;
780 newStreamProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
781 newStreamProperty
.size
.LowPart
= 0;
782 newStreamProperty
.size
.HighPart
= 0;
784 newStreamProperty
.previousProperty
= PROPERTY_NULL
;
785 newStreamProperty
.nextProperty
= PROPERTY_NULL
;
786 newStreamProperty
.dirProperty
= PROPERTY_NULL
;
788 /* call CoFileTime to get the current time
789 newStreamProperty.timeStampS1
790 newStreamProperty.timeStampD1
791 newStreamProperty.timeStampS2
792 newStreamProperty.timeStampD2
795 /* newStreamProperty.propertyUniqueID */
798 * Get a free property or create a new one
800 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
803 * Save the new property into the new property spot
805 StorageImpl_WriteProperty(
806 This
->ancestorStorage
,
811 * Find a spot in the property chain for our newly created property.
819 * Open the stream to return it.
821 newStream
= StgStreamImpl_Construct(This
, newPropertyIndex
);
825 *ppstm
= (IStream
*)newStream
;
828 * Since we are returning a pointer to the interface, we have to nail down
831 StgStreamImpl_AddRef(*ppstm
);
835 return STG_E_INSUFFICIENTMEMORY
;
841 /************************************************************************
842 * Storage32BaseImpl_SetClass (IStorage)
844 * This method will write the specified CLSID in the property of this
847 * See Windows documentation for more details on IStorage methods.
849 HRESULT WINAPI
StorageBaseImpl_SetClass(
851 REFCLSID clsid
) /* [in] */
853 ICOM_THIS(StorageBaseImpl
,iface
);
854 HRESULT hRes
= E_FAIL
;
855 StgProperty curProperty
;
858 success
= StorageImpl_ReadProperty(This
->ancestorStorage
,
859 This
->rootPropertySetIndex
,
863 curProperty
.propertyUniqueID
= *clsid
;
865 success
= StorageImpl_WriteProperty(This
->ancestorStorage
,
866 This
->rootPropertySetIndex
,
875 /************************************************************************
876 ** Storage32Impl implementation
879 /************************************************************************
880 * Storage32Impl_CreateStorage (IStorage)
882 * This method will create the storage object within the provided storage.
884 * See Windows documentation for more details on IStorage methods.
886 HRESULT WINAPI
StorageImpl_CreateStorage(
888 const OLECHAR
*pwcsName
, /* [string][in] */
889 DWORD grfMode
, /* [in] */
890 DWORD reserved1
, /* [in] */
891 DWORD reserved2
, /* [in] */
892 IStorage
**ppstg
) /* [out] */
894 StorageImpl
* const This
=(StorageImpl
*)iface
;
896 IEnumSTATSTGImpl
*propertyEnumeration
;
897 StgProperty currentProperty
;
898 StgProperty newProperty
;
899 ULONG foundPropertyIndex
;
900 ULONG newPropertyIndex
;
904 * Validate parameters
907 return STG_E_INVALIDPOINTER
;
910 return STG_E_INVALIDNAME
;
913 * Validate the STGM flags
915 if ( FAILED( validateSTGM(grfMode
) ) ||
916 (grfMode
& STGM_DELETEONRELEASE
) )
917 return STG_E_INVALIDFLAG
;
920 * Initialize the out parameter
925 * Create a property enumeration and search the properties
927 propertyEnumeration
= IEnumSTATSTGImpl_Construct( This
->ancestorStorage
,
928 This
->rootPropertySetIndex
);
930 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
933 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
935 if (foundPropertyIndex
!= PROPERTY_NULL
)
938 * An element with this name already exists
940 if (grfMode
& STGM_CREATE
)
941 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsName
);
943 return STG_E_FILEALREADYEXISTS
;
947 * memset the empty property
949 memset(&newProperty
, 0, sizeof(StgProperty
));
951 newProperty
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
953 if (newProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
954 return STG_E_INVALIDNAME
;
956 lstrcpyW(newProperty
.name
, pwcsName
);
958 newProperty
.propertyType
= PROPTYPE_STORAGE
;
959 newProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
960 newProperty
.size
.LowPart
= 0;
961 newProperty
.size
.HighPart
= 0;
963 newProperty
.previousProperty
= PROPERTY_NULL
;
964 newProperty
.nextProperty
= PROPERTY_NULL
;
965 newProperty
.dirProperty
= PROPERTY_NULL
;
967 /* call CoFileTime to get the current time
968 newProperty.timeStampS1
969 newProperty.timeStampD1
970 newProperty.timeStampS2
971 newProperty.timeStampD2
974 /* newStorageProperty.propertyUniqueID */
977 * Obtain a free property in the property chain
979 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
982 * Save the new property into the new property spot
984 StorageImpl_WriteProperty(
985 This
->ancestorStorage
,
990 * Find a spot in the property chain for our newly created property.
998 * Open it to get a pointer to return.
1000 hr
= StorageBaseImpl_OpenStorage(
1009 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1018 /***************************************************************************
1022 * Get a free property or create a new one.
1024 static ULONG
getFreeProperty(
1025 StorageImpl
*storage
)
1027 ULONG currentPropertyIndex
= 0;
1028 ULONG newPropertyIndex
= PROPERTY_NULL
;
1029 BOOL readSucessful
= TRUE
;
1030 StgProperty currentProperty
;
1035 * Start by reading the root property
1037 readSucessful
= StorageImpl_ReadProperty(storage
->ancestorStorage
,
1038 currentPropertyIndex
,
1042 if (currentProperty
.sizeOfNameString
== 0)
1045 * The property existis and is available, we found it.
1047 newPropertyIndex
= currentPropertyIndex
;
1053 * We exhausted the property list, we will create more space below
1055 newPropertyIndex
= currentPropertyIndex
;
1057 currentPropertyIndex
++;
1059 } while (newPropertyIndex
== PROPERTY_NULL
);
1062 * grow the property chain
1064 if (! readSucessful
)
1066 StgProperty emptyProperty
;
1067 ULARGE_INTEGER newSize
;
1068 ULONG propertyIndex
;
1069 ULONG lastProperty
= 0;
1070 ULONG blockCount
= 0;
1073 * obtain the new count of property blocks
1075 blockCount
= BlockChainStream_GetCount(
1076 storage
->ancestorStorage
->rootBlockChain
)+1;
1079 * initialize the size used by the property stream
1081 newSize
.HighPart
= 0;
1082 newSize
.LowPart
= storage
->bigBlockSize
* blockCount
;
1085 * add a property block to the property chain
1087 BlockChainStream_SetSize(storage
->ancestorStorage
->rootBlockChain
, newSize
);
1090 * memset the empty property in order to initialize the unused newly
1093 memset(&emptyProperty
, 0, sizeof(StgProperty
));
1098 lastProperty
= storage
->bigBlockSize
/ PROPSET_BLOCK_SIZE
* blockCount
;
1101 propertyIndex
= newPropertyIndex
;
1102 propertyIndex
< lastProperty
;
1105 StorageImpl_WriteProperty(
1106 storage
->ancestorStorage
,
1112 return newPropertyIndex
;
1115 /****************************************************************************
1119 * Case insensitive comparaison of StgProperty.name by first considering
1122 * Returns <0 when newPrpoerty < currentProperty
1123 * >0 when newPrpoerty > currentProperty
1124 * 0 when newPrpoerty == currentProperty
1126 static LONG
propertyNameCmp(
1127 OLECHAR
*newProperty
,
1128 OLECHAR
*currentProperty
)
1130 LONG sizeOfNew
= (lstrlenW(newProperty
) +1) * sizeof(WCHAR
);
1131 LONG sizeOfCur
= (lstrlenW(currentProperty
)+1) * sizeof(WCHAR
);
1132 LONG diff
= sizeOfNew
- sizeOfCur
;
1137 * We compare the string themselves only when they are of the same lenght
1139 WCHAR wsnew
[PROPERTY_NAME_MAX_LEN
];
1140 WCHAR wscur
[PROPERTY_NAME_MAX_LEN
];
1142 diff
= lstrcmpW( (LPCWSTR
)CRTDLL__wcsupr(
1143 lstrcpynW(wsnew
, newProperty
, sizeOfNew
)),
1144 (LPCWSTR
)CRTDLL__wcsupr(
1145 lstrcpynW(wscur
, currentProperty
, sizeOfCur
)));
1151 /****************************************************************************
1155 * Properly link this new element in the property chain.
1157 static void updatePropertyChain(
1158 StorageImpl
*storage
,
1159 ULONG newPropertyIndex
,
1160 StgProperty newProperty
)
1162 StgProperty currentProperty
;
1165 * Read the root property
1167 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1168 storage
->rootPropertySetIndex
,
1171 if (currentProperty
.dirProperty
!= PROPERTY_NULL
)
1174 * The root storage contains some element, therefore, start the research
1175 * for the appropriate location.
1178 ULONG current
, next
, previous
, currentPropertyId
;
1181 * Keep the StgProperty sequence number of the storage first property
1183 currentPropertyId
= currentProperty
.dirProperty
;
1188 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1189 currentProperty
.dirProperty
,
1192 previous
= currentProperty
.previousProperty
;
1193 next
= currentProperty
.nextProperty
;
1194 current
= currentPropertyId
;
1198 LONG diff
= propertyNameCmp( newProperty
.name
, currentProperty
.name
);
1202 if (previous
!= PROPERTY_NULL
)
1204 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1211 currentProperty
.previousProperty
= newPropertyIndex
;
1212 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1220 if (next
!= PROPERTY_NULL
)
1222 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1229 currentProperty
.nextProperty
= newPropertyIndex
;
1230 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1237 previous
= currentProperty
.previousProperty
;
1238 next
= currentProperty
.nextProperty
;
1244 * The root storage is empty, link the new property to it's dir property
1246 currentProperty
.dirProperty
= newPropertyIndex
;
1247 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1248 storage
->rootPropertySetIndex
,
1254 /*************************************************************************
1257 HRESULT WINAPI
StorageImpl_CopyTo(
1259 DWORD ciidExclude
, /* [in] */
1260 const IID
*rgiidExclude
,/* [size_is][unique][in] */
1261 SNB snbExclude
, /* [unique][in] */
1262 IStorage
*pstgDest
) /* [unique][in] */
1267 /*************************************************************************
1268 * MoveElementTo (IStorage)
1270 HRESULT WINAPI
StorageImpl_MoveElementTo(
1272 const OLECHAR
*pwcsName
, /* [string][in] */
1273 IStorage
*pstgDest
, /* [unique][in] */
1274 const OLECHAR
*pwcsNewName
,/* [string][in] */
1275 DWORD grfFlags
) /* [in] */
1280 /*************************************************************************
1283 HRESULT WINAPI
StorageImpl_Commit(
1285 DWORD grfCommitFlags
)/* [in] */
1287 FIXME(ole
, "(%ld): stub!\n", grfCommitFlags
);
1291 /*************************************************************************
1294 HRESULT WINAPI
StorageImpl_Revert(
1300 /*************************************************************************
1301 * DestroyElement (IStorage)
1303 * Stategy: This implementation is build this way for simplicity not for speed.
1304 * I always delete the top most element of the enumeration and adjust
1305 * the deleted element pointer all the time. This takes longer to
1306 * do but allow to reinvoke DestroyElement whenever we encounter a
1307 * storage object. The optimisation reside in the usage of another
1308 * enumeration stategy that would give all the leaves of a storage
1309 * first. (postfix order)
1311 HRESULT WINAPI
StorageImpl_DestroyElement(
1313 const OLECHAR
*pwcsName
)/* [string][in] */
1315 StorageImpl
* const This
=(StorageImpl
*)iface
;
1317 IEnumSTATSTGImpl
* propertyEnumeration
;
1320 StgProperty propertyToDelete
;
1321 StgProperty parentProperty
;
1322 ULONG foundPropertyIndexToDelete
;
1323 ULONG typeOfRelation
;
1324 ULONG parentPropertyId
;
1327 * Perform a sanity check on the parameters.
1330 return STG_E_INVALIDPOINTER
;
1333 * Create a property enumeration to search the property with the given name
1335 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
1336 This
->ancestorStorage
,
1337 This
->rootPropertySetIndex
);
1339 foundPropertyIndexToDelete
= IEnumSTATSTGImpl_FindProperty(
1340 propertyEnumeration
,
1344 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1346 if ( foundPropertyIndexToDelete
== PROPERTY_NULL
)
1348 return STG_E_FILENOTFOUND
;
1352 * Find the parent property of the property to delete (the one that
1353 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1354 * the parent is This. Otherwise, the parent is one of it's sibling...
1358 * First, read This's StgProperty..
1360 res
= StorageImpl_ReadProperty(
1361 This
->ancestorStorage
,
1362 This
->rootPropertySetIndex
,
1368 * Second, check to see if by any chance the actual storage (This) is not
1369 * the parent of the property to delete... We never know...
1371 if ( parentProperty
.dirProperty
== foundPropertyIndexToDelete
)
1374 * Set data as it would have been done in the else part...
1376 typeOfRelation
= PROPERTY_RELATION_DIR
;
1377 parentPropertyId
= This
->rootPropertySetIndex
;
1382 * Create a property enumeration to search the parent properties, and
1383 * delete it once done.
1385 IEnumSTATSTGImpl
* propertyEnumeration2
;
1387 propertyEnumeration2
= IEnumSTATSTGImpl_Construct(
1388 This
->ancestorStorage
,
1389 This
->rootPropertySetIndex
);
1391 typeOfRelation
= IEnumSTATSTGImpl_FindParentProperty(
1392 propertyEnumeration2
,
1393 foundPropertyIndexToDelete
,
1397 IEnumSTATSTGImpl_Destroy(propertyEnumeration2
);
1400 if ( propertyToDelete
.propertyType
== PROPTYPE_STORAGE
)
1402 hr
= deleteStorageProperty(
1404 propertyToDelete
.name
);
1406 else if ( propertyToDelete
.propertyType
== PROPTYPE_STREAM
)
1408 hr
= deleteStreamProperty(
1410 foundPropertyIndexToDelete
,
1418 * Adjust the property chain
1420 hr
= adjustPropertyChain(
1431 /*********************************************************************
1435 * Perform the deletion of a complete storage node
1438 static HRESULT
deleteStorageProperty(
1439 StorageImpl
*parentStorage
,
1440 OLECHAR
*propertyToDeleteName
)
1442 IEnumSTATSTG
*elements
= 0;
1443 IStorage
*childStorage
= 0;
1444 STATSTG currentElement
;
1446 HRESULT destroyHr
= S_OK
;
1449 * Open the storage and enumerate it
1451 hr
= StorageBaseImpl_OpenStorage(
1452 (IStorage
*)parentStorage
,
1453 propertyToDeleteName
,
1455 STGM_SHARE_EXCLUSIVE
,
1466 * Enumerate the elements
1468 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1473 * Obtain the next element
1475 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
1478 destroyHr
= StorageImpl_DestroyElement(
1479 (IStorage
*)childStorage
,
1480 (OLECHAR
*)currentElement
.pwcsName
);
1482 CoTaskMemFree(currentElement
.pwcsName
);
1486 * We need to Reset the enumeration every time because we delete elements
1487 * and the enumeration could be invalid
1489 IEnumSTATSTG_Reset(elements
);
1491 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
1493 IStorage_Release(childStorage
);
1494 IEnumSTATSTG_Release(elements
);
1499 /*********************************************************************
1503 * Perform the deletion of a stream node
1506 static HRESULT
deleteStreamProperty(
1507 StorageImpl
*parentStorage
,
1508 ULONG indexOfPropertyToDelete
,
1509 StgProperty propertyToDelete
)
1513 ULARGE_INTEGER size
;
1518 hr
= StorageBaseImpl_OpenStream(
1519 (IStorage
*)parentStorage
,
1520 (OLECHAR
*)propertyToDelete
.name
,
1522 STGM_SHARE_EXCLUSIVE
,
1534 hr
= IStream_SetSize(pis
, size
);
1542 * Invalidate the property by zeroing it's name member.
1544 propertyToDelete
.sizeOfNameString
= 0;
1547 * Here we should re-read the property so we get the updated pointer
1548 * but since we are here to zap it, I don't do it...
1551 StorageImpl_WriteProperty(
1552 parentStorage
->ancestorStorage
,
1553 indexOfPropertyToDelete
,
1559 /*********************************************************************
1563 * Finds a placeholder for the StgProperty within the Storage
1566 static HRESULT
findPlaceholder(
1567 StorageImpl
*storage
,
1568 ULONG propertyIndexToStore
,
1569 ULONG storePropertyIndex
,
1572 StgProperty storeProperty
;
1577 * Read the storage property
1579 res
= StorageImpl_ReadProperty(
1580 storage
->ancestorStorage
,
1589 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1591 if (storeProperty
.previousProperty
!= PROPERTY_NULL
)
1593 return findPlaceholder(
1595 propertyIndexToStore
,
1596 storeProperty
.previousProperty
,
1601 storeProperty
.previousProperty
= propertyIndexToStore
;
1604 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
1606 if (storeProperty
.nextProperty
!= PROPERTY_NULL
)
1608 return findPlaceholder(
1610 propertyIndexToStore
,
1611 storeProperty
.nextProperty
,
1616 storeProperty
.nextProperty
= propertyIndexToStore
;
1619 else if (typeOfRelation
== PROPERTY_RELATION_DIR
)
1621 if (storeProperty
.dirProperty
!= PROPERTY_NULL
)
1623 return findPlaceholder(
1625 propertyIndexToStore
,
1626 storeProperty
.dirProperty
,
1631 storeProperty
.dirProperty
= propertyIndexToStore
;
1635 hr
= StorageImpl_WriteProperty(
1636 storage
->ancestorStorage
,
1648 /*************************************************************************
1652 * This method takes the previous and the next property link of a property
1653 * to be deleted and find them a place in the Storage.
1655 static HRESULT
adjustPropertyChain(
1657 StgProperty propertyToDelete
,
1658 StgProperty parentProperty
,
1659 ULONG parentPropertyId
,
1662 ULONG newLinkProperty
= PROPERTY_NULL
;
1663 BOOL needToFindAPlaceholder
= FALSE
;
1664 ULONG storeNode
= PROPERTY_NULL
;
1665 ULONG toStoreNode
= PROPERTY_NULL
;
1666 INT relationType
= 0;
1670 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1672 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1675 * Set the parent previous to the property to delete previous
1677 newLinkProperty
= propertyToDelete
.previousProperty
;
1679 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1682 * We also need to find a storage for the other link, setup variables
1683 * to do this at the end...
1685 needToFindAPlaceholder
= TRUE
;
1686 storeNode
= propertyToDelete
.previousProperty
;
1687 toStoreNode
= propertyToDelete
.nextProperty
;
1688 relationType
= PROPERTY_RELATION_NEXT
;
1691 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1694 * Set the parent previous to the property to delete next
1696 newLinkProperty
= propertyToDelete
.nextProperty
;
1700 * Link it for real...
1702 parentProperty
.previousProperty
= newLinkProperty
;
1705 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
1707 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1710 * Set the parent next to the property to delete next previous
1712 newLinkProperty
= propertyToDelete
.previousProperty
;
1714 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1717 * We also need to find a storage for the other link, setup variables
1718 * to do this at the end...
1720 needToFindAPlaceholder
= TRUE
;
1721 storeNode
= propertyToDelete
.previousProperty
;
1722 toStoreNode
= propertyToDelete
.nextProperty
;
1723 relationType
= PROPERTY_RELATION_NEXT
;
1726 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1729 * Set the parent next to the property to delete next
1731 newLinkProperty
= propertyToDelete
.nextProperty
;
1735 * Link it for real...
1737 parentProperty
.nextProperty
= newLinkProperty
;
1739 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
1741 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1744 * Set the parent dir to the property to delete previous
1746 newLinkProperty
= propertyToDelete
.previousProperty
;
1748 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1751 * We also need to find a storage for the other link, setup variables
1752 * to do this at the end...
1754 needToFindAPlaceholder
= TRUE
;
1755 storeNode
= propertyToDelete
.previousProperty
;
1756 toStoreNode
= propertyToDelete
.nextProperty
;
1757 relationType
= PROPERTY_RELATION_NEXT
;
1760 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1763 * Set the parent dir to the property to delete next
1765 newLinkProperty
= propertyToDelete
.nextProperty
;
1769 * Link it for real...
1771 parentProperty
.dirProperty
= newLinkProperty
;
1775 * Write back the parent property
1777 res
= StorageImpl_WriteProperty(
1778 This
->ancestorStorage
,
1787 * If a placeholder is required for the other link, then, find one and
1788 * get out of here...
1790 if (needToFindAPlaceholder
)
1792 hr
= findPlaceholder(
1803 /******************************************************************************
1804 * SetElementTimes (IStorage)
1806 HRESULT WINAPI
StorageImpl_SetElementTimes(
1808 const OLECHAR
*pwcsName
,/* [string][in] */
1809 const FILETIME
*pctime
, /* [in] */
1810 const FILETIME
*patime
, /* [in] */
1811 const FILETIME
*pmtime
) /* [in] */
1816 /******************************************************************************
1817 * SetStateBits (IStorage)
1819 HRESULT WINAPI
StorageImpl_SetStateBits(
1821 DWORD grfStateBits
,/* [in] */
1822 DWORD grfMask
) /* [in] */
1827 HRESULT
StorageImpl_Construct(
1833 StgProperty currentProperty
;
1835 ULONG currentPropertyIndex
;
1837 if ( FAILED( validateSTGM(openFlags
) ))
1838 return STG_E_INVALIDFLAG
;
1840 memset(This
, 0, sizeof(StorageImpl
));
1843 * Initialize the virtual fgunction table.
1845 This
->lpvtbl
= &Storage32Impl_Vtbl
;
1846 This
->v_destructor
= &StorageImpl_Destroy
;
1849 * This is the top-level storage so initialize the ancester pointer
1852 This
->ancestorStorage
= This
;
1855 * Initialize the physical support of the storage.
1857 This
->hFile
= hFile
;
1860 * Initialize the big block cache.
1862 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
1863 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
1864 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
1866 This
->bigBlockSize
);
1868 if (This
->bigBlockFile
== 0)
1871 if (openFlags
& STGM_CREATE
)
1873 ULARGE_INTEGER size
;
1874 BYTE
* bigBlockBuffer
;
1877 * Initialize all header variables:
1878 * - The big block depot consists of one block and it is at block 0
1879 * - The properties start at block 1
1880 * - There is no small block depot
1882 memset( This
->bigBlockDepotStart
,
1884 sizeof(This
->bigBlockDepotStart
));
1886 This
->bigBlockDepotCount
= 1;
1887 This
->bigBlockDepotStart
[0] = 0;
1888 This
->rootStartBlock
= 1;
1889 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
1890 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
1891 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
1892 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
1893 This
->extBigBlockDepotCount
= 0;
1895 StorageImpl_SaveFileHeader(This
);
1898 * Add one block for the big block depot and one block for the properties
1901 size
.LowPart
= This
->bigBlockSize
* 3;
1902 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
1905 * Initialize the big block depot
1907 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, 0);
1908 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
1909 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
1910 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
1911 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
1916 * Load the header for the file.
1918 hr
= StorageImpl_LoadFileHeader(This
);
1922 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
1929 * There is no block depot cached yet.
1931 This
->indexBlockDepotCached
= 0xFFFFFFFF;
1934 * Start searching for free blocks with block 0.
1936 This
->prevFreeBlock
= 0;
1939 * Create the block chain abstractions.
1941 This
->rootBlockChain
=
1942 BlockChainStream_Construct(This
, &This
->rootStartBlock
, PROPERTY_NULL
);
1944 This
->smallBlockDepotChain
= BlockChainStream_Construct(
1946 &This
->smallBlockDepotStart
,
1950 * Write the root property
1952 if (openFlags
& STGM_CREATE
)
1954 StgProperty rootProp
;
1956 * Initialize the property chain
1958 memset(&rootProp
, 0, sizeof(rootProp
));
1959 lstrcpyAtoW(rootProp
.name
, rootPropertyName
);
1961 rootProp
.sizeOfNameString
= (lstrlenW(rootProp
.name
)+1) * sizeof(WCHAR
);
1962 rootProp
.propertyType
= PROPTYPE_ROOT
;
1963 rootProp
.previousProperty
= PROPERTY_NULL
;
1964 rootProp
.nextProperty
= PROPERTY_NULL
;
1965 rootProp
.dirProperty
= PROPERTY_NULL
;
1966 rootProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
1967 rootProp
.size
.HighPart
= 0;
1968 rootProp
.size
.LowPart
= 0;
1970 StorageImpl_WriteProperty(This
, 0, &rootProp
);
1974 * Find the ID of the root int he property sets.
1976 currentPropertyIndex
= 0;
1980 readSucessful
= StorageImpl_ReadProperty(
1982 currentPropertyIndex
,
1987 if ( (currentProperty
.sizeOfNameString
!= 0 ) &&
1988 (currentProperty
.propertyType
== PROPTYPE_ROOT
) )
1990 This
->rootPropertySetIndex
= currentPropertyIndex
;
1994 currentPropertyIndex
++;
1996 } while (readSucessful
&& (This
->rootPropertySetIndex
== PROPERTY_NULL
) );
2005 * Create the block chain abstraction for the small block root chain.
2007 This
->smallBlockRootChain
= BlockChainStream_Construct(
2010 This
->rootPropertySetIndex
);
2015 void StorageImpl_Destroy(
2018 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2019 BlockChainStream_Destroy(This
->rootBlockChain
);
2020 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2022 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2026 /******************************************************************************
2027 * Storage32Impl_GetNextFreeBigBlock
2029 * Returns the index of the next free big block.
2030 * If the big block depot is filled, this method will enlarge it.
2033 ULONG
StorageImpl_GetNextFreeBigBlock(
2036 ULONG depotBlockIndexPos
;
2038 ULONG depotBlockOffset
;
2039 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2040 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2042 ULONG freeBlock
= BLOCK_UNUSED
;
2044 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2045 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2048 * Scan the entire big block depot until we find a block marked free
2050 while (nextBlockIndex
!= BLOCK_UNUSED
)
2052 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2054 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2057 * Grow the primary depot.
2059 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2061 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2064 * Add a block depot.
2066 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2067 This
->bigBlockDepotCount
++;
2068 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2071 * Flag it as a block depot.
2073 StorageImpl_SetNextBlockInChain(This
,
2077 /* Save new header information.
2079 StorageImpl_SaveFileHeader(This
);
2084 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2086 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2089 * Grow the extended depot.
2091 ULONG extIndex
= BLOCK_UNUSED
;
2092 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2093 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2095 if (extBlockOffset
== 0)
2097 /* We need an extended block.
2099 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2100 This
->extBigBlockDepotCount
++;
2101 depotBlockIndexPos
= extIndex
+ 1;
2104 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2107 * Add a block depot and mark it in the extended block.
2109 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2110 This
->bigBlockDepotCount
++;
2111 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2113 /* Flag the block depot.
2115 StorageImpl_SetNextBlockInChain(This
,
2119 /* If necessary, flag the extended depot block.
2121 if (extIndex
!= BLOCK_UNUSED
)
2122 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2124 /* Save header information.
2126 StorageImpl_SaveFileHeader(This
);
2130 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2132 if (depotBuffer
!= 0)
2134 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2135 ( nextBlockIndex
!= BLOCK_UNUSED
))
2137 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2139 if (nextBlockIndex
== BLOCK_UNUSED
)
2141 freeBlock
= (depotIndex
* blocksPerDepot
) +
2142 (depotBlockOffset
/sizeof(ULONG
));
2145 depotBlockOffset
+= sizeof(ULONG
);
2148 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2152 depotBlockOffset
= 0;
2155 This
->prevFreeBlock
= freeBlock
;
2160 /******************************************************************************
2161 * Storage32Impl_AddBlockDepot
2163 * This will create a depot block, essentially it is a block initialized
2166 void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2170 blockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
2173 * Initialize blocks as free
2175 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2177 StorageImpl_ReleaseBigBlock(This
, blockBuffer
);
2180 /******************************************************************************
2181 * Storage32Impl_GetExtDepotBlock
2183 * Returns the index of the block that corresponds to the specified depot
2184 * index. This method is only for depot indexes equal or greater than
2185 * COUNT_BBDEPOTINHEADER.
2187 ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2189 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2190 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2191 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2192 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2193 ULONG blockIndex
= BLOCK_UNUSED
;
2194 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2196 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2198 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2199 return BLOCK_UNUSED
;
2201 while (extBlockCount
> 0)
2203 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2207 if (extBlockIndex
!= BLOCK_UNUSED
)
2211 depotBuffer
= StorageImpl_GetROBigBlock(This
, extBlockIndex
);
2213 if (depotBuffer
!= 0)
2215 StorageUtl_ReadDWord(depotBuffer
,
2216 extBlockOffset
* sizeof(ULONG
),
2219 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2226 /******************************************************************************
2227 * Storage32Impl_SetExtDepotBlock
2229 * Associates the specified block index to the specified depot index.
2230 * This method is only for depot indexes equal or greater than
2231 * COUNT_BBDEPOTINHEADER.
2233 void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
,
2237 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2238 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2239 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2240 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2241 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2243 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2245 while (extBlockCount
> 0)
2247 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2251 if (extBlockIndex
!= BLOCK_UNUSED
)
2255 depotBuffer
= StorageImpl_GetBigBlock(This
, extBlockIndex
);
2257 if (depotBuffer
!= 0)
2259 StorageUtl_WriteDWord(depotBuffer
,
2260 extBlockOffset
* sizeof(ULONG
),
2263 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2268 /******************************************************************************
2269 * Storage32Impl_AddExtBlockDepot
2271 * Creates an extended depot block.
2273 ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2275 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2276 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2277 BYTE
* depotBuffer
= NULL
;
2278 ULONG index
= BLOCK_UNUSED
;
2279 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2280 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2281 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2283 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2284 blocksPerDepotBlock
;
2286 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2289 * The first extended block.
2291 This
->extBigBlockDepotStart
= index
;
2297 * Follow the chain to the last one.
2299 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2301 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2305 * Add the new extended block to the chain.
2307 depotBuffer
= StorageImpl_GetBigBlock(This
, nextExtBlock
);
2308 StorageUtl_WriteDWord(depotBuffer
, nextBlockOffset
, index
);
2309 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2313 * Initialize this block.
2315 depotBuffer
= StorageImpl_GetBigBlock(This
, index
);
2316 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2317 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2322 /******************************************************************************
2323 * Storage32Impl_FreeBigBlock
2325 * This method will flag the specified block as free in the big block depot.
2327 void StorageImpl_FreeBigBlock(
2331 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2333 if (blockIndex
< This
->prevFreeBlock
)
2334 This
->prevFreeBlock
= blockIndex
;
2337 /************************************************************************
2338 * Storage32Impl_GetNextBlockInChain
2340 * This method will retrieve the block index of the next big block in
2343 * Params: This - Pointer to the Storage object.
2344 * blockIndex - Index of the block to retrieve the chain
2347 * Returns: This method returns the index of the next block in the chain.
2348 * It will return the constants:
2349 * BLOCK_SPECIAL - If the block given was not part of a
2351 * BLOCK_END_OF_CHAIN - If the block given was the last in
2353 * BLOCK_UNUSED - If the block given was not past of a chain
2355 * BLOCK_EXTBBDEPOT - This block is part of the extended
2358 * See Windows documentation for more details on IStorage methods.
2360 ULONG
StorageImpl_GetNextBlockInChain(
2364 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2365 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2366 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2367 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2369 ULONG depotBlockIndexPos
;
2371 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2374 * Cache the currently accessed depot block.
2376 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2378 This
->indexBlockDepotCached
= depotBlockCount
;
2380 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2382 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2387 * We have to look in the extended depot.
2389 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2392 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2398 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2400 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &nextBlockIndex
);
2401 This
->blockDepotCached
[index
] = nextBlockIndex
;
2404 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2408 nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2410 return nextBlockIndex
;
2413 /******************************************************************************
2414 * Storage32Impl_GetNextExtendedBlock
2416 * Given an extended block this method will return the next extended block.
2419 * The last ULONG of an extended block is the block index of the next
2420 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2424 * - The index of the next extended block
2425 * - BLOCK_UNUSED: there is no next extended block.
2426 * - Any other return values denotes failure.
2428 ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
2430 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2431 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2434 depotBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
2438 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2440 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2443 return nextBlockIndex
;
2446 /******************************************************************************
2447 * Storage32Impl_SetNextBlockInChain
2449 * This method will write the index of the specified block's next block
2450 * in the big block depot.
2452 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2455 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2456 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2457 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2460 void StorageImpl_SetNextBlockInChain(
2465 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2466 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2467 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2468 ULONG depotBlockIndexPos
;
2471 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2472 assert(blockIndex
!= nextBlock
);
2474 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2476 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2481 * We have to look in the extended depot.
2483 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2486 depotBuffer
= StorageImpl_GetBigBlock(This
, depotBlockIndexPos
);
2490 StorageUtl_WriteDWord(depotBuffer
, depotBlockOffset
, nextBlock
);
2491 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2495 * Update the cached block depot, if necessary.
2497 if (depotBlockCount
== This
->indexBlockDepotCached
)
2499 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
2503 /******************************************************************************
2504 * Storage32Impl_LoadFileHeader
2506 * This method will read in the file header, i.e. big block index -1.
2508 HRESULT
StorageImpl_LoadFileHeader(
2511 HRESULT hr
= STG_E_FILENOTFOUND
;
2512 void* headerBigBlock
= NULL
;
2516 * Get a pointer to the big block of data containing the header.
2518 headerBigBlock
= StorageImpl_GetROBigBlock(This
, -1);
2521 * Extract the information from the header.
2523 if (headerBigBlock
!=0)
2526 * Check for the "magic number" signature and return an error if it is not
2529 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2531 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2532 return STG_E_OLDFORMAT
;
2535 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2537 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2538 return STG_E_INVALIDHEADER
;
2541 StorageUtl_ReadWord(
2543 OFFSET_BIGBLOCKSIZEBITS
,
2544 &This
->bigBlockSizeBits
);
2546 StorageUtl_ReadWord(
2548 OFFSET_SMALLBLOCKSIZEBITS
,
2549 &This
->smallBlockSizeBits
);
2551 StorageUtl_ReadDWord(
2553 OFFSET_BBDEPOTCOUNT
,
2554 &This
->bigBlockDepotCount
);
2556 StorageUtl_ReadDWord(
2558 OFFSET_ROOTSTARTBLOCK
,
2559 &This
->rootStartBlock
);
2561 StorageUtl_ReadDWord(
2563 OFFSET_SBDEPOTSTART
,
2564 &This
->smallBlockDepotStart
);
2566 StorageUtl_ReadDWord(
2568 OFFSET_EXTBBDEPOTSTART
,
2569 &This
->extBigBlockDepotStart
);
2571 StorageUtl_ReadDWord(
2573 OFFSET_EXTBBDEPOTCOUNT
,
2574 &This
->extBigBlockDepotCount
);
2576 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2578 StorageUtl_ReadDWord(
2580 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2581 &(This
->bigBlockDepotStart
[index
]));
2585 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2589 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
2590 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
2594 This
->bigBlockSize
= 0x000000001 >> (DWORD
)This
->bigBlockSizeBits
;
2595 This
->smallBlockSize
= 0x000000001 >> (DWORD
)This
->smallBlockSizeBits
;
2599 * Right now, the code is making some assumptions about the size of the
2600 * blocks, just make sure they are what we're expecting.
2602 assert( (This
->bigBlockSize
==DEF_BIG_BLOCK_SIZE
) &&
2603 (This
->smallBlockSize
==DEF_SMALL_BLOCK_SIZE
));
2606 * Release the block.
2608 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2616 /******************************************************************************
2617 * Storage32Impl_SaveFileHeader
2619 * This method will save to the file the header, i.e. big block -1.
2621 void StorageImpl_SaveFileHeader(
2624 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
2629 * Get a pointer to the big block of data containing the header.
2631 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
2634 * If the block read failed, the file is probably new.
2639 * Initialize for all unknown fields.
2641 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
2644 * Initialize the magic number.
2646 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
2649 * And a bunch of things we don't know what they mean
2651 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
2652 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
2653 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
2654 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
2655 StorageUtl_WriteDWord(headerBigBlock
, 0x40, (DWORD
)0x0001);
2659 * Write the information to the header.
2661 if (headerBigBlock
!=0)
2663 StorageUtl_WriteWord(
2665 OFFSET_BIGBLOCKSIZEBITS
,
2666 This
->bigBlockSizeBits
);
2668 StorageUtl_WriteWord(
2670 OFFSET_SMALLBLOCKSIZEBITS
,
2671 This
->smallBlockSizeBits
);
2673 StorageUtl_WriteDWord(
2675 OFFSET_BBDEPOTCOUNT
,
2676 This
->bigBlockDepotCount
);
2678 StorageUtl_WriteDWord(
2680 OFFSET_ROOTSTARTBLOCK
,
2681 This
->rootStartBlock
);
2683 StorageUtl_WriteDWord(
2685 OFFSET_SBDEPOTSTART
,
2686 This
->smallBlockDepotStart
);
2688 StorageUtl_WriteDWord(
2690 OFFSET_EXTBBDEPOTSTART
,
2691 This
->extBigBlockDepotStart
);
2693 StorageUtl_WriteDWord(
2695 OFFSET_EXTBBDEPOTCOUNT
,
2696 This
->extBigBlockDepotCount
);
2698 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2700 StorageUtl_WriteDWord(
2702 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2703 (This
->bigBlockDepotStart
[index
]));
2708 * Write the big block back to the file.
2710 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
2713 /******************************************************************************
2714 * Storage32Impl_ReadProperty
2716 * This method will read the specified property from the property chain.
2718 BOOL
StorageImpl_ReadProperty(
2721 StgProperty
* buffer
)
2723 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
2724 ULARGE_INTEGER offsetInPropSet
;
2728 offsetInPropSet
.HighPart
= 0;
2729 offsetInPropSet
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
2731 readSucessful
= BlockChainStream_ReadAt(
2732 This
->rootBlockChain
,
2740 memset(buffer
->name
, 0, sizeof(buffer
->name
));
2743 currentProperty
+OFFSET_PS_NAME
,
2744 PROPERTY_NAME_BUFFER_LEN
);
2746 memcpy(&buffer
->propertyType
, currentProperty
+ OFFSET_PS_PROPERTYTYPE
, 1);
2748 StorageUtl_ReadWord(
2750 OFFSET_PS_NAMELENGTH
,
2751 &buffer
->sizeOfNameString
);
2753 StorageUtl_ReadDWord(
2755 OFFSET_PS_PREVIOUSPROP
,
2756 &buffer
->previousProperty
);
2758 StorageUtl_ReadDWord(
2761 &buffer
->nextProperty
);
2763 StorageUtl_ReadDWord(
2766 &buffer
->dirProperty
);
2768 StorageUtl_ReadGUID(
2771 &buffer
->propertyUniqueID
);
2773 StorageUtl_ReadDWord(
2776 &buffer
->timeStampS1
);
2778 StorageUtl_ReadDWord(
2781 &buffer
->timeStampD1
);
2783 StorageUtl_ReadDWord(
2786 &buffer
->timeStampS2
);
2788 StorageUtl_ReadDWord(
2791 &buffer
->timeStampD2
);
2793 StorageUtl_ReadDWord(
2795 OFFSET_PS_STARTBLOCK
,
2796 &buffer
->startingBlock
);
2798 StorageUtl_ReadDWord(
2801 &buffer
->size
.LowPart
);
2803 buffer
->size
.HighPart
= 0;
2806 return readSucessful
;
2809 /*********************************************************************
2810 * Write the specified property into the property chain
2812 BOOL
StorageImpl_WriteProperty(
2815 StgProperty
* buffer
)
2817 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
2818 ULARGE_INTEGER offsetInPropSet
;
2819 BOOL writeSucessful
;
2822 offsetInPropSet
.HighPart
= 0;
2823 offsetInPropSet
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
2825 memset(currentProperty
, 0, PROPSET_BLOCK_SIZE
);
2828 currentProperty
+ OFFSET_PS_NAME
,
2830 PROPERTY_NAME_BUFFER_LEN
);
2832 memcpy(currentProperty
+ OFFSET_PS_PROPERTYTYPE
, &buffer
->propertyType
, 1);
2835 * Reassign the size in case of mistake....
2837 buffer
->sizeOfNameString
= (lstrlenW(buffer
->name
)+1) * sizeof(WCHAR
);
2839 StorageUtl_WriteWord(
2841 OFFSET_PS_NAMELENGTH
,
2842 buffer
->sizeOfNameString
);
2844 StorageUtl_WriteDWord(
2846 OFFSET_PS_PREVIOUSPROP
,
2847 buffer
->previousProperty
);
2849 StorageUtl_WriteDWord(
2852 buffer
->nextProperty
);
2854 StorageUtl_WriteDWord(
2857 buffer
->dirProperty
);
2859 StorageUtl_WriteGUID(
2862 &buffer
->propertyUniqueID
);
2864 StorageUtl_WriteDWord(
2867 buffer
->timeStampS1
);
2869 StorageUtl_WriteDWord(
2872 buffer
->timeStampD1
);
2874 StorageUtl_WriteDWord(
2877 buffer
->timeStampS2
);
2879 StorageUtl_WriteDWord(
2882 buffer
->timeStampD2
);
2884 StorageUtl_WriteDWord(
2886 OFFSET_PS_STARTBLOCK
,
2887 buffer
->startingBlock
);
2889 StorageUtl_WriteDWord(
2892 buffer
->size
.LowPart
);
2894 writeSucessful
= BlockChainStream_WriteAt(This
->rootBlockChain
,
2899 return writeSucessful
;
2902 BOOL
StorageImpl_ReadBigBlock(
2907 void* bigBlockBuffer
;
2909 bigBlockBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
2911 if (bigBlockBuffer
!=0)
2913 memcpy(buffer
, bigBlockBuffer
, This
->bigBlockSize
);
2915 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
2923 BOOL
StorageImpl_WriteBigBlock(
2928 void* bigBlockBuffer
;
2930 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
2932 if (bigBlockBuffer
!=0)
2934 memcpy(bigBlockBuffer
, buffer
, This
->bigBlockSize
);
2936 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
2944 void* StorageImpl_GetROBigBlock(
2948 return BIGBLOCKFILE_GetROBigBlock(This
->bigBlockFile
, blockIndex
);
2951 void* StorageImpl_GetBigBlock(
2955 return BIGBLOCKFILE_GetBigBlock(This
->bigBlockFile
, blockIndex
);
2958 void StorageImpl_ReleaseBigBlock(
2962 BIGBLOCKFILE_ReleaseBigBlock(This
->bigBlockFile
, pBigBlock
);
2965 /******************************************************************************
2966 * Storage32Impl_SmallBlocksToBigBlocks
2968 * This method will convert a small block chain to a big block chain.
2969 * The small block chain will be destroyed.
2971 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
2973 SmallBlockChainStream
** ppsbChain
)
2975 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
2976 ULARGE_INTEGER size
, offset
;
2977 ULONG cbRead
, cbWritten
, cbTotalRead
, cbTotalWritten
;
2978 ULONG propertyIndex
;
2979 BOOL successRead
, successWrite
;
2980 StgProperty chainProperty
;
2981 BYTE buffer
[DEF_SMALL_BLOCK_SIZE
];
2982 BlockChainStream
*bbTempChain
= NULL
;
2983 BlockChainStream
*bigBlockChain
= NULL
;
2986 * Create a temporary big block chain that doesn't have
2987 * an associated property. This temporary chain will be
2988 * used to copy data from small blocks to big blocks.
2990 bbTempChain
= BlockChainStream_Construct(This
,
2995 * Grow the big block chain.
2997 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
2998 BlockChainStream_SetSize(bbTempChain
, size
);
3001 * Copy the contents of the small block chain to the big block chain
3002 * by small block size increments.
3005 offset
.HighPart
= 0;
3011 successRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3016 cbTotalRead
+= cbRead
;
3018 successWrite
= BlockChainStream_WriteAt(bbTempChain
,
3023 cbTotalWritten
+= cbWritten
;
3025 offset
.LowPart
+= This
->smallBlockSize
;
3027 } while (successRead
&& successWrite
);
3029 assert(cbTotalRead
== cbTotalWritten
);
3032 * Destroy the small block chain.
3034 propertyIndex
= (*ppsbChain
)->ownerPropertyIndex
;
3037 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3038 SmallBlockChainStream_Destroy(*ppsbChain
);
3042 * Change the property information. This chain is now a big block chain
3043 * and it doesn't reside in the small blocks chain anymore.
3045 StorageImpl_ReadProperty(This
, propertyIndex
, &chainProperty
);
3047 chainProperty
.startingBlock
= bbHeadOfChain
;
3049 StorageImpl_WriteProperty(This
, propertyIndex
, &chainProperty
);
3052 * Destroy the temporary propertyless big block chain.
3053 * Create a new big block chain associated with this property.
3055 BlockChainStream_Destroy(bbTempChain
);
3056 bigBlockChain
= BlockChainStream_Construct(This
,
3060 return bigBlockChain
;
3063 /******************************************************************************
3064 ** Storage32InternalImpl implementation
3067 StorageInternalImpl
* StorageInternalImpl_Construct(
3068 StorageImpl
* ancestorStorage
,
3069 ULONG rootPropertyIndex
)
3071 StorageInternalImpl
* newStorage
;
3074 * Allocate space for the new storage object
3076 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl
));
3080 memset(newStorage
, 0, sizeof(StorageInternalImpl
));
3083 * Initialize the virtual function table.
3085 newStorage
->lpvtbl
= &Storage32InternalImpl_Vtbl
;
3086 newStorage
->v_destructor
= &StorageInternalImpl_Destroy
;
3089 * Keep the ancestor storage pointer and nail a reference to it.
3091 newStorage
->ancestorStorage
= ancestorStorage
;
3092 StorageBaseImpl_AddRef((IStorage
*)(newStorage
->ancestorStorage
));
3095 * Keep the index of the root property set for this storage,
3097 newStorage
->rootPropertySetIndex
= rootPropertyIndex
;
3105 void StorageInternalImpl_Destroy(
3106 StorageInternalImpl
* This
)
3108 StorageBaseImpl_Release((IStorage
*)This
->ancestorStorage
);
3109 HeapFree(GetProcessHeap(), 0, This
);
3112 /******************************************************************************
3114 ** Storage32InternalImpl_Commit
3116 ** The non-root storages cannot be opened in transacted mode thus this function
3119 HRESULT WINAPI
StorageInternalImpl_Commit(
3121 DWORD grfCommitFlags
) /* [in] */
3126 /******************************************************************************
3128 ** Storage32InternalImpl_Revert
3130 ** The non-root storages cannot be opened in transacted mode thus this function
3133 HRESULT WINAPI
StorageInternalImpl_Revert(
3139 /******************************************************************************
3140 ** IEnumSTATSTGImpl implementation
3143 IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
3144 StorageImpl
* parentStorage
,
3145 ULONG firstPropertyNode
)
3147 IEnumSTATSTGImpl
* newEnumeration
;
3149 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
3151 if (newEnumeration
!=0)
3154 * Set-up the virtual function table and reference count.
3156 newEnumeration
->lpvtbl
= &IEnumSTATSTGImpl_Vtbl
;
3157 newEnumeration
->ref
= 0;
3160 * We want to nail-down the reference to the storage in case the
3161 * enumeration out-lives the storage in the client application.
3163 newEnumeration
->parentStorage
= parentStorage
;
3164 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
3166 newEnumeration
->firstPropertyNode
= firstPropertyNode
;
3169 * Initialize the search stack
3171 newEnumeration
->stackSize
= 0;
3172 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
3173 newEnumeration
->stackToVisit
=
3174 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
)*ENUMSTATSGT_SIZE_INCREMENT
);
3177 * Make sure the current node of the iterator is the first one.
3179 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
3182 return newEnumeration
;
3185 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3187 IStorage_Release((IStorage
*)This
->parentStorage
);
3188 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3189 HeapFree(GetProcessHeap(), 0, This
);
3192 HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3193 IEnumSTATSTG
* iface
,
3197 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3200 * Perform a sanity check on the parameters.
3203 return E_INVALIDARG
;
3206 * Initialize the return parameter.
3211 * Compare the riid with the interface IDs implemented by this object.
3213 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
3215 *ppvObject
= (IEnumSTATSTG
*)This
;
3217 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IEnumSTATSTG
)) == 0)
3219 *ppvObject
= (IEnumSTATSTG
*)This
;
3223 * Check that we obtained an interface.
3225 if ((*ppvObject
)==0)
3226 return E_NOINTERFACE
;
3229 * Query Interface always increases the reference count by one when it is
3232 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG
*)This
);
3237 ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3238 IEnumSTATSTG
* iface
)
3240 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3246 ULONG WINAPI
IEnumSTATSTGImpl_Release(
3247 IEnumSTATSTG
* iface
)
3249 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3257 * If the reference count goes down to 0, perform suicide.
3261 IEnumSTATSTGImpl_Destroy(This
);
3267 HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3268 IEnumSTATSTG
* iface
,
3271 ULONG
* pceltFetched
)
3273 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3275 StgProperty currentProperty
;
3276 STATSTG
* currentReturnStruct
= rgelt
;
3277 ULONG objectFetched
= 0;
3278 ULONG currentSearchNode
;
3281 * Perform a sanity check on the parameters.
3283 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3284 return E_INVALIDARG
;
3287 * To avoid the special case, get another pointer to a ULONG value if
3288 * the caller didn't supply one.
3290 if (pceltFetched
==0)
3291 pceltFetched
= &objectFetched
;
3294 * Start the iteration, we will iterate until we hit the end of the
3295 * linked list or until we hit the number of items to iterate through
3300 * Start with the node at the top of the stack.
3302 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3304 while ( ( *pceltFetched
< celt
) &&
3305 ( currentSearchNode
!=PROPERTY_NULL
) )
3308 * Remove the top node from the stack
3310 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3313 * Read the property from the storage.
3315 StorageImpl_ReadProperty(This
->parentStorage
,
3320 * Copy the information to the return buffer.
3322 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct
,
3327 * Step to the next item in the iteration
3330 currentReturnStruct
++;
3333 * Push the next search node in the search stack.
3335 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3338 * continue the iteration.
3340 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3343 if (*pceltFetched
== celt
)
3350 HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3351 IEnumSTATSTG
* iface
,
3354 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3356 StgProperty currentProperty
;
3357 ULONG objectFetched
= 0;
3358 ULONG currentSearchNode
;
3361 * Start with the node at the top of the stack.
3363 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3365 while ( (objectFetched
< celt
) &&
3366 (currentSearchNode
!=PROPERTY_NULL
) )
3369 * Remove the top node from the stack
3371 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3374 * Read the property from the storage.
3376 StorageImpl_ReadProperty(This
->parentStorage
,
3381 * Step to the next item in the iteration
3386 * Push the next search node in the search stack.
3388 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3391 * continue the iteration.
3393 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3396 if (objectFetched
== celt
)
3402 HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3403 IEnumSTATSTG
* iface
)
3405 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3407 StgProperty rootProperty
;
3411 * Re-initialize the search stack to an empty stack
3413 This
->stackSize
= 0;
3416 * Read the root property from the storage.
3418 readSucessful
= StorageImpl_ReadProperty(
3419 This
->parentStorage
,
3420 This
->firstPropertyNode
,
3425 assert(rootProperty
.sizeOfNameString
!=0);
3428 * Push the search node in the search stack.
3430 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.dirProperty
);
3436 HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3437 IEnumSTATSTG
* iface
,
3438 IEnumSTATSTG
** ppenum
)
3440 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3442 IEnumSTATSTGImpl
* newClone
;
3445 * Perform a sanity check on the parameters.
3448 return E_INVALIDARG
;
3450 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3451 This
->firstPropertyNode
);
3455 * The new clone enumeration must point to the same current node as
3458 newClone
->stackSize
= This
->stackSize
;
3459 newClone
->stackMaxSize
= This
->stackMaxSize
;
3460 newClone
->stackToVisit
=
3461 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3464 newClone
->stackToVisit
,
3466 sizeof(ULONG
) * newClone
->stackSize
);
3468 *ppenum
= (IEnumSTATSTG
*)newClone
;
3471 * Don't forget to nail down a reference to the clone before
3474 IEnumSTATSTGImpl_AddRef(*ppenum
);
3479 INT
IEnumSTATSTGImpl_FindParentProperty(
3480 IEnumSTATSTGImpl
*This
,
3481 ULONG childProperty
,
3482 StgProperty
*currentProperty
,
3485 ULONG currentSearchNode
;
3489 * To avoid the special case, get another pointer to a ULONG value if
3490 * the caller didn't supply one.
3493 thisNodeId
= &foundNode
;
3496 * Start with the node at the top of the stack.
3498 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3501 while (currentSearchNode
!=PROPERTY_NULL
)
3504 * Store the current node in the returned parameters
3506 *thisNodeId
= currentSearchNode
;
3509 * Remove the top node from the stack
3511 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3514 * Read the property from the storage.
3516 StorageImpl_ReadProperty(
3517 This
->parentStorage
,
3521 if (currentProperty
->previousProperty
== childProperty
)
3522 return PROPERTY_RELATION_PREVIOUS
;
3524 else if (currentProperty
->nextProperty
== childProperty
)
3525 return PROPERTY_RELATION_NEXT
;
3527 else if (currentProperty
->dirProperty
== childProperty
)
3528 return PROPERTY_RELATION_DIR
;
3531 * Push the next search node in the search stack.
3533 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3536 * continue the iteration.
3538 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3541 return PROPERTY_NULL
;
3544 ULONG
IEnumSTATSTGImpl_FindProperty(
3545 IEnumSTATSTGImpl
* This
,
3546 const OLECHAR
* lpszPropName
,
3547 StgProperty
* currentProperty
)
3549 ULONG currentSearchNode
;
3552 * Start with the node at the top of the stack.
3554 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3556 while (currentSearchNode
!=PROPERTY_NULL
)
3559 * Remove the top node from the stack
3561 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3564 * Read the property from the storage.
3566 StorageImpl_ReadProperty(This
->parentStorage
,
3570 if ( propertyNameCmp(
3571 (OLECHAR
*)currentProperty
->name
,
3572 (OLECHAR
*)lpszPropName
) == 0)
3573 return currentSearchNode
;
3576 * Push the next search node in the search stack.
3578 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3581 * continue the iteration.
3583 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3586 return PROPERTY_NULL
;
3589 void IEnumSTATSTGImpl_PushSearchNode(
3590 IEnumSTATSTGImpl
* This
,
3593 StgProperty rootProperty
;
3597 * First, make sure we're not trying to push an unexisting node.
3599 if (nodeToPush
==PROPERTY_NULL
)
3603 * First push the node to the stack
3605 if (This
->stackSize
== This
->stackMaxSize
)
3607 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
3609 This
->stackToVisit
= HeapReAlloc(
3613 sizeof(ULONG
) * This
->stackMaxSize
);
3616 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
3620 * Read the root property from the storage.
3622 readSucessful
= StorageImpl_ReadProperty(
3623 This
->parentStorage
,
3629 assert(rootProperty
.sizeOfNameString
!=0);
3632 * Push the previous search node in the search stack.
3634 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.previousProperty
);
3638 ULONG
IEnumSTATSTGImpl_PopSearchNode(
3639 IEnumSTATSTGImpl
* This
,
3644 if (This
->stackSize
== 0)
3645 return PROPERTY_NULL
;
3647 topNode
= This
->stackToVisit
[This
->stackSize
-1];
3655 /******************************************************************************
3656 ** StorageUtl implementation
3659 void StorageUtl_ReadWord(void* buffer
, ULONG offset
, WORD
* value
)
3661 memcpy(value
, (BYTE
*)buffer
+offset
, sizeof(WORD
));
3664 void StorageUtl_WriteWord(void* buffer
, ULONG offset
, WORD value
)
3666 memcpy((BYTE
*)buffer
+offset
, &value
, sizeof(WORD
));
3669 void StorageUtl_ReadDWord(void* buffer
, ULONG offset
, DWORD
* value
)
3671 memcpy(value
, (BYTE
*)buffer
+offset
, sizeof(DWORD
));
3674 void StorageUtl_WriteDWord(void* buffer
, ULONG offset
, DWORD value
)
3676 memcpy((BYTE
*)buffer
+offset
, &value
, sizeof(DWORD
));
3679 void StorageUtl_ReadGUID(void* buffer
, ULONG offset
, GUID
* value
)
3681 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
3682 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
3683 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
3685 memcpy(value
->Data4
, (BYTE
*)buffer
+offset
+8, sizeof(value
->Data4
));
3688 void StorageUtl_WriteGUID(void* buffer
, ULONG offset
, GUID
* value
)
3690 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
3691 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
3692 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
3694 memcpy((BYTE
*)buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
3697 void StorageUtl_CopyPropertyToSTATSTG(
3698 STATSTG
* destination
,
3699 StgProperty
* source
,
3703 * The copy of the string occurs only when the flag is not set
3705 if ((statFlags
& STATFLAG_NONAME
) != 0)
3707 destination
->pwcsName
= 0;
3711 destination
->pwcsName
=
3712 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
3714 lstrcpyW((LPWSTR
)destination
->pwcsName
, source
->name
);
3717 switch (source
->propertyType
)
3719 case PROPTYPE_STORAGE
:
3721 destination
->type
= STGTY_STORAGE
;
3723 case PROPTYPE_STREAM
:
3724 destination
->type
= STGTY_STREAM
;
3727 destination
->type
= STGTY_STREAM
;
3731 destination
->cbSize
= source
->size
;
3733 currentReturnStruct->mtime = {0}; TODO
3734 currentReturnStruct->ctime = {0};
3735 currentReturnStruct->atime = {0};
3737 destination
->grfMode
= 0;
3738 destination
->grfLocksSupported
= 0;
3739 destination
->clsid
= source
->propertyUniqueID
;
3740 destination
->grfStateBits
= 0;
3741 destination
->reserved
= 0;
3744 /******************************************************************************
3745 ** BlockChainStream implementation
3748 BlockChainStream
* BlockChainStream_Construct(
3749 StorageImpl
* parentStorage
,
3750 ULONG
* headOfStreamPlaceHolder
,
3751 ULONG propertyIndex
)
3753 BlockChainStream
* newStream
;
3756 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
3758 newStream
->parentStorage
= parentStorage
;
3759 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
3760 newStream
->ownerPropertyIndex
= propertyIndex
;
3761 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
3762 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
3763 newStream
->numBlocks
= 0;
3765 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
3767 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
3769 newStream
->numBlocks
++;
3770 newStream
->tailIndex
= blockIndex
;
3772 blockIndex
= StorageImpl_GetNextBlockInChain(
3780 void BlockChainStream_Destroy(BlockChainStream
* This
)
3782 HeapFree(GetProcessHeap(), 0, This
);
3785 /******************************************************************************
3786 * BlockChainStream_GetHeadOfChain
3788 * Returns the head of this stream chain.
3789 * Some special chains don't have properties, their heads are kept in
3790 * This->headOfStreamPlaceHolder.
3793 ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
3795 StgProperty chainProperty
;
3798 if (This
->headOfStreamPlaceHolder
!= 0)
3799 return *(This
->headOfStreamPlaceHolder
);
3801 if (This
->ownerPropertyIndex
!= PROPERTY_NULL
)
3803 readSucessful
= StorageImpl_ReadProperty(
3804 This
->parentStorage
,
3805 This
->ownerPropertyIndex
,
3810 return chainProperty
.startingBlock
;
3814 return BLOCK_END_OF_CHAIN
;
3817 /******************************************************************************
3818 * BlockChainStream_GetCount
3820 * Returns the number of blocks that comprises this chain.
3821 * This is not the size of the stream as the last block may not be full!
3824 ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
3829 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
3831 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
3835 blockIndex
= StorageImpl_GetNextBlockInChain(
3836 This
->parentStorage
,
3843 /******************************************************************************
3844 * BlockChainStream_ReadAt
3846 * Reads a specified number of bytes from this chain at the specified offset.
3847 * bytesRead may be NULL.
3848 * Failure will be returned if the specified number of bytes has not been read.
3850 BOOL
BlockChainStream_ReadAt(BlockChainStream
* This
,
3851 ULARGE_INTEGER offset
,
3856 ULONG blockNoInSequence
= offset
.LowPart
/ This
->parentStorage
->bigBlockSize
;
3857 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->bigBlockSize
;
3858 ULONG bytesToReadInBuffer
;
3861 BYTE
* bigBlockBuffer
;
3863 if (This
->lastBlockNoInSequence
== 0xFFFFFFFF)
3864 This
->lastBlockNoInSequence
= blockNoInSequence
;
3866 * Find the first block in the stream that contains part of the buffer.
3868 if (blockNoInSequence
> This
->lastBlockNoInSequence
)
3870 ULONG temp
= blockNoInSequence
;
3872 blockIndex
= This
->lastBlockNoInSequenceIndex
;
3873 blockNoInSequence
-= This
->lastBlockNoInSequence
;
3874 This
->lastBlockNoInSequence
= temp
;
3878 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
3879 This
->lastBlockNoInSequence
= blockNoInSequence
;
3882 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
3885 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
3887 blockNoInSequence
--;
3890 This
->lastBlockNoInSequenceIndex
= blockIndex
;
3893 * Start reading the buffer.
3896 bufferWalker
= buffer
;
3898 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
3901 * Calculate how many bytes we can copy from this big block.
3903 bytesToReadInBuffer
=
3904 MIN(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
3907 * Copy those bytes to the buffer
3910 StorageImpl_GetROBigBlock(This
->parentStorage
, blockIndex
);
3912 memcpy(bufferWalker
, bigBlockBuffer
+ offsetInBlock
, bytesToReadInBuffer
);
3914 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
3917 * Step to the next big block.
3920 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
3922 bufferWalker
+= bytesToReadInBuffer
;
3923 size
-= bytesToReadInBuffer
;
3924 *bytesRead
+= bytesToReadInBuffer
;
3925 offsetInBlock
= 0; /* There is no offset on the next block */
3932 /******************************************************************************
3933 * BlockChainStream_WriteAt
3935 * Writes the specified number of bytes to this chain at the specified offset.
3936 * bytesWritten may be NULL.
3937 * Will fail if not all specified number of bytes have been written.
3939 BOOL
BlockChainStream_WriteAt(BlockChainStream
* This
,
3940 ULARGE_INTEGER offset
,
3943 ULONG
* bytesWritten
)
3945 ULONG blockNoInSequence
= offset
.LowPart
/ This
->parentStorage
->bigBlockSize
;
3946 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->bigBlockSize
;
3950 BYTE
* bigBlockBuffer
;
3952 if (This
->lastBlockNoInSequence
== 0xFFFFFFFF)
3953 This
->lastBlockNoInSequence
= blockNoInSequence
;
3956 * Find the first block in the stream that contains part of the buffer.
3958 if (blockNoInSequence
> This
->lastBlockNoInSequence
)
3960 ULONG temp
= blockNoInSequence
;
3962 blockIndex
= This
->lastBlockNoInSequenceIndex
;
3963 blockNoInSequence
-= This
->lastBlockNoInSequence
;
3964 This
->lastBlockNoInSequence
= temp
;
3968 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
3969 This
->lastBlockNoInSequence
= blockNoInSequence
;
3972 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
3975 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
3977 blockNoInSequence
--;
3980 This
->lastBlockNoInSequenceIndex
= blockIndex
;
3983 * Here, I'm casting away the constness on the buffer variable
3984 * This is OK since we don't intend to modify that buffer.
3987 bufferWalker
= (BYTE
*)buffer
;
3989 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
3992 * Calculate how many bytes we can copy from this big block.
3995 MIN(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
3998 * Copy those bytes to the buffer
4000 bigBlockBuffer
= StorageImpl_GetBigBlock(This
->parentStorage
, blockIndex
);
4002 memcpy(bigBlockBuffer
+ offsetInBlock
, bufferWalker
, bytesToWrite
);
4004 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4007 * Step to the next big block.
4010 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4012 bufferWalker
+= bytesToWrite
;
4013 size
-= bytesToWrite
;
4014 *bytesWritten
+= bytesToWrite
;
4015 offsetInBlock
= 0; /* There is no offset on the next block */
4021 /******************************************************************************
4022 * BlockChainStream_Shrink
4024 * Shrinks this chain in the big block depot.
4026 BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4027 ULARGE_INTEGER newSize
)
4029 ULONG blockIndex
, extraBlock
;
4034 * Figure out how many blocks are needed to contain the new size
4036 numBlocks
= newSize
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4038 if ((newSize
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4041 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4044 * Go to the new end of chain
4046 while (count
< numBlocks
)
4049 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4054 /* Get the next block before marking the new end */
4056 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4058 /* Mark the new end of chain */
4059 StorageImpl_SetNextBlockInChain(
4060 This
->parentStorage
,
4062 BLOCK_END_OF_CHAIN
);
4064 This
->tailIndex
= blockIndex
;
4065 This
->numBlocks
= numBlocks
;
4068 * Mark the extra blocks as free
4070 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4073 StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
);
4075 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4076 extraBlock
= blockIndex
;
4082 /******************************************************************************
4083 * BlockChainStream_Enlarge
4085 * Grows this chain in the big block depot.
4087 BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4088 ULARGE_INTEGER newSize
)
4090 ULONG blockIndex
, currentBlock
;
4092 ULONG oldNumBlocks
= 0;
4094 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4097 * Empty chain. Create the head.
4099 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4101 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4102 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4104 BLOCK_END_OF_CHAIN
);
4106 if (This
->headOfStreamPlaceHolder
!= 0)
4108 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4112 StgProperty chainProp
;
4113 assert(This
->ownerPropertyIndex
!= PROPERTY_NULL
);
4115 StorageImpl_ReadProperty(
4116 This
->parentStorage
,
4117 This
->ownerPropertyIndex
,
4120 chainProp
.startingBlock
= blockIndex
;
4122 StorageImpl_WriteProperty(
4123 This
->parentStorage
,
4124 This
->ownerPropertyIndex
,
4128 This
->tailIndex
= blockIndex
;
4129 This
->numBlocks
= 1;
4133 * Figure out how many blocks are needed to contain this stream
4135 newNumBlocks
= newSize
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4137 if ((newSize
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4141 * Go to the current end of chain
4143 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4145 currentBlock
= blockIndex
;
4147 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4150 currentBlock
= blockIndex
;
4153 StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
);
4156 This
->tailIndex
= currentBlock
;
4159 currentBlock
= This
->tailIndex
;
4160 oldNumBlocks
= This
->numBlocks
;
4163 * Add new blocks to the chain
4165 while (oldNumBlocks
< newNumBlocks
)
4167 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4169 StorageImpl_SetNextBlockInChain(
4170 This
->parentStorage
,
4174 StorageImpl_SetNextBlockInChain(
4175 This
->parentStorage
,
4177 BLOCK_END_OF_CHAIN
);
4179 currentBlock
= blockIndex
;
4183 This
->tailIndex
= blockIndex
;
4184 This
->numBlocks
= newNumBlocks
;
4189 /******************************************************************************
4190 * BlockChainStream_SetSize
4192 * Sets the size of this stream. The big block depot will be updated.
4193 * The file will grow if we grow the chain.
4195 * TODO: Free the actual blocks in the file when we shrink the chain.
4196 * Currently, the blocks are still in the file. So the file size
4197 * doesn't shrink even if we shrink streams.
4199 BOOL
BlockChainStream_SetSize(
4200 BlockChainStream
* This
,
4201 ULARGE_INTEGER newSize
)
4203 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4205 if (newSize
.LowPart
== size
.LowPart
)
4208 if (newSize
.LowPart
< size
.LowPart
)
4210 BlockChainStream_Shrink(This
, newSize
);
4214 ULARGE_INTEGER fileSize
=
4215 BIGBLOCKFILE_GetSize(This
->parentStorage
->bigBlockFile
);
4217 ULONG diff
= newSize
.LowPart
- size
.LowPart
;
4220 * Make sure the file stays a multiple of blocksize
4222 if ((diff
% This
->parentStorage
->bigBlockSize
) != 0)
4223 diff
+= (This
->parentStorage
->bigBlockSize
-
4224 (diff
% This
->parentStorage
->bigBlockSize
) );
4226 fileSize
.LowPart
+= diff
;
4227 BIGBLOCKFILE_SetSize(This
->parentStorage
->bigBlockFile
, fileSize
);
4229 BlockChainStream_Enlarge(This
, newSize
);
4235 /******************************************************************************
4236 * BlockChainStream_GetSize
4238 * Returns the size of this chain.
4239 * Will return the block count if this chain doesn't have a property.
4241 ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4243 StgProperty chainProperty
;
4245 if(This
->headOfStreamPlaceHolder
== NULL
)
4248 * This chain is a data stream read the property and return
4249 * the appropriate size
4251 StorageImpl_ReadProperty(
4252 This
->parentStorage
,
4253 This
->ownerPropertyIndex
,
4256 return chainProperty
.size
;
4261 * this chain is a chain that does not have a property, figure out the
4262 * size by making the product number of used blocks times the
4265 ULARGE_INTEGER result
;
4266 result
.HighPart
= 0;
4269 BlockChainStream_GetCount(This
) *
4270 This
->parentStorage
->bigBlockSize
;
4276 /******************************************************************************
4277 ** SmallBlockChainStream implementation
4280 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4281 StorageImpl
* parentStorage
,
4282 ULONG propertyIndex
)
4284 SmallBlockChainStream
* newStream
;
4286 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4288 newStream
->parentStorage
= parentStorage
;
4289 newStream
->ownerPropertyIndex
= propertyIndex
;
4294 void SmallBlockChainStream_Destroy(
4295 SmallBlockChainStream
* This
)
4297 HeapFree(GetProcessHeap(), 0, This
);
4300 /******************************************************************************
4301 * SmallBlockChainStream_GetHeadOfChain
4303 * Returns the head of this chain of small blocks.
4305 ULONG
SmallBlockChainStream_GetHeadOfChain(
4306 SmallBlockChainStream
* This
)
4308 StgProperty chainProperty
;
4311 if (This
->ownerPropertyIndex
)
4313 readSucessful
= StorageImpl_ReadProperty(
4314 This
->parentStorage
,
4315 This
->ownerPropertyIndex
,
4320 return chainProperty
.startingBlock
;
4325 return BLOCK_END_OF_CHAIN
;
4328 /******************************************************************************
4329 * SmallBlockChainStream_GetNextBlockInChain
4331 * Returns the index of the next small block in this chain.
4334 * - BLOCK_END_OF_CHAIN: end of this chain
4335 * - BLOCK_UNUSED: small block 'blockIndex' is free
4337 ULONG
SmallBlockChainStream_GetNextBlockInChain(
4338 SmallBlockChainStream
* This
,
4341 ULARGE_INTEGER offsetOfBlockInDepot
;
4343 ULONG nextBlockInChain
= BLOCK_END_OF_CHAIN
;
4347 offsetOfBlockInDepot
.HighPart
= 0;
4348 offsetOfBlockInDepot
.LowPart
= blockIndex
* sizeof(ULONG
);
4351 * Read those bytes in the buffer from the small block file.
4353 success
= BlockChainStream_ReadAt(
4354 This
->parentStorage
->smallBlockDepotChain
,
4355 offsetOfBlockInDepot
,
4362 StorageUtl_ReadDWord(&buffer
, 0, &nextBlockInChain
);
4365 return nextBlockInChain
;
4368 /******************************************************************************
4369 * SmallBlockChainStream_SetNextBlockInChain
4371 * Writes the index of the next block of the specified block in the small
4373 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4374 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4376 void SmallBlockChainStream_SetNextBlockInChain(
4377 SmallBlockChainStream
* This
,
4381 ULARGE_INTEGER offsetOfBlockInDepot
;
4385 offsetOfBlockInDepot
.HighPart
= 0;
4386 offsetOfBlockInDepot
.LowPart
= blockIndex
* sizeof(ULONG
);
4388 StorageUtl_WriteDWord(&buffer
, 0, nextBlock
);
4391 * Read those bytes in the buffer from the small block file.
4393 BlockChainStream_WriteAt(
4394 This
->parentStorage
->smallBlockDepotChain
,
4395 offsetOfBlockInDepot
,
4401 /******************************************************************************
4402 * SmallBlockChainStream_FreeBlock
4404 * Flag small block 'blockIndex' as free in the small block depot.
4406 void SmallBlockChainStream_FreeBlock(
4407 SmallBlockChainStream
* This
,
4410 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4413 /******************************************************************************
4414 * SmallBlockChainStream_GetNextFreeBlock
4416 * Returns the index of a free small block. The small block depot will be
4417 * enlarged if necessary. The small block chain will also be enlarged if
4420 ULONG
SmallBlockChainStream_GetNextFreeBlock(
4421 SmallBlockChainStream
* This
)
4423 ULARGE_INTEGER offsetOfBlockInDepot
;
4426 ULONG blockIndex
= 0;
4427 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
4428 BOOL success
= TRUE
;
4429 ULONG smallBlocksPerBigBlock
;
4431 offsetOfBlockInDepot
.HighPart
= 0;
4434 * Scan the small block depot for a free block
4436 while (nextBlockIndex
!= BLOCK_UNUSED
)
4438 offsetOfBlockInDepot
.LowPart
= blockIndex
* sizeof(ULONG
);
4440 success
= BlockChainStream_ReadAt(
4441 This
->parentStorage
->smallBlockDepotChain
,
4442 offsetOfBlockInDepot
,
4448 * If we run out of space for the small block depot, enlarge it
4452 StorageUtl_ReadDWord(&buffer
, 0, &nextBlockIndex
);
4454 if (nextBlockIndex
!= BLOCK_UNUSED
)
4460 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
4462 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
4463 ULONG nextBlock
, newsbdIndex
;
4464 BYTE
* smallBlockDepot
;
4466 nextBlock
= sbdIndex
;
4467 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
4469 sbdIndex
= nextBlock
;
4471 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
);
4474 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4475 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
4476 StorageImpl_SetNextBlockInChain(
4477 This
->parentStorage
,
4481 StorageImpl_SetNextBlockInChain(
4482 This
->parentStorage
,
4484 BLOCK_END_OF_CHAIN
);
4487 * Initialize all the small blocks to free
4490 StorageImpl_GetBigBlock(This
->parentStorage
, newsbdIndex
);
4492 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
4493 StorageImpl_ReleaseBigBlock(This
->parentStorage
, smallBlockDepot
);
4498 * We have just created the small block depot.
4500 StgProperty rootProp
;
4504 * Save it in the header
4506 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
4507 StorageImpl_SaveFileHeader(This
->parentStorage
);
4510 * And allocate the first big block that will contain small blocks
4513 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4515 StorageImpl_SetNextBlockInChain(
4516 This
->parentStorage
,
4518 BLOCK_END_OF_CHAIN
);
4520 StorageImpl_ReadProperty(
4521 This
->parentStorage
,
4522 This
->parentStorage
->rootPropertySetIndex
,
4525 rootProp
.startingBlock
= sbStartIndex
;
4526 rootProp
.size
.HighPart
= 0;
4527 rootProp
.size
.LowPart
= This
->parentStorage
->bigBlockSize
;
4529 StorageImpl_WriteProperty(
4530 This
->parentStorage
,
4531 This
->parentStorage
->rootPropertySetIndex
,
4537 smallBlocksPerBigBlock
=
4538 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
4541 * Verify if we have to allocate big blocks to contain small blocks
4543 if (blockIndex
% smallBlocksPerBigBlock
== 0)
4545 StgProperty rootProp
;
4546 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
4548 StorageImpl_ReadProperty(
4549 This
->parentStorage
,
4550 This
->parentStorage
->rootPropertySetIndex
,
4553 if (rootProp
.size
.LowPart
<
4554 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
4556 rootProp
.size
.LowPart
+= This
->parentStorage
->bigBlockSize
;
4558 BlockChainStream_SetSize(
4559 This
->parentStorage
->smallBlockRootChain
,
4562 StorageImpl_WriteProperty(
4563 This
->parentStorage
,
4564 This
->parentStorage
->rootPropertySetIndex
,
4572 /******************************************************************************
4573 * SmallBlockChainStream_ReadAt
4575 * Reads a specified number of bytes from this chain at the specified offset.
4576 * bytesRead may be NULL.
4577 * Failure will be returned if the specified number of bytes has not been read.
4579 BOOL
SmallBlockChainStream_ReadAt(
4580 SmallBlockChainStream
* This
,
4581 ULARGE_INTEGER offset
,
4586 ULARGE_INTEGER offsetInBigBlockFile
;
4587 ULONG blockNoInSequence
=
4588 offset
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4590 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->smallBlockSize
;
4591 ULONG bytesToReadInBuffer
;
4593 ULONG bytesReadFromBigBlockFile
;
4597 * This should never happen on a small block file.
4599 assert(offset
.HighPart
==0);
4602 * Find the first block in the stream that contains part of the buffer.
4604 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4606 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4608 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4610 blockNoInSequence
--;
4614 * Start reading the buffer.
4617 bufferWalker
= buffer
;
4619 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4622 * Calculate how many bytes we can copy from this small block.
4624 bytesToReadInBuffer
=
4625 MIN(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
4628 * Calculate the offset of the small block in the small block file.
4630 offsetInBigBlockFile
.HighPart
= 0;
4631 offsetInBigBlockFile
.LowPart
=
4632 blockIndex
* This
->parentStorage
->smallBlockSize
;
4634 offsetInBigBlockFile
.LowPart
+= offsetInBlock
;
4637 * Read those bytes in the buffer from the small block file.
4639 BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
4640 offsetInBigBlockFile
,
4641 bytesToReadInBuffer
,
4643 &bytesReadFromBigBlockFile
);
4645 assert(bytesReadFromBigBlockFile
== bytesToReadInBuffer
);
4648 * Step to the next big block.
4650 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4651 bufferWalker
+= bytesToReadInBuffer
;
4652 size
-= bytesToReadInBuffer
;
4653 *bytesRead
+= bytesToReadInBuffer
;
4654 offsetInBlock
= 0; /* There is no offset on the next block */
4660 /******************************************************************************
4661 * SmallBlockChainStream_WriteAt
4663 * Writes the specified number of bytes to this chain at the specified offset.
4664 * bytesWritten may be NULL.
4665 * Will fail if not all specified number of bytes have been written.
4667 BOOL
SmallBlockChainStream_WriteAt(
4668 SmallBlockChainStream
* This
,
4669 ULARGE_INTEGER offset
,
4672 ULONG
* bytesWritten
)
4674 ULARGE_INTEGER offsetInBigBlockFile
;
4675 ULONG blockNoInSequence
=
4676 offset
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4678 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->smallBlockSize
;
4679 ULONG bytesToWriteInBuffer
;
4681 ULONG bytesWrittenFromBigBlockFile
;
4685 * This should never happen on a small block file.
4687 assert(offset
.HighPart
==0);
4690 * Find the first block in the stream that contains part of the buffer.
4692 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4694 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4696 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4698 blockNoInSequence
--;
4702 * Start writing the buffer.
4704 * Here, I'm casting away the constness on the buffer variable
4705 * This is OK since we don't intend to modify that buffer.
4708 bufferWalker
= (BYTE
*)buffer
;
4709 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4712 * Calculate how many bytes we can copy to this small block.
4714 bytesToWriteInBuffer
=
4715 MIN(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
4718 * Calculate the offset of the small block in the small block file.
4720 offsetInBigBlockFile
.HighPart
= 0;
4721 offsetInBigBlockFile
.LowPart
=
4722 blockIndex
* This
->parentStorage
->smallBlockSize
;
4724 offsetInBigBlockFile
.LowPart
+= offsetInBlock
;
4727 * Write those bytes in the buffer to the small block file.
4729 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockRootChain
,
4730 offsetInBigBlockFile
,
4731 bytesToWriteInBuffer
,
4733 &bytesWrittenFromBigBlockFile
);
4735 assert(bytesWrittenFromBigBlockFile
== bytesToWriteInBuffer
);
4738 * Step to the next big block.
4740 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4741 bufferWalker
+= bytesToWriteInBuffer
;
4742 size
-= bytesToWriteInBuffer
;
4743 *bytesWritten
+= bytesToWriteInBuffer
;
4744 offsetInBlock
= 0; /* There is no offset on the next block */
4750 /******************************************************************************
4751 * SmallBlockChainStream_Shrink
4753 * Shrinks this chain in the small block depot.
4755 BOOL
SmallBlockChainStream_Shrink(
4756 SmallBlockChainStream
* This
,
4757 ULARGE_INTEGER newSize
)
4759 ULONG blockIndex
, extraBlock
;
4763 numBlocks
= newSize
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4765 if ((newSize
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
4768 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4771 * Go to the new end of chain
4773 while (count
< numBlocks
)
4775 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4779 /* Get the next block before marking the new end */
4780 extraBlock
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4782 /* Mark the new end of chain */
4783 SmallBlockChainStream_SetNextBlockInChain(
4786 BLOCK_END_OF_CHAIN
);
4789 * Mark the extra blocks as free
4791 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4793 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
);
4794 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
4795 extraBlock
= blockIndex
;
4801 /******************************************************************************
4802 * SmallBlockChainStream_Enlarge
4804 * Grows this chain in the small block depot.
4806 BOOL
SmallBlockChainStream_Enlarge(
4807 SmallBlockChainStream
* This
,
4808 ULARGE_INTEGER newSize
)
4810 ULONG blockIndex
, currentBlock
;
4812 ULONG oldNumBlocks
= 0;
4814 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4819 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4821 StgProperty chainProp
;
4823 StorageImpl_ReadProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
4826 chainProp
.startingBlock
= SmallBlockChainStream_GetNextFreeBlock(This
);
4828 StorageImpl_WriteProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
4831 blockIndex
= chainProp
.startingBlock
;
4832 SmallBlockChainStream_SetNextBlockInChain(
4835 BLOCK_END_OF_CHAIN
);
4838 currentBlock
= blockIndex
;
4841 * Figure out how many blocks are needed to contain this stream
4843 newNumBlocks
= newSize
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4845 if ((newSize
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
4849 * Go to the current end of chain
4851 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4854 currentBlock
= blockIndex
;
4855 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
);
4859 * Add new blocks to the chain
4861 while (oldNumBlocks
< newNumBlocks
)
4863 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
4864 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
4866 SmallBlockChainStream_SetNextBlockInChain(
4869 BLOCK_END_OF_CHAIN
);
4871 currentBlock
= blockIndex
;
4878 /******************************************************************************
4879 * SmallBlockChainStream_GetCount
4881 * Returns the number of blocks that comprises this chain.
4882 * This is not the size of this chain as the last block may not be full!
4884 ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
4889 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4891 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4895 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4901 /******************************************************************************
4902 * SmallBlockChainStream_SetSize
4904 * Sets the size of this stream.
4905 * The file will grow if we grow the chain.
4907 * TODO: Free the actual blocks in the file when we shrink the chain.
4908 * Currently, the blocks are still in the file. So the file size
4909 * doesn't shrink even if we shrink streams.
4911 BOOL
SmallBlockChainStream_SetSize(
4912 SmallBlockChainStream
* This
,
4913 ULARGE_INTEGER newSize
)
4915 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
4917 if (newSize
.LowPart
== size
.LowPart
)
4920 if (newSize
.LowPart
< size
.LowPart
)
4922 SmallBlockChainStream_Shrink(This
, newSize
);
4926 SmallBlockChainStream_Enlarge(This
, newSize
);
4932 /******************************************************************************
4933 * SmallBlockChainStream_GetSize
4935 * Returns the size of this chain.
4937 ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
4939 StgProperty chainProperty
;
4941 StorageImpl_ReadProperty(
4942 This
->parentStorage
,
4943 This
->ownerPropertyIndex
,
4946 return chainProperty
.size
;
4949 /******************************************************************************
4950 * StgCreateDocfile32 [OLE32.144]
4952 HRESULT WINAPI
StgCreateDocfile(
4956 IStorage
**ppstgOpen
)
4958 StorageImpl
* newStorage
= 0;
4959 HANDLE hFile
= INVALID_HANDLE_VALUE
;
4964 DWORD fileAttributes
;
4965 WCHAR tempFileName
[MAX_PATH
];
4968 * Validate the parameters
4971 return STG_E_INVALIDPOINTER
;
4974 * Validate the STGM flags
4976 if ( FAILED( validateSTGM(grfMode
) ))
4977 return STG_E_INVALIDFLAG
;
4980 * Generate a unique name.
4984 WCHAR tempPath
[MAX_PATH
];
4985 WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
4987 memset(tempPath
, 0, sizeof(tempPath
));
4988 memset(tempFileName
, 0, sizeof(tempFileName
));
4990 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
4993 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
4994 pwcsName
= tempFileName
;
4996 return STG_E_INSUFFICIENTMEMORY
;
5000 * Interpret the STGM value grfMode
5002 shareMode
= GetShareModeFromSTGM(grfMode
);
5003 accessMode
= GetAccessModeFromSTGM(grfMode
);
5004 creationMode
= GetCreationModeFromSTGM(grfMode
);
5006 if (grfMode
& STGM_DELETEONRELEASE
)
5007 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5009 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5011 if (grfMode
& STGM_TRANSACTED
)
5012 FIXME(ole
, "Transacted mode not implemented.\n");
5015 * Initialize the "out" parameter.
5019 hFile
= CreateFileW(pwcsName
,
5027 if (hFile
== INVALID_HANDLE_VALUE
)
5033 * Allocate and initialize the new IStorage32object.
5035 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5037 if (newStorage
== 0)
5038 return STG_E_INSUFFICIENTMEMORY
;
5040 hr
= StorageImpl_Construct(
5047 HeapFree(GetProcessHeap(), 0, newStorage
);
5052 * Get an "out" pointer for the caller.
5054 hr
= StorageBaseImpl_QueryInterface(
5055 (IStorage
*)newStorage
,
5056 (REFIID
)&IID_IStorage
,
5062 /******************************************************************************
5063 * StgOpenStorage32 [OLE32.148]
5065 HRESULT WINAPI
StgOpenStorage(
5066 const OLECHAR
*pwcsName
,
5067 IStorage
*pstgPriority
,
5071 IStorage
**ppstgOpen
)
5073 StorageImpl
* newStorage
= 0;
5080 * Perform a sanity check
5082 if (( pwcsName
== 0) || (ppstgOpen
== 0) )
5083 return STG_E_INVALIDPOINTER
;
5086 * Validate the STGM flags
5088 if ( FAILED( validateSTGM(grfMode
) ))
5089 return STG_E_INVALIDFLAG
;
5092 * Interpret the STGM value grfMode
5094 shareMode
= GetShareModeFromSTGM(grfMode
);
5095 accessMode
= GetAccessModeFromSTGM(grfMode
);
5098 * Initialize the "out" parameter.
5102 hFile
= CreateFileW( pwcsName
,
5107 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
5111 if (hFile
==INVALID_HANDLE_VALUE
)
5117 * Allocate and initialize the new IStorage32object.
5119 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5121 if (newStorage
== 0)
5122 return STG_E_INSUFFICIENTMEMORY
;
5124 hr
= StorageImpl_Construct(
5131 HeapFree(GetProcessHeap(), 0, newStorage
);
5136 * Get an "out" pointer for the caller.
5138 hr
= StorageBaseImpl_QueryInterface(
5139 (IStorage
*)newStorage
,
5140 (REFIID
)&IID_IStorage
,
5146 /******************************************************************************
5147 * WriteClassStg32 [OLE32.148]
5149 * This method will store the specified CLSID in the specified storage object
5151 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
5157 hRes
= IStorage_SetClass(pStg
, rclsid
);
5163 /****************************************************************************
5164 * This method validate a STGM parameter that can contain the values below
5166 * STGM_DIRECT 0x00000000
5167 * STGM_TRANSACTED 0x00010000
5168 * STGM_SIMPLE 0x08000000
5170 * STGM_READ 0x00000000
5171 * STGM_WRITE 0x00000001
5172 * STGM_READWRITE 0x00000002
5174 * STGM_SHARE_DENY_NONE 0x00000040
5175 * STGM_SHARE_DENY_READ 0x00000030
5176 * STGM_SHARE_DENY_WRITE 0x00000020
5177 * STGM_SHARE_EXCLUSIVE 0x00000010
5179 * STGM_PRIORITY 0x00040000
5180 * STGM_DELETEONRELEASE 0x04000000
5182 * STGM_CREATE 0x00001000
5183 * STGM_CONVERT 0x00020000
5184 * STGM_FAILIFTHERE 0x00000000
5186 * STGM_NOSCRATCH 0x00100000
5187 * STGM_NOSNAPSHOT 0x00200000
5189 static HRESULT
validateSTGM(DWORD stgm
)
5191 BOOL bSTGM_TRANSACTED
= ((stgm
& STGM_TRANSACTED
) == STGM_TRANSACTED
);
5192 BOOL bSTGM_SIMPLE
= ((stgm
& STGM_SIMPLE
) == STGM_SIMPLE
);
5193 BOOL bSTGM_DIRECT
= ! (bSTGM_TRANSACTED
|| bSTGM_SIMPLE
);
5195 BOOL bSTGM_WRITE
= ((stgm
& STGM_WRITE
) == STGM_WRITE
);
5196 BOOL bSTGM_READWRITE
= ((stgm
& STGM_READWRITE
) == STGM_READWRITE
);
5197 BOOL bSTGM_READ
= ! (bSTGM_WRITE
|| bSTGM_READWRITE
);
5199 BOOL bSTGM_SHARE_DENY_NONE
=
5200 ((stgm
& STGM_SHARE_DENY_NONE
) == STGM_SHARE_DENY_NONE
);
5202 BOOL bSTGM_SHARE_DENY_READ
=
5203 ((stgm
& STGM_SHARE_DENY_READ
) == STGM_SHARE_DENY_READ
);
5205 BOOL bSTGM_SHARE_DENY_WRITE
=
5206 ((stgm
& STGM_SHARE_DENY_WRITE
) == STGM_SHARE_DENY_WRITE
);
5208 BOOL bSTGM_SHARE_EXCLUSIVE
=
5209 ((stgm
& STGM_SHARE_EXCLUSIVE
) == STGM_SHARE_EXCLUSIVE
);
5211 BOOL bSTGM_CREATE
= ((stgm
& STGM_CREATE
) == STGM_CREATE
);
5212 BOOL bSTGM_CONVERT
= ((stgm
& STGM_CONVERT
) == STGM_CONVERT
);
5214 BOOL bSTGM_NOSCRATCH
= ((stgm
& STGM_NOSCRATCH
) == STGM_NOSCRATCH
);
5215 BOOL bSTGM_NOSNAPSHOT
= ((stgm
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
);
5218 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5220 if ( ! bSTGM_DIRECT
)
5221 if( bSTGM_TRANSACTED
&& bSTGM_SIMPLE
)
5225 * STGM_WRITE | STGM_READWRITE | STGM_READ
5228 if( bSTGM_WRITE
&& bSTGM_READWRITE
)
5232 * STGM_SHARE_DENY_NONE | others
5233 * (I assume here that DENY_READ implies DENY_WRITE)
5235 if ( bSTGM_SHARE_DENY_NONE
)
5236 if ( bSTGM_SHARE_DENY_READ
||
5237 bSTGM_SHARE_DENY_WRITE
||
5238 bSTGM_SHARE_EXCLUSIVE
)
5242 * STGM_CREATE | STGM_CONVERT
5243 * if both are false, STGM_FAILIFTHERE is set to TRUE
5245 if ( bSTGM_CREATE
&& bSTGM_CONVERT
)
5249 * STGM_NOSCRATCH requires STGM_TRANSACTED
5251 if ( bSTGM_NOSCRATCH
&& ! bSTGM_TRANSACTED
)
5255 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5256 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5258 if (bSTGM_NOSNAPSHOT
)
5260 if ( ! ( bSTGM_TRANSACTED
&&
5261 !(bSTGM_SHARE_EXCLUSIVE
|| bSTGM_SHARE_DENY_WRITE
)) )
5268 /****************************************************************************
5269 * GetShareModeFromSTGM
5271 * This method will return a share mode flag from a STGM value.
5272 * The STGM value is assumed valid.
5274 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
5276 DWORD dwShareMode
= 0;
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 if ((bSTGM_SHARE_EXCLUSIVE
) || (bSTGM_SHARE_DENY_READ
))
5292 if (bSTGM_SHARE_DENY_NONE
)
5293 dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
5295 if (bSTGM_SHARE_DENY_WRITE
)
5296 dwShareMode
= FILE_SHARE_READ
;
5301 /****************************************************************************
5302 * GetAccessModeFromSTGM
5304 * This method will return an access mode flag from a STGM value.
5305 * The STGM value is assumed valid.
5307 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
5309 DWORD dwDesiredAccess
= 0;
5310 BOOL bSTGM_WRITE
= ((stgm
& STGM_WRITE
) == STGM_WRITE
);
5311 BOOL bSTGM_READWRITE
= ((stgm
& STGM_READWRITE
) == STGM_READWRITE
);
5312 BOOL bSTGM_READ
= ! (bSTGM_WRITE
|| bSTGM_READWRITE
);
5315 dwDesiredAccess
= GENERIC_READ
;
5318 dwDesiredAccess
|= GENERIC_WRITE
;
5320 if (bSTGM_READWRITE
)
5321 dwDesiredAccess
= GENERIC_READ
| GENERIC_WRITE
;
5323 return dwDesiredAccess
;
5326 /****************************************************************************
5327 * GetCreationModeFromSTGM
5329 * This method will return a creation mode flag from a STGM value.
5330 * The STGM value is assumed valid.
5332 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
5334 if ( stgm
& STGM_CREATE
)
5335 return CREATE_ALWAYS
;
5336 if (stgm
& STGM_CONVERT
) {
5337 FIXME(ole
, "STGM_CONVERT not implemented!\n");
5340 /* All other cases */
5341 if (stgm
& ~ (STGM_CREATE
|STGM_CONVERT
))
5342 FIXME(ole
,"unhandled storage mode : 0x%08lx\n",stgm
& ~ (STGM_CREATE
|STGM_CONVERT
));