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