Simplify propertyNameCmp() and fix a warning in the way by removing
[wine/dcerpc.git] / ole / storage32.c
blob24e45e9f834904032cea392c10e372b5513bc6b0
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;
906 * Validate parameters
908 if (ppstg == 0)
909 return STG_E_INVALIDPOINTER;
911 if (pwcsName == 0)
912 return STG_E_INVALIDNAME;
915 * Validate the STGM flags
917 if ( FAILED( validateSTGM(grfMode) ) ||
918 (grfMode & STGM_DELETEONRELEASE) )
919 return STG_E_INVALIDFLAG;
922 * Initialize the out parameter
924 *ppstg = 0;
927 * Create a property enumeration and search the properties
929 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
930 This->rootPropertySetIndex);
932 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
933 pwcsName,
934 &currentProperty);
935 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
937 if (foundPropertyIndex != PROPERTY_NULL)
940 * An element with this name already exists
942 if (grfMode & STGM_CREATE)
943 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsName);
944 else
945 return STG_E_FILEALREADYEXISTS;
949 * memset the empty property
951 memset(&newProperty, 0, sizeof(StgProperty));
953 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
955 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
956 return STG_E_INVALIDNAME;
958 lstrcpyW(newProperty.name, pwcsName);
960 newProperty.propertyType = PROPTYPE_STORAGE;
961 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
962 newProperty.size.LowPart = 0;
963 newProperty.size.HighPart = 0;
965 newProperty.previousProperty = PROPERTY_NULL;
966 newProperty.nextProperty = PROPERTY_NULL;
967 newProperty.dirProperty = PROPERTY_NULL;
969 /* call CoFileTime to get the current time
970 newProperty.timeStampS1
971 newProperty.timeStampD1
972 newProperty.timeStampS2
973 newProperty.timeStampD2
976 /* newStorageProperty.propertyUniqueID */
979 * Obtain a free property in the property chain
981 newPropertyIndex = getFreeProperty(This->ancestorStorage);
984 * Save the new property into the new property spot
986 StorageImpl_WriteProperty(
987 This->ancestorStorage,
988 newPropertyIndex,
989 &newProperty);
992 * Find a spot in the property chain for our newly created property.
994 updatePropertyChain(
995 This,
996 newPropertyIndex,
997 newProperty);
1000 * Open it to get a pointer to return.
1002 hr = StorageBaseImpl_OpenStorage(
1003 iface,
1004 (OLECHAR*)pwcsName,
1006 grfMode,
1009 ppstg);
1011 if( (hr != S_OK) || (*ppstg == NULL))
1013 return hr;
1017 return S_OK;
1021 /***************************************************************************
1023 * Internal Method
1025 * Get a free property or create a new one.
1027 static ULONG getFreeProperty(
1028 StorageImpl *storage)
1030 ULONG currentPropertyIndex = 0;
1031 ULONG newPropertyIndex = PROPERTY_NULL;
1032 BOOL readSucessful = TRUE;
1033 StgProperty currentProperty;
1038 * Start by reading the root property
1040 readSucessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1041 currentPropertyIndex,
1042 &currentProperty);
1043 if (readSucessful)
1045 if (currentProperty.sizeOfNameString == 0)
1048 * The property existis and is available, we found it.
1050 newPropertyIndex = currentPropertyIndex;
1053 else
1056 * We exhausted the property list, we will create more space below
1058 newPropertyIndex = currentPropertyIndex;
1060 currentPropertyIndex++;
1062 } while (newPropertyIndex == PROPERTY_NULL);
1065 * grow the property chain
1067 if (! readSucessful)
1069 StgProperty emptyProperty;
1070 ULARGE_INTEGER newSize;
1071 ULONG propertyIndex;
1072 ULONG lastProperty = 0;
1073 ULONG blockCount = 0;
1076 * obtain the new count of property blocks
1078 blockCount = BlockChainStream_GetCount(
1079 storage->ancestorStorage->rootBlockChain)+1;
1082 * initialize the size used by the property stream
1084 newSize.HighPart = 0;
1085 newSize.LowPart = storage->bigBlockSize * blockCount;
1088 * add a property block to the property chain
1090 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1093 * memset the empty property in order to initialize the unused newly
1094 * created property
1096 memset(&emptyProperty, 0, sizeof(StgProperty));
1099 * initialize them
1101 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1103 for(
1104 propertyIndex = newPropertyIndex;
1105 propertyIndex < lastProperty;
1106 propertyIndex++)
1108 StorageImpl_WriteProperty(
1109 storage->ancestorStorage,
1110 propertyIndex,
1111 &emptyProperty);
1115 return newPropertyIndex;
1118 /****************************************************************************
1120 * Internal Method
1122 * Case insensitive comparaison of StgProperty.name by first considering
1123 * their size.
1125 * Returns <0 when newPrpoerty < currentProperty
1126 * >0 when newPrpoerty > currentProperty
1127 * 0 when newPrpoerty == currentProperty
1129 static LONG propertyNameCmp(
1130 OLECHAR *newProperty,
1131 OLECHAR *currentProperty)
1133 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1135 if (diff == 0)
1138 * We compare the string themselves only when they are of the same lenght
1140 diff = lstrcmpiW( newProperty, currentProperty);
1143 return diff;
1146 /****************************************************************************
1148 * Internal Method
1150 * Properly link this new element in the property chain.
1152 static void updatePropertyChain(
1153 StorageImpl *storage,
1154 ULONG newPropertyIndex,
1155 StgProperty newProperty)
1157 StgProperty currentProperty;
1160 * Read the root property
1162 StorageImpl_ReadProperty(storage->ancestorStorage,
1163 storage->rootPropertySetIndex,
1164 &currentProperty);
1166 if (currentProperty.dirProperty != PROPERTY_NULL)
1169 * The root storage contains some element, therefore, start the research
1170 * for the appropriate location.
1172 BOOL found = 0;
1173 ULONG current, next, previous, currentPropertyId;
1176 * Keep the StgProperty sequence number of the storage first property
1178 currentPropertyId = currentProperty.dirProperty;
1181 * Read
1183 StorageImpl_ReadProperty(storage->ancestorStorage,
1184 currentProperty.dirProperty,
1185 &currentProperty);
1187 previous = currentProperty.previousProperty;
1188 next = currentProperty.nextProperty;
1189 current = currentPropertyId;
1191 while (found == 0)
1193 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1195 if (diff < 0)
1197 if (previous != PROPERTY_NULL)
1199 StorageImpl_ReadProperty(storage->ancestorStorage,
1200 previous,
1201 &currentProperty);
1202 current = previous;
1204 else
1206 currentProperty.previousProperty = newPropertyIndex;
1207 StorageImpl_WriteProperty(storage->ancestorStorage,
1208 current,
1209 &currentProperty);
1210 found = 1;
1213 else
1215 if (next != PROPERTY_NULL)
1217 StorageImpl_ReadProperty(storage->ancestorStorage,
1218 next,
1219 &currentProperty);
1220 current = next;
1222 else
1224 currentProperty.nextProperty = newPropertyIndex;
1225 StorageImpl_WriteProperty(storage->ancestorStorage,
1226 current,
1227 &currentProperty);
1228 found = 1;
1232 previous = currentProperty.previousProperty;
1233 next = currentProperty.nextProperty;
1236 else
1239 * The root storage is empty, link the new property to it's dir property
1241 currentProperty.dirProperty = newPropertyIndex;
1242 StorageImpl_WriteProperty(storage->ancestorStorage,
1243 storage->rootPropertySetIndex,
1244 &currentProperty);
1249 /*************************************************************************
1250 * CopyTo (IStorage)
1252 HRESULT WINAPI StorageImpl_CopyTo(
1253 IStorage* iface,
1254 DWORD ciidExclude, /* [in] */
1255 const IID *rgiidExclude,/* [size_is][unique][in] */
1256 SNB snbExclude, /* [unique][in] */
1257 IStorage *pstgDest) /* [unique][in] */
1259 IEnumSTATSTG *elements = 0;
1260 STATSTG curElement, strStat;
1261 HRESULT hr;
1262 IStorage *pstgTmp, *pstgChild;
1263 IStream *pstrTmp, *pstrChild;
1265 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1266 FIXME( ole, "Exclude option not implemented\n");
1269 * Perform a sanity check
1271 if ( pstgDest == 0 )
1272 return STG_E_INVALIDPOINTER;
1275 * Enumerate the elements
1277 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1279 if ( hr != S_OK )
1280 return hr;
1283 * set the class ID
1285 StorageBaseImpl_Stat( iface, &curElement, STATFLAG_NONAME);
1286 IStorage_SetClass( pstgDest, &curElement.clsid );
1291 * Obtain the next element
1293 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1295 if ( hr == S_FALSE )
1297 hr = S_OK; /* done, every element has been copied */
1298 break;
1301 if (curElement.type == STGTY_STORAGE)
1304 * open child source storage
1306 hr = StorageBaseImpl_OpenStorage( iface, curElement.pwcsName, NULL,
1307 STGM_READ|STGM_SHARE_EXCLUSIVE,
1308 NULL, 0, &pstgChild );
1310 if (hr != S_OK)
1311 break;
1314 * Check if destination storage is not a child of the source
1315 * storage, which will cause an infinite loop
1317 if (pstgChild == pstgDest)
1319 IEnumSTATSTG_Release(elements);
1321 return STG_E_ACCESSDENIED;
1325 * create a new storage in destination storage
1327 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1328 STGM_FAILIFTHERE|STGM_WRITE, 0, 0,
1329 &pstgTmp );
1331 * if it already exist, don't create a new one use this one
1333 if (hr == STG_E_FILEALREADYEXISTS)
1335 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1336 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1337 NULL, 0, &pstgTmp );
1340 if (hr != S_OK)
1341 break;
1345 * do the copy recursively
1347 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1348 snbExclude, pstgTmp );
1350 IStorage_Release( pstgTmp );
1351 IStorage_Release( pstgChild );
1353 else if (curElement.type == STGTY_STREAM)
1356 * create a new stream in destination storage. If the stream already
1357 * exist, it will be deleted and a new one will be created.
1359 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1360 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1361 0, 0, &pstrTmp );
1363 if (hr != S_OK)
1364 break;
1367 * open child stream storage
1369 hr = StorageBaseImpl_OpenStream( iface, curElement.pwcsName, NULL,
1370 STGM_READ|STGM_SHARE_EXCLUSIVE,
1371 0, &pstrChild );
1373 if (hr != S_OK)
1374 break;
1377 * Get the size of the stream
1379 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1382 * do the copy
1384 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1385 NULL, NULL );
1387 IStream_Release( pstrTmp );
1388 IStream_Release( pstrChild );
1390 else
1392 WARN(ole, "unknown element type: %ld\n", curElement.type);
1395 } while (hr == S_OK);
1398 * Clean-up
1400 IEnumSTATSTG_Release(elements);
1402 return hr;
1405 /*************************************************************************
1406 * MoveElementTo (IStorage)
1408 HRESULT WINAPI StorageImpl_MoveElementTo(
1409 IStorage* iface,
1410 const OLECHAR *pwcsName, /* [string][in] */
1411 IStorage *pstgDest, /* [unique][in] */
1412 const OLECHAR *pwcsNewName,/* [string][in] */
1413 DWORD grfFlags) /* [in] */
1415 FIXME(ole, "not implemented!\n");
1416 return E_NOTIMPL;
1419 /*************************************************************************
1420 * Commit (IStorage)
1422 HRESULT WINAPI StorageImpl_Commit(
1423 IStorage* iface,
1424 DWORD grfCommitFlags)/* [in] */
1426 FIXME(ole, "(%ld): stub!\n", grfCommitFlags);
1427 return S_OK;
1430 /*************************************************************************
1431 * Revert (IStorage)
1433 HRESULT WINAPI StorageImpl_Revert(
1434 IStorage* iface)
1436 FIXME(ole, "not implemented!\n");
1437 return E_NOTIMPL;
1440 /*************************************************************************
1441 * DestroyElement (IStorage)
1443 * Stategy: This implementation is build this way for simplicity not for speed.
1444 * I always delete the top most element of the enumeration and adjust
1445 * the deleted element pointer all the time. This takes longer to
1446 * do but allow to reinvoke DestroyElement whenever we encounter a
1447 * storage object. The optimisation reside in the usage of another
1448 * enumeration stategy that would give all the leaves of a storage
1449 * first. (postfix order)
1451 HRESULT WINAPI StorageImpl_DestroyElement(
1452 IStorage* iface,
1453 const OLECHAR *pwcsName)/* [string][in] */
1455 StorageImpl* const This=(StorageImpl*)iface;
1457 IEnumSTATSTGImpl* propertyEnumeration;
1458 HRESULT hr = S_OK;
1459 BOOL res;
1460 StgProperty propertyToDelete;
1461 StgProperty parentProperty;
1462 ULONG foundPropertyIndexToDelete;
1463 ULONG typeOfRelation;
1464 ULONG parentPropertyId;
1467 * Perform a sanity check on the parameters.
1469 if (pwcsName==NULL)
1470 return STG_E_INVALIDPOINTER;
1473 * Create a property enumeration to search the property with the given name
1475 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1476 This->ancestorStorage,
1477 This->rootPropertySetIndex);
1479 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1480 propertyEnumeration,
1481 pwcsName,
1482 &propertyToDelete);
1484 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1486 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1488 return STG_E_FILENOTFOUND;
1492 * Find the parent property of the property to delete (the one that
1493 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1494 * the parent is This. Otherwise, the parent is one of it's sibling...
1498 * First, read This's StgProperty..
1500 res = StorageImpl_ReadProperty(
1501 This->ancestorStorage,
1502 This->rootPropertySetIndex,
1503 &parentProperty);
1505 assert(res==TRUE);
1508 * Second, check to see if by any chance the actual storage (This) is not
1509 * the parent of the property to delete... We never know...
1511 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1514 * Set data as it would have been done in the else part...
1516 typeOfRelation = PROPERTY_RELATION_DIR;
1517 parentPropertyId = This->rootPropertySetIndex;
1519 else
1522 * Create a property enumeration to search the parent properties, and
1523 * delete it once done.
1525 IEnumSTATSTGImpl* propertyEnumeration2;
1527 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1528 This->ancestorStorage,
1529 This->rootPropertySetIndex);
1531 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1532 propertyEnumeration2,
1533 foundPropertyIndexToDelete,
1534 &parentProperty,
1535 &parentPropertyId);
1537 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1540 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1542 hr = deleteStorageProperty(
1543 This,
1544 propertyToDelete.name);
1546 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1548 hr = deleteStreamProperty(
1549 This,
1550 foundPropertyIndexToDelete,
1551 propertyToDelete);
1554 if (hr!=S_OK)
1555 return hr;
1558 * Adjust the property chain
1560 hr = adjustPropertyChain(
1561 This,
1562 propertyToDelete,
1563 parentProperty,
1564 parentPropertyId,
1565 typeOfRelation);
1567 return hr;
1571 /*********************************************************************
1573 * Internal Method
1575 * Perform the deletion of a complete storage node
1578 static HRESULT deleteStorageProperty(
1579 StorageImpl *parentStorage,
1580 OLECHAR *propertyToDeleteName)
1582 IEnumSTATSTG *elements = 0;
1583 IStorage *childStorage = 0;
1584 STATSTG currentElement;
1585 HRESULT hr;
1586 HRESULT destroyHr = S_OK;
1589 * Open the storage and enumerate it
1591 hr = StorageBaseImpl_OpenStorage(
1592 (IStorage*)parentStorage,
1593 propertyToDeleteName,
1595 STGM_SHARE_EXCLUSIVE,
1598 &childStorage);
1600 if (hr != S_OK)
1602 return hr;
1606 * Enumerate the elements
1608 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1613 * Obtain the next element
1615 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1616 if (hr==S_OK)
1618 destroyHr = StorageImpl_DestroyElement(
1619 (IStorage*)childStorage,
1620 (OLECHAR*)currentElement.pwcsName);
1622 CoTaskMemFree(currentElement.pwcsName);
1626 * We need to Reset the enumeration every time because we delete elements
1627 * and the enumeration could be invalid
1629 IEnumSTATSTG_Reset(elements);
1631 } while ((hr == S_OK) && (destroyHr == S_OK));
1633 IStorage_Release(childStorage);
1634 IEnumSTATSTG_Release(elements);
1636 return destroyHr;
1639 /*********************************************************************
1641 * Internal Method
1643 * Perform the deletion of a stream node
1646 static HRESULT deleteStreamProperty(
1647 StorageImpl *parentStorage,
1648 ULONG indexOfPropertyToDelete,
1649 StgProperty propertyToDelete)
1651 IStream *pis;
1652 HRESULT hr;
1653 ULARGE_INTEGER size;
1655 size.HighPart = 0;
1656 size.LowPart = 0;
1658 hr = StorageBaseImpl_OpenStream(
1659 (IStorage*)parentStorage,
1660 (OLECHAR*)propertyToDelete.name,
1661 NULL,
1662 STGM_SHARE_EXCLUSIVE,
1664 &pis);
1666 if (hr!=S_OK)
1668 return(hr);
1672 * Zap the stream
1674 hr = IStream_SetSize(pis, size);
1676 if(hr != S_OK)
1678 return hr;
1682 * Invalidate the property by zeroing it's name member.
1684 propertyToDelete.sizeOfNameString = 0;
1687 * Here we should re-read the property so we get the updated pointer
1688 * but since we are here to zap it, I don't do it...
1691 StorageImpl_WriteProperty(
1692 parentStorage->ancestorStorage,
1693 indexOfPropertyToDelete,
1694 &propertyToDelete);
1696 return S_OK;
1699 /*********************************************************************
1701 * Internal Method
1703 * Finds a placeholder for the StgProperty within the Storage
1706 static HRESULT findPlaceholder(
1707 StorageImpl *storage,
1708 ULONG propertyIndexToStore,
1709 ULONG storePropertyIndex,
1710 INT typeOfRelation)
1712 StgProperty storeProperty;
1713 HRESULT hr = S_OK;
1714 BOOL res = TRUE;
1717 * Read the storage property
1719 res = StorageImpl_ReadProperty(
1720 storage->ancestorStorage,
1721 storePropertyIndex,
1722 &storeProperty);
1724 if(! res)
1726 return E_FAIL;
1729 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1731 if (storeProperty.previousProperty != PROPERTY_NULL)
1733 return findPlaceholder(
1734 storage,
1735 propertyIndexToStore,
1736 storeProperty.previousProperty,
1737 typeOfRelation);
1739 else
1741 storeProperty.previousProperty = propertyIndexToStore;
1744 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1746 if (storeProperty.nextProperty != PROPERTY_NULL)
1748 return findPlaceholder(
1749 storage,
1750 propertyIndexToStore,
1751 storeProperty.nextProperty,
1752 typeOfRelation);
1754 else
1756 storeProperty.nextProperty = propertyIndexToStore;
1759 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1761 if (storeProperty.dirProperty != PROPERTY_NULL)
1763 return findPlaceholder(
1764 storage,
1765 propertyIndexToStore,
1766 storeProperty.dirProperty,
1767 typeOfRelation);
1769 else
1771 storeProperty.dirProperty = propertyIndexToStore;
1775 hr = StorageImpl_WriteProperty(
1776 storage->ancestorStorage,
1777 storePropertyIndex,
1778 &storeProperty);
1780 if(! hr)
1782 return E_FAIL;
1785 return S_OK;
1788 /*************************************************************************
1790 * Internal Method
1792 * This method takes the previous and the next property link of a property
1793 * to be deleted and find them a place in the Storage.
1795 static HRESULT adjustPropertyChain(
1796 StorageImpl *This,
1797 StgProperty propertyToDelete,
1798 StgProperty parentProperty,
1799 ULONG parentPropertyId,
1800 INT typeOfRelation)
1802 ULONG newLinkProperty = PROPERTY_NULL;
1803 BOOL needToFindAPlaceholder = FALSE;
1804 ULONG storeNode = PROPERTY_NULL;
1805 ULONG toStoreNode = PROPERTY_NULL;
1806 INT relationType = 0;
1807 HRESULT hr = S_OK;
1808 BOOL res = TRUE;
1810 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1812 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1815 * Set the parent previous to the property to delete previous
1817 newLinkProperty = propertyToDelete.previousProperty;
1819 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1822 * We also need to find a storage for the other link, setup variables
1823 * to do this at the end...
1825 needToFindAPlaceholder = TRUE;
1826 storeNode = propertyToDelete.previousProperty;
1827 toStoreNode = propertyToDelete.nextProperty;
1828 relationType = PROPERTY_RELATION_NEXT;
1831 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1834 * Set the parent previous to the property to delete next
1836 newLinkProperty = propertyToDelete.nextProperty;
1840 * Link it for real...
1842 parentProperty.previousProperty = newLinkProperty;
1845 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1847 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1850 * Set the parent next to the property to delete next previous
1852 newLinkProperty = propertyToDelete.previousProperty;
1854 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1857 * We also need to find a storage for the other link, setup variables
1858 * to do this at the end...
1860 needToFindAPlaceholder = TRUE;
1861 storeNode = propertyToDelete.previousProperty;
1862 toStoreNode = propertyToDelete.nextProperty;
1863 relationType = PROPERTY_RELATION_NEXT;
1866 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1869 * Set the parent next to the property to delete next
1871 newLinkProperty = propertyToDelete.nextProperty;
1875 * Link it for real...
1877 parentProperty.nextProperty = newLinkProperty;
1879 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
1881 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1884 * Set the parent dir to the property to delete previous
1886 newLinkProperty = propertyToDelete.previousProperty;
1888 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1891 * We also need to find a storage for the other link, setup variables
1892 * to do this at the end...
1894 needToFindAPlaceholder = TRUE;
1895 storeNode = propertyToDelete.previousProperty;
1896 toStoreNode = propertyToDelete.nextProperty;
1897 relationType = PROPERTY_RELATION_NEXT;
1900 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1903 * Set the parent dir to the property to delete next
1905 newLinkProperty = propertyToDelete.nextProperty;
1909 * Link it for real...
1911 parentProperty.dirProperty = newLinkProperty;
1915 * Write back the parent property
1917 res = StorageImpl_WriteProperty(
1918 This->ancestorStorage,
1919 parentPropertyId,
1920 &parentProperty);
1921 if(! res)
1923 return E_FAIL;
1927 * If a placeholder is required for the other link, then, find one and
1928 * get out of here...
1930 if (needToFindAPlaceholder)
1932 hr = findPlaceholder(
1933 This,
1934 toStoreNode,
1935 storeNode,
1936 relationType);
1939 return hr;
1943 /******************************************************************************
1944 * SetElementTimes (IStorage)
1946 HRESULT WINAPI StorageImpl_SetElementTimes(
1947 IStorage* iface,
1948 const OLECHAR *pwcsName,/* [string][in] */
1949 const FILETIME *pctime, /* [in] */
1950 const FILETIME *patime, /* [in] */
1951 const FILETIME *pmtime) /* [in] */
1953 FIXME(ole, "not implemented!\n");
1954 return E_NOTIMPL;
1957 /******************************************************************************
1958 * SetStateBits (IStorage)
1960 HRESULT WINAPI StorageImpl_SetStateBits(
1961 IStorage* iface,
1962 DWORD grfStateBits,/* [in] */
1963 DWORD grfMask) /* [in] */
1965 FIXME(ole, "not implemented!\n");
1966 return E_NOTIMPL;
1969 HRESULT StorageImpl_Construct(
1970 StorageImpl* This,
1971 HANDLE hFile,
1972 DWORD openFlags)
1974 HRESULT hr = S_OK;
1975 StgProperty currentProperty;
1976 BOOL readSucessful;
1977 ULONG currentPropertyIndex;
1979 if ( FAILED( validateSTGM(openFlags) ))
1980 return STG_E_INVALIDFLAG;
1982 memset(This, 0, sizeof(StorageImpl));
1985 * Initialize the virtual fgunction table.
1987 This->lpvtbl = &Storage32Impl_Vtbl;
1988 This->v_destructor = &StorageImpl_Destroy;
1991 * This is the top-level storage so initialize the ancester pointer
1992 * to this.
1994 This->ancestorStorage = This;
1997 * Initialize the physical support of the storage.
1999 This->hFile = hFile;
2002 * Initialize the big block cache.
2004 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2005 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2006 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2007 openFlags,
2008 This->bigBlockSize);
2010 if (This->bigBlockFile == 0)
2011 return E_FAIL;
2013 if (openFlags & STGM_CREATE)
2015 ULARGE_INTEGER size;
2016 BYTE* bigBlockBuffer;
2019 * Initialize all header variables:
2020 * - The big block depot consists of one block and it is at block 0
2021 * - The properties start at block 1
2022 * - There is no small block depot
2024 memset( This->bigBlockDepotStart,
2025 BLOCK_UNUSED,
2026 sizeof(This->bigBlockDepotStart));
2028 This->bigBlockDepotCount = 1;
2029 This->bigBlockDepotStart[0] = 0;
2030 This->rootStartBlock = 1;
2031 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2032 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2033 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2034 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2035 This->extBigBlockDepotCount = 0;
2037 StorageImpl_SaveFileHeader(This);
2040 * Add one block for the big block depot and one block for the properties
2042 size.HighPart = 0;
2043 size.LowPart = This->bigBlockSize * 3;
2044 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2047 * Initialize the big block depot
2049 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2050 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2051 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2052 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2053 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2055 else
2058 * Load the header for the file.
2060 hr = StorageImpl_LoadFileHeader(This);
2062 if (FAILED(hr))
2064 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2066 return hr;
2071 * There is no block depot cached yet.
2073 This->indexBlockDepotCached = 0xFFFFFFFF;
2076 * Start searching for free blocks with block 0.
2078 This->prevFreeBlock = 0;
2081 * Create the block chain abstractions.
2083 This->rootBlockChain =
2084 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2086 This->smallBlockDepotChain = BlockChainStream_Construct(
2087 This,
2088 &This->smallBlockDepotStart,
2089 PROPERTY_NULL);
2092 * Write the root property
2094 if (openFlags & STGM_CREATE)
2096 StgProperty rootProp;
2098 * Initialize the property chain
2100 memset(&rootProp, 0, sizeof(rootProp));
2101 lstrcpyAtoW(rootProp.name, rootPropertyName);
2103 rootProp.sizeOfNameString = (lstrlenW(rootProp.name)+1) * sizeof(WCHAR);
2104 rootProp.propertyType = PROPTYPE_ROOT;
2105 rootProp.previousProperty = PROPERTY_NULL;
2106 rootProp.nextProperty = PROPERTY_NULL;
2107 rootProp.dirProperty = PROPERTY_NULL;
2108 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2109 rootProp.size.HighPart = 0;
2110 rootProp.size.LowPart = 0;
2112 StorageImpl_WriteProperty(This, 0, &rootProp);
2116 * Find the ID of the root int he property sets.
2118 currentPropertyIndex = 0;
2122 readSucessful = StorageImpl_ReadProperty(
2123 This,
2124 currentPropertyIndex,
2125 &currentProperty);
2127 if (readSucessful)
2129 if ( (currentProperty.sizeOfNameString != 0 ) &&
2130 (currentProperty.propertyType == PROPTYPE_ROOT) )
2132 This->rootPropertySetIndex = currentPropertyIndex;
2136 currentPropertyIndex++;
2138 } while (readSucessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2140 if (!readSucessful)
2142 /* TODO CLEANUP */
2143 return E_FAIL;
2147 * Create the block chain abstraction for the small block root chain.
2149 This->smallBlockRootChain = BlockChainStream_Construct(
2150 This,
2151 NULL,
2152 This->rootPropertySetIndex);
2154 return hr;
2157 void StorageImpl_Destroy(
2158 StorageImpl* This)
2160 BlockChainStream_Destroy(This->smallBlockRootChain);
2161 BlockChainStream_Destroy(This->rootBlockChain);
2162 BlockChainStream_Destroy(This->smallBlockDepotChain);
2164 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2165 return;
2168 /******************************************************************************
2169 * Storage32Impl_GetNextFreeBigBlock
2171 * Returns the index of the next free big block.
2172 * If the big block depot is filled, this method will enlarge it.
2175 ULONG StorageImpl_GetNextFreeBigBlock(
2176 StorageImpl* This)
2178 ULONG depotBlockIndexPos;
2179 void *depotBuffer;
2180 ULONG depotBlockOffset;
2181 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2182 ULONG nextBlockIndex = BLOCK_SPECIAL;
2183 int depotIndex = 0;
2184 ULONG freeBlock = BLOCK_UNUSED;
2186 depotIndex = This->prevFreeBlock / blocksPerDepot;
2187 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2190 * Scan the entire big block depot until we find a block marked free
2192 while (nextBlockIndex != BLOCK_UNUSED)
2194 if (depotIndex < COUNT_BBDEPOTINHEADER)
2196 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2199 * Grow the primary depot.
2201 if (depotBlockIndexPos == BLOCK_UNUSED)
2203 depotBlockIndexPos = depotIndex*blocksPerDepot;
2206 * Add a block depot.
2208 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2209 This->bigBlockDepotCount++;
2210 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2213 * Flag it as a block depot.
2215 StorageImpl_SetNextBlockInChain(This,
2216 depotBlockIndexPos,
2217 BLOCK_SPECIAL);
2219 /* Save new header information.
2221 StorageImpl_SaveFileHeader(This);
2224 else
2226 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2228 if (depotBlockIndexPos == BLOCK_UNUSED)
2231 * Grow the extended depot.
2233 ULONG extIndex = BLOCK_UNUSED;
2234 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2235 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2237 if (extBlockOffset == 0)
2239 /* We need an extended block.
2241 extIndex = Storage32Impl_AddExtBlockDepot(This);
2242 This->extBigBlockDepotCount++;
2243 depotBlockIndexPos = extIndex + 1;
2245 else
2246 depotBlockIndexPos = depotIndex * blocksPerDepot;
2249 * Add a block depot and mark it in the extended block.
2251 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2252 This->bigBlockDepotCount++;
2253 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2255 /* Flag the block depot.
2257 StorageImpl_SetNextBlockInChain(This,
2258 depotBlockIndexPos,
2259 BLOCK_SPECIAL);
2261 /* If necessary, flag the extended depot block.
2263 if (extIndex != BLOCK_UNUSED)
2264 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2266 /* Save header information.
2268 StorageImpl_SaveFileHeader(This);
2272 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2274 if (depotBuffer != 0)
2276 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2277 ( nextBlockIndex != BLOCK_UNUSED))
2279 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2281 if (nextBlockIndex == BLOCK_UNUSED)
2283 freeBlock = (depotIndex * blocksPerDepot) +
2284 (depotBlockOffset/sizeof(ULONG));
2287 depotBlockOffset += sizeof(ULONG);
2290 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2293 depotIndex++;
2294 depotBlockOffset = 0;
2297 This->prevFreeBlock = freeBlock;
2299 return freeBlock;
2302 /******************************************************************************
2303 * Storage32Impl_AddBlockDepot
2305 * This will create a depot block, essentially it is a block initialized
2306 * to BLOCK_UNUSEDs.
2308 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2310 BYTE* blockBuffer;
2312 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2315 * Initialize blocks as free
2317 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2319 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2322 /******************************************************************************
2323 * Storage32Impl_GetExtDepotBlock
2325 * Returns the index of the block that corresponds to the specified depot
2326 * index. This method is only for depot indexes equal or greater than
2327 * COUNT_BBDEPOTINHEADER.
2329 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2331 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2332 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2333 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2334 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2335 ULONG blockIndex = BLOCK_UNUSED;
2336 ULONG extBlockIndex = This->extBigBlockDepotStart;
2338 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2340 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2341 return BLOCK_UNUSED;
2343 while (extBlockCount > 0)
2345 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2346 extBlockCount--;
2349 if (extBlockIndex != BLOCK_UNUSED)
2351 BYTE* depotBuffer;
2353 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2355 if (depotBuffer != 0)
2357 StorageUtl_ReadDWord(depotBuffer,
2358 extBlockOffset * sizeof(ULONG),
2359 &blockIndex);
2361 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2365 return blockIndex;
2368 /******************************************************************************
2369 * Storage32Impl_SetExtDepotBlock
2371 * Associates the specified block index to the specified depot index.
2372 * This method is only for depot indexes equal or greater than
2373 * COUNT_BBDEPOTINHEADER.
2375 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2376 ULONG depotIndex,
2377 ULONG blockIndex)
2379 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2380 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2381 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2382 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2383 ULONG extBlockIndex = This->extBigBlockDepotStart;
2385 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2387 while (extBlockCount > 0)
2389 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2390 extBlockCount--;
2393 if (extBlockIndex != BLOCK_UNUSED)
2395 BYTE* depotBuffer;
2397 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2399 if (depotBuffer != 0)
2401 StorageUtl_WriteDWord(depotBuffer,
2402 extBlockOffset * sizeof(ULONG),
2403 blockIndex);
2405 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2410 /******************************************************************************
2411 * Storage32Impl_AddExtBlockDepot
2413 * Creates an extended depot block.
2415 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2417 ULONG numExtBlocks = This->extBigBlockDepotCount;
2418 ULONG nextExtBlock = This->extBigBlockDepotStart;
2419 BYTE* depotBuffer = NULL;
2420 ULONG index = BLOCK_UNUSED;
2421 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2422 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2423 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2425 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2426 blocksPerDepotBlock;
2428 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2431 * The first extended block.
2433 This->extBigBlockDepotStart = index;
2435 else
2437 int i;
2439 * Follow the chain to the last one.
2441 for (i = 0; i < (numExtBlocks - 1); i++)
2443 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2447 * Add the new extended block to the chain.
2449 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2450 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2451 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2455 * Initialize this block.
2457 depotBuffer = StorageImpl_GetBigBlock(This, index);
2458 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2459 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2461 return index;
2464 /******************************************************************************
2465 * Storage32Impl_FreeBigBlock
2467 * This method will flag the specified block as free in the big block depot.
2469 void StorageImpl_FreeBigBlock(
2470 StorageImpl* This,
2471 ULONG blockIndex)
2473 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2475 if (blockIndex < This->prevFreeBlock)
2476 This->prevFreeBlock = blockIndex;
2479 /************************************************************************
2480 * Storage32Impl_GetNextBlockInChain
2482 * This method will retrieve the block index of the next big block in
2483 * in the chain.
2485 * Params: This - Pointer to the Storage object.
2486 * blockIndex - Index of the block to retrieve the chain
2487 * for.
2489 * Returns: This method returns the index of the next block in the chain.
2490 * It will return the constants:
2491 * BLOCK_SPECIAL - If the block given was not part of a
2492 * chain.
2493 * BLOCK_END_OF_CHAIN - If the block given was the last in
2494 * a chain.
2495 * BLOCK_UNUSED - If the block given was not past of a chain
2496 * and is available.
2497 * BLOCK_EXTBBDEPOT - This block is part of the extended
2498 * big block depot.
2500 * See Windows documentation for more details on IStorage methods.
2502 ULONG StorageImpl_GetNextBlockInChain(
2503 StorageImpl* This,
2504 ULONG blockIndex)
2506 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2507 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2508 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2509 ULONG nextBlockIndex = BLOCK_SPECIAL;
2510 void* depotBuffer;
2511 ULONG depotBlockIndexPos;
2513 assert(depotBlockCount < This->bigBlockDepotCount);
2516 * Cache the currently accessed depot block.
2518 if (depotBlockCount != This->indexBlockDepotCached)
2520 This->indexBlockDepotCached = depotBlockCount;
2522 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2524 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2526 else
2529 * We have to look in the extended depot.
2531 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2534 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2536 if (depotBuffer!=0)
2538 int index;
2540 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2542 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2543 This->blockDepotCached[index] = nextBlockIndex;
2546 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2550 nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2552 return nextBlockIndex;
2555 /******************************************************************************
2556 * Storage32Impl_GetNextExtendedBlock
2558 * Given an extended block this method will return the next extended block.
2560 * NOTES:
2561 * The last ULONG of an extended block is the block index of the next
2562 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2563 * depot.
2565 * Return values:
2566 * - The index of the next extended block
2567 * - BLOCK_UNUSED: there is no next extended block.
2568 * - Any other return values denotes failure.
2570 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2572 ULONG nextBlockIndex = BLOCK_SPECIAL;
2573 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2574 void* depotBuffer;
2576 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2578 if (depotBuffer!=0)
2580 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2582 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2585 return nextBlockIndex;
2588 /******************************************************************************
2589 * Storage32Impl_SetNextBlockInChain
2591 * This method will write the index of the specified block's next block
2592 * in the big block depot.
2594 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2595 * do the following
2597 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2598 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2599 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2602 void StorageImpl_SetNextBlockInChain(
2603 StorageImpl* This,
2604 ULONG blockIndex,
2605 ULONG nextBlock)
2607 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2608 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2609 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2610 ULONG depotBlockIndexPos;
2611 void* depotBuffer;
2613 assert(depotBlockCount < This->bigBlockDepotCount);
2614 assert(blockIndex != nextBlock);
2616 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2618 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2620 else
2623 * We have to look in the extended depot.
2625 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2628 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2630 if (depotBuffer!=0)
2632 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2633 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2637 * Update the cached block depot, if necessary.
2639 if (depotBlockCount == This->indexBlockDepotCached)
2641 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2645 /******************************************************************************
2646 * Storage32Impl_LoadFileHeader
2648 * This method will read in the file header, i.e. big block index -1.
2650 HRESULT StorageImpl_LoadFileHeader(
2651 StorageImpl* This)
2653 HRESULT hr = STG_E_FILENOTFOUND;
2654 void* headerBigBlock = NULL;
2655 int index;
2658 * Get a pointer to the big block of data containing the header.
2660 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2663 * Extract the information from the header.
2665 if (headerBigBlock!=0)
2668 * Check for the "magic number" signature and return an error if it is not
2669 * found.
2671 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2673 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2674 return STG_E_OLDFORMAT;
2677 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2679 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2680 return STG_E_INVALIDHEADER;
2683 StorageUtl_ReadWord(
2684 headerBigBlock,
2685 OFFSET_BIGBLOCKSIZEBITS,
2686 &This->bigBlockSizeBits);
2688 StorageUtl_ReadWord(
2689 headerBigBlock,
2690 OFFSET_SMALLBLOCKSIZEBITS,
2691 &This->smallBlockSizeBits);
2693 StorageUtl_ReadDWord(
2694 headerBigBlock,
2695 OFFSET_BBDEPOTCOUNT,
2696 &This->bigBlockDepotCount);
2698 StorageUtl_ReadDWord(
2699 headerBigBlock,
2700 OFFSET_ROOTSTARTBLOCK,
2701 &This->rootStartBlock);
2703 StorageUtl_ReadDWord(
2704 headerBigBlock,
2705 OFFSET_SBDEPOTSTART,
2706 &This->smallBlockDepotStart);
2708 StorageUtl_ReadDWord(
2709 headerBigBlock,
2710 OFFSET_EXTBBDEPOTSTART,
2711 &This->extBigBlockDepotStart);
2713 StorageUtl_ReadDWord(
2714 headerBigBlock,
2715 OFFSET_EXTBBDEPOTCOUNT,
2716 &This->extBigBlockDepotCount);
2718 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2720 StorageUtl_ReadDWord(
2721 headerBigBlock,
2722 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2723 &(This->bigBlockDepotStart[index]));
2727 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2729 if ((1 << 2) == 4)
2731 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2732 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2734 else
2736 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2737 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2741 * Right now, the code is making some assumptions about the size of the
2742 * blocks, just make sure they are what we're expecting.
2744 assert( (This->bigBlockSize==DEF_BIG_BLOCK_SIZE) &&
2745 (This->smallBlockSize==DEF_SMALL_BLOCK_SIZE));
2748 * Release the block.
2750 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2752 hr = S_OK;
2755 return hr;
2758 /******************************************************************************
2759 * Storage32Impl_SaveFileHeader
2761 * This method will save to the file the header, i.e. big block -1.
2763 void StorageImpl_SaveFileHeader(
2764 StorageImpl* This)
2766 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2767 int index;
2768 BOOL success;
2771 * Get a pointer to the big block of data containing the header.
2773 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2776 * If the block read failed, the file is probably new.
2778 if (!success)
2781 * Initialize for all unknown fields.
2783 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2786 * Initialize the magic number.
2788 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2791 * And a bunch of things we don't know what they mean
2793 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2794 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2795 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2796 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2797 StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
2801 * Write the information to the header.
2803 if (headerBigBlock!=0)
2805 StorageUtl_WriteWord(
2806 headerBigBlock,
2807 OFFSET_BIGBLOCKSIZEBITS,
2808 This->bigBlockSizeBits);
2810 StorageUtl_WriteWord(
2811 headerBigBlock,
2812 OFFSET_SMALLBLOCKSIZEBITS,
2813 This->smallBlockSizeBits);
2815 StorageUtl_WriteDWord(
2816 headerBigBlock,
2817 OFFSET_BBDEPOTCOUNT,
2818 This->bigBlockDepotCount);
2820 StorageUtl_WriteDWord(
2821 headerBigBlock,
2822 OFFSET_ROOTSTARTBLOCK,
2823 This->rootStartBlock);
2825 StorageUtl_WriteDWord(
2826 headerBigBlock,
2827 OFFSET_SBDEPOTSTART,
2828 This->smallBlockDepotStart);
2830 StorageUtl_WriteDWord(
2831 headerBigBlock,
2832 OFFSET_EXTBBDEPOTSTART,
2833 This->extBigBlockDepotStart);
2835 StorageUtl_WriteDWord(
2836 headerBigBlock,
2837 OFFSET_EXTBBDEPOTCOUNT,
2838 This->extBigBlockDepotCount);
2840 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2842 StorageUtl_WriteDWord(
2843 headerBigBlock,
2844 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2845 (This->bigBlockDepotStart[index]));
2850 * Write the big block back to the file.
2852 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2855 /******************************************************************************
2856 * Storage32Impl_ReadProperty
2858 * This method will read the specified property from the property chain.
2860 BOOL StorageImpl_ReadProperty(
2861 StorageImpl* This,
2862 ULONG index,
2863 StgProperty* buffer)
2865 BYTE currentProperty[PROPSET_BLOCK_SIZE];
2866 ULARGE_INTEGER offsetInPropSet;
2867 BOOL readSucessful;
2868 ULONG bytesRead;
2870 offsetInPropSet.HighPart = 0;
2871 offsetInPropSet.LowPart = index * PROPSET_BLOCK_SIZE;
2873 readSucessful = BlockChainStream_ReadAt(
2874 This->rootBlockChain,
2875 offsetInPropSet,
2876 PROPSET_BLOCK_SIZE,
2877 currentProperty,
2878 &bytesRead);
2880 if (readSucessful)
2882 memset(buffer->name, 0, sizeof(buffer->name));
2883 memcpy(
2884 buffer->name,
2885 currentProperty+OFFSET_PS_NAME,
2886 PROPERTY_NAME_BUFFER_LEN );
2888 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
2890 StorageUtl_ReadWord(
2891 currentProperty,
2892 OFFSET_PS_NAMELENGTH,
2893 &buffer->sizeOfNameString);
2895 StorageUtl_ReadDWord(
2896 currentProperty,
2897 OFFSET_PS_PREVIOUSPROP,
2898 &buffer->previousProperty);
2900 StorageUtl_ReadDWord(
2901 currentProperty,
2902 OFFSET_PS_NEXTPROP,
2903 &buffer->nextProperty);
2905 StorageUtl_ReadDWord(
2906 currentProperty,
2907 OFFSET_PS_DIRPROP,
2908 &buffer->dirProperty);
2910 StorageUtl_ReadGUID(
2911 currentProperty,
2912 OFFSET_PS_GUID,
2913 &buffer->propertyUniqueID);
2915 StorageUtl_ReadDWord(
2916 currentProperty,
2917 OFFSET_PS_TSS1,
2918 &buffer->timeStampS1);
2920 StorageUtl_ReadDWord(
2921 currentProperty,
2922 OFFSET_PS_TSD1,
2923 &buffer->timeStampD1);
2925 StorageUtl_ReadDWord(
2926 currentProperty,
2927 OFFSET_PS_TSS2,
2928 &buffer->timeStampS2);
2930 StorageUtl_ReadDWord(
2931 currentProperty,
2932 OFFSET_PS_TSD2,
2933 &buffer->timeStampD2);
2935 StorageUtl_ReadDWord(
2936 currentProperty,
2937 OFFSET_PS_STARTBLOCK,
2938 &buffer->startingBlock);
2940 StorageUtl_ReadDWord(
2941 currentProperty,
2942 OFFSET_PS_SIZE,
2943 &buffer->size.LowPart);
2945 buffer->size.HighPart = 0;
2948 return readSucessful;
2951 /*********************************************************************
2952 * Write the specified property into the property chain
2954 BOOL StorageImpl_WriteProperty(
2955 StorageImpl* This,
2956 ULONG index,
2957 StgProperty* buffer)
2959 BYTE currentProperty[PROPSET_BLOCK_SIZE];
2960 ULARGE_INTEGER offsetInPropSet;
2961 BOOL writeSucessful;
2962 ULONG bytesWritten;
2964 offsetInPropSet.HighPart = 0;
2965 offsetInPropSet.LowPart = index * PROPSET_BLOCK_SIZE;
2967 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
2969 memcpy(
2970 currentProperty + OFFSET_PS_NAME,
2971 buffer->name,
2972 PROPERTY_NAME_BUFFER_LEN );
2974 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
2977 * Reassign the size in case of mistake....
2979 buffer->sizeOfNameString = (lstrlenW(buffer->name)+1) * sizeof(WCHAR);
2981 StorageUtl_WriteWord(
2982 currentProperty,
2983 OFFSET_PS_NAMELENGTH,
2984 buffer->sizeOfNameString);
2986 StorageUtl_WriteDWord(
2987 currentProperty,
2988 OFFSET_PS_PREVIOUSPROP,
2989 buffer->previousProperty);
2991 StorageUtl_WriteDWord(
2992 currentProperty,
2993 OFFSET_PS_NEXTPROP,
2994 buffer->nextProperty);
2996 StorageUtl_WriteDWord(
2997 currentProperty,
2998 OFFSET_PS_DIRPROP,
2999 buffer->dirProperty);
3001 StorageUtl_WriteGUID(
3002 currentProperty,
3003 OFFSET_PS_GUID,
3004 &buffer->propertyUniqueID);
3006 StorageUtl_WriteDWord(
3007 currentProperty,
3008 OFFSET_PS_TSS1,
3009 buffer->timeStampS1);
3011 StorageUtl_WriteDWord(
3012 currentProperty,
3013 OFFSET_PS_TSD1,
3014 buffer->timeStampD1);
3016 StorageUtl_WriteDWord(
3017 currentProperty,
3018 OFFSET_PS_TSS2,
3019 buffer->timeStampS2);
3021 StorageUtl_WriteDWord(
3022 currentProperty,
3023 OFFSET_PS_TSD2,
3024 buffer->timeStampD2);
3026 StorageUtl_WriteDWord(
3027 currentProperty,
3028 OFFSET_PS_STARTBLOCK,
3029 buffer->startingBlock);
3031 StorageUtl_WriteDWord(
3032 currentProperty,
3033 OFFSET_PS_SIZE,
3034 buffer->size.LowPart);
3036 writeSucessful = BlockChainStream_WriteAt(This->rootBlockChain,
3037 offsetInPropSet,
3038 PROPSET_BLOCK_SIZE,
3039 currentProperty,
3040 &bytesWritten);
3041 return writeSucessful;
3044 BOOL StorageImpl_ReadBigBlock(
3045 StorageImpl* This,
3046 ULONG blockIndex,
3047 void* buffer)
3049 void* bigBlockBuffer;
3051 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3053 if (bigBlockBuffer!=0)
3055 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3057 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3059 return TRUE;
3062 return FALSE;
3065 BOOL StorageImpl_WriteBigBlock(
3066 StorageImpl* This,
3067 ULONG blockIndex,
3068 void* buffer)
3070 void* bigBlockBuffer;
3072 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3074 if (bigBlockBuffer!=0)
3076 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3078 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3080 return TRUE;
3083 return FALSE;
3086 void* StorageImpl_GetROBigBlock(
3087 StorageImpl* This,
3088 ULONG blockIndex)
3090 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3093 void* StorageImpl_GetBigBlock(
3094 StorageImpl* This,
3095 ULONG blockIndex)
3097 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3100 void StorageImpl_ReleaseBigBlock(
3101 StorageImpl* This,
3102 void* pBigBlock)
3104 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3107 /******************************************************************************
3108 * Storage32Impl_SmallBlocksToBigBlocks
3110 * This method will convert a small block chain to a big block chain.
3111 * The small block chain will be destroyed.
3113 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3114 StorageImpl* This,
3115 SmallBlockChainStream** ppsbChain)
3117 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3118 ULARGE_INTEGER size, offset;
3119 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3120 ULONG propertyIndex;
3121 BOOL successRead, successWrite;
3122 StgProperty chainProperty;
3123 BYTE buffer[DEF_SMALL_BLOCK_SIZE];
3124 BlockChainStream *bbTempChain = NULL;
3125 BlockChainStream *bigBlockChain = NULL;
3128 * Create a temporary big block chain that doesn't have
3129 * an associated property. This temporary chain will be
3130 * used to copy data from small blocks to big blocks.
3132 bbTempChain = BlockChainStream_Construct(This,
3133 &bbHeadOfChain,
3134 PROPERTY_NULL);
3137 * Grow the big block chain.
3139 size = SmallBlockChainStream_GetSize(*ppsbChain);
3140 BlockChainStream_SetSize(bbTempChain, size);
3143 * Copy the contents of the small block chain to the big block chain
3144 * by small block size increments.
3146 offset.LowPart = 0;
3147 offset.HighPart = 0;
3148 cbTotalRead = 0;
3149 cbTotalWritten = 0;
3153 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3154 offset,
3155 sizeof(buffer),
3156 buffer,
3157 &cbRead);
3158 cbTotalRead += cbRead;
3160 successWrite = BlockChainStream_WriteAt(bbTempChain,
3161 offset,
3162 cbRead,
3163 buffer,
3164 &cbWritten);
3165 cbTotalWritten += cbWritten;
3167 offset.LowPart += This->smallBlockSize;
3169 } while (successRead && successWrite);
3171 assert(cbTotalRead == cbTotalWritten);
3174 * Destroy the small block chain.
3176 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3177 size.HighPart = 0;
3178 size.LowPart = 0;
3179 SmallBlockChainStream_SetSize(*ppsbChain, size);
3180 SmallBlockChainStream_Destroy(*ppsbChain);
3181 *ppsbChain = 0;
3184 * Change the property information. This chain is now a big block chain
3185 * and it doesn't reside in the small blocks chain anymore.
3187 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3189 chainProperty.startingBlock = bbHeadOfChain;
3191 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3194 * Destroy the temporary propertyless big block chain.
3195 * Create a new big block chain associated with this property.
3197 BlockChainStream_Destroy(bbTempChain);
3198 bigBlockChain = BlockChainStream_Construct(This,
3199 NULL,
3200 propertyIndex);
3202 return bigBlockChain;
3205 /******************************************************************************
3206 ** Storage32InternalImpl implementation
3209 StorageInternalImpl* StorageInternalImpl_Construct(
3210 StorageImpl* ancestorStorage,
3211 ULONG rootPropertyIndex)
3213 StorageInternalImpl* newStorage;
3216 * Allocate space for the new storage object
3218 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3220 if (newStorage!=0)
3222 memset(newStorage, 0, sizeof(StorageInternalImpl));
3225 * Initialize the virtual function table.
3227 newStorage->lpvtbl = &Storage32InternalImpl_Vtbl;
3228 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3231 * Keep the ancestor storage pointer and nail a reference to it.
3233 newStorage->ancestorStorage = ancestorStorage;
3234 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3237 * Keep the index of the root property set for this storage,
3239 newStorage->rootPropertySetIndex = rootPropertyIndex;
3241 return newStorage;
3244 return 0;
3247 void StorageInternalImpl_Destroy(
3248 StorageInternalImpl* This)
3250 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3251 HeapFree(GetProcessHeap(), 0, This);
3254 /******************************************************************************
3256 ** Storage32InternalImpl_Commit
3258 ** The non-root storages cannot be opened in transacted mode thus this function
3259 ** does nothing.
3261 HRESULT WINAPI StorageInternalImpl_Commit(
3262 IStorage* iface,
3263 DWORD grfCommitFlags) /* [in] */
3265 return S_OK;
3268 /******************************************************************************
3270 ** Storage32InternalImpl_Revert
3272 ** The non-root storages cannot be opened in transacted mode thus this function
3273 ** does nothing.
3275 HRESULT WINAPI StorageInternalImpl_Revert(
3276 IStorage* iface)
3278 return S_OK;
3281 /******************************************************************************
3282 ** IEnumSTATSTGImpl implementation
3285 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3286 StorageImpl* parentStorage,
3287 ULONG firstPropertyNode)
3289 IEnumSTATSTGImpl* newEnumeration;
3291 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3293 if (newEnumeration!=0)
3296 * Set-up the virtual function table and reference count.
3298 newEnumeration->lpvtbl = &IEnumSTATSTGImpl_Vtbl;
3299 newEnumeration->ref = 0;
3302 * We want to nail-down the reference to the storage in case the
3303 * enumeration out-lives the storage in the client application.
3305 newEnumeration->parentStorage = parentStorage;
3306 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3308 newEnumeration->firstPropertyNode = firstPropertyNode;
3311 * Initialize the search stack
3313 newEnumeration->stackSize = 0;
3314 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3315 newEnumeration->stackToVisit =
3316 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3319 * Make sure the current node of the iterator is the first one.
3321 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3324 return newEnumeration;
3327 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3329 IStorage_Release((IStorage*)This->parentStorage);
3330 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3331 HeapFree(GetProcessHeap(), 0, This);
3334 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3335 IEnumSTATSTG* iface,
3336 REFIID riid,
3337 void** ppvObject)
3339 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3342 * Perform a sanity check on the parameters.
3344 if (ppvObject==0)
3345 return E_INVALIDARG;
3348 * Initialize the return parameter.
3350 *ppvObject = 0;
3353 * Compare the riid with the interface IDs implemented by this object.
3355 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3357 *ppvObject = (IEnumSTATSTG*)This;
3359 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3361 *ppvObject = (IEnumSTATSTG*)This;
3365 * Check that we obtained an interface.
3367 if ((*ppvObject)==0)
3368 return E_NOINTERFACE;
3371 * Query Interface always increases the reference count by one when it is
3372 * successful
3374 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3376 return S_OK;
3379 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3380 IEnumSTATSTG* iface)
3382 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3384 This->ref++;
3385 return This->ref;
3388 ULONG WINAPI IEnumSTATSTGImpl_Release(
3389 IEnumSTATSTG* iface)
3391 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3393 ULONG newRef;
3395 This->ref--;
3396 newRef = This->ref;
3399 * If the reference count goes down to 0, perform suicide.
3401 if (newRef==0)
3403 IEnumSTATSTGImpl_Destroy(This);
3406 return newRef;;
3409 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3410 IEnumSTATSTG* iface,
3411 ULONG celt,
3412 STATSTG* rgelt,
3413 ULONG* pceltFetched)
3415 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3417 StgProperty currentProperty;
3418 STATSTG* currentReturnStruct = rgelt;
3419 ULONG objectFetched = 0;
3420 ULONG currentSearchNode;
3423 * Perform a sanity check on the parameters.
3425 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3426 return E_INVALIDARG;
3429 * To avoid the special case, get another pointer to a ULONG value if
3430 * the caller didn't supply one.
3432 if (pceltFetched==0)
3433 pceltFetched = &objectFetched;
3436 * Start the iteration, we will iterate until we hit the end of the
3437 * linked list or until we hit the number of items to iterate through
3439 *pceltFetched = 0;
3442 * Start with the node at the top of the stack.
3444 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3446 while ( ( *pceltFetched < celt) &&
3447 ( currentSearchNode!=PROPERTY_NULL) )
3450 * Remove the top node from the stack
3452 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3455 * Read the property from the storage.
3457 StorageImpl_ReadProperty(This->parentStorage,
3458 currentSearchNode,
3459 &currentProperty);
3462 * Copy the information to the return buffer.
3464 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3465 &currentProperty,
3466 STATFLAG_DEFAULT);
3469 * Step to the next item in the iteration
3471 (*pceltFetched)++;
3472 currentReturnStruct++;
3475 * Push the next search node in the search stack.
3477 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3480 * continue the iteration.
3482 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3485 if (*pceltFetched == celt)
3486 return S_OK;
3488 return S_FALSE;
3492 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3493 IEnumSTATSTG* iface,
3494 ULONG celt)
3496 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3498 StgProperty currentProperty;
3499 ULONG objectFetched = 0;
3500 ULONG currentSearchNode;
3503 * Start with the node at the top of the stack.
3505 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3507 while ( (objectFetched < celt) &&
3508 (currentSearchNode!=PROPERTY_NULL) )
3511 * Remove the top node from the stack
3513 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3516 * Read the property from the storage.
3518 StorageImpl_ReadProperty(This->parentStorage,
3519 currentSearchNode,
3520 &currentProperty);
3523 * Step to the next item in the iteration
3525 objectFetched++;
3528 * Push the next search node in the search stack.
3530 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3533 * continue the iteration.
3535 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3538 if (objectFetched == celt)
3539 return S_OK;
3541 return S_FALSE;
3544 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3545 IEnumSTATSTG* iface)
3547 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3549 StgProperty rootProperty;
3550 BOOL readSucessful;
3553 * Re-initialize the search stack to an empty stack
3555 This->stackSize = 0;
3558 * Read the root property from the storage.
3560 readSucessful = StorageImpl_ReadProperty(
3561 This->parentStorage,
3562 This->firstPropertyNode,
3563 &rootProperty);
3565 if (readSucessful)
3567 assert(rootProperty.sizeOfNameString!=0);
3570 * Push the search node in the search stack.
3572 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3575 return S_OK;
3578 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3579 IEnumSTATSTG* iface,
3580 IEnumSTATSTG** ppenum)
3582 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3584 IEnumSTATSTGImpl* newClone;
3587 * Perform a sanity check on the parameters.
3589 if (ppenum==0)
3590 return E_INVALIDARG;
3592 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3593 This->firstPropertyNode);
3597 * The new clone enumeration must point to the same current node as
3598 * the ole one.
3600 newClone->stackSize = This->stackSize ;
3601 newClone->stackMaxSize = This->stackMaxSize ;
3602 newClone->stackToVisit =
3603 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3605 memcpy(
3606 newClone->stackToVisit,
3607 This->stackToVisit,
3608 sizeof(ULONG) * newClone->stackSize);
3610 *ppenum = (IEnumSTATSTG*)newClone;
3613 * Don't forget to nail down a reference to the clone before
3614 * returning it.
3616 IEnumSTATSTGImpl_AddRef(*ppenum);
3618 return S_OK;
3621 INT IEnumSTATSTGImpl_FindParentProperty(
3622 IEnumSTATSTGImpl *This,
3623 ULONG childProperty,
3624 StgProperty *currentProperty,
3625 ULONG *thisNodeId)
3627 ULONG currentSearchNode;
3628 ULONG foundNode;
3631 * To avoid the special case, get another pointer to a ULONG value if
3632 * the caller didn't supply one.
3634 if (thisNodeId==0)
3635 thisNodeId = &foundNode;
3638 * Start with the node at the top of the stack.
3640 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3643 while (currentSearchNode!=PROPERTY_NULL)
3646 * Store the current node in the returned parameters
3648 *thisNodeId = currentSearchNode;
3651 * Remove the top node from the stack
3653 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3656 * Read the property from the storage.
3658 StorageImpl_ReadProperty(
3659 This->parentStorage,
3660 currentSearchNode,
3661 currentProperty);
3663 if (currentProperty->previousProperty == childProperty)
3664 return PROPERTY_RELATION_PREVIOUS;
3666 else if (currentProperty->nextProperty == childProperty)
3667 return PROPERTY_RELATION_NEXT;
3669 else if (currentProperty->dirProperty == childProperty)
3670 return PROPERTY_RELATION_DIR;
3673 * Push the next search node in the search stack.
3675 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3678 * continue the iteration.
3680 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3683 return PROPERTY_NULL;
3686 ULONG IEnumSTATSTGImpl_FindProperty(
3687 IEnumSTATSTGImpl* This,
3688 const OLECHAR* lpszPropName,
3689 StgProperty* currentProperty)
3691 ULONG currentSearchNode;
3694 * Start with the node at the top of the stack.
3696 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3698 while (currentSearchNode!=PROPERTY_NULL)
3701 * Remove the top node from the stack
3703 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3706 * Read the property from the storage.
3708 StorageImpl_ReadProperty(This->parentStorage,
3709 currentSearchNode,
3710 currentProperty);
3712 if ( propertyNameCmp(
3713 (OLECHAR*)currentProperty->name,
3714 (OLECHAR*)lpszPropName) == 0)
3715 return currentSearchNode;
3718 * Push the next search node in the search stack.
3720 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3723 * continue the iteration.
3725 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3728 return PROPERTY_NULL;
3731 void IEnumSTATSTGImpl_PushSearchNode(
3732 IEnumSTATSTGImpl* This,
3733 ULONG nodeToPush)
3735 StgProperty rootProperty;
3736 BOOL readSucessful;
3739 * First, make sure we're not trying to push an unexisting node.
3741 if (nodeToPush==PROPERTY_NULL)
3742 return;
3745 * First push the node to the stack
3747 if (This->stackSize == This->stackMaxSize)
3749 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3751 This->stackToVisit = HeapReAlloc(
3752 GetProcessHeap(),
3754 This->stackToVisit,
3755 sizeof(ULONG) * This->stackMaxSize);
3758 This->stackToVisit[This->stackSize] = nodeToPush;
3759 This->stackSize++;
3762 * Read the root property from the storage.
3764 readSucessful = StorageImpl_ReadProperty(
3765 This->parentStorage,
3766 nodeToPush,
3767 &rootProperty);
3769 if (readSucessful)
3771 assert(rootProperty.sizeOfNameString!=0);
3774 * Push the previous search node in the search stack.
3776 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3780 ULONG IEnumSTATSTGImpl_PopSearchNode(
3781 IEnumSTATSTGImpl* This,
3782 BOOL remove)
3784 ULONG topNode;
3786 if (This->stackSize == 0)
3787 return PROPERTY_NULL;
3789 topNode = This->stackToVisit[This->stackSize-1];
3791 if (remove)
3792 This->stackSize--;
3794 return topNode;
3797 /******************************************************************************
3798 ** StorageUtl implementation
3801 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
3803 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
3806 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
3808 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
3811 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
3813 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
3816 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
3818 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
3821 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
3823 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
3824 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
3825 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
3827 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
3830 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
3832 StorageUtl_WriteDWord(buffer, offset, value->Data1);
3833 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
3834 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
3836 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
3839 void StorageUtl_CopyPropertyToSTATSTG(
3840 STATSTG* destination,
3841 StgProperty* source,
3842 int statFlags)
3845 * The copy of the string occurs only when the flag is not set
3847 if ((statFlags & STATFLAG_NONAME) != 0)
3849 destination->pwcsName = 0;
3851 else
3853 destination->pwcsName =
3854 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
3856 lstrcpyW((LPWSTR)destination->pwcsName, source->name);
3859 switch (source->propertyType)
3861 case PROPTYPE_STORAGE:
3862 case PROPTYPE_ROOT:
3863 destination->type = STGTY_STORAGE;
3864 break;
3865 case PROPTYPE_STREAM:
3866 destination->type = STGTY_STREAM;
3867 break;
3868 default:
3869 destination->type = STGTY_STREAM;
3870 break;
3873 destination->cbSize = source->size;
3875 currentReturnStruct->mtime = {0}; TODO
3876 currentReturnStruct->ctime = {0};
3877 currentReturnStruct->atime = {0};
3879 destination->grfMode = 0;
3880 destination->grfLocksSupported = 0;
3881 destination->clsid = source->propertyUniqueID;
3882 destination->grfStateBits = 0;
3883 destination->reserved = 0;
3886 /******************************************************************************
3887 ** BlockChainStream implementation
3890 BlockChainStream* BlockChainStream_Construct(
3891 StorageImpl* parentStorage,
3892 ULONG* headOfStreamPlaceHolder,
3893 ULONG propertyIndex)
3895 BlockChainStream* newStream;
3896 ULONG blockIndex;
3898 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
3900 newStream->parentStorage = parentStorage;
3901 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
3902 newStream->ownerPropertyIndex = propertyIndex;
3903 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
3904 newStream->tailIndex = BLOCK_END_OF_CHAIN;
3905 newStream->numBlocks = 0;
3907 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
3909 while (blockIndex != BLOCK_END_OF_CHAIN)
3911 newStream->numBlocks++;
3912 newStream->tailIndex = blockIndex;
3914 blockIndex = StorageImpl_GetNextBlockInChain(
3915 parentStorage,
3916 blockIndex);
3919 return newStream;
3922 void BlockChainStream_Destroy(BlockChainStream* This)
3924 HeapFree(GetProcessHeap(), 0, This);
3927 /******************************************************************************
3928 * BlockChainStream_GetHeadOfChain
3930 * Returns the head of this stream chain.
3931 * Some special chains don't have properties, their heads are kept in
3932 * This->headOfStreamPlaceHolder.
3935 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
3937 StgProperty chainProperty;
3938 BOOL readSucessful;
3940 if (This->headOfStreamPlaceHolder != 0)
3941 return *(This->headOfStreamPlaceHolder);
3943 if (This->ownerPropertyIndex != PROPERTY_NULL)
3945 readSucessful = StorageImpl_ReadProperty(
3946 This->parentStorage,
3947 This->ownerPropertyIndex,
3948 &chainProperty);
3950 if (readSucessful)
3952 return chainProperty.startingBlock;
3956 return BLOCK_END_OF_CHAIN;
3959 /******************************************************************************
3960 * BlockChainStream_GetCount
3962 * Returns the number of blocks that comprises this chain.
3963 * This is not the size of the stream as the last block may not be full!
3966 ULONG BlockChainStream_GetCount(BlockChainStream* This)
3968 ULONG blockIndex;
3969 ULONG count = 0;
3971 blockIndex = BlockChainStream_GetHeadOfChain(This);
3973 while (blockIndex != BLOCK_END_OF_CHAIN)
3975 count++;
3977 blockIndex = StorageImpl_GetNextBlockInChain(
3978 This->parentStorage,
3979 blockIndex);
3982 return count;
3985 /******************************************************************************
3986 * BlockChainStream_ReadAt
3988 * Reads a specified number of bytes from this chain at the specified offset.
3989 * bytesRead may be NULL.
3990 * Failure will be returned if the specified number of bytes has not been read.
3992 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
3993 ULARGE_INTEGER offset,
3994 ULONG size,
3995 void* buffer,
3996 ULONG* bytesRead)
3998 ULONG blockNoInSequence = offset.LowPart / This->parentStorage->bigBlockSize;
3999 ULONG offsetInBlock = offset.LowPart % This->parentStorage->bigBlockSize;
4000 ULONG bytesToReadInBuffer;
4001 ULONG blockIndex;
4002 BYTE* bufferWalker;
4003 BYTE* bigBlockBuffer;
4005 if (This->lastBlockNoInSequence == 0xFFFFFFFF)
4006 This->lastBlockNoInSequence = blockNoInSequence;
4008 * Find the first block in the stream that contains part of the buffer.
4010 if (blockNoInSequence > This->lastBlockNoInSequence)
4012 ULONG temp = blockNoInSequence;
4014 blockIndex = This->lastBlockNoInSequenceIndex;
4015 blockNoInSequence -= This->lastBlockNoInSequence;
4016 This->lastBlockNoInSequence = temp;
4018 else
4020 blockIndex = BlockChainStream_GetHeadOfChain(This);
4021 This->lastBlockNoInSequence = blockNoInSequence;
4024 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4026 blockIndex =
4027 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4029 blockNoInSequence--;
4032 This->lastBlockNoInSequenceIndex = blockIndex;
4035 * Start reading the buffer.
4037 *bytesRead = 0;
4038 bufferWalker = buffer;
4040 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4043 * Calculate how many bytes we can copy from this big block.
4045 bytesToReadInBuffer =
4046 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4049 * Copy those bytes to the buffer
4051 bigBlockBuffer =
4052 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4054 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4056 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4059 * Step to the next big block.
4061 blockIndex =
4062 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4064 bufferWalker += bytesToReadInBuffer;
4065 size -= bytesToReadInBuffer;
4066 *bytesRead += bytesToReadInBuffer;
4067 offsetInBlock = 0; /* There is no offset on the next block */
4071 return (size == 0);
4074 /******************************************************************************
4075 * BlockChainStream_WriteAt
4077 * Writes the specified number of bytes to this chain at the specified offset.
4078 * bytesWritten may be NULL.
4079 * Will fail if not all specified number of bytes have been written.
4081 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4082 ULARGE_INTEGER offset,
4083 ULONG size,
4084 const void* buffer,
4085 ULONG* bytesWritten)
4087 ULONG blockNoInSequence = offset.LowPart / This->parentStorage->bigBlockSize;
4088 ULONG offsetInBlock = offset.LowPart % This->parentStorage->bigBlockSize;
4089 ULONG bytesToWrite;
4090 ULONG blockIndex;
4091 BYTE* bufferWalker;
4092 BYTE* bigBlockBuffer;
4094 if (This->lastBlockNoInSequence == 0xFFFFFFFF)
4095 This->lastBlockNoInSequence = blockNoInSequence;
4098 * Find the first block in the stream that contains part of the buffer.
4100 if (blockNoInSequence > This->lastBlockNoInSequence)
4102 ULONG temp = blockNoInSequence;
4104 blockIndex = This->lastBlockNoInSequenceIndex;
4105 blockNoInSequence -= This->lastBlockNoInSequence;
4106 This->lastBlockNoInSequence = temp;
4108 else
4110 blockIndex = BlockChainStream_GetHeadOfChain(This);
4111 This->lastBlockNoInSequence = blockNoInSequence;
4114 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4116 blockIndex =
4117 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4119 blockNoInSequence--;
4122 This->lastBlockNoInSequenceIndex = blockIndex;
4125 * Here, I'm casting away the constness on the buffer variable
4126 * This is OK since we don't intend to modify that buffer.
4128 *bytesWritten = 0;
4129 bufferWalker = (BYTE*)buffer;
4131 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4134 * Calculate how many bytes we can copy from this big block.
4136 bytesToWrite =
4137 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4140 * Copy those bytes to the buffer
4142 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4144 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4146 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4149 * Step to the next big block.
4151 blockIndex =
4152 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4154 bufferWalker += bytesToWrite;
4155 size -= bytesToWrite;
4156 *bytesWritten += bytesToWrite;
4157 offsetInBlock = 0; /* There is no offset on the next block */
4160 return (size == 0);
4163 /******************************************************************************
4164 * BlockChainStream_Shrink
4166 * Shrinks this chain in the big block depot.
4168 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4169 ULARGE_INTEGER newSize)
4171 ULONG blockIndex, extraBlock;
4172 ULONG numBlocks;
4173 ULONG count = 1;
4176 * Figure out how many blocks are needed to contain the new size
4178 numBlocks = newSize.LowPart / This->parentStorage->bigBlockSize;
4180 if ((newSize.LowPart % This->parentStorage->bigBlockSize) != 0)
4181 numBlocks++;
4183 blockIndex = BlockChainStream_GetHeadOfChain(This);
4186 * Go to the new end of chain
4188 while (count < numBlocks)
4190 blockIndex =
4191 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4193 count++;
4196 /* Get the next block before marking the new end */
4197 extraBlock =
4198 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4200 /* Mark the new end of chain */
4201 StorageImpl_SetNextBlockInChain(
4202 This->parentStorage,
4203 blockIndex,
4204 BLOCK_END_OF_CHAIN);
4206 This->tailIndex = blockIndex;
4207 This->numBlocks = numBlocks;
4210 * Mark the extra blocks as free
4212 while (extraBlock != BLOCK_END_OF_CHAIN)
4214 blockIndex =
4215 StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4217 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4218 extraBlock = blockIndex;
4221 return TRUE;
4224 /******************************************************************************
4225 * BlockChainStream_Enlarge
4227 * Grows this chain in the big block depot.
4229 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4230 ULARGE_INTEGER newSize)
4232 ULONG blockIndex, currentBlock;
4233 ULONG newNumBlocks;
4234 ULONG oldNumBlocks = 0;
4236 blockIndex = BlockChainStream_GetHeadOfChain(This);
4239 * Empty chain. Create the head.
4241 if (blockIndex == BLOCK_END_OF_CHAIN)
4243 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4244 StorageImpl_SetNextBlockInChain(This->parentStorage,
4245 blockIndex,
4246 BLOCK_END_OF_CHAIN);
4248 if (This->headOfStreamPlaceHolder != 0)
4250 *(This->headOfStreamPlaceHolder) = blockIndex;
4252 else
4254 StgProperty chainProp;
4255 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4257 StorageImpl_ReadProperty(
4258 This->parentStorage,
4259 This->ownerPropertyIndex,
4260 &chainProp);
4262 chainProp.startingBlock = blockIndex;
4264 StorageImpl_WriteProperty(
4265 This->parentStorage,
4266 This->ownerPropertyIndex,
4267 &chainProp);
4270 This->tailIndex = blockIndex;
4271 This->numBlocks = 1;
4275 * Figure out how many blocks are needed to contain this stream
4277 newNumBlocks = newSize.LowPart / This->parentStorage->bigBlockSize;
4279 if ((newSize.LowPart % This->parentStorage->bigBlockSize) != 0)
4280 newNumBlocks++;
4283 * Go to the current end of chain
4285 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4287 currentBlock = blockIndex;
4289 while (blockIndex != BLOCK_END_OF_CHAIN)
4291 This->numBlocks++;
4292 currentBlock = blockIndex;
4294 blockIndex =
4295 StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4298 This->tailIndex = currentBlock;
4301 currentBlock = This->tailIndex;
4302 oldNumBlocks = This->numBlocks;
4305 * Add new blocks to the chain
4307 while (oldNumBlocks < newNumBlocks)
4309 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4311 StorageImpl_SetNextBlockInChain(
4312 This->parentStorage,
4313 currentBlock,
4314 blockIndex);
4316 StorageImpl_SetNextBlockInChain(
4317 This->parentStorage,
4318 blockIndex,
4319 BLOCK_END_OF_CHAIN);
4321 currentBlock = blockIndex;
4322 oldNumBlocks++;
4325 This->tailIndex = blockIndex;
4326 This->numBlocks = newNumBlocks;
4328 return TRUE;
4331 /******************************************************************************
4332 * BlockChainStream_SetSize
4334 * Sets the size of this stream. The big block depot will be updated.
4335 * The file will grow if we grow the chain.
4337 * TODO: Free the actual blocks in the file when we shrink the chain.
4338 * Currently, the blocks are still in the file. So the file size
4339 * doesn't shrink even if we shrink streams.
4341 BOOL BlockChainStream_SetSize(
4342 BlockChainStream* This,
4343 ULARGE_INTEGER newSize)
4345 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4347 if (newSize.LowPart == size.LowPart)
4348 return TRUE;
4350 if (newSize.LowPart < size.LowPart)
4352 BlockChainStream_Shrink(This, newSize);
4354 else
4356 ULARGE_INTEGER fileSize =
4357 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4359 ULONG diff = newSize.LowPart - size.LowPart;
4362 * Make sure the file stays a multiple of blocksize
4364 if ((diff % This->parentStorage->bigBlockSize) != 0)
4365 diff += (This->parentStorage->bigBlockSize -
4366 (diff % This->parentStorage->bigBlockSize) );
4368 fileSize.LowPart += diff;
4369 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4371 BlockChainStream_Enlarge(This, newSize);
4374 return TRUE;
4377 /******************************************************************************
4378 * BlockChainStream_GetSize
4380 * Returns the size of this chain.
4381 * Will return the block count if this chain doesn't have a property.
4383 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4385 StgProperty chainProperty;
4387 if(This->headOfStreamPlaceHolder == NULL)
4390 * This chain is a data stream read the property and return
4391 * the appropriate size
4393 StorageImpl_ReadProperty(
4394 This->parentStorage,
4395 This->ownerPropertyIndex,
4396 &chainProperty);
4398 return chainProperty.size;
4400 else
4403 * this chain is a chain that does not have a property, figure out the
4404 * size by making the product number of used blocks times the
4405 * size of them
4407 ULARGE_INTEGER result;
4408 result.HighPart = 0;
4410 result.LowPart =
4411 BlockChainStream_GetCount(This) *
4412 This->parentStorage->bigBlockSize;
4414 return result;
4418 /******************************************************************************
4419 ** SmallBlockChainStream implementation
4422 SmallBlockChainStream* SmallBlockChainStream_Construct(
4423 StorageImpl* parentStorage,
4424 ULONG propertyIndex)
4426 SmallBlockChainStream* newStream;
4428 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4430 newStream->parentStorage = parentStorage;
4431 newStream->ownerPropertyIndex = propertyIndex;
4433 return newStream;
4436 void SmallBlockChainStream_Destroy(
4437 SmallBlockChainStream* This)
4439 HeapFree(GetProcessHeap(), 0, This);
4442 /******************************************************************************
4443 * SmallBlockChainStream_GetHeadOfChain
4445 * Returns the head of this chain of small blocks.
4447 ULONG SmallBlockChainStream_GetHeadOfChain(
4448 SmallBlockChainStream* This)
4450 StgProperty chainProperty;
4451 BOOL readSucessful;
4453 if (This->ownerPropertyIndex)
4455 readSucessful = StorageImpl_ReadProperty(
4456 This->parentStorage,
4457 This->ownerPropertyIndex,
4458 &chainProperty);
4460 if (readSucessful)
4462 return chainProperty.startingBlock;
4467 return BLOCK_END_OF_CHAIN;
4470 /******************************************************************************
4471 * SmallBlockChainStream_GetNextBlockInChain
4473 * Returns the index of the next small block in this chain.
4475 * Return Values:
4476 * - BLOCK_END_OF_CHAIN: end of this chain
4477 * - BLOCK_UNUSED: small block 'blockIndex' is free
4479 ULONG SmallBlockChainStream_GetNextBlockInChain(
4480 SmallBlockChainStream* This,
4481 ULONG blockIndex)
4483 ULARGE_INTEGER offsetOfBlockInDepot;
4484 DWORD buffer;
4485 ULONG nextBlockInChain = BLOCK_END_OF_CHAIN;
4486 ULONG bytesRead;
4487 BOOL success;
4489 offsetOfBlockInDepot.HighPart = 0;
4490 offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
4493 * Read those bytes in the buffer from the small block file.
4495 success = BlockChainStream_ReadAt(
4496 This->parentStorage->smallBlockDepotChain,
4497 offsetOfBlockInDepot,
4498 sizeof(DWORD),
4499 &buffer,
4500 &bytesRead);
4502 if (success)
4504 StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4507 return nextBlockInChain;
4510 /******************************************************************************
4511 * SmallBlockChainStream_SetNextBlockInChain
4513 * Writes the index of the next block of the specified block in the small
4514 * block depot.
4515 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4516 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4518 void SmallBlockChainStream_SetNextBlockInChain(
4519 SmallBlockChainStream* This,
4520 ULONG blockIndex,
4521 ULONG nextBlock)
4523 ULARGE_INTEGER offsetOfBlockInDepot;
4524 DWORD buffer;
4525 ULONG bytesWritten;
4527 offsetOfBlockInDepot.HighPart = 0;
4528 offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
4530 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4533 * Read those bytes in the buffer from the small block file.
4535 BlockChainStream_WriteAt(
4536 This->parentStorage->smallBlockDepotChain,
4537 offsetOfBlockInDepot,
4538 sizeof(DWORD),
4539 &buffer,
4540 &bytesWritten);
4543 /******************************************************************************
4544 * SmallBlockChainStream_FreeBlock
4546 * Flag small block 'blockIndex' as free in the small block depot.
4548 void SmallBlockChainStream_FreeBlock(
4549 SmallBlockChainStream* This,
4550 ULONG blockIndex)
4552 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4555 /******************************************************************************
4556 * SmallBlockChainStream_GetNextFreeBlock
4558 * Returns the index of a free small block. The small block depot will be
4559 * enlarged if necessary. The small block chain will also be enlarged if
4560 * necessary.
4562 ULONG SmallBlockChainStream_GetNextFreeBlock(
4563 SmallBlockChainStream* This)
4565 ULARGE_INTEGER offsetOfBlockInDepot;
4566 DWORD buffer;
4567 ULONG bytesRead;
4568 ULONG blockIndex = 0;
4569 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4570 BOOL success = TRUE;
4571 ULONG smallBlocksPerBigBlock;
4573 offsetOfBlockInDepot.HighPart = 0;
4576 * Scan the small block depot for a free block
4578 while (nextBlockIndex != BLOCK_UNUSED)
4580 offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
4582 success = BlockChainStream_ReadAt(
4583 This->parentStorage->smallBlockDepotChain,
4584 offsetOfBlockInDepot,
4585 sizeof(DWORD),
4586 &buffer,
4587 &bytesRead);
4590 * If we run out of space for the small block depot, enlarge it
4592 if (success)
4594 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4596 if (nextBlockIndex != BLOCK_UNUSED)
4597 blockIndex++;
4599 else
4601 ULONG count =
4602 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4604 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4605 ULONG nextBlock, newsbdIndex;
4606 BYTE* smallBlockDepot;
4608 nextBlock = sbdIndex;
4609 while (nextBlock != BLOCK_END_OF_CHAIN)
4611 sbdIndex = nextBlock;
4612 nextBlock =
4613 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4616 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4617 if (sbdIndex != BLOCK_END_OF_CHAIN)
4618 StorageImpl_SetNextBlockInChain(
4619 This->parentStorage,
4620 sbdIndex,
4621 newsbdIndex);
4623 StorageImpl_SetNextBlockInChain(
4624 This->parentStorage,
4625 newsbdIndex,
4626 BLOCK_END_OF_CHAIN);
4629 * Initialize all the small blocks to free
4631 smallBlockDepot =
4632 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4634 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4635 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4637 if (count == 0)
4640 * We have just created the small block depot.
4642 StgProperty rootProp;
4643 ULONG sbStartIndex;
4646 * Save it in the header
4648 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4649 StorageImpl_SaveFileHeader(This->parentStorage);
4652 * And allocate the first big block that will contain small blocks
4654 sbStartIndex =
4655 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4657 StorageImpl_SetNextBlockInChain(
4658 This->parentStorage,
4659 sbStartIndex,
4660 BLOCK_END_OF_CHAIN);
4662 StorageImpl_ReadProperty(
4663 This->parentStorage,
4664 This->parentStorage->rootPropertySetIndex,
4665 &rootProp);
4667 rootProp.startingBlock = sbStartIndex;
4668 rootProp.size.HighPart = 0;
4669 rootProp.size.LowPart = This->parentStorage->bigBlockSize;
4671 StorageImpl_WriteProperty(
4672 This->parentStorage,
4673 This->parentStorage->rootPropertySetIndex,
4674 &rootProp);
4679 smallBlocksPerBigBlock =
4680 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4683 * Verify if we have to allocate big blocks to contain small blocks
4685 if (blockIndex % smallBlocksPerBigBlock == 0)
4687 StgProperty rootProp;
4688 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4690 StorageImpl_ReadProperty(
4691 This->parentStorage,
4692 This->parentStorage->rootPropertySetIndex,
4693 &rootProp);
4695 if (rootProp.size.LowPart <
4696 (blocksRequired * This->parentStorage->bigBlockSize))
4698 rootProp.size.LowPart += This->parentStorage->bigBlockSize;
4700 BlockChainStream_SetSize(
4701 This->parentStorage->smallBlockRootChain,
4702 rootProp.size);
4704 StorageImpl_WriteProperty(
4705 This->parentStorage,
4706 This->parentStorage->rootPropertySetIndex,
4707 &rootProp);
4711 return blockIndex;
4714 /******************************************************************************
4715 * SmallBlockChainStream_ReadAt
4717 * Reads a specified number of bytes from this chain at the specified offset.
4718 * bytesRead may be NULL.
4719 * Failure will be returned if the specified number of bytes has not been read.
4721 BOOL SmallBlockChainStream_ReadAt(
4722 SmallBlockChainStream* This,
4723 ULARGE_INTEGER offset,
4724 ULONG size,
4725 void* buffer,
4726 ULONG* bytesRead)
4728 ULARGE_INTEGER offsetInBigBlockFile;
4729 ULONG blockNoInSequence =
4730 offset.LowPart / This->parentStorage->smallBlockSize;
4732 ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize;
4733 ULONG bytesToReadInBuffer;
4734 ULONG blockIndex;
4735 ULONG bytesReadFromBigBlockFile;
4736 BYTE* bufferWalker;
4739 * This should never happen on a small block file.
4741 assert(offset.HighPart==0);
4744 * Find the first block in the stream that contains part of the buffer.
4746 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4748 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4750 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4752 blockNoInSequence--;
4756 * Start reading the buffer.
4758 *bytesRead = 0;
4759 bufferWalker = buffer;
4761 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4764 * Calculate how many bytes we can copy from this small block.
4766 bytesToReadInBuffer =
4767 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4770 * Calculate the offset of the small block in the small block file.
4772 offsetInBigBlockFile.HighPart = 0;
4773 offsetInBigBlockFile.LowPart =
4774 blockIndex * This->parentStorage->smallBlockSize;
4776 offsetInBigBlockFile.LowPart += offsetInBlock;
4779 * Read those bytes in the buffer from the small block file.
4781 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
4782 offsetInBigBlockFile,
4783 bytesToReadInBuffer,
4784 bufferWalker,
4785 &bytesReadFromBigBlockFile);
4787 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
4790 * Step to the next big block.
4792 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4793 bufferWalker += bytesToReadInBuffer;
4794 size -= bytesToReadInBuffer;
4795 *bytesRead += bytesToReadInBuffer;
4796 offsetInBlock = 0; /* There is no offset on the next block */
4799 return (size == 0);
4802 /******************************************************************************
4803 * SmallBlockChainStream_WriteAt
4805 * Writes the specified number of bytes to this chain at the specified offset.
4806 * bytesWritten may be NULL.
4807 * Will fail if not all specified number of bytes have been written.
4809 BOOL SmallBlockChainStream_WriteAt(
4810 SmallBlockChainStream* This,
4811 ULARGE_INTEGER offset,
4812 ULONG size,
4813 const void* buffer,
4814 ULONG* bytesWritten)
4816 ULARGE_INTEGER offsetInBigBlockFile;
4817 ULONG blockNoInSequence =
4818 offset.LowPart / This->parentStorage->smallBlockSize;
4820 ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize;
4821 ULONG bytesToWriteInBuffer;
4822 ULONG blockIndex;
4823 ULONG bytesWrittenFromBigBlockFile;
4824 BYTE* bufferWalker;
4827 * This should never happen on a small block file.
4829 assert(offset.HighPart==0);
4832 * Find the first block in the stream that contains part of the buffer.
4834 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4836 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4838 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4840 blockNoInSequence--;
4844 * Start writing the buffer.
4846 * Here, I'm casting away the constness on the buffer variable
4847 * This is OK since we don't intend to modify that buffer.
4849 *bytesWritten = 0;
4850 bufferWalker = (BYTE*)buffer;
4851 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4854 * Calculate how many bytes we can copy to this small block.
4856 bytesToWriteInBuffer =
4857 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4860 * Calculate the offset of the small block in the small block file.
4862 offsetInBigBlockFile.HighPart = 0;
4863 offsetInBigBlockFile.LowPart =
4864 blockIndex * This->parentStorage->smallBlockSize;
4866 offsetInBigBlockFile.LowPart += offsetInBlock;
4869 * Write those bytes in the buffer to the small block file.
4871 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
4872 offsetInBigBlockFile,
4873 bytesToWriteInBuffer,
4874 bufferWalker,
4875 &bytesWrittenFromBigBlockFile);
4877 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
4880 * Step to the next big block.
4882 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4883 bufferWalker += bytesToWriteInBuffer;
4884 size -= bytesToWriteInBuffer;
4885 *bytesWritten += bytesToWriteInBuffer;
4886 offsetInBlock = 0; /* There is no offset on the next block */
4889 return (size == 0);
4892 /******************************************************************************
4893 * SmallBlockChainStream_Shrink
4895 * Shrinks this chain in the small block depot.
4897 BOOL SmallBlockChainStream_Shrink(
4898 SmallBlockChainStream* This,
4899 ULARGE_INTEGER newSize)
4901 ULONG blockIndex, extraBlock;
4902 ULONG numBlocks;
4903 ULONG count = 1;
4905 numBlocks = newSize.LowPart / This->parentStorage->smallBlockSize;
4907 if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0)
4908 numBlocks++;
4910 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4913 * Go to the new end of chain
4915 while (count < numBlocks)
4917 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4918 count++;
4921 /* Get the next block before marking the new end */
4922 extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4924 /* Mark the new end of chain */
4925 SmallBlockChainStream_SetNextBlockInChain(
4926 This,
4927 blockIndex,
4928 BLOCK_END_OF_CHAIN);
4931 * Mark the extra blocks as free
4933 while (extraBlock != BLOCK_END_OF_CHAIN)
4935 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
4936 SmallBlockChainStream_FreeBlock(This, extraBlock);
4937 extraBlock = blockIndex;
4940 return TRUE;
4943 /******************************************************************************
4944 * SmallBlockChainStream_Enlarge
4946 * Grows this chain in the small block depot.
4948 BOOL SmallBlockChainStream_Enlarge(
4949 SmallBlockChainStream* This,
4950 ULARGE_INTEGER newSize)
4952 ULONG blockIndex, currentBlock;
4953 ULONG newNumBlocks;
4954 ULONG oldNumBlocks = 0;
4956 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4959 * Empty chain
4961 if (blockIndex == BLOCK_END_OF_CHAIN)
4963 StgProperty chainProp;
4965 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
4966 &chainProp);
4968 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
4970 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
4971 &chainProp);
4973 blockIndex = chainProp.startingBlock;
4974 SmallBlockChainStream_SetNextBlockInChain(
4975 This,
4976 blockIndex,
4977 BLOCK_END_OF_CHAIN);
4980 currentBlock = blockIndex;
4983 * Figure out how many blocks are needed to contain this stream
4985 newNumBlocks = newSize.LowPart / This->parentStorage->smallBlockSize;
4987 if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0)
4988 newNumBlocks++;
4991 * Go to the current end of chain
4993 while (blockIndex != BLOCK_END_OF_CHAIN)
4995 oldNumBlocks++;
4996 currentBlock = blockIndex;
4997 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5001 * Add new blocks to the chain
5003 while (oldNumBlocks < newNumBlocks)
5005 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5006 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5008 SmallBlockChainStream_SetNextBlockInChain(
5009 This,
5010 blockIndex,
5011 BLOCK_END_OF_CHAIN);
5013 currentBlock = blockIndex;
5014 oldNumBlocks++;
5017 return TRUE;
5020 /******************************************************************************
5021 * SmallBlockChainStream_GetCount
5023 * Returns the number of blocks that comprises this chain.
5024 * This is not the size of this chain as the last block may not be full!
5026 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5028 ULONG blockIndex;
5029 ULONG count = 0;
5031 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5033 while (blockIndex != BLOCK_END_OF_CHAIN)
5035 count++;
5037 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5040 return count;
5043 /******************************************************************************
5044 * SmallBlockChainStream_SetSize
5046 * Sets the size of this stream.
5047 * The file will grow if we grow the chain.
5049 * TODO: Free the actual blocks in the file when we shrink the chain.
5050 * Currently, the blocks are still in the file. So the file size
5051 * doesn't shrink even if we shrink streams.
5053 BOOL SmallBlockChainStream_SetSize(
5054 SmallBlockChainStream* This,
5055 ULARGE_INTEGER newSize)
5057 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5059 if (newSize.LowPart == size.LowPart)
5060 return TRUE;
5062 if (newSize.LowPart < size.LowPart)
5064 SmallBlockChainStream_Shrink(This, newSize);
5066 else
5068 SmallBlockChainStream_Enlarge(This, newSize);
5071 return TRUE;
5074 /******************************************************************************
5075 * SmallBlockChainStream_GetSize
5077 * Returns the size of this chain.
5079 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5081 StgProperty chainProperty;
5083 StorageImpl_ReadProperty(
5084 This->parentStorage,
5085 This->ownerPropertyIndex,
5086 &chainProperty);
5088 return chainProperty.size;
5091 /******************************************************************************
5092 * StgCreateDocfile32 [OLE32.144]
5094 HRESULT WINAPI StgCreateDocfile(
5095 LPCOLESTR pwcsName,
5096 DWORD grfMode,
5097 DWORD reserved,
5098 IStorage **ppstgOpen)
5100 StorageImpl* newStorage = 0;
5101 HANDLE hFile = INVALID_HANDLE_VALUE;
5102 HRESULT hr = S_OK;
5103 DWORD shareMode;
5104 DWORD accessMode;
5105 DWORD creationMode;
5106 DWORD fileAttributes;
5107 WCHAR tempFileName[MAX_PATH];
5110 * Validate the parameters
5112 if (ppstgOpen == 0)
5113 return STG_E_INVALIDPOINTER;
5116 * Validate the STGM flags
5118 if ( FAILED( validateSTGM(grfMode) ))
5119 return STG_E_INVALIDFLAG;
5122 * Generate a unique name.
5124 if (pwcsName == 0)
5126 WCHAR tempPath[MAX_PATH];
5127 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5129 memset(tempPath, 0, sizeof(tempPath));
5130 memset(tempFileName, 0, sizeof(tempFileName));
5132 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5133 tempPath[0] = '.';
5135 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5136 pwcsName = tempFileName;
5137 else
5138 return STG_E_INSUFFICIENTMEMORY;
5142 * Interpret the STGM value grfMode
5144 shareMode = GetShareModeFromSTGM(grfMode);
5145 accessMode = GetAccessModeFromSTGM(grfMode);
5146 creationMode = GetCreationModeFromSTGM(grfMode);
5148 if (grfMode & STGM_DELETEONRELEASE)
5149 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5150 else
5151 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5153 if (grfMode & STGM_TRANSACTED)
5154 FIXME(ole, "Transacted mode not implemented.\n");
5157 * Initialize the "out" parameter.
5159 *ppstgOpen = 0;
5161 hFile = CreateFileW(pwcsName,
5162 accessMode,
5163 shareMode,
5164 NULL,
5165 creationMode,
5166 fileAttributes,
5169 if (hFile == INVALID_HANDLE_VALUE)
5171 return E_FAIL;
5175 * Allocate and initialize the new IStorage32object.
5177 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5179 if (newStorage == 0)
5180 return STG_E_INSUFFICIENTMEMORY;
5182 hr = StorageImpl_Construct(
5183 newStorage,
5184 hFile,
5185 grfMode);
5187 if (FAILED(hr))
5189 HeapFree(GetProcessHeap(), 0, newStorage);
5190 return hr;
5194 * Get an "out" pointer for the caller.
5196 hr = StorageBaseImpl_QueryInterface(
5197 (IStorage*)newStorage,
5198 (REFIID)&IID_IStorage,
5199 (void**)ppstgOpen);
5201 return hr;
5204 /******************************************************************************
5205 * StgOpenStorage32 [OLE32.148]
5207 HRESULT WINAPI StgOpenStorage(
5208 const OLECHAR *pwcsName,
5209 IStorage *pstgPriority,
5210 DWORD grfMode,
5211 SNB snbExclude,
5212 DWORD reserved,
5213 IStorage **ppstgOpen)
5215 StorageImpl* newStorage = 0;
5216 HRESULT hr = S_OK;
5217 HANDLE hFile = 0;
5218 DWORD shareMode;
5219 DWORD accessMode;
5222 * Perform a sanity check
5224 if (( pwcsName == 0) || (ppstgOpen == 0) )
5225 return STG_E_INVALIDPOINTER;
5228 * Validate the STGM flags
5230 if ( FAILED( validateSTGM(grfMode) ))
5231 return STG_E_INVALIDFLAG;
5234 * Interpret the STGM value grfMode
5236 shareMode = GetShareModeFromSTGM(grfMode);
5237 accessMode = GetAccessModeFromSTGM(grfMode);
5240 * Initialize the "out" parameter.
5242 *ppstgOpen = 0;
5244 hFile = CreateFileW( pwcsName,
5245 accessMode,
5246 shareMode,
5247 NULL,
5248 OPEN_EXISTING,
5249 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5253 if (hFile==INVALID_HANDLE_VALUE)
5255 return E_FAIL;
5259 * Allocate and initialize the new IStorage32object.
5261 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5263 if (newStorage == 0)
5264 return STG_E_INSUFFICIENTMEMORY;
5266 hr = StorageImpl_Construct(
5267 newStorage,
5268 hFile,
5269 grfMode);
5271 if (FAILED(hr))
5273 HeapFree(GetProcessHeap(), 0, newStorage);
5274 return hr;
5278 * Get an "out" pointer for the caller.
5280 hr = StorageBaseImpl_QueryInterface(
5281 (IStorage*)newStorage,
5282 (REFIID)&IID_IStorage,
5283 (void**)ppstgOpen);
5285 return hr;
5288 /******************************************************************************
5289 * WriteClassStg32 [OLE32.148]
5291 * This method will store the specified CLSID in the specified storage object
5293 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5295 HRESULT hRes;
5297 assert(pStg != 0);
5299 hRes = IStorage_SetClass(pStg, rclsid);
5301 return hRes;
5304 /*******************************************************************************************
5305 * ReadClassStg
5307 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5309 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5311 STATSTG pstatstg;
5312 HRESULT hRes;
5314 TRACE(ole,"()\n");
5316 if(pclsid==NULL)
5317 return E_POINTER;
5319 * read a STATSTG structure (contains the clsid) from the storage
5321 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5323 if(SUCCEEDED(hRes))
5324 *pclsid=pstatstg.clsid;
5326 return hRes;
5329 /*************************************************************************************
5330 * OleLoadFromStream
5332 * This function loads an object from stream
5334 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5336 CLSID clsid;
5337 HRESULT res;
5339 FIXME(ole,"(),stub!\n");
5341 res=ReadClassStm(pStm,&clsid);
5343 if (SUCCEEDED(res)){
5345 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5347 if (SUCCEEDED(res))
5349 res=IPersistStream_Load((IPersistStream*)ppvObj,pStm);
5352 return res;
5355 /************************************************************************************************
5356 * OleSaveToStream
5358 * This function saves an object with the IPersistStream interface on it to the specified stream
5360 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5363 CLSID clsid;
5364 HRESULT res;
5366 TRACE(ole,"(%p,%p)\n",pPStm,pStm);
5368 res=IPersistStream_GetClassID(pPStm,&clsid);
5370 if (SUCCEEDED(res)){
5372 res=WriteClassStm(pStm,&clsid);
5374 if (SUCCEEDED(res))
5376 res=IPersistStream_Save(pPStm,pStm,FALSE);
5379 return res;
5382 /****************************************************************************
5383 * This method validate a STGM parameter that can contain the values below
5385 * STGM_DIRECT 0x00000000
5386 * STGM_TRANSACTED 0x00010000
5387 * STGM_SIMPLE 0x08000000
5389 * STGM_READ 0x00000000
5390 * STGM_WRITE 0x00000001
5391 * STGM_READWRITE 0x00000002
5393 * STGM_SHARE_DENY_NONE 0x00000040
5394 * STGM_SHARE_DENY_READ 0x00000030
5395 * STGM_SHARE_DENY_WRITE 0x00000020
5396 * STGM_SHARE_EXCLUSIVE 0x00000010
5398 * STGM_PRIORITY 0x00040000
5399 * STGM_DELETEONRELEASE 0x04000000
5401 * STGM_CREATE 0x00001000
5402 * STGM_CONVERT 0x00020000
5403 * STGM_FAILIFTHERE 0x00000000
5405 * STGM_NOSCRATCH 0x00100000
5406 * STGM_NOSNAPSHOT 0x00200000
5408 static HRESULT validateSTGM(DWORD stgm)
5410 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5411 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5412 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5414 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5415 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5416 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5418 BOOL bSTGM_SHARE_DENY_NONE =
5419 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5421 BOOL bSTGM_SHARE_DENY_READ =
5422 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5424 BOOL bSTGM_SHARE_DENY_WRITE =
5425 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5427 BOOL bSTGM_SHARE_EXCLUSIVE =
5428 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5430 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5431 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5433 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5434 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5437 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5439 if ( ! bSTGM_DIRECT )
5440 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5441 return E_FAIL;
5444 * STGM_WRITE | STGM_READWRITE | STGM_READ
5446 if ( ! bSTGM_READ )
5447 if( bSTGM_WRITE && bSTGM_READWRITE )
5448 return E_FAIL;
5451 * STGM_SHARE_DENY_NONE | others
5452 * (I assume here that DENY_READ implies DENY_WRITE)
5454 if ( bSTGM_SHARE_DENY_NONE )
5455 if ( bSTGM_SHARE_DENY_READ ||
5456 bSTGM_SHARE_DENY_WRITE ||
5457 bSTGM_SHARE_EXCLUSIVE)
5458 return E_FAIL;
5461 * STGM_CREATE | STGM_CONVERT
5462 * if both are false, STGM_FAILIFTHERE is set to TRUE
5464 if ( bSTGM_CREATE && bSTGM_CONVERT )
5465 return E_FAIL;
5468 * STGM_NOSCRATCH requires STGM_TRANSACTED
5470 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5471 return E_FAIL;
5474 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5475 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5477 if (bSTGM_NOSNAPSHOT)
5479 if ( ! ( bSTGM_TRANSACTED &&
5480 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5481 return E_FAIL;
5484 return S_OK;
5487 /****************************************************************************
5488 * GetShareModeFromSTGM
5490 * This method will return a share mode flag from a STGM value.
5491 * The STGM value is assumed valid.
5493 static DWORD GetShareModeFromSTGM(DWORD stgm)
5495 DWORD dwShareMode = 0;
5496 BOOL bSTGM_SHARE_DENY_NONE =
5497 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5499 BOOL bSTGM_SHARE_DENY_READ =
5500 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5502 BOOL bSTGM_SHARE_DENY_WRITE =
5503 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5505 BOOL bSTGM_SHARE_EXCLUSIVE =
5506 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5508 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5509 dwShareMode = 0;
5511 if (bSTGM_SHARE_DENY_NONE)
5512 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5514 if (bSTGM_SHARE_DENY_WRITE)
5515 dwShareMode = FILE_SHARE_READ;
5517 return dwShareMode;
5520 /****************************************************************************
5521 * GetAccessModeFromSTGM
5523 * This method will return an access mode flag from a STGM value.
5524 * The STGM value is assumed valid.
5526 static DWORD GetAccessModeFromSTGM(DWORD stgm)
5528 DWORD dwDesiredAccess = GENERIC_READ;
5529 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5530 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5531 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5533 if (bSTGM_READ)
5534 dwDesiredAccess = GENERIC_READ;
5536 if (bSTGM_WRITE)
5537 dwDesiredAccess |= GENERIC_WRITE;
5539 if (bSTGM_READWRITE)
5540 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5542 return dwDesiredAccess;
5545 /****************************************************************************
5546 * GetCreationModeFromSTGM
5548 * This method will return a creation mode flag from a STGM value.
5549 * The STGM value is assumed valid.
5551 static DWORD GetCreationModeFromSTGM(DWORD stgm)
5553 if ( stgm & STGM_CREATE)
5554 return CREATE_ALWAYS;
5555 if (stgm & STGM_CONVERT) {
5556 FIXME(ole, "STGM_CONVERT not implemented!\n");
5557 return CREATE_NEW;
5559 /* All other cases */
5560 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5561 FIXME(ole,"unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
5562 return CREATE_NEW;