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"
26 #include "storage32.h"
30 static const char rootPropertyName
[] = "Root Entry";
32 /***********************************************************************
33 * Forward declaration of internal functions used by the method DestroyElement
35 static HRESULT
deleteStorageProperty(
36 StorageImpl
*parentStorage
,
37 OLECHAR
*propertyToDeleteName
);
39 static HRESULT
deleteStreamProperty(
40 StorageImpl
*parentStorage
,
41 ULONG foundPropertyIndexToDelete
,
42 StgProperty propertyToDelete
);
44 static HRESULT
findPlaceholder(
46 ULONG propertyIndexToStore
,
47 ULONG storagePropertyIndex
,
50 static HRESULT
adjustPropertyChain(
52 StgProperty propertyToDelete
,
53 StgProperty parentProperty
,
54 ULONG parentPropertyId
,
57 /***********************************************************************
58 * Declaration of the functions used to manipulate StgProperty
61 static ULONG
getFreeProperty(
62 StorageImpl
*storage
);
64 static void updatePropertyChain(
66 ULONG newPropertyIndex
,
67 StgProperty newProperty
);
69 static LONG
propertyNameCmp(
71 OLECHAR
*currentProperty
);
74 /***********************************************************************
75 * Declaration of miscellaneous functions...
77 static HRESULT
validateSTGM(DWORD stgmValue
);
79 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
80 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
81 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
84 * Virtual function table for the IStorage32Impl class.
86 static ICOM_VTABLE(IStorage
) Storage32Impl_Vtbl
=
88 StorageBaseImpl_QueryInterface
,
89 StorageBaseImpl_AddRef
,
90 StorageBaseImpl_Release
,
91 StorageBaseImpl_CreateStream
,
92 StorageBaseImpl_OpenStream
,
93 StorageImpl_CreateStorage
,
94 StorageBaseImpl_OpenStorage
,
96 StorageImpl_MoveElementTo
,
99 StorageBaseImpl_EnumElements
,
100 StorageImpl_DestroyElement
,
101 StorageBaseImpl_RenameElement
,
102 StorageImpl_SetElementTimes
,
103 StorageBaseImpl_SetClass
,
104 StorageImpl_SetStateBits
,
109 * Virtual function table for the Storage32InternalImpl class.
111 static ICOM_VTABLE(IStorage
) Storage32InternalImpl_Vtbl
=
113 StorageBaseImpl_QueryInterface
,
114 StorageBaseImpl_AddRef
,
115 StorageBaseImpl_Release
,
116 StorageBaseImpl_CreateStream
,
117 StorageBaseImpl_OpenStream
,
118 StorageImpl_CreateStorage
,
119 StorageBaseImpl_OpenStorage
,
121 StorageImpl_MoveElementTo
,
122 StorageInternalImpl_Commit
,
123 StorageInternalImpl_Revert
,
124 StorageBaseImpl_EnumElements
,
125 StorageImpl_DestroyElement
,
126 StorageBaseImpl_RenameElement
,
127 StorageImpl_SetElementTimes
,
128 StorageBaseImpl_SetClass
,
129 StorageImpl_SetStateBits
,
134 * Virtual function table for the IEnumSTATSTGImpl class.
136 static ICOM_VTABLE(IEnumSTATSTG
) IEnumSTATSTGImpl_Vtbl
=
138 IEnumSTATSTGImpl_QueryInterface
,
139 IEnumSTATSTGImpl_AddRef
,
140 IEnumSTATSTGImpl_Release
,
141 IEnumSTATSTGImpl_Next
,
142 IEnumSTATSTGImpl_Skip
,
143 IEnumSTATSTGImpl_Reset
,
144 IEnumSTATSTGImpl_Clone
151 /************************************************************************
152 ** Storage32BaseImpl implementatiion
155 /************************************************************************
156 * Storage32BaseImpl_QueryInterface (IUnknown)
158 * This method implements the common QueryInterface for all IStorage32
159 * implementations contained in this file.
161 * See Windows documentation for more details on IUnknown methods.
163 HRESULT WINAPI
StorageBaseImpl_QueryInterface(
168 ICOM_THIS(StorageBaseImpl
,iface
);
170 * Perform a sanity check on the parameters.
172 if ( (This
==0) || (ppvObject
==0) )
176 * Initialize the return parameter.
181 * Compare the riid with the interface IDs implemented by this object.
183 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
185 *ppvObject
= (IStorage
*)This
;
187 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IStorage
)) == 0)
189 *ppvObject
= (IStorage
*)This
;
193 * Check that we obtained an interface.
196 return E_NOINTERFACE
;
199 * Query Interface always increases the reference count by one when it is
202 StorageBaseImpl_AddRef(iface
);
207 /************************************************************************
208 * Storage32BaseImpl_AddRef (IUnknown)
210 * This method implements the common AddRef for all IStorage32
211 * implementations contained in this file.
213 * See Windows documentation for more details on IUnknown methods.
215 ULONG WINAPI
StorageBaseImpl_AddRef(
218 ICOM_THIS(StorageBaseImpl
,iface
);
224 /************************************************************************
225 * Storage32BaseImpl_Release (IUnknown)
227 * This method implements the common Release for all IStorage32
228 * implementations contained in this file.
230 * See Windows documentation for more details on IUnknown methods.
232 ULONG WINAPI
StorageBaseImpl_Release(
235 ICOM_THIS(StorageBaseImpl
,iface
);
237 * Decrease the reference count on this object.
242 * If the reference count goes down to 0, perform suicide.
247 * Since we are using a system of base-classes, we want to call the
248 * destructor of the appropriate derived class. To do this, we are
249 * using virtual functions to implement the destructor.
251 This
->v_destructor(This
);
259 /************************************************************************
260 * Storage32BaseImpl_OpenStream (IStorage)
262 * This method will open the specified stream object from the current storage.
264 * See Windows documentation for more details on IStorage methods.
266 HRESULT WINAPI
StorageBaseImpl_OpenStream(
268 const OLECHAR
* pwcsName
, /* [string][in] */
269 void* reserved1
, /* [unique][in] */
270 DWORD grfMode
, /* [in] */
271 DWORD reserved2
, /* [in] */
272 IStream
** ppstm
) /* [out] */
274 ICOM_THIS(StorageBaseImpl
,iface
);
275 IEnumSTATSTGImpl
* propertyEnumeration
;
276 StgStreamImpl
* newStream
;
277 StgProperty currentProperty
;
278 ULONG foundPropertyIndex
;
281 * Perform a sanity check on the parameters.
283 if ( (pwcsName
==NULL
) || (ppstm
==0) )
287 * Initialize the out parameter
292 * Validate the STGM flags
294 if ( FAILED( validateSTGM(grfMode
) ))
295 return STG_E_INVALIDFLAG
;
300 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
301 (grfMode
& STGM_DELETEONRELEASE
) ||
302 (grfMode
& STGM_TRANSACTED
) )
303 return STG_E_INVALIDFUNCTION
;
306 * Create a property enumeration to search the properties
308 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
309 This
->ancestorStorage
,
310 This
->rootPropertySetIndex
);
313 * Search the enumeration for the property with the given name
315 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
321 * Delete the property enumeration since we don't need it anymore
323 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
326 * If it was found, construct the stream object and return a pointer to it.
328 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
329 (currentProperty
.propertyType
==PROPTYPE_STREAM
) )
331 newStream
= StgStreamImpl_Construct(This
, foundPropertyIndex
);
335 *ppstm
= (IStream
*)newStream
;
338 * Since we are returning a pointer to the interface, we have to
339 * nail down the reference.
341 StgStreamImpl_AddRef(*ppstm
);
346 return E_OUTOFMEMORY
;
349 return STG_E_FILENOTFOUND
;
352 /************************************************************************
353 * Storage32BaseImpl_OpenStorage (IStorage)
355 * This method will open a new storage object from the current storage.
357 * See Windows documentation for more details on IStorage methods.
359 HRESULT WINAPI
StorageBaseImpl_OpenStorage(
361 const OLECHAR
* pwcsName
, /* [string][unique][in] */
362 IStorage
* pstgPriority
, /* [unique][in] */
363 DWORD grfMode
, /* [in] */
364 SNB snbExclude
, /* [unique][in] */
365 DWORD reserved
, /* [in] */
366 IStorage
** ppstg
) /* [out] */
368 ICOM_THIS(StorageBaseImpl
,iface
);
369 StorageInternalImpl
* newStorage
;
370 IEnumSTATSTGImpl
* propertyEnumeration
;
371 StgProperty currentProperty
;
372 ULONG foundPropertyIndex
;
375 * Perform a sanity check on the parameters.
377 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
381 * Validate the STGM flags
383 if ( FAILED( validateSTGM(grfMode
) ))
384 return STG_E_INVALIDFLAG
;
389 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
390 (grfMode
& STGM_DELETEONRELEASE
) ||
391 (grfMode
& STGM_PRIORITY
) )
392 return STG_E_INVALIDFUNCTION
;
395 * Initialize the out parameter
400 * Create a property enumeration to search the properties
402 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
403 This
->ancestorStorage
,
404 This
->rootPropertySetIndex
);
407 * Search the enumeration for the property with the given name
409 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
415 * Delete the property enumeration since we don't need it anymore
417 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
420 * If it was found, construct the stream object and return a pointer to it.
422 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
423 (currentProperty
.propertyType
==PROPTYPE_STORAGE
) )
426 * Construct a new Storage object
428 newStorage
= StorageInternalImpl_Construct(
429 This
->ancestorStorage
,
434 *ppstg
= (IStorage
*)newStorage
;
437 * Since we are returning a pointer to the interface,
438 * we have to nail down the reference.
440 StorageBaseImpl_AddRef(*ppstg
);
445 return STG_E_INSUFFICIENTMEMORY
;
448 return STG_E_FILENOTFOUND
;
451 /************************************************************************
452 * Storage32BaseImpl_EnumElements (IStorage)
454 * This method will create an enumerator object that can be used to
455 * retrieve informatino about all the properties in the storage object.
457 * See Windows documentation for more details on IStorage methods.
459 HRESULT WINAPI
StorageBaseImpl_EnumElements(
461 DWORD reserved1
, /* [in] */
462 void* reserved2
, /* [size_is][unique][in] */
463 DWORD reserved3
, /* [in] */
464 IEnumSTATSTG
** ppenum
) /* [out] */
466 ICOM_THIS(StorageBaseImpl
,iface
);
467 IEnumSTATSTGImpl
* newEnum
;
470 * Perform a sanity check on the parameters.
472 if ( (This
==0) || (ppenum
==0))
476 * Construct the enumerator.
478 newEnum
= IEnumSTATSTGImpl_Construct(
479 This
->ancestorStorage
,
480 This
->rootPropertySetIndex
);
484 *ppenum
= (IEnumSTATSTG
*)newEnum
;
487 * Don't forget to nail down a reference to the new object before
490 IEnumSTATSTGImpl_AddRef(*ppenum
);
495 return E_OUTOFMEMORY
;
498 /************************************************************************
499 * Storage32BaseImpl_Stat (IStorage)
501 * This method will retrieve information about this storage object.
503 * See Windows documentation for more details on IStorage methods.
505 HRESULT WINAPI
StorageBaseImpl_Stat(
507 STATSTG
* pstatstg
, /* [out] */
508 DWORD grfStatFlag
) /* [in] */
510 ICOM_THIS(StorageBaseImpl
,iface
);
511 StgProperty curProperty
;
515 * Perform a sanity check on the parameters.
517 if ( (This
==0) || (pstatstg
==0))
521 * Read the information from the property.
523 readSucessful
= StorageImpl_ReadProperty(
524 This
->ancestorStorage
,
525 This
->rootPropertySetIndex
,
530 StorageUtl_CopyPropertyToSTATSTG(
541 /************************************************************************
542 * Storage32BaseImpl_RenameElement (IStorage)
544 * This method will rename the specified element.
546 * See Windows documentation for more details on IStorage methods.
548 * Implementation notes: The method used to rename consists of creating a clone
549 * of the deleted StgProperty object setting it with the new name and to
550 * perform a DestroyElement of the old StgProperty.
552 HRESULT WINAPI
StorageBaseImpl_RenameElement(
554 const OLECHAR
* pwcsOldName
, /* [in] */
555 const OLECHAR
* pwcsNewName
) /* [in] */
557 ICOM_THIS(StorageBaseImpl
,iface
);
558 IEnumSTATSTGImpl
* propertyEnumeration
;
559 StgProperty currentProperty
;
560 ULONG foundPropertyIndex
;
563 * Create a property enumeration to search the properties
565 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
566 This
->rootPropertySetIndex
);
569 * Search the enumeration for the new property name
571 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
575 if (foundPropertyIndex
!= PROPERTY_NULL
)
578 * There is already a property with the new name
580 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
581 return STG_E_FILEALREADYEXISTS
;
584 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)propertyEnumeration
);
587 * Search the enumeration for the old property name
589 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
594 * Delete the property enumeration since we don't need it anymore
596 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
598 if (foundPropertyIndex
!= PROPERTY_NULL
)
600 StgProperty renamedProperty
;
601 ULONG renamedPropertyIndex
;
604 * Setup a new property for the renamed property
606 renamedProperty
.sizeOfNameString
=
607 ( lstrlenW(pwcsNewName
)+1 ) * sizeof(WCHAR
);
609 if (renamedProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
610 return STG_E_INVALIDNAME
;
612 lstrcpyW(renamedProperty
.name
, pwcsNewName
);
614 renamedProperty
.propertyType
= currentProperty
.propertyType
;
615 renamedProperty
.startingBlock
= currentProperty
.startingBlock
;
616 renamedProperty
.size
.LowPart
= currentProperty
.size
.LowPart
;
617 renamedProperty
.size
.HighPart
= currentProperty
.size
.HighPart
;
619 renamedProperty
.previousProperty
= PROPERTY_NULL
;
620 renamedProperty
.nextProperty
= PROPERTY_NULL
;
623 * Bring the dirProperty link in case it is a storage and in which
624 * case the renamed storage elements don't require to be reorganized.
626 renamedProperty
.dirProperty
= currentProperty
.dirProperty
;
628 /* call CoFileTime to get the current time
629 renamedProperty.timeStampS1
630 renamedProperty.timeStampD1
631 renamedProperty.timeStampS2
632 renamedProperty.timeStampD2
633 renamedProperty.propertyUniqueID
637 * Obtain a free property in the property chain
639 renamedPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
642 * Save the new property into the new property spot
644 StorageImpl_WriteProperty(
645 This
->ancestorStorage
,
646 renamedPropertyIndex
,
650 * Find a spot in the property chain for our newly created property.
654 renamedPropertyIndex
,
658 * At this point the renamed property has been inserted in the tree,
659 * now, before to Destroy the old property we must zeroed it's dirProperty
660 * otherwise the DestroyProperty below will zap it all and we do not want
662 * Also, we fake that the old property is a storage so the DestroyProperty
663 * will not do a SetSize(0) on the stream data.
665 * This means that we need to tweek the StgProperty if it is a stream or a
668 currentProperty
.dirProperty
= PROPERTY_NULL
;
669 currentProperty
.propertyType
= PROPTYPE_STORAGE
;
670 StorageImpl_WriteProperty(
671 This
->ancestorStorage
,
676 * Invoke Destroy to get rid of the ole property and automatically redo
677 * the linking of it's previous and next members...
679 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsOldName
);
685 * There is no property with the old name
687 return STG_E_FILENOTFOUND
;
693 /************************************************************************
694 * Storage32BaseImpl_CreateStream (IStorage)
696 * This method will create a stream object within this storage
698 * See Windows documentation for more details on IStorage methods.
700 HRESULT WINAPI
StorageBaseImpl_CreateStream(
702 const OLECHAR
* pwcsName
, /* [string][in] */
703 DWORD grfMode
, /* [in] */
704 DWORD reserved1
, /* [in] */
705 DWORD reserved2
, /* [in] */
706 IStream
** ppstm
) /* [out] */
708 ICOM_THIS(StorageBaseImpl
,iface
);
709 IEnumSTATSTGImpl
* propertyEnumeration
;
710 StgStreamImpl
* newStream
;
711 StgProperty currentProperty
, newStreamProperty
;
712 ULONG foundPropertyIndex
, newPropertyIndex
;
715 * Validate parameters
718 return STG_E_INVALIDPOINTER
;
721 return STG_E_INVALIDNAME
;
724 * Validate the STGM flags
726 if ( FAILED( validateSTGM(grfMode
) ))
727 return STG_E_INVALIDFLAG
;
732 if ( !(grfMode
& STGM_SHARE_EXCLUSIVE
) ||
733 (grfMode
& STGM_DELETEONRELEASE
) ||
734 (grfMode
& STGM_TRANSACTED
) )
735 return STG_E_INVALIDFUNCTION
;
738 * Initialize the out parameter
743 * Create a property enumeration to search the properties
745 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
746 This
->rootPropertySetIndex
);
748 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
752 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
754 if (foundPropertyIndex
!= PROPERTY_NULL
)
757 * An element with this name already exists
759 if (grfMode
& STGM_CREATE
)
760 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsName
);
762 return STG_E_FILEALREADYEXISTS
;
766 * memset the empty property
768 memset(&newStreamProperty
, 0, sizeof(StgProperty
));
770 newStreamProperty
.sizeOfNameString
=
771 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
773 if (newStreamProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
774 return STG_E_INVALIDNAME
;
776 lstrcpyW(newStreamProperty
.name
, pwcsName
);
778 newStreamProperty
.propertyType
= PROPTYPE_STREAM
;
779 newStreamProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
780 newStreamProperty
.size
.LowPart
= 0;
781 newStreamProperty
.size
.HighPart
= 0;
783 newStreamProperty
.previousProperty
= PROPERTY_NULL
;
784 newStreamProperty
.nextProperty
= PROPERTY_NULL
;
785 newStreamProperty
.dirProperty
= PROPERTY_NULL
;
787 /* call CoFileTime to get the current time
788 newStreamProperty.timeStampS1
789 newStreamProperty.timeStampD1
790 newStreamProperty.timeStampS2
791 newStreamProperty.timeStampD2
794 /* newStreamProperty.propertyUniqueID */
797 * Get a free property or create a new one
799 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
802 * Save the new property into the new property spot
804 StorageImpl_WriteProperty(
805 This
->ancestorStorage
,
810 * Find a spot in the property chain for our newly created property.
818 * Open the stream to return it.
820 newStream
= StgStreamImpl_Construct(This
, newPropertyIndex
);
824 *ppstm
= (IStream
*)newStream
;
827 * Since we are returning a pointer to the interface, we have to nail down
830 StgStreamImpl_AddRef(*ppstm
);
834 return STG_E_INSUFFICIENTMEMORY
;
840 /************************************************************************
841 * Storage32BaseImpl_SetClass (IStorage)
843 * This method will write the specified CLSID in the property of this
846 * See Windows documentation for more details on IStorage methods.
848 HRESULT WINAPI
StorageBaseImpl_SetClass(
850 REFCLSID clsid
) /* [in] */
852 ICOM_THIS(StorageBaseImpl
,iface
);
853 HRESULT hRes
= E_FAIL
;
854 StgProperty curProperty
;
857 success
= StorageImpl_ReadProperty(This
->ancestorStorage
,
858 This
->rootPropertySetIndex
,
862 curProperty
.propertyUniqueID
= *clsid
;
864 success
= StorageImpl_WriteProperty(This
->ancestorStorage
,
865 This
->rootPropertySetIndex
,
874 /************************************************************************
875 ** Storage32Impl implementation
878 /************************************************************************
879 * Storage32Impl_CreateStorage (IStorage)
881 * This method will create the storage object within the provided storage.
883 * See Windows documentation for more details on IStorage methods.
885 HRESULT WINAPI
StorageImpl_CreateStorage(
887 const OLECHAR
*pwcsName
, /* [string][in] */
888 DWORD grfMode
, /* [in] */
889 DWORD reserved1
, /* [in] */
890 DWORD reserved2
, /* [in] */
891 IStorage
**ppstg
) /* [out] */
893 StorageImpl
* const This
=(StorageImpl
*)iface
;
895 IEnumSTATSTGImpl
*propertyEnumeration
;
896 StgProperty currentProperty
;
897 StgProperty newProperty
;
898 ULONG foundPropertyIndex
;
899 ULONG newPropertyIndex
;
903 * Validate parameters
906 return STG_E_INVALIDPOINTER
;
909 return STG_E_INVALIDNAME
;
912 * Validate the STGM flags
914 if ( FAILED( validateSTGM(grfMode
) ) ||
915 (grfMode
& STGM_DELETEONRELEASE
) )
916 return STG_E_INVALIDFLAG
;
919 * Initialize the out parameter
924 * Create a property enumeration and search the properties
926 propertyEnumeration
= IEnumSTATSTGImpl_Construct( This
->ancestorStorage
,
927 This
->rootPropertySetIndex
);
929 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
932 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
934 if (foundPropertyIndex
!= PROPERTY_NULL
)
937 * An element with this name already exists
939 if (grfMode
& STGM_CREATE
)
940 StorageImpl_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsName
);
942 return STG_E_FILEALREADYEXISTS
;
946 * memset the empty property
948 memset(&newProperty
, 0, sizeof(StgProperty
));
950 newProperty
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
952 if (newProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
953 return STG_E_INVALIDNAME
;
955 lstrcpyW(newProperty
.name
, pwcsName
);
957 newProperty
.propertyType
= PROPTYPE_STORAGE
;
958 newProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
959 newProperty
.size
.LowPart
= 0;
960 newProperty
.size
.HighPart
= 0;
962 newProperty
.previousProperty
= PROPERTY_NULL
;
963 newProperty
.nextProperty
= PROPERTY_NULL
;
964 newProperty
.dirProperty
= PROPERTY_NULL
;
966 /* call CoFileTime to get the current time
967 newProperty.timeStampS1
968 newProperty.timeStampD1
969 newProperty.timeStampS2
970 newProperty.timeStampD2
973 /* newStorageProperty.propertyUniqueID */
976 * Obtain a free property in the property chain
978 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
981 * Save the new property into the new property spot
983 StorageImpl_WriteProperty(
984 This
->ancestorStorage
,
989 * Find a spot in the property chain for our newly created property.
997 * Open it to get a pointer to return.
999 hr
= StorageBaseImpl_OpenStorage(
1008 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1017 /***************************************************************************
1021 * Get a free property or create a new one.
1023 static ULONG
getFreeProperty(
1024 StorageImpl
*storage
)
1026 ULONG currentPropertyIndex
= 0;
1027 ULONG newPropertyIndex
= PROPERTY_NULL
;
1028 BOOL readSucessful
= TRUE
;
1029 StgProperty currentProperty
;
1034 * Start by reading the root property
1036 readSucessful
= StorageImpl_ReadProperty(storage
->ancestorStorage
,
1037 currentPropertyIndex
,
1041 if (currentProperty
.sizeOfNameString
== 0)
1044 * The property existis and is available, we found it.
1046 newPropertyIndex
= currentPropertyIndex
;
1052 * We exhausted the property list, we will create more space below
1054 newPropertyIndex
= currentPropertyIndex
;
1056 currentPropertyIndex
++;
1058 } while (newPropertyIndex
== PROPERTY_NULL
);
1061 * grow the property chain
1063 if (! readSucessful
)
1065 StgProperty emptyProperty
;
1066 ULARGE_INTEGER newSize
;
1067 ULONG propertyIndex
;
1068 ULONG lastProperty
= 0;
1069 ULONG blockCount
= 0;
1072 * obtain the new count of property blocks
1074 blockCount
= BlockChainStream_GetCount(
1075 storage
->ancestorStorage
->rootBlockChain
)+1;
1078 * initialize the size used by the property stream
1080 newSize
.HighPart
= 0;
1081 newSize
.LowPart
= storage
->bigBlockSize
* blockCount
;
1084 * add a property block to the property chain
1086 BlockChainStream_SetSize(storage
->ancestorStorage
->rootBlockChain
, newSize
);
1089 * memset the empty property in order to initialize the unused newly
1092 memset(&emptyProperty
, 0, sizeof(StgProperty
));
1097 lastProperty
= storage
->bigBlockSize
/ PROPSET_BLOCK_SIZE
* blockCount
;
1100 propertyIndex
= newPropertyIndex
;
1101 propertyIndex
< lastProperty
;
1104 StorageImpl_WriteProperty(
1105 storage
->ancestorStorage
,
1111 return newPropertyIndex
;
1114 /****************************************************************************
1118 * Case insensitive comparaison of StgProperty.name by first considering
1121 * Returns <0 when newPrpoerty < currentProperty
1122 * >0 when newPrpoerty > currentProperty
1123 * 0 when newPrpoerty == currentProperty
1125 static LONG
propertyNameCmp(
1126 OLECHAR
*newProperty
,
1127 OLECHAR
*currentProperty
)
1129 LONG sizeOfNew
= (lstrlenW(newProperty
) +1) * sizeof(WCHAR
);
1130 LONG sizeOfCur
= (lstrlenW(currentProperty
)+1) * sizeof(WCHAR
);
1131 LONG diff
= sizeOfNew
- sizeOfCur
;
1136 * We compare the string themselves only when they are of the same lenght
1138 WCHAR wsnew
[PROPERTY_NAME_MAX_LEN
];
1139 WCHAR wscur
[PROPERTY_NAME_MAX_LEN
];
1141 diff
= lstrcmpW( (LPCWSTR
)CRTDLL__wcsupr(
1142 lstrcpynW(wsnew
, newProperty
, sizeOfNew
)),
1143 (LPCWSTR
)CRTDLL__wcsupr(
1144 lstrcpynW(wscur
, currentProperty
, sizeOfCur
)));
1150 /****************************************************************************
1154 * Properly link this new element in the property chain.
1156 static void updatePropertyChain(
1157 StorageImpl
*storage
,
1158 ULONG newPropertyIndex
,
1159 StgProperty newProperty
)
1161 StgProperty currentProperty
;
1164 * Read the root property
1166 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1167 storage
->rootPropertySetIndex
,
1170 if (currentProperty
.dirProperty
!= PROPERTY_NULL
)
1173 * The root storage contains some element, therefore, start the research
1174 * for the appropriate location.
1177 ULONG current
, next
, previous
, currentPropertyId
;
1180 * Keep the StgProperty sequence number of the storage first property
1182 currentPropertyId
= currentProperty
.dirProperty
;
1187 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1188 currentProperty
.dirProperty
,
1191 previous
= currentProperty
.previousProperty
;
1192 next
= currentProperty
.nextProperty
;
1193 current
= currentPropertyId
;
1197 LONG diff
= propertyNameCmp( newProperty
.name
, currentProperty
.name
);
1201 if (previous
!= PROPERTY_NULL
)
1203 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1210 currentProperty
.previousProperty
= newPropertyIndex
;
1211 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1219 if (next
!= PROPERTY_NULL
)
1221 StorageImpl_ReadProperty(storage
->ancestorStorage
,
1228 currentProperty
.nextProperty
= newPropertyIndex
;
1229 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1236 previous
= currentProperty
.previousProperty
;
1237 next
= currentProperty
.nextProperty
;
1243 * The root storage is empty, link the new property to it's dir property
1245 currentProperty
.dirProperty
= newPropertyIndex
;
1246 StorageImpl_WriteProperty(storage
->ancestorStorage
,
1247 storage
->rootPropertySetIndex
,
1253 /*************************************************************************
1256 HRESULT WINAPI
StorageImpl_CopyTo(
1258 DWORD ciidExclude
, /* [in] */
1259 const IID
*rgiidExclude
,/* [size_is][unique][in] */
1260 SNB snbExclude
, /* [unique][in] */
1261 IStorage
*pstgDest
) /* [unique][in] */
1266 /*************************************************************************
1267 * MoveElementTo (IStorage)
1269 HRESULT WINAPI
StorageImpl_MoveElementTo(
1271 const OLECHAR
*pwcsName
, /* [string][in] */
1272 IStorage
*pstgDest
, /* [unique][in] */
1273 const OLECHAR
*pwcsNewName
,/* [string][in] */
1274 DWORD grfFlags
) /* [in] */
1279 /*************************************************************************
1282 HRESULT WINAPI
StorageImpl_Commit(
1284 DWORD grfCommitFlags
)/* [in] */
1286 FIXME(ole
, "(%ld): stub!\n", grfCommitFlags
);
1290 /*************************************************************************
1293 HRESULT WINAPI
StorageImpl_Revert(
1299 /*************************************************************************
1300 * DestroyElement (IStorage)
1302 * Stategy: This implementation is build this way for simplicity not for speed.
1303 * I always delete the top most element of the enumeration and adjust
1304 * the deleted element pointer all the time. This takes longer to
1305 * do but allow to reinvoke DestroyElement whenever we encounter a
1306 * storage object. The optimisation reside in the usage of another
1307 * enumeration stategy that would give all the leaves of a storage
1308 * first. (postfix order)
1310 HRESULT WINAPI
StorageImpl_DestroyElement(
1312 const OLECHAR
*pwcsName
)/* [string][in] */
1314 StorageImpl
* const This
=(StorageImpl
*)iface
;
1316 IEnumSTATSTGImpl
* propertyEnumeration
;
1319 StgProperty propertyToDelete
;
1320 StgProperty parentProperty
;
1321 ULONG foundPropertyIndexToDelete
;
1322 ULONG typeOfRelation
;
1323 ULONG parentPropertyId
;
1326 * Perform a sanity check on the parameters.
1329 return STG_E_INVALIDPOINTER
;
1332 * Create a property enumeration to search the property with the given name
1334 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
1335 This
->ancestorStorage
,
1336 This
->rootPropertySetIndex
);
1338 foundPropertyIndexToDelete
= IEnumSTATSTGImpl_FindProperty(
1339 propertyEnumeration
,
1343 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1345 if ( foundPropertyIndexToDelete
== PROPERTY_NULL
)
1347 return STG_E_FILENOTFOUND
;
1351 * Find the parent property of the property to delete (the one that
1352 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1353 * the parent is This. Otherwise, the parent is one of it's sibling...
1357 * First, read This's StgProperty..
1359 res
= StorageImpl_ReadProperty(
1360 This
->ancestorStorage
,
1361 This
->rootPropertySetIndex
,
1367 * Second, check to see if by any chance the actual storage (This) is not
1368 * the parent of the property to delete... We never know...
1370 if ( parentProperty
.dirProperty
== foundPropertyIndexToDelete
)
1373 * Set data as it would have been done in the else part...
1375 typeOfRelation
= PROPERTY_RELATION_DIR
;
1376 parentPropertyId
= This
->rootPropertySetIndex
;
1381 * Create a property enumeration to search the parent properties, and
1382 * delete it once done.
1384 IEnumSTATSTGImpl
* propertyEnumeration2
;
1386 propertyEnumeration2
= IEnumSTATSTGImpl_Construct(
1387 This
->ancestorStorage
,
1388 This
->rootPropertySetIndex
);
1390 typeOfRelation
= IEnumSTATSTGImpl_FindParentProperty(
1391 propertyEnumeration2
,
1392 foundPropertyIndexToDelete
,
1396 IEnumSTATSTGImpl_Destroy(propertyEnumeration2
);
1399 if ( propertyToDelete
.propertyType
== PROPTYPE_STORAGE
)
1401 hr
= deleteStorageProperty(
1403 propertyToDelete
.name
);
1405 else if ( propertyToDelete
.propertyType
== PROPTYPE_STREAM
)
1407 hr
= deleteStreamProperty(
1409 foundPropertyIndexToDelete
,
1417 * Adjust the property chain
1419 hr
= adjustPropertyChain(
1430 /*********************************************************************
1434 * Perform the deletion of a complete storage node
1437 static HRESULT
deleteStorageProperty(
1438 StorageImpl
*parentStorage
,
1439 OLECHAR
*propertyToDeleteName
)
1441 IEnumSTATSTG
*elements
= 0;
1442 IStorage
*childStorage
= 0;
1443 STATSTG currentElement
;
1445 HRESULT destroyHr
= S_OK
;
1448 * Open the storage and enumerate it
1450 hr
= StorageBaseImpl_OpenStorage(
1451 (IStorage
*)parentStorage
,
1452 propertyToDeleteName
,
1454 STGM_SHARE_EXCLUSIVE
,
1465 * Enumerate the elements
1467 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1472 * Obtain the next element
1474 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
1477 destroyHr
= StorageImpl_DestroyElement(
1478 (IStorage
*)childStorage
,
1479 (OLECHAR
*)currentElement
.pwcsName
);
1481 CoTaskMemFree(currentElement
.pwcsName
);
1485 * We need to Reset the enumeration every time because we delete elements
1486 * and the enumeration could be invalid
1488 IEnumSTATSTG_Reset(elements
);
1490 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
1492 IStorage_Release(childStorage
);
1493 IEnumSTATSTG_Release(elements
);
1498 /*********************************************************************
1502 * Perform the deletion of a stream node
1505 static HRESULT
deleteStreamProperty(
1506 StorageImpl
*parentStorage
,
1507 ULONG indexOfPropertyToDelete
,
1508 StgProperty propertyToDelete
)
1512 ULARGE_INTEGER size
;
1517 hr
= StorageBaseImpl_OpenStream(
1518 (IStorage
*)parentStorage
,
1519 (OLECHAR
*)propertyToDelete
.name
,
1521 STGM_SHARE_EXCLUSIVE
,
1533 hr
= IStream_SetSize(pis
, size
);
1541 * Invalidate the property by zeroing it's name member.
1543 propertyToDelete
.sizeOfNameString
= 0;
1546 * Here we should re-read the property so we get the updated pointer
1547 * but since we are here to zap it, I don't do it...
1550 StorageImpl_WriteProperty(
1551 parentStorage
->ancestorStorage
,
1552 indexOfPropertyToDelete
,
1558 /*********************************************************************
1562 * Finds a placeholder for the StgProperty within the Storage
1565 static HRESULT
findPlaceholder(
1566 StorageImpl
*storage
,
1567 ULONG propertyIndexToStore
,
1568 ULONG storePropertyIndex
,
1571 StgProperty storeProperty
;
1576 * Read the storage property
1578 res
= StorageImpl_ReadProperty(
1579 storage
->ancestorStorage
,
1588 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1590 if (storeProperty
.previousProperty
!= PROPERTY_NULL
)
1592 return findPlaceholder(
1594 propertyIndexToStore
,
1595 storeProperty
.previousProperty
,
1600 storeProperty
.previousProperty
= propertyIndexToStore
;
1603 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
1605 if (storeProperty
.nextProperty
!= PROPERTY_NULL
)
1607 return findPlaceholder(
1609 propertyIndexToStore
,
1610 storeProperty
.nextProperty
,
1615 storeProperty
.nextProperty
= propertyIndexToStore
;
1618 else if (typeOfRelation
== PROPERTY_RELATION_DIR
)
1620 if (storeProperty
.dirProperty
!= PROPERTY_NULL
)
1622 return findPlaceholder(
1624 propertyIndexToStore
,
1625 storeProperty
.dirProperty
,
1630 storeProperty
.dirProperty
= propertyIndexToStore
;
1634 hr
= StorageImpl_WriteProperty(
1635 storage
->ancestorStorage
,
1647 /*************************************************************************
1651 * This method takes the previous and the next property link of a property
1652 * to be deleted and find them a place in the Storage.
1654 static HRESULT
adjustPropertyChain(
1656 StgProperty propertyToDelete
,
1657 StgProperty parentProperty
,
1658 ULONG parentPropertyId
,
1661 ULONG newLinkProperty
= PROPERTY_NULL
;
1662 BOOL needToFindAPlaceholder
= FALSE
;
1663 ULONG storeNode
= PROPERTY_NULL
;
1664 ULONG toStoreNode
= PROPERTY_NULL
;
1665 INT relationType
= 0;
1669 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1671 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1674 * Set the parent previous to the property to delete previous
1676 newLinkProperty
= propertyToDelete
.previousProperty
;
1678 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1681 * We also need to find a storage for the other link, setup variables
1682 * to do this at the end...
1684 needToFindAPlaceholder
= TRUE
;
1685 storeNode
= propertyToDelete
.previousProperty
;
1686 toStoreNode
= propertyToDelete
.nextProperty
;
1687 relationType
= PROPERTY_RELATION_NEXT
;
1690 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1693 * Set the parent previous to the property to delete next
1695 newLinkProperty
= propertyToDelete
.nextProperty
;
1699 * Link it for real...
1701 parentProperty
.previousProperty
= newLinkProperty
;
1704 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
1706 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1709 * Set the parent next to the property to delete next previous
1711 newLinkProperty
= propertyToDelete
.previousProperty
;
1713 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1716 * We also need to find a storage for the other link, setup variables
1717 * to do this at the end...
1719 needToFindAPlaceholder
= TRUE
;
1720 storeNode
= propertyToDelete
.previousProperty
;
1721 toStoreNode
= propertyToDelete
.nextProperty
;
1722 relationType
= PROPERTY_RELATION_NEXT
;
1725 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1728 * Set the parent next to the property to delete next
1730 newLinkProperty
= propertyToDelete
.nextProperty
;
1734 * Link it for real...
1736 parentProperty
.nextProperty
= newLinkProperty
;
1738 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
1740 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1743 * Set the parent dir to the property to delete previous
1745 newLinkProperty
= propertyToDelete
.previousProperty
;
1747 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1750 * We also need to find a storage for the other link, setup variables
1751 * to do this at the end...
1753 needToFindAPlaceholder
= TRUE
;
1754 storeNode
= propertyToDelete
.previousProperty
;
1755 toStoreNode
= propertyToDelete
.nextProperty
;
1756 relationType
= PROPERTY_RELATION_NEXT
;
1759 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1762 * Set the parent dir to the property to delete next
1764 newLinkProperty
= propertyToDelete
.nextProperty
;
1768 * Link it for real...
1770 parentProperty
.dirProperty
= newLinkProperty
;
1774 * Write back the parent property
1776 res
= StorageImpl_WriteProperty(
1777 This
->ancestorStorage
,
1786 * If a placeholder is required for the other link, then, find one and
1787 * get out of here...
1789 if (needToFindAPlaceholder
)
1791 hr
= findPlaceholder(
1802 /******************************************************************************
1803 * SetElementTimes (IStorage)
1805 HRESULT WINAPI
StorageImpl_SetElementTimes(
1807 const OLECHAR
*pwcsName
,/* [string][in] */
1808 const FILETIME
*pctime
, /* [in] */
1809 const FILETIME
*patime
, /* [in] */
1810 const FILETIME
*pmtime
) /* [in] */
1815 /******************************************************************************
1816 * SetStateBits (IStorage)
1818 HRESULT WINAPI
StorageImpl_SetStateBits(
1820 DWORD grfStateBits
,/* [in] */
1821 DWORD grfMask
) /* [in] */
1826 HRESULT
StorageImpl_Construct(
1832 StgProperty currentProperty
;
1834 ULONG currentPropertyIndex
;
1836 if ( FAILED( validateSTGM(openFlags
) ))
1837 return STG_E_INVALIDFLAG
;
1839 memset(This
, 0, sizeof(StorageImpl
));
1842 * Initialize the virtual fgunction table.
1844 This
->lpvtbl
= &Storage32Impl_Vtbl
;
1845 This
->v_destructor
= &StorageImpl_Destroy
;
1848 * This is the top-level storage so initialize the ancester pointer
1851 This
->ancestorStorage
= This
;
1854 * Initialize the physical support of the storage.
1856 This
->hFile
= hFile
;
1859 * Initialize the big block cache.
1861 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
1862 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
1863 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
1865 This
->bigBlockSize
);
1867 if (This
->bigBlockFile
== 0)
1870 if (openFlags
& STGM_CREATE
)
1872 ULARGE_INTEGER size
;
1873 BYTE
* bigBlockBuffer
;
1876 * Initialize all header variables:
1877 * - The big block depot consists of one block and it is at block 0
1878 * - The properties start at block 1
1879 * - There is no small block depot
1881 memset( This
->bigBlockDepotStart
,
1883 sizeof(This
->bigBlockDepotStart
));
1885 This
->bigBlockDepotCount
= 1;
1886 This
->bigBlockDepotStart
[0] = 0;
1887 This
->rootStartBlock
= 1;
1888 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
1889 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
1890 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
1891 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
1892 This
->extBigBlockDepotCount
= 0;
1894 StorageImpl_SaveFileHeader(This
);
1897 * Add one block for the big block depot and one block for the properties
1900 size
.LowPart
= This
->bigBlockSize
* 3;
1901 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
1904 * Initialize the big block depot
1906 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, 0);
1907 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
1908 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
1909 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
1910 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
1915 * Load the header for the file.
1917 StorageImpl_LoadFileHeader(This
);
1921 * There is no block depot cached yet.
1923 This
->indexBlockDepotCached
= 0xFFFFFFFF;
1926 * Start searching for free blocks with block 0.
1928 This
->prevFreeBlock
= 0;
1931 * Create the block chain abstractions.
1933 This
->rootBlockChain
=
1934 BlockChainStream_Construct(This
, &This
->rootStartBlock
, PROPERTY_NULL
);
1936 This
->smallBlockDepotChain
= BlockChainStream_Construct(
1938 &This
->smallBlockDepotStart
,
1942 * Write the root property
1944 if (openFlags
& STGM_CREATE
)
1946 StgProperty rootProp
;
1948 * Initialize the property chain
1950 memset(&rootProp
, 0, sizeof(rootProp
));
1951 lstrcpyAtoW(rootProp
.name
, rootPropertyName
);
1953 rootProp
.sizeOfNameString
= (lstrlenW(rootProp
.name
)+1) * sizeof(WCHAR
);
1954 rootProp
.propertyType
= PROPTYPE_ROOT
;
1955 rootProp
.previousProperty
= PROPERTY_NULL
;
1956 rootProp
.nextProperty
= PROPERTY_NULL
;
1957 rootProp
.dirProperty
= PROPERTY_NULL
;
1958 rootProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
1959 rootProp
.size
.HighPart
= 0;
1960 rootProp
.size
.LowPart
= 0;
1962 StorageImpl_WriteProperty(This
, 0, &rootProp
);
1966 * Find the ID of the root int he property sets.
1968 currentPropertyIndex
= 0;
1972 readSucessful
= StorageImpl_ReadProperty(
1974 currentPropertyIndex
,
1979 if ( (currentProperty
.sizeOfNameString
!= 0 ) &&
1980 (currentProperty
.propertyType
== PROPTYPE_ROOT
) )
1982 This
->rootPropertySetIndex
= currentPropertyIndex
;
1986 currentPropertyIndex
++;
1988 } while (readSucessful
&& (This
->rootPropertySetIndex
== PROPERTY_NULL
) );
1997 * Create the block chain abstraction for the small block root chain.
1999 This
->smallBlockRootChain
= BlockChainStream_Construct(
2002 This
->rootPropertySetIndex
);
2007 void StorageImpl_Destroy(
2010 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2011 BlockChainStream_Destroy(This
->rootBlockChain
);
2012 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2014 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2018 /******************************************************************************
2019 * Storage32Impl_GetNextFreeBigBlock
2021 * Returns the index of the next free big block.
2022 * If the big block depot is filled, this method will enlarge it.
2025 ULONG
StorageImpl_GetNextFreeBigBlock(
2028 ULONG depotBlockIndexPos
;
2030 ULONG depotBlockOffset
;
2031 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2032 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2034 ULONG freeBlock
= BLOCK_UNUSED
;
2036 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2037 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2040 * Scan the entire big block depot until we find a block marked free
2042 while (nextBlockIndex
!= BLOCK_UNUSED
)
2044 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2046 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2049 * Grow the primary depot.
2051 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2053 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2056 * Add a block depot.
2058 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2059 This
->bigBlockDepotCount
++;
2060 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2063 * Flag it as a block depot.
2065 StorageImpl_SetNextBlockInChain(This
,
2069 /* Save new header information.
2071 StorageImpl_SaveFileHeader(This
);
2076 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2078 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2081 * Grow the extended depot.
2083 ULONG extIndex
= BLOCK_UNUSED
;
2084 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2085 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2087 if (extBlockOffset
== 0)
2089 /* We need an extended block.
2091 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2092 This
->extBigBlockDepotCount
++;
2093 depotBlockIndexPos
= extIndex
+ 1;
2096 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2099 * Add a block depot and mark it in the extended block.
2101 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2102 This
->bigBlockDepotCount
++;
2103 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2105 /* Flag the block depot.
2107 StorageImpl_SetNextBlockInChain(This
,
2111 /* If necessary, flag the extended depot block.
2113 if (extIndex
!= BLOCK_UNUSED
)
2114 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2116 /* Save header information.
2118 StorageImpl_SaveFileHeader(This
);
2122 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2124 if (depotBuffer
!= 0)
2126 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2127 ( nextBlockIndex
!= BLOCK_UNUSED
))
2129 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2131 if (nextBlockIndex
== BLOCK_UNUSED
)
2133 freeBlock
= (depotIndex
* blocksPerDepot
) +
2134 (depotBlockOffset
/sizeof(ULONG
));
2137 depotBlockOffset
+= sizeof(ULONG
);
2140 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2144 depotBlockOffset
= 0;
2147 This
->prevFreeBlock
= freeBlock
;
2152 /******************************************************************************
2153 * Storage32Impl_AddBlockDepot
2155 * This will create a depot block, essentially it is a block initialized
2158 void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2162 blockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
2165 * Initialize blocks as free
2167 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2169 StorageImpl_ReleaseBigBlock(This
, blockBuffer
);
2172 /******************************************************************************
2173 * Storage32Impl_GetExtDepotBlock
2175 * Returns the index of the block that corresponds to the specified depot
2176 * index. This method is only for depot indexes equal or greater than
2177 * COUNT_BBDEPOTINHEADER.
2179 ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2181 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2182 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2183 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2184 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2185 ULONG blockIndex
= BLOCK_UNUSED
;
2186 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2188 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2190 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2191 return BLOCK_UNUSED
;
2193 while (extBlockCount
> 0)
2195 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2199 if (extBlockIndex
!= BLOCK_UNUSED
)
2203 depotBuffer
= StorageImpl_GetROBigBlock(This
, extBlockIndex
);
2205 if (depotBuffer
!= 0)
2207 StorageUtl_ReadDWord(depotBuffer
,
2208 extBlockOffset
* sizeof(ULONG
),
2211 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2218 /******************************************************************************
2219 * Storage32Impl_SetExtDepotBlock
2221 * Associates the specified block index to the specified depot index.
2222 * This method is only for depot indexes equal or greater than
2223 * COUNT_BBDEPOTINHEADER.
2225 void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
,
2229 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2230 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2231 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2232 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2233 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2235 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2237 while (extBlockCount
> 0)
2239 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2243 if (extBlockIndex
!= BLOCK_UNUSED
)
2247 depotBuffer
= StorageImpl_GetBigBlock(This
, extBlockIndex
);
2249 if (depotBuffer
!= 0)
2251 StorageUtl_WriteDWord(depotBuffer
,
2252 extBlockOffset
* sizeof(ULONG
),
2255 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2260 /******************************************************************************
2261 * Storage32Impl_AddExtBlockDepot
2263 * Creates an extended depot block.
2265 ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2267 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2268 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2269 BYTE
* depotBuffer
= NULL
;
2270 ULONG index
= BLOCK_UNUSED
;
2271 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2272 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2273 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2275 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2276 blocksPerDepotBlock
;
2278 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2281 * The first extended block.
2283 This
->extBigBlockDepotStart
= index
;
2289 * Follow the chain to the last one.
2291 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2293 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2297 * Add the new extended block to the chain.
2299 depotBuffer
= StorageImpl_GetBigBlock(This
, nextExtBlock
);
2300 StorageUtl_WriteDWord(depotBuffer
, nextBlockOffset
, index
);
2301 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2305 * Initialize this block.
2307 depotBuffer
= StorageImpl_GetBigBlock(This
, index
);
2308 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2309 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2314 /******************************************************************************
2315 * Storage32Impl_FreeBigBlock
2317 * This method will flag the specified block as free in the big block depot.
2319 void StorageImpl_FreeBigBlock(
2323 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2325 if (blockIndex
< This
->prevFreeBlock
)
2326 This
->prevFreeBlock
= blockIndex
;
2329 /************************************************************************
2330 * Storage32Impl_GetNextBlockInChain
2332 * This method will retrieve the block index of the next big block in
2335 * Params: This - Pointer to the Storage object.
2336 * blockIndex - Index of the block to retrieve the chain
2339 * Returns: This method returns the index of the next block in the chain.
2340 * It will return the constants:
2341 * BLOCK_SPECIAL - If the block given was not part of a
2343 * BLOCK_END_OF_CHAIN - If the block given was the last in
2345 * BLOCK_UNUSED - If the block given was not past of a chain
2347 * BLOCK_EXTBBDEPOT - This block is part of the extended
2350 * See Windows documentation for more details on IStorage methods.
2352 ULONG
StorageImpl_GetNextBlockInChain(
2356 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2357 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2358 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2359 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2361 ULONG depotBlockIndexPos
;
2363 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2366 * Cache the currently accessed depot block.
2368 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2370 This
->indexBlockDepotCached
= depotBlockCount
;
2372 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2374 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2379 * We have to look in the extended depot.
2381 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2384 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2390 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2392 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &nextBlockIndex
);
2393 This
->blockDepotCached
[index
] = nextBlockIndex
;
2396 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2400 nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2402 return nextBlockIndex
;
2405 /******************************************************************************
2406 * Storage32Impl_GetNextExtendedBlock
2408 * Given an extended block this method will return the next extended block.
2411 * The last ULONG of an extended block is the block index of the next
2412 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2416 * - The index of the next extended block
2417 * - BLOCK_UNUSED: there is no next extended block.
2418 * - Any other return values denotes failure.
2420 ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
2422 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2423 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2426 depotBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
2430 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2432 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2435 return nextBlockIndex
;
2438 /******************************************************************************
2439 * Storage32Impl_SetNextBlockInChain
2441 * This method will write the index of the specified block's next block
2442 * in the big block depot.
2444 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2447 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2448 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2449 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2452 void StorageImpl_SetNextBlockInChain(
2457 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2458 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2459 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2460 ULONG depotBlockIndexPos
;
2463 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2464 assert(blockIndex
!= nextBlock
);
2466 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2468 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2473 * We have to look in the extended depot.
2475 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2478 depotBuffer
= StorageImpl_GetBigBlock(This
, depotBlockIndexPos
);
2482 StorageUtl_WriteDWord(depotBuffer
, depotBlockOffset
, nextBlock
);
2483 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2487 * Update the cached block depot, if necessary.
2489 if (depotBlockCount
== This
->indexBlockDepotCached
)
2491 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
2495 /******************************************************************************
2496 * Storage32Impl_LoadFileHeader
2498 * This method will read in the file header, i.e. big block index -1.
2500 HRESULT
StorageImpl_LoadFileHeader(
2503 HRESULT hr
= STG_E_FILENOTFOUND
;
2504 void* headerBigBlock
= NULL
;
2508 * Get a pointer to the big block of data containing the header.
2510 headerBigBlock
= StorageImpl_GetROBigBlock(This
, -1);
2513 * Extract the information from the header.
2515 if (headerBigBlock
!=0)
2518 * Check for the "magic number" signature and return an error if it is not
2521 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2523 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2524 return STG_E_OLDFORMAT
;
2527 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2529 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2530 return STG_E_INVALIDHEADER
;
2533 StorageUtl_ReadWord(
2535 OFFSET_BIGBLOCKSIZEBITS
,
2536 &This
->bigBlockSizeBits
);
2538 StorageUtl_ReadWord(
2540 OFFSET_SMALLBLOCKSIZEBITS
,
2541 &This
->smallBlockSizeBits
);
2543 StorageUtl_ReadDWord(
2545 OFFSET_BBDEPOTCOUNT
,
2546 &This
->bigBlockDepotCount
);
2548 StorageUtl_ReadDWord(
2550 OFFSET_ROOTSTARTBLOCK
,
2551 &This
->rootStartBlock
);
2553 StorageUtl_ReadDWord(
2555 OFFSET_SBDEPOTSTART
,
2556 &This
->smallBlockDepotStart
);
2558 StorageUtl_ReadDWord(
2560 OFFSET_EXTBBDEPOTSTART
,
2561 &This
->extBigBlockDepotStart
);
2563 StorageUtl_ReadDWord(
2565 OFFSET_EXTBBDEPOTCOUNT
,
2566 &This
->extBigBlockDepotCount
);
2568 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2570 StorageUtl_ReadDWord(
2572 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2573 &(This
->bigBlockDepotStart
[index
]));
2577 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2581 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
2582 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
2586 This
->bigBlockSize
= 0x000000001 >> (DWORD
)This
->bigBlockSizeBits
;
2587 This
->smallBlockSize
= 0x000000001 >> (DWORD
)This
->smallBlockSizeBits
;
2591 * Right now, the code is making some assumptions about the size of the
2592 * blocks, just make sure they are what we're expecting.
2594 assert( (This
->bigBlockSize
==DEF_BIG_BLOCK_SIZE
) &&
2595 (This
->smallBlockSize
==DEF_SMALL_BLOCK_SIZE
));
2598 * Release the block.
2600 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2606 /******************************************************************************
2607 * Storage32Impl_SaveFileHeader
2609 * This method will save to the file the header, i.e. big block -1.
2611 void StorageImpl_SaveFileHeader(
2614 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
2619 * Get a pointer to the big block of data containing the header.
2621 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
2624 * If the block read failed, the file is probably new.
2629 * Initialize for all unknown fields.
2631 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
2634 * Initialize the magic number.
2636 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
2639 * And a bunch of things we don't know what they mean
2641 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
2642 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
2643 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
2644 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
2645 StorageUtl_WriteDWord(headerBigBlock
, 0x40, (DWORD
)0x0001);
2649 * Write the information to the header.
2651 if (headerBigBlock
!=0)
2653 StorageUtl_WriteWord(
2655 OFFSET_BIGBLOCKSIZEBITS
,
2656 This
->bigBlockSizeBits
);
2658 StorageUtl_WriteWord(
2660 OFFSET_SMALLBLOCKSIZEBITS
,
2661 This
->smallBlockSizeBits
);
2663 StorageUtl_WriteDWord(
2665 OFFSET_BBDEPOTCOUNT
,
2666 This
->bigBlockDepotCount
);
2668 StorageUtl_WriteDWord(
2670 OFFSET_ROOTSTARTBLOCK
,
2671 This
->rootStartBlock
);
2673 StorageUtl_WriteDWord(
2675 OFFSET_SBDEPOTSTART
,
2676 This
->smallBlockDepotStart
);
2678 StorageUtl_WriteDWord(
2680 OFFSET_EXTBBDEPOTSTART
,
2681 This
->extBigBlockDepotStart
);
2683 StorageUtl_WriteDWord(
2685 OFFSET_EXTBBDEPOTCOUNT
,
2686 This
->extBigBlockDepotCount
);
2688 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2690 StorageUtl_WriteDWord(
2692 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2693 (This
->bigBlockDepotStart
[index
]));
2698 * Write the big block back to the file.
2700 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
2703 /******************************************************************************
2704 * Storage32Impl_ReadProperty
2706 * This method will read the specified property from the property chain.
2708 BOOL
StorageImpl_ReadProperty(
2711 StgProperty
* buffer
)
2713 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
2714 ULARGE_INTEGER offsetInPropSet
;
2718 offsetInPropSet
.HighPart
= 0;
2719 offsetInPropSet
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
2721 readSucessful
= BlockChainStream_ReadAt(
2722 This
->rootBlockChain
,
2730 memset(buffer
->name
, 0, sizeof(buffer
->name
));
2733 currentProperty
+OFFSET_PS_NAME
,
2734 PROPERTY_NAME_BUFFER_LEN
);
2736 memcpy(&buffer
->propertyType
, currentProperty
+ OFFSET_PS_PROPERTYTYPE
, 1);
2738 StorageUtl_ReadWord(
2740 OFFSET_PS_NAMELENGTH
,
2741 &buffer
->sizeOfNameString
);
2743 StorageUtl_ReadDWord(
2745 OFFSET_PS_PREVIOUSPROP
,
2746 &buffer
->previousProperty
);
2748 StorageUtl_ReadDWord(
2751 &buffer
->nextProperty
);
2753 StorageUtl_ReadDWord(
2756 &buffer
->dirProperty
);
2758 StorageUtl_ReadGUID(
2761 &buffer
->propertyUniqueID
);
2763 StorageUtl_ReadDWord(
2766 &buffer
->timeStampS1
);
2768 StorageUtl_ReadDWord(
2771 &buffer
->timeStampD1
);
2773 StorageUtl_ReadDWord(
2776 &buffer
->timeStampS2
);
2778 StorageUtl_ReadDWord(
2781 &buffer
->timeStampD2
);
2783 StorageUtl_ReadDWord(
2785 OFFSET_PS_STARTBLOCK
,
2786 &buffer
->startingBlock
);
2788 StorageUtl_ReadDWord(
2791 &buffer
->size
.LowPart
);
2793 buffer
->size
.HighPart
= 0;
2796 return readSucessful
;
2799 /*********************************************************************
2800 * Write the specified property into the property chain
2802 BOOL
StorageImpl_WriteProperty(
2805 StgProperty
* buffer
)
2807 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
2808 ULARGE_INTEGER offsetInPropSet
;
2809 BOOL writeSucessful
;
2812 offsetInPropSet
.HighPart
= 0;
2813 offsetInPropSet
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
2815 memset(currentProperty
, 0, PROPSET_BLOCK_SIZE
);
2818 currentProperty
+ OFFSET_PS_NAME
,
2820 PROPERTY_NAME_BUFFER_LEN
);
2822 memcpy(currentProperty
+ OFFSET_PS_PROPERTYTYPE
, &buffer
->propertyType
, 1);
2825 * Reassign the size in case of mistake....
2827 buffer
->sizeOfNameString
= (lstrlenW(buffer
->name
)+1) * sizeof(WCHAR
);
2829 StorageUtl_WriteWord(
2831 OFFSET_PS_NAMELENGTH
,
2832 buffer
->sizeOfNameString
);
2834 StorageUtl_WriteDWord(
2836 OFFSET_PS_PREVIOUSPROP
,
2837 buffer
->previousProperty
);
2839 StorageUtl_WriteDWord(
2842 buffer
->nextProperty
);
2844 StorageUtl_WriteDWord(
2847 buffer
->dirProperty
);
2849 StorageUtl_WriteGUID(
2852 &buffer
->propertyUniqueID
);
2854 StorageUtl_WriteDWord(
2857 buffer
->timeStampS1
);
2859 StorageUtl_WriteDWord(
2862 buffer
->timeStampD1
);
2864 StorageUtl_WriteDWord(
2867 buffer
->timeStampS2
);
2869 StorageUtl_WriteDWord(
2872 buffer
->timeStampD2
);
2874 StorageUtl_WriteDWord(
2876 OFFSET_PS_STARTBLOCK
,
2877 buffer
->startingBlock
);
2879 StorageUtl_WriteDWord(
2882 buffer
->size
.LowPart
);
2884 writeSucessful
= BlockChainStream_WriteAt(This
->rootBlockChain
,
2889 return writeSucessful
;
2892 BOOL
StorageImpl_ReadBigBlock(
2897 void* bigBlockBuffer
;
2899 bigBlockBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
2901 if (bigBlockBuffer
!=0)
2903 memcpy(buffer
, bigBlockBuffer
, This
->bigBlockSize
);
2905 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
2913 BOOL
StorageImpl_WriteBigBlock(
2918 void* bigBlockBuffer
;
2920 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
2922 if (bigBlockBuffer
!=0)
2924 memcpy(bigBlockBuffer
, buffer
, This
->bigBlockSize
);
2926 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
2934 void* StorageImpl_GetROBigBlock(
2938 return BIGBLOCKFILE_GetROBigBlock(This
->bigBlockFile
, blockIndex
);
2941 void* StorageImpl_GetBigBlock(
2945 return BIGBLOCKFILE_GetBigBlock(This
->bigBlockFile
, blockIndex
);
2948 void StorageImpl_ReleaseBigBlock(
2952 BIGBLOCKFILE_ReleaseBigBlock(This
->bigBlockFile
, pBigBlock
);
2955 /******************************************************************************
2956 * Storage32Impl_SmallBlocksToBigBlocks
2958 * This method will convert a small block chain to a big block chain.
2959 * The small block chain will be destroyed.
2961 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
2963 SmallBlockChainStream
** ppsbChain
)
2965 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
2966 ULARGE_INTEGER size
, offset
;
2967 ULONG cbRead
, cbWritten
, cbTotalRead
, cbTotalWritten
;
2968 ULONG propertyIndex
;
2969 BOOL successRead
, successWrite
;
2970 StgProperty chainProperty
;
2971 BYTE buffer
[DEF_SMALL_BLOCK_SIZE
];
2972 BlockChainStream
*bbTempChain
= NULL
;
2973 BlockChainStream
*bigBlockChain
= NULL
;
2976 * Create a temporary big block chain that doesn't have
2977 * an associated property. This temporary chain will be
2978 * used to copy data from small blocks to big blocks.
2980 bbTempChain
= BlockChainStream_Construct(This
,
2985 * Grow the big block chain.
2987 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
2988 BlockChainStream_SetSize(bbTempChain
, size
);
2991 * Copy the contents of the small block chain to the big block chain
2992 * by small block size increments.
2995 offset
.HighPart
= 0;
3001 successRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3006 cbTotalRead
+= cbRead
;
3008 successWrite
= BlockChainStream_WriteAt(bbTempChain
,
3013 cbTotalWritten
+= cbWritten
;
3015 offset
.LowPart
+= This
->smallBlockSize
;
3017 } while (successRead
&& successWrite
);
3019 assert(cbTotalRead
== cbTotalWritten
);
3022 * Destroy the small block chain.
3024 propertyIndex
= (*ppsbChain
)->ownerPropertyIndex
;
3027 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3028 SmallBlockChainStream_Destroy(*ppsbChain
);
3032 * Change the property information. This chain is now a big block chain
3033 * and it doesn't reside in the small blocks chain anymore.
3035 StorageImpl_ReadProperty(This
, propertyIndex
, &chainProperty
);
3037 chainProperty
.startingBlock
= bbHeadOfChain
;
3039 StorageImpl_WriteProperty(This
, propertyIndex
, &chainProperty
);
3042 * Destroy the temporary propertyless big block chain.
3043 * Create a new big block chain associated with this property.
3045 BlockChainStream_Destroy(bbTempChain
);
3046 bigBlockChain
= BlockChainStream_Construct(This
,
3050 return bigBlockChain
;
3053 /******************************************************************************
3054 ** Storage32InternalImpl implementation
3057 StorageInternalImpl
* StorageInternalImpl_Construct(
3058 StorageImpl
* ancestorStorage
,
3059 ULONG rootPropertyIndex
)
3061 StorageInternalImpl
* newStorage
;
3064 * Allocate space for the new storage object
3066 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl
));
3070 memset(newStorage
, 0, sizeof(StorageInternalImpl
));
3073 * Initialize the virtual function table.
3075 newStorage
->lpvtbl
= &Storage32InternalImpl_Vtbl
;
3076 newStorage
->v_destructor
= &StorageInternalImpl_Destroy
;
3079 * Keep the ancestor storage pointer and nail a reference to it.
3081 newStorage
->ancestorStorage
= ancestorStorage
;
3082 StorageBaseImpl_AddRef((IStorage
*)(newStorage
->ancestorStorage
));
3085 * Keep the index of the root property set for this storage,
3087 newStorage
->rootPropertySetIndex
= rootPropertyIndex
;
3095 void StorageInternalImpl_Destroy(
3096 StorageInternalImpl
* This
)
3098 StorageBaseImpl_Release((IStorage
*)This
->ancestorStorage
);
3099 HeapFree(GetProcessHeap(), 0, This
);
3102 /******************************************************************************
3104 ** Storage32InternalImpl_Commit
3106 ** The non-root storages cannot be opened in transacted mode thus this function
3109 HRESULT WINAPI
StorageInternalImpl_Commit(
3111 DWORD grfCommitFlags
) /* [in] */
3116 /******************************************************************************
3118 ** Storage32InternalImpl_Revert
3120 ** The non-root storages cannot be opened in transacted mode thus this function
3123 HRESULT WINAPI
StorageInternalImpl_Revert(
3129 /******************************************************************************
3130 ** IEnumSTATSTGImpl implementation
3133 IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
3134 StorageImpl
* parentStorage
,
3135 ULONG firstPropertyNode
)
3137 IEnumSTATSTGImpl
* newEnumeration
;
3139 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
3141 if (newEnumeration
!=0)
3144 * Set-up the virtual function table and reference count.
3146 newEnumeration
->lpvtbl
= &IEnumSTATSTGImpl_Vtbl
;
3147 newEnumeration
->ref
= 0;
3150 * We want to nail-down the reference to the storage in case the
3151 * enumeration out-lives the storage in the client application.
3153 newEnumeration
->parentStorage
= parentStorage
;
3154 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
3156 newEnumeration
->firstPropertyNode
= firstPropertyNode
;
3159 * Initialize the search stack
3161 newEnumeration
->stackSize
= 0;
3162 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
3163 newEnumeration
->stackToVisit
=
3164 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
)*ENUMSTATSGT_SIZE_INCREMENT
);
3167 * Make sure the current node of the iterator is the first one.
3169 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
3172 return newEnumeration
;
3175 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3177 IStorage_Release((IStorage
*)This
->parentStorage
);
3178 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3179 HeapFree(GetProcessHeap(), 0, This
);
3182 HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3183 IEnumSTATSTG
* iface
,
3187 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3190 * Perform a sanity check on the parameters.
3193 return E_INVALIDARG
;
3196 * Initialize the return parameter.
3201 * Compare the riid with the interface IDs implemented by this object.
3203 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
3205 *ppvObject
= (IEnumSTATSTG
*)This
;
3207 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IEnumSTATSTG
)) == 0)
3209 *ppvObject
= (IEnumSTATSTG
*)This
;
3213 * Check that we obtained an interface.
3215 if ((*ppvObject
)==0)
3216 return E_NOINTERFACE
;
3219 * Query Interface always increases the reference count by one when it is
3222 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG
*)This
);
3227 ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3228 IEnumSTATSTG
* iface
)
3230 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3236 ULONG WINAPI
IEnumSTATSTGImpl_Release(
3237 IEnumSTATSTG
* iface
)
3239 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3247 * If the reference count goes down to 0, perform suicide.
3251 IEnumSTATSTGImpl_Destroy(This
);
3257 HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3258 IEnumSTATSTG
* iface
,
3261 ULONG
* pceltFetched
)
3263 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3265 StgProperty currentProperty
;
3266 STATSTG
* currentReturnStruct
= rgelt
;
3267 ULONG objectFetched
= 0;
3268 ULONG currentSearchNode
;
3271 * Perform a sanity check on the parameters.
3273 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3274 return E_INVALIDARG
;
3277 * To avoid the special case, get another pointer to a ULONG value if
3278 * the caller didn't supply one.
3280 if (pceltFetched
==0)
3281 pceltFetched
= &objectFetched
;
3284 * Start the iteration, we will iterate until we hit the end of the
3285 * linked list or until we hit the number of items to iterate through
3290 * Start with the node at the top of the stack.
3292 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3294 while ( ( *pceltFetched
< celt
) &&
3295 ( currentSearchNode
!=PROPERTY_NULL
) )
3298 * Remove the top node from the stack
3300 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3303 * Read the property from the storage.
3305 StorageImpl_ReadProperty(This
->parentStorage
,
3310 * Copy the information to the return buffer.
3312 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct
,
3317 * Step to the next item in the iteration
3320 currentReturnStruct
++;
3323 * Push the next search node in the search stack.
3325 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3328 * continue the iteration.
3330 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3333 if (*pceltFetched
== celt
)
3340 HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3341 IEnumSTATSTG
* iface
,
3344 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3346 StgProperty currentProperty
;
3347 ULONG objectFetched
= 0;
3348 ULONG currentSearchNode
;
3351 * Start with the node at the top of the stack.
3353 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3355 while ( (objectFetched
< celt
) &&
3356 (currentSearchNode
!=PROPERTY_NULL
) )
3359 * Remove the top node from the stack
3361 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3364 * Read the property from the storage.
3366 StorageImpl_ReadProperty(This
->parentStorage
,
3371 * Step to the next item in the iteration
3376 * Push the next search node in the search stack.
3378 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3381 * continue the iteration.
3383 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3386 if (objectFetched
== celt
)
3392 HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3393 IEnumSTATSTG
* iface
)
3395 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3397 StgProperty rootProperty
;
3401 * Re-initialize the search stack to an empty stack
3403 This
->stackSize
= 0;
3406 * Read the root property from the storage.
3408 readSucessful
= StorageImpl_ReadProperty(
3409 This
->parentStorage
,
3410 This
->firstPropertyNode
,
3415 assert(rootProperty
.sizeOfNameString
!=0);
3418 * Push the search node in the search stack.
3420 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.dirProperty
);
3426 HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3427 IEnumSTATSTG
* iface
,
3428 IEnumSTATSTG
** ppenum
)
3430 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3432 IEnumSTATSTGImpl
* newClone
;
3435 * Perform a sanity check on the parameters.
3438 return E_INVALIDARG
;
3440 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3441 This
->firstPropertyNode
);
3445 * The new clone enumeration must point to the same current node as
3448 newClone
->stackSize
= This
->stackSize
;
3449 newClone
->stackMaxSize
= This
->stackMaxSize
;
3450 newClone
->stackToVisit
=
3451 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3454 newClone
->stackToVisit
,
3456 sizeof(ULONG
) * newClone
->stackSize
);
3458 *ppenum
= (IEnumSTATSTG
*)newClone
;
3461 * Don't forget to nail down a reference to the clone before
3464 IEnumSTATSTGImpl_AddRef(*ppenum
);
3469 INT
IEnumSTATSTGImpl_FindParentProperty(
3470 IEnumSTATSTGImpl
*This
,
3471 ULONG childProperty
,
3472 StgProperty
*currentProperty
,
3475 ULONG currentSearchNode
;
3479 * To avoid the special case, get another pointer to a ULONG value if
3480 * the caller didn't supply one.
3483 thisNodeId
= &foundNode
;
3486 * Start with the node at the top of the stack.
3488 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3491 while (currentSearchNode
!=PROPERTY_NULL
)
3494 * Store the current node in the returned parameters
3496 *thisNodeId
= currentSearchNode
;
3499 * Remove the top node from the stack
3501 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3504 * Read the property from the storage.
3506 StorageImpl_ReadProperty(
3507 This
->parentStorage
,
3511 if (currentProperty
->previousProperty
== childProperty
)
3512 return PROPERTY_RELATION_PREVIOUS
;
3514 else if (currentProperty
->nextProperty
== childProperty
)
3515 return PROPERTY_RELATION_NEXT
;
3517 else if (currentProperty
->dirProperty
== childProperty
)
3518 return PROPERTY_RELATION_DIR
;
3521 * Push the next search node in the search stack.
3523 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3526 * continue the iteration.
3528 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3531 return PROPERTY_NULL
;
3534 ULONG
IEnumSTATSTGImpl_FindProperty(
3535 IEnumSTATSTGImpl
* This
,
3536 const OLECHAR
* lpszPropName
,
3537 StgProperty
* currentProperty
)
3539 ULONG currentSearchNode
;
3542 * Start with the node at the top of the stack.
3544 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3546 while (currentSearchNode
!=PROPERTY_NULL
)
3549 * Remove the top node from the stack
3551 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3554 * Read the property from the storage.
3556 StorageImpl_ReadProperty(This
->parentStorage
,
3560 if ( propertyNameCmp(
3561 (OLECHAR
*)currentProperty
->name
,
3562 (OLECHAR
*)lpszPropName
) == 0)
3563 return currentSearchNode
;
3566 * Push the next search node in the search stack.
3568 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3571 * continue the iteration.
3573 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3576 return PROPERTY_NULL
;
3579 void IEnumSTATSTGImpl_PushSearchNode(
3580 IEnumSTATSTGImpl
* This
,
3583 StgProperty rootProperty
;
3587 * First, make sure we're not trying to push an unexisting node.
3589 if (nodeToPush
==PROPERTY_NULL
)
3593 * First push the node to the stack
3595 if (This
->stackSize
== This
->stackMaxSize
)
3597 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
3599 This
->stackToVisit
= HeapReAlloc(
3603 sizeof(ULONG
) * This
->stackMaxSize
);
3606 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
3610 * Read the root property from the storage.
3612 readSucessful
= StorageImpl_ReadProperty(
3613 This
->parentStorage
,
3619 assert(rootProperty
.sizeOfNameString
!=0);
3622 * Push the previous search node in the search stack.
3624 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.previousProperty
);
3628 ULONG
IEnumSTATSTGImpl_PopSearchNode(
3629 IEnumSTATSTGImpl
* This
,
3634 if (This
->stackSize
== 0)
3635 return PROPERTY_NULL
;
3637 topNode
= This
->stackToVisit
[This
->stackSize
-1];
3645 /******************************************************************************
3646 ** StorageUtl implementation
3649 void StorageUtl_ReadWord(void* buffer
, ULONG offset
, WORD
* value
)
3651 memcpy(value
, (BYTE
*)buffer
+offset
, sizeof(WORD
));
3654 void StorageUtl_WriteWord(void* buffer
, ULONG offset
, WORD value
)
3656 memcpy((BYTE
*)buffer
+offset
, &value
, sizeof(WORD
));
3659 void StorageUtl_ReadDWord(void* buffer
, ULONG offset
, DWORD
* value
)
3661 memcpy(value
, (BYTE
*)buffer
+offset
, sizeof(DWORD
));
3664 void StorageUtl_WriteDWord(void* buffer
, ULONG offset
, DWORD value
)
3666 memcpy((BYTE
*)buffer
+offset
, &value
, sizeof(DWORD
));
3669 void StorageUtl_ReadGUID(void* buffer
, ULONG offset
, GUID
* value
)
3671 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
3672 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
3673 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
3675 memcpy(value
->Data4
, (BYTE
*)buffer
+offset
+8, sizeof(value
->Data4
));
3678 void StorageUtl_WriteGUID(void* buffer
, ULONG offset
, GUID
* value
)
3680 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
3681 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
3682 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
3684 memcpy((BYTE
*)buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
3687 void StorageUtl_CopyPropertyToSTATSTG(
3688 STATSTG
* destination
,
3689 StgProperty
* source
,
3693 * The copy of the string occurs only when the flag is not set
3695 if ((statFlags
& STATFLAG_NONAME
) != 0)
3697 destination
->pwcsName
= 0;
3701 destination
->pwcsName
=
3702 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
3704 lstrcpyW((LPWSTR
)destination
->pwcsName
, source
->name
);
3707 switch (source
->propertyType
)
3709 case PROPTYPE_STORAGE
:
3711 destination
->type
= STGTY_STORAGE
;
3713 case PROPTYPE_STREAM
:
3714 destination
->type
= STGTY_STREAM
;
3717 destination
->type
= STGTY_STREAM
;
3721 destination
->cbSize
= source
->size
;
3723 currentReturnStruct->mtime = {0}; TODO
3724 currentReturnStruct->ctime = {0};
3725 currentReturnStruct->atime = {0};
3727 destination
->grfMode
= 0;
3728 destination
->grfLocksSupported
= 0;
3729 destination
->clsid
= source
->propertyUniqueID
;
3730 destination
->grfStateBits
= 0;
3731 destination
->reserved
= 0;
3734 /******************************************************************************
3735 ** BlockChainStream implementation
3738 BlockChainStream
* BlockChainStream_Construct(
3739 StorageImpl
* parentStorage
,
3740 ULONG
* headOfStreamPlaceHolder
,
3741 ULONG propertyIndex
)
3743 BlockChainStream
* newStream
;
3746 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
3748 newStream
->parentStorage
= parentStorage
;
3749 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
3750 newStream
->ownerPropertyIndex
= propertyIndex
;
3751 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
3752 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
3753 newStream
->numBlocks
= 0;
3755 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
3757 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
3759 newStream
->numBlocks
++;
3760 newStream
->tailIndex
= blockIndex
;
3762 blockIndex
= StorageImpl_GetNextBlockInChain(
3770 void BlockChainStream_Destroy(BlockChainStream
* This
)
3772 HeapFree(GetProcessHeap(), 0, This
);
3775 /******************************************************************************
3776 * BlockChainStream_GetHeadOfChain
3778 * Returns the head of this stream chain.
3779 * Some special chains don't have properties, their heads are kept in
3780 * This->headOfStreamPlaceHolder.
3783 ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
3785 StgProperty chainProperty
;
3788 if (This
->headOfStreamPlaceHolder
!= 0)
3789 return *(This
->headOfStreamPlaceHolder
);
3791 if (This
->ownerPropertyIndex
!= PROPERTY_NULL
)
3793 readSucessful
= StorageImpl_ReadProperty(
3794 This
->parentStorage
,
3795 This
->ownerPropertyIndex
,
3800 return chainProperty
.startingBlock
;
3804 return BLOCK_END_OF_CHAIN
;
3807 /******************************************************************************
3808 * BlockChainStream_GetCount
3810 * Returns the number of blocks that comprises this chain.
3811 * This is not the size of the stream as the last block may not be full!
3814 ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
3819 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
3821 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
3825 blockIndex
= StorageImpl_GetNextBlockInChain(
3826 This
->parentStorage
,
3833 /******************************************************************************
3834 * BlockChainStream_ReadAt
3836 * Reads a specified number of bytes from this chain at the specified offset.
3837 * bytesRead may be NULL.
3838 * Failure will be returned if the specified number of bytes has not been read.
3840 BOOL
BlockChainStream_ReadAt(BlockChainStream
* This
,
3841 ULARGE_INTEGER offset
,
3846 ULONG blockNoInSequence
= offset
.LowPart
/ This
->parentStorage
->bigBlockSize
;
3847 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->bigBlockSize
;
3848 ULONG bytesToReadInBuffer
;
3851 BYTE
* bigBlockBuffer
;
3853 if (This
->lastBlockNoInSequence
== 0xFFFFFFFF)
3854 This
->lastBlockNoInSequence
= blockNoInSequence
;
3856 * Find the first block in the stream that contains part of the buffer.
3858 if (blockNoInSequence
> This
->lastBlockNoInSequence
)
3860 ULONG temp
= blockNoInSequence
;
3862 blockIndex
= This
->lastBlockNoInSequenceIndex
;
3863 blockNoInSequence
-= This
->lastBlockNoInSequence
;
3864 This
->lastBlockNoInSequence
= temp
;
3868 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
3869 This
->lastBlockNoInSequence
= blockNoInSequence
;
3872 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
3875 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
3877 blockNoInSequence
--;
3880 This
->lastBlockNoInSequenceIndex
= blockIndex
;
3883 * Start reading the buffer.
3886 bufferWalker
= buffer
;
3888 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
3891 * Calculate how many bytes we can copy from this big block.
3893 bytesToReadInBuffer
=
3894 MIN(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
3897 * Copy those bytes to the buffer
3900 StorageImpl_GetROBigBlock(This
->parentStorage
, blockIndex
);
3902 memcpy(bufferWalker
, bigBlockBuffer
+ offsetInBlock
, bytesToReadInBuffer
);
3904 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
3907 * Step to the next big block.
3910 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
3912 bufferWalker
+= bytesToReadInBuffer
;
3913 size
-= bytesToReadInBuffer
;
3914 *bytesRead
+= bytesToReadInBuffer
;
3915 offsetInBlock
= 0; /* There is no offset on the next block */
3922 /******************************************************************************
3923 * BlockChainStream_WriteAt
3925 * Writes the specified number of bytes to this chain at the specified offset.
3926 * bytesWritten may be NULL.
3927 * Will fail if not all specified number of bytes have been written.
3929 BOOL
BlockChainStream_WriteAt(BlockChainStream
* This
,
3930 ULARGE_INTEGER offset
,
3933 ULONG
* bytesWritten
)
3935 ULONG blockNoInSequence
= offset
.LowPart
/ This
->parentStorage
->bigBlockSize
;
3936 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->bigBlockSize
;
3940 BYTE
* bigBlockBuffer
;
3942 if (This
->lastBlockNoInSequence
== 0xFFFFFFFF)
3943 This
->lastBlockNoInSequence
= blockNoInSequence
;
3946 * Find the first block in the stream that contains part of the buffer.
3948 if (blockNoInSequence
> This
->lastBlockNoInSequence
)
3950 ULONG temp
= blockNoInSequence
;
3952 blockIndex
= This
->lastBlockNoInSequenceIndex
;
3953 blockNoInSequence
-= This
->lastBlockNoInSequence
;
3954 This
->lastBlockNoInSequence
= temp
;
3958 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
3959 This
->lastBlockNoInSequence
= blockNoInSequence
;
3962 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
3965 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
3967 blockNoInSequence
--;
3970 This
->lastBlockNoInSequenceIndex
= blockIndex
;
3973 * Here, I'm casting away the constness on the buffer variable
3974 * This is OK since we don't intend to modify that buffer.
3977 bufferWalker
= (BYTE
*)buffer
;
3979 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
3982 * Calculate how many bytes we can copy from this big block.
3985 MIN(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
3988 * Copy those bytes to the buffer
3990 bigBlockBuffer
= StorageImpl_GetBigBlock(This
->parentStorage
, blockIndex
);
3992 memcpy(bigBlockBuffer
+ offsetInBlock
, bufferWalker
, bytesToWrite
);
3994 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
3997 * Step to the next big block.
4000 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4002 bufferWalker
+= bytesToWrite
;
4003 size
-= bytesToWrite
;
4004 *bytesWritten
+= bytesToWrite
;
4005 offsetInBlock
= 0; /* There is no offset on the next block */
4011 /******************************************************************************
4012 * BlockChainStream_Shrink
4014 * Shrinks this chain in the big block depot.
4016 BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4017 ULARGE_INTEGER newSize
)
4019 ULONG blockIndex
, extraBlock
;
4024 * Figure out how many blocks are needed to contain the new size
4026 numBlocks
= newSize
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4028 if ((newSize
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4031 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4034 * Go to the new end of chain
4036 while (count
< numBlocks
)
4039 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4044 /* Get the next block before marking the new end */
4046 StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
);
4048 /* Mark the new end of chain */
4049 StorageImpl_SetNextBlockInChain(
4050 This
->parentStorage
,
4052 BLOCK_END_OF_CHAIN
);
4054 This
->tailIndex
= blockIndex
;
4055 This
->numBlocks
= numBlocks
;
4058 * Mark the extra blocks as free
4060 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4063 StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
);
4065 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4066 extraBlock
= blockIndex
;
4072 /******************************************************************************
4073 * BlockChainStream_Enlarge
4075 * Grows this chain in the big block depot.
4077 BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4078 ULARGE_INTEGER newSize
)
4080 ULONG blockIndex
, currentBlock
;
4082 ULONG oldNumBlocks
= 0;
4084 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4087 * Empty chain. Create the head.
4089 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4091 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4092 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4094 BLOCK_END_OF_CHAIN
);
4096 if (This
->headOfStreamPlaceHolder
!= 0)
4098 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4102 StgProperty chainProp
;
4103 assert(This
->ownerPropertyIndex
!= PROPERTY_NULL
);
4105 StorageImpl_ReadProperty(
4106 This
->parentStorage
,
4107 This
->ownerPropertyIndex
,
4110 chainProp
.startingBlock
= blockIndex
;
4112 StorageImpl_WriteProperty(
4113 This
->parentStorage
,
4114 This
->ownerPropertyIndex
,
4118 This
->tailIndex
= blockIndex
;
4119 This
->numBlocks
= 1;
4123 * Figure out how many blocks are needed to contain this stream
4125 newNumBlocks
= newSize
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4127 if ((newSize
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4131 * Go to the current end of chain
4133 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4135 currentBlock
= blockIndex
;
4137 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4140 currentBlock
= blockIndex
;
4143 StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
);
4146 This
->tailIndex
= currentBlock
;
4149 currentBlock
= This
->tailIndex
;
4150 oldNumBlocks
= This
->numBlocks
;
4153 * Add new blocks to the chain
4155 while (oldNumBlocks
< newNumBlocks
)
4157 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4159 StorageImpl_SetNextBlockInChain(
4160 This
->parentStorage
,
4164 StorageImpl_SetNextBlockInChain(
4165 This
->parentStorage
,
4167 BLOCK_END_OF_CHAIN
);
4169 currentBlock
= blockIndex
;
4173 This
->tailIndex
= blockIndex
;
4174 This
->numBlocks
= newNumBlocks
;
4179 /******************************************************************************
4180 * BlockChainStream_SetSize
4182 * Sets the size of this stream. The big block depot will be updated.
4183 * The file will grow if we grow the chain.
4185 * TODO: Free the actual blocks in the file when we shrink the chain.
4186 * Currently, the blocks are still in the file. So the file size
4187 * doesn't shrink even if we shrink streams.
4189 BOOL
BlockChainStream_SetSize(
4190 BlockChainStream
* This
,
4191 ULARGE_INTEGER newSize
)
4193 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4195 if (newSize
.LowPart
== size
.LowPart
)
4198 if (newSize
.LowPart
< size
.LowPart
)
4200 BlockChainStream_Shrink(This
, newSize
);
4204 ULARGE_INTEGER fileSize
=
4205 BIGBLOCKFILE_GetSize(This
->parentStorage
->bigBlockFile
);
4207 ULONG diff
= newSize
.LowPart
- size
.LowPart
;
4210 * Make sure the file stays a multiple of blocksize
4212 if ((diff
% This
->parentStorage
->bigBlockSize
) != 0)
4213 diff
+= (This
->parentStorage
->bigBlockSize
-
4214 (diff
% This
->parentStorage
->bigBlockSize
) );
4216 fileSize
.LowPart
+= diff
;
4217 BIGBLOCKFILE_SetSize(This
->parentStorage
->bigBlockFile
, fileSize
);
4219 BlockChainStream_Enlarge(This
, newSize
);
4225 /******************************************************************************
4226 * BlockChainStream_GetSize
4228 * Returns the size of this chain.
4229 * Will return the block count if this chain doesn't have a property.
4231 ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4233 StgProperty chainProperty
;
4235 if(This
->headOfStreamPlaceHolder
== NULL
)
4238 * This chain is a data stream read the property and return
4239 * the appropriate size
4241 StorageImpl_ReadProperty(
4242 This
->parentStorage
,
4243 This
->ownerPropertyIndex
,
4246 return chainProperty
.size
;
4251 * this chain is a chain that does not have a property, figure out the
4252 * size by making the product number of used blocks times the
4255 ULARGE_INTEGER result
;
4256 result
.HighPart
= 0;
4259 BlockChainStream_GetCount(This
) *
4260 This
->parentStorage
->bigBlockSize
;
4266 /******************************************************************************
4267 ** SmallBlockChainStream implementation
4270 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4271 StorageImpl
* parentStorage
,
4272 ULONG propertyIndex
)
4274 SmallBlockChainStream
* newStream
;
4276 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4278 newStream
->parentStorage
= parentStorage
;
4279 newStream
->ownerPropertyIndex
= propertyIndex
;
4284 void SmallBlockChainStream_Destroy(
4285 SmallBlockChainStream
* This
)
4287 HeapFree(GetProcessHeap(), 0, This
);
4290 /******************************************************************************
4291 * SmallBlockChainStream_GetHeadOfChain
4293 * Returns the head of this chain of small blocks.
4295 ULONG
SmallBlockChainStream_GetHeadOfChain(
4296 SmallBlockChainStream
* This
)
4298 StgProperty chainProperty
;
4301 if (This
->ownerPropertyIndex
)
4303 readSucessful
= StorageImpl_ReadProperty(
4304 This
->parentStorage
,
4305 This
->ownerPropertyIndex
,
4310 return chainProperty
.startingBlock
;
4315 return BLOCK_END_OF_CHAIN
;
4318 /******************************************************************************
4319 * SmallBlockChainStream_GetNextBlockInChain
4321 * Returns the index of the next small block in this chain.
4324 * - BLOCK_END_OF_CHAIN: end of this chain
4325 * - BLOCK_UNUSED: small block 'blockIndex' is free
4327 ULONG
SmallBlockChainStream_GetNextBlockInChain(
4328 SmallBlockChainStream
* This
,
4331 ULARGE_INTEGER offsetOfBlockInDepot
;
4333 ULONG nextBlockInChain
= BLOCK_END_OF_CHAIN
;
4337 offsetOfBlockInDepot
.HighPart
= 0;
4338 offsetOfBlockInDepot
.LowPart
= blockIndex
* sizeof(ULONG
);
4341 * Read those bytes in the buffer from the small block file.
4343 success
= BlockChainStream_ReadAt(
4344 This
->parentStorage
->smallBlockDepotChain
,
4345 offsetOfBlockInDepot
,
4352 StorageUtl_ReadDWord(&buffer
, 0, &nextBlockInChain
);
4355 return nextBlockInChain
;
4358 /******************************************************************************
4359 * SmallBlockChainStream_SetNextBlockInChain
4361 * Writes the index of the next block of the specified block in the small
4363 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4364 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4366 void SmallBlockChainStream_SetNextBlockInChain(
4367 SmallBlockChainStream
* This
,
4371 ULARGE_INTEGER offsetOfBlockInDepot
;
4375 offsetOfBlockInDepot
.HighPart
= 0;
4376 offsetOfBlockInDepot
.LowPart
= blockIndex
* sizeof(ULONG
);
4378 StorageUtl_WriteDWord(&buffer
, 0, nextBlock
);
4381 * Read those bytes in the buffer from the small block file.
4383 BlockChainStream_WriteAt(
4384 This
->parentStorage
->smallBlockDepotChain
,
4385 offsetOfBlockInDepot
,
4391 /******************************************************************************
4392 * SmallBlockChainStream_FreeBlock
4394 * Flag small block 'blockIndex' as free in the small block depot.
4396 void SmallBlockChainStream_FreeBlock(
4397 SmallBlockChainStream
* This
,
4400 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4403 /******************************************************************************
4404 * SmallBlockChainStream_GetNextFreeBlock
4406 * Returns the index of a free small block. The small block depot will be
4407 * enlarged if necessary. The small block chain will also be enlarged if
4410 ULONG
SmallBlockChainStream_GetNextFreeBlock(
4411 SmallBlockChainStream
* This
)
4413 ULARGE_INTEGER offsetOfBlockInDepot
;
4416 ULONG blockIndex
= 0;
4417 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
4418 BOOL success
= TRUE
;
4419 ULONG smallBlocksPerBigBlock
;
4421 offsetOfBlockInDepot
.HighPart
= 0;
4424 * Scan the small block depot for a free block
4426 while (nextBlockIndex
!= BLOCK_UNUSED
)
4428 offsetOfBlockInDepot
.LowPart
= blockIndex
* sizeof(ULONG
);
4430 success
= BlockChainStream_ReadAt(
4431 This
->parentStorage
->smallBlockDepotChain
,
4432 offsetOfBlockInDepot
,
4438 * If we run out of space for the small block depot, enlarge it
4442 StorageUtl_ReadDWord(&buffer
, 0, &nextBlockIndex
);
4444 if (nextBlockIndex
!= BLOCK_UNUSED
)
4450 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
4452 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
4453 ULONG nextBlock
, newsbdIndex
;
4454 BYTE
* smallBlockDepot
;
4456 nextBlock
= sbdIndex
;
4457 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
4459 sbdIndex
= nextBlock
;
4461 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
);
4464 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4465 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
4466 StorageImpl_SetNextBlockInChain(
4467 This
->parentStorage
,
4471 StorageImpl_SetNextBlockInChain(
4472 This
->parentStorage
,
4474 BLOCK_END_OF_CHAIN
);
4477 * Initialize all the small blocks to free
4480 StorageImpl_GetBigBlock(This
->parentStorage
, newsbdIndex
);
4482 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
4483 StorageImpl_ReleaseBigBlock(This
->parentStorage
, smallBlockDepot
);
4488 * We have just created the small block depot.
4490 StgProperty rootProp
;
4494 * Save it in the header
4496 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
4497 StorageImpl_SaveFileHeader(This
->parentStorage
);
4500 * And allocate the first big block that will contain small blocks
4503 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4505 StorageImpl_SetNextBlockInChain(
4506 This
->parentStorage
,
4508 BLOCK_END_OF_CHAIN
);
4510 StorageImpl_ReadProperty(
4511 This
->parentStorage
,
4512 This
->parentStorage
->rootPropertySetIndex
,
4515 rootProp
.startingBlock
= sbStartIndex
;
4516 rootProp
.size
.HighPart
= 0;
4517 rootProp
.size
.LowPart
= This
->parentStorage
->bigBlockSize
;
4519 StorageImpl_WriteProperty(
4520 This
->parentStorage
,
4521 This
->parentStorage
->rootPropertySetIndex
,
4527 smallBlocksPerBigBlock
=
4528 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
4531 * Verify if we have to allocate big blocks to contain small blocks
4533 if (blockIndex
% smallBlocksPerBigBlock
== 0)
4535 StgProperty rootProp
;
4536 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
4538 StorageImpl_ReadProperty(
4539 This
->parentStorage
,
4540 This
->parentStorage
->rootPropertySetIndex
,
4543 if (rootProp
.size
.LowPart
<
4544 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
4546 rootProp
.size
.LowPart
+= This
->parentStorage
->bigBlockSize
;
4548 BlockChainStream_SetSize(
4549 This
->parentStorage
->smallBlockRootChain
,
4552 StorageImpl_WriteProperty(
4553 This
->parentStorage
,
4554 This
->parentStorage
->rootPropertySetIndex
,
4562 /******************************************************************************
4563 * SmallBlockChainStream_ReadAt
4565 * Reads a specified number of bytes from this chain at the specified offset.
4566 * bytesRead may be NULL.
4567 * Failure will be returned if the specified number of bytes has not been read.
4569 BOOL
SmallBlockChainStream_ReadAt(
4570 SmallBlockChainStream
* This
,
4571 ULARGE_INTEGER offset
,
4576 ULARGE_INTEGER offsetInBigBlockFile
;
4577 ULONG blockNoInSequence
=
4578 offset
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4580 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->smallBlockSize
;
4581 ULONG bytesToReadInBuffer
;
4583 ULONG bytesReadFromBigBlockFile
;
4587 * This should never happen on a small block file.
4589 assert(offset
.HighPart
==0);
4592 * Find the first block in the stream that contains part of the buffer.
4594 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4596 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4598 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4600 blockNoInSequence
--;
4604 * Start reading the buffer.
4607 bufferWalker
= buffer
;
4609 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4612 * Calculate how many bytes we can copy from this small block.
4614 bytesToReadInBuffer
=
4615 MIN(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
4618 * Calculate the offset of the small block in the small block file.
4620 offsetInBigBlockFile
.HighPart
= 0;
4621 offsetInBigBlockFile
.LowPart
=
4622 blockIndex
* This
->parentStorage
->smallBlockSize
;
4624 offsetInBigBlockFile
.LowPart
+= offsetInBlock
;
4627 * Read those bytes in the buffer from the small block file.
4629 BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
4630 offsetInBigBlockFile
,
4631 bytesToReadInBuffer
,
4633 &bytesReadFromBigBlockFile
);
4635 assert(bytesReadFromBigBlockFile
== bytesToReadInBuffer
);
4638 * Step to the next big block.
4640 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4641 bufferWalker
+= bytesToReadInBuffer
;
4642 size
-= bytesToReadInBuffer
;
4643 *bytesRead
+= bytesToReadInBuffer
;
4644 offsetInBlock
= 0; /* There is no offset on the next block */
4650 /******************************************************************************
4651 * SmallBlockChainStream_WriteAt
4653 * Writes the specified number of bytes to this chain at the specified offset.
4654 * bytesWritten may be NULL.
4655 * Will fail if not all specified number of bytes have been written.
4657 BOOL
SmallBlockChainStream_WriteAt(
4658 SmallBlockChainStream
* This
,
4659 ULARGE_INTEGER offset
,
4662 ULONG
* bytesWritten
)
4664 ULARGE_INTEGER offsetInBigBlockFile
;
4665 ULONG blockNoInSequence
=
4666 offset
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4668 ULONG offsetInBlock
= offset
.LowPart
% This
->parentStorage
->smallBlockSize
;
4669 ULONG bytesToWriteInBuffer
;
4671 ULONG bytesWrittenFromBigBlockFile
;
4675 * This should never happen on a small block file.
4677 assert(offset
.HighPart
==0);
4680 * Find the first block in the stream that contains part of the buffer.
4682 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4684 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4686 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4688 blockNoInSequence
--;
4692 * Start writing the buffer.
4694 * Here, I'm casting away the constness on the buffer variable
4695 * This is OK since we don't intend to modify that buffer.
4698 bufferWalker
= (BYTE
*)buffer
;
4699 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4702 * Calculate how many bytes we can copy to this small block.
4704 bytesToWriteInBuffer
=
4705 MIN(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
4708 * Calculate the offset of the small block in the small block file.
4710 offsetInBigBlockFile
.HighPart
= 0;
4711 offsetInBigBlockFile
.LowPart
=
4712 blockIndex
* This
->parentStorage
->smallBlockSize
;
4714 offsetInBigBlockFile
.LowPart
+= offsetInBlock
;
4717 * Write those bytes in the buffer to the small block file.
4719 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockRootChain
,
4720 offsetInBigBlockFile
,
4721 bytesToWriteInBuffer
,
4723 &bytesWrittenFromBigBlockFile
);
4725 assert(bytesWrittenFromBigBlockFile
== bytesToWriteInBuffer
);
4728 * Step to the next big block.
4730 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4731 bufferWalker
+= bytesToWriteInBuffer
;
4732 size
-= bytesToWriteInBuffer
;
4733 *bytesWritten
+= bytesToWriteInBuffer
;
4734 offsetInBlock
= 0; /* There is no offset on the next block */
4740 /******************************************************************************
4741 * SmallBlockChainStream_Shrink
4743 * Shrinks this chain in the small block depot.
4745 BOOL
SmallBlockChainStream_Shrink(
4746 SmallBlockChainStream
* This
,
4747 ULARGE_INTEGER newSize
)
4749 ULONG blockIndex
, extraBlock
;
4753 numBlocks
= newSize
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4755 if ((newSize
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
4758 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4761 * Go to the new end of chain
4763 while (count
< numBlocks
)
4765 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4769 /* Get the next block before marking the new end */
4770 extraBlock
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4772 /* Mark the new end of chain */
4773 SmallBlockChainStream_SetNextBlockInChain(
4776 BLOCK_END_OF_CHAIN
);
4779 * Mark the extra blocks as free
4781 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4783 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
);
4784 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
4785 extraBlock
= blockIndex
;
4791 /******************************************************************************
4792 * SmallBlockChainStream_Enlarge
4794 * Grows this chain in the small block depot.
4796 BOOL
SmallBlockChainStream_Enlarge(
4797 SmallBlockChainStream
* This
,
4798 ULARGE_INTEGER newSize
)
4800 ULONG blockIndex
, currentBlock
;
4802 ULONG oldNumBlocks
= 0;
4804 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4809 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4811 StgProperty chainProp
;
4813 StorageImpl_ReadProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
4816 chainProp
.startingBlock
= SmallBlockChainStream_GetNextFreeBlock(This
);
4818 StorageImpl_WriteProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
4821 blockIndex
= chainProp
.startingBlock
;
4822 SmallBlockChainStream_SetNextBlockInChain(
4825 BLOCK_END_OF_CHAIN
);
4828 currentBlock
= blockIndex
;
4831 * Figure out how many blocks are needed to contain this stream
4833 newNumBlocks
= newSize
.LowPart
/ This
->parentStorage
->smallBlockSize
;
4835 if ((newSize
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
4839 * Go to the current end of chain
4841 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4844 currentBlock
= blockIndex
;
4845 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
);
4849 * Add new blocks to the chain
4851 while (oldNumBlocks
< newNumBlocks
)
4853 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
4854 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
4856 SmallBlockChainStream_SetNextBlockInChain(
4859 BLOCK_END_OF_CHAIN
);
4861 currentBlock
= blockIndex
;
4868 /******************************************************************************
4869 * SmallBlockChainStream_GetCount
4871 * Returns the number of blocks that comprises this chain.
4872 * This is not the size of this chain as the last block may not be full!
4874 ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
4879 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
4881 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4885 blockIndex
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
);
4891 /******************************************************************************
4892 * SmallBlockChainStream_SetSize
4894 * Sets the size of this stream.
4895 * The file will grow if we grow the chain.
4897 * TODO: Free the actual blocks in the file when we shrink the chain.
4898 * Currently, the blocks are still in the file. So the file size
4899 * doesn't shrink even if we shrink streams.
4901 BOOL
SmallBlockChainStream_SetSize(
4902 SmallBlockChainStream
* This
,
4903 ULARGE_INTEGER newSize
)
4905 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
4907 if (newSize
.LowPart
== size
.LowPart
)
4910 if (newSize
.LowPart
< size
.LowPart
)
4912 SmallBlockChainStream_Shrink(This
, newSize
);
4916 SmallBlockChainStream_Enlarge(This
, newSize
);
4922 /******************************************************************************
4923 * SmallBlockChainStream_GetSize
4925 * Returns the size of this chain.
4927 ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
4929 StgProperty chainProperty
;
4931 StorageImpl_ReadProperty(
4932 This
->parentStorage
,
4933 This
->ownerPropertyIndex
,
4936 return chainProperty
.size
;
4939 /******************************************************************************
4940 * StgCreateDocfile32 [OLE32.144]
4941 * TODO Validate grfMode (STGM)
4943 HRESULT WINAPI
StgCreateDocfile(
4947 IStorage
**ppstgOpen
)
4949 StorageImpl
* newStorage
= 0;
4950 HANDLE hFile
= INVALID_HANDLE_VALUE
;
4955 DWORD fileAttributes
;
4958 * Validate the parameters
4960 if ((ppstgOpen
== 0) || (pwcsName
== 0))
4961 return STG_E_INVALIDPOINTER
;
4964 * Validate the STGM flags
4966 if ( FAILED( validateSTGM(grfMode
) ))
4967 return STG_E_INVALIDFLAG
;
4970 * Interpret the STGM value grfMode
4972 shareMode
= GetShareModeFromSTGM(grfMode
);
4973 accessMode
= GetAccessModeFromSTGM(grfMode
);
4974 creationMode
= GetCreationModeFromSTGM(grfMode
);
4976 if (grfMode
& STGM_DELETEONRELEASE
)
4977 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
4979 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
4981 if (grfMode
& STGM_TRANSACTED
)
4982 FIXME(ole
, "Transacted mode not implemented.\n");
4985 * Initialize the "out" parameter.
4989 hFile
= CreateFileW(pwcsName
,
4997 if (hFile
== INVALID_HANDLE_VALUE
)
5003 * Allocate and initialize the new IStorage32object.
5005 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5007 if (newStorage
== 0)
5008 return STG_E_INSUFFICIENTMEMORY
;
5010 hr
= StorageImpl_Construct(
5019 * Get an "out" pointer for the caller.
5021 hr
= StorageBaseImpl_QueryInterface(
5022 (IStorage
*)newStorage
,
5023 (REFIID
)&IID_IStorage
,
5029 /******************************************************************************
5030 * StgOpenStorage32 [OLE32.148]
5032 HRESULT WINAPI
StgOpenStorage(
5033 const OLECHAR
*pwcsName
,
5034 IStorage
*pstgPriority
,
5038 IStorage
**ppstgOpen
)
5040 StorageImpl
* newStorage
= 0;
5047 * Perform a sanity check
5049 if (( pwcsName
== 0) || (ppstgOpen
== 0) )
5050 return STG_E_INVALIDPOINTER
;
5053 * Validate the STGM flags
5055 if ( FAILED( validateSTGM(grfMode
) ))
5056 return STG_E_INVALIDFLAG
;
5059 * Interpret the STGM value grfMode
5061 shareMode
= GetShareModeFromSTGM(grfMode
);
5062 accessMode
= GetAccessModeFromSTGM(grfMode
);
5065 * Initialize the "out" parameter.
5069 hFile
= CreateFileW( pwcsName
,
5074 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
5078 if (hFile
==INVALID_HANDLE_VALUE
)
5084 * Allocate and initialize the new IStorage32object.
5086 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5088 if (newStorage
== 0)
5089 return STG_E_INSUFFICIENTMEMORY
;
5091 hr
= StorageImpl_Construct(
5100 * Get an "out" pointer for the caller.
5102 hr
= StorageBaseImpl_QueryInterface(
5103 (IStorage
*)newStorage
,
5104 (REFIID
)&IID_IStorage
,
5110 /******************************************************************************
5111 * WriteClassStg32 [OLE32.148]
5113 * This method will store the specified CLSID in the specified storage object
5115 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
5121 hRes
= IStorage_SetClass(pStg
, rclsid
);
5127 /****************************************************************************
5128 * This method validate a STGM parameter that can contain the values below
5130 * STGM_DIRECT 0x00000000
5131 * STGM_TRANSACTED 0x00010000
5132 * STGM_SIMPLE 0x08000000
5134 * STGM_READ 0x00000000
5135 * STGM_WRITE 0x00000001
5136 * STGM_READWRITE 0x00000002
5138 * STGM_SHARE_DENY_NONE 0x00000040
5139 * STGM_SHARE_DENY_READ 0x00000030
5140 * STGM_SHARE_DENY_WRITE 0x00000020
5141 * STGM_SHARE_EXCLUSIVE 0x00000010
5143 * STGM_PRIORITY 0x00040000
5144 * STGM_DELETEONRELEASE 0x04000000
5146 * STGM_CREATE 0x00001000
5147 * STGM_CONVERT 0x00020000
5148 * STGM_FAILIFTHERE 0x00000000
5150 * STGM_NOSCRATCH 0x00100000
5151 * STGM_NOSNAPSHOT 0x00200000
5153 static HRESULT
validateSTGM(DWORD stgm
)
5155 BOOL bSTGM_TRANSACTED
= ((stgm
& STGM_TRANSACTED
) == STGM_TRANSACTED
);
5156 BOOL bSTGM_SIMPLE
= ((stgm
& STGM_SIMPLE
) == STGM_SIMPLE
);
5157 BOOL bSTGM_DIRECT
= ! (bSTGM_TRANSACTED
|| bSTGM_SIMPLE
);
5159 BOOL bSTGM_WRITE
= ((stgm
& STGM_WRITE
) == STGM_WRITE
);
5160 BOOL bSTGM_READWRITE
= ((stgm
& STGM_READWRITE
) == STGM_READWRITE
);
5161 BOOL bSTGM_READ
= ! (bSTGM_WRITE
|| bSTGM_READWRITE
);
5163 BOOL bSTGM_SHARE_DENY_NONE
=
5164 ((stgm
& STGM_SHARE_DENY_NONE
) == STGM_SHARE_DENY_NONE
);
5166 BOOL bSTGM_SHARE_DENY_READ
=
5167 ((stgm
& STGM_SHARE_DENY_READ
) == STGM_SHARE_DENY_READ
);
5169 BOOL bSTGM_SHARE_DENY_WRITE
=
5170 ((stgm
& STGM_SHARE_DENY_WRITE
) == STGM_SHARE_DENY_WRITE
);
5172 BOOL bSTGM_SHARE_EXCLUSIVE
=
5173 ((stgm
& STGM_SHARE_EXCLUSIVE
) == STGM_SHARE_EXCLUSIVE
);
5175 BOOL bSTGM_CREATE
= ((stgm
& STGM_CREATE
) == STGM_CREATE
);
5176 BOOL bSTGM_CONVERT
= ((stgm
& STGM_CONVERT
) == STGM_CONVERT
);
5178 BOOL bSTGM_NOSCRATCH
= ((stgm
& STGM_NOSCRATCH
) == STGM_NOSCRATCH
);
5179 BOOL bSTGM_NOSNAPSHOT
= ((stgm
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
);
5182 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5184 if ( ! bSTGM_DIRECT
)
5185 if( bSTGM_TRANSACTED
&& bSTGM_SIMPLE
)
5189 * STGM_WRITE | STGM_READWRITE | STGM_READ
5192 if( bSTGM_WRITE
&& bSTGM_READWRITE
)
5196 * STGM_SHARE_DENY_NONE | others
5197 * (I assume here that DENY_READ implies DENY_WRITE)
5199 if ( bSTGM_SHARE_DENY_NONE
)
5200 if ( bSTGM_SHARE_DENY_READ
||
5201 bSTGM_SHARE_DENY_WRITE
||
5202 bSTGM_SHARE_EXCLUSIVE
)
5206 * STGM_CREATE | STGM_CONVERT
5207 * if both are false, STGM_FAILIFTHERE is set to TRUE
5209 if ( bSTGM_CREATE
&& bSTGM_CONVERT
)
5213 * STGM_NOSCRATCH requires STGM_TRANSACTED
5215 if ( bSTGM_NOSCRATCH
&& ! bSTGM_TRANSACTED
)
5219 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5220 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5222 if (bSTGM_NOSNAPSHOT
)
5224 if ( ! ( bSTGM_TRANSACTED
&&
5225 !(bSTGM_SHARE_EXCLUSIVE
|| bSTGM_SHARE_DENY_WRITE
)) )
5232 /****************************************************************************
5233 * GetShareModeFromSTGM
5235 * This method will return a share mode flag from a STGM value.
5236 * The STGM value is assumed valid.
5238 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
5240 DWORD dwShareMode
= 0;
5241 BOOL bSTGM_SHARE_DENY_NONE
=
5242 ((stgm
& STGM_SHARE_DENY_NONE
) == STGM_SHARE_DENY_NONE
);
5244 BOOL bSTGM_SHARE_DENY_READ
=
5245 ((stgm
& STGM_SHARE_DENY_READ
) == STGM_SHARE_DENY_READ
);
5247 BOOL bSTGM_SHARE_DENY_WRITE
=
5248 ((stgm
& STGM_SHARE_DENY_WRITE
) == STGM_SHARE_DENY_WRITE
);
5250 BOOL bSTGM_SHARE_EXCLUSIVE
=
5251 ((stgm
& STGM_SHARE_EXCLUSIVE
) == STGM_SHARE_EXCLUSIVE
);
5253 if ((bSTGM_SHARE_EXCLUSIVE
) || (bSTGM_SHARE_DENY_READ
))
5256 if (bSTGM_SHARE_DENY_NONE
)
5257 dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
5259 if (bSTGM_SHARE_DENY_WRITE
)
5260 dwShareMode
= FILE_SHARE_READ
;
5265 /****************************************************************************
5266 * GetAccessModeFromSTGM
5268 * This method will return an access mode flag from a STGM value.
5269 * The STGM value is assumed valid.
5271 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
5273 DWORD dwDesiredAccess
= 0;
5274 BOOL bSTGM_WRITE
= ((stgm
& STGM_WRITE
) == STGM_WRITE
);
5275 BOOL bSTGM_READWRITE
= ((stgm
& STGM_READWRITE
) == STGM_READWRITE
);
5276 BOOL bSTGM_READ
= ! (bSTGM_WRITE
|| bSTGM_READWRITE
);
5279 dwDesiredAccess
= GENERIC_READ
;
5282 dwDesiredAccess
|= GENERIC_WRITE
;
5284 if (bSTGM_READWRITE
)
5285 dwDesiredAccess
= GENERIC_READ
| GENERIC_WRITE
;
5287 return dwDesiredAccess
;
5290 /****************************************************************************
5291 * GetCreationModeFromSTGM
5293 * This method will return a creation mode flag from a STGM value.
5294 * The STGM value is assumed valid.
5296 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
5298 if ( stgm
& STGM_CREATE
)
5299 return CREATE_ALWAYS
;
5300 if (stgm
& STGM_CONVERT
) {
5301 FIXME(ole
, "STGM_CONVERT not implemented!\n");
5304 /* All other cases */
5305 if (stgm
& ~ (STGM_CREATE
|STGM_CONVERT
))
5306 FIXME(ole
,"unhandled storage mode : 0x%08lx\n",stgm
& ~ (STGM_CREATE
|STGM_CONVERT
));