New debug scheme with explicit debug channels declaration.
[wine.git] / ole / storage32.c
blob3b585dfef11d99df94f2a33b1eae37d03b4ae053
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 DEFAULT_DEBUG_CHANNEL(ole)
32 #define FILE_BEGIN 0
34 static const char rootPropertyName[] = "Root Entry";
36 /***********************************************************************
37 * Forward declaration of internal functions used by the method DestroyElement
39 static HRESULT deleteStorageProperty(
40 StorageImpl *parentStorage,
41 OLECHAR *propertyToDeleteName);
43 static HRESULT deleteStreamProperty(
44 StorageImpl *parentStorage,
45 ULONG foundPropertyIndexToDelete,
46 StgProperty propertyToDelete);
48 static HRESULT findPlaceholder(
49 StorageImpl *storage,
50 ULONG propertyIndexToStore,
51 ULONG storagePropertyIndex,
52 INT typeOfRelation);
54 static HRESULT adjustPropertyChain(
55 StorageImpl *This,
56 StgProperty propertyToDelete,
57 StgProperty parentProperty,
58 ULONG parentPropertyId,
59 INT typeOfRelation);
61 /***********************************************************************
62 * Declaration of the functions used to manipulate StgProperty
65 static ULONG getFreeProperty(
66 StorageImpl *storage);
68 static void updatePropertyChain(
69 StorageImpl *storage,
70 ULONG newPropertyIndex,
71 StgProperty newProperty);
73 static LONG propertyNameCmp(
74 OLECHAR *newProperty,
75 OLECHAR *currentProperty);
78 /***********************************************************************
79 * Declaration of miscellaneous functions...
81 static HRESULT validateSTGM(DWORD stgmValue);
83 static DWORD GetShareModeFromSTGM(DWORD stgm);
84 static DWORD GetAccessModeFromSTGM(DWORD stgm);
85 static DWORD GetCreationModeFromSTGM(DWORD stgm);
88 * Virtual function table for the IStorage32Impl class.
90 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
92 StorageBaseImpl_QueryInterface,
93 StorageBaseImpl_AddRef,
94 StorageBaseImpl_Release,
95 StorageBaseImpl_CreateStream,
96 StorageBaseImpl_OpenStream,
97 StorageImpl_CreateStorage,
98 StorageBaseImpl_OpenStorage,
99 StorageImpl_CopyTo,
100 StorageImpl_MoveElementTo,
101 StorageImpl_Commit,
102 StorageImpl_Revert,
103 StorageBaseImpl_EnumElements,
104 StorageImpl_DestroyElement,
105 StorageBaseImpl_RenameElement,
106 StorageImpl_SetElementTimes,
107 StorageBaseImpl_SetClass,
108 StorageImpl_SetStateBits,
109 StorageBaseImpl_Stat
113 * Virtual function table for the Storage32InternalImpl class.
115 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
117 StorageBaseImpl_QueryInterface,
118 StorageBaseImpl_AddRef,
119 StorageBaseImpl_Release,
120 StorageBaseImpl_CreateStream,
121 StorageBaseImpl_OpenStream,
122 StorageImpl_CreateStorage,
123 StorageBaseImpl_OpenStorage,
124 StorageImpl_CopyTo,
125 StorageImpl_MoveElementTo,
126 StorageInternalImpl_Commit,
127 StorageInternalImpl_Revert,
128 StorageBaseImpl_EnumElements,
129 StorageImpl_DestroyElement,
130 StorageBaseImpl_RenameElement,
131 StorageImpl_SetElementTimes,
132 StorageBaseImpl_SetClass,
133 StorageImpl_SetStateBits,
134 StorageBaseImpl_Stat
138 * Virtual function table for the IEnumSTATSTGImpl class.
140 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
142 IEnumSTATSTGImpl_QueryInterface,
143 IEnumSTATSTGImpl_AddRef,
144 IEnumSTATSTGImpl_Release,
145 IEnumSTATSTGImpl_Next,
146 IEnumSTATSTGImpl_Skip,
147 IEnumSTATSTGImpl_Reset,
148 IEnumSTATSTGImpl_Clone
155 /************************************************************************
156 ** Storage32BaseImpl implementatiion
159 /************************************************************************
160 * Storage32BaseImpl_QueryInterface (IUnknown)
162 * This method implements the common QueryInterface for all IStorage32
163 * implementations contained in this file.
165 * See Windows documentation for more details on IUnknown methods.
167 HRESULT WINAPI StorageBaseImpl_QueryInterface(
168 IStorage* iface,
169 REFIID riid,
170 void** ppvObject)
172 ICOM_THIS(StorageBaseImpl,iface);
174 * Perform a sanity check on the parameters.
176 if ( (This==0) || (ppvObject==0) )
177 return E_INVALIDARG;
180 * Initialize the return parameter.
182 *ppvObject = 0;
185 * Compare the riid with the interface IDs implemented by this object.
187 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
189 *ppvObject = (IStorage*)This;
191 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
193 *ppvObject = (IStorage*)This;
197 * Check that we obtained an interface.
199 if ((*ppvObject)==0)
200 return E_NOINTERFACE;
203 * Query Interface always increases the reference count by one when it is
204 * successful
206 StorageBaseImpl_AddRef(iface);
208 return S_OK;
211 /************************************************************************
212 * Storage32BaseImpl_AddRef (IUnknown)
214 * This method implements the common AddRef for all IStorage32
215 * implementations contained in this file.
217 * See Windows documentation for more details on IUnknown methods.
219 ULONG WINAPI StorageBaseImpl_AddRef(
220 IStorage* iface)
222 ICOM_THIS(StorageBaseImpl,iface);
223 This->ref++;
225 return This->ref;
228 /************************************************************************
229 * Storage32BaseImpl_Release (IUnknown)
231 * This method implements the common Release for all IStorage32
232 * implementations contained in this file.
234 * See Windows documentation for more details on IUnknown methods.
236 ULONG WINAPI StorageBaseImpl_Release(
237 IStorage* iface)
239 ICOM_THIS(StorageBaseImpl,iface);
241 * Decrease the reference count on this object.
243 This->ref--;
246 * If the reference count goes down to 0, perform suicide.
248 if (This->ref==0)
251 * Since we are using a system of base-classes, we want to call the
252 * destructor of the appropriate derived class. To do this, we are
253 * using virtual functions to implement the destructor.
255 This->v_destructor(This);
257 return 0;
260 return This->ref;
263 /************************************************************************
264 * Storage32BaseImpl_OpenStream (IStorage)
266 * This method will open the specified stream object from the current storage.
268 * See Windows documentation for more details on IStorage methods.
270 HRESULT WINAPI StorageBaseImpl_OpenStream(
271 IStorage* iface,
272 const OLECHAR* pwcsName, /* [string][in] */
273 void* reserved1, /* [unique][in] */
274 DWORD grfMode, /* [in] */
275 DWORD reserved2, /* [in] */
276 IStream** ppstm) /* [out] */
278 ICOM_THIS(StorageBaseImpl,iface);
279 IEnumSTATSTGImpl* propertyEnumeration;
280 StgStreamImpl* newStream;
281 StgProperty currentProperty;
282 ULONG foundPropertyIndex;
285 * Perform a sanity check on the parameters.
287 if ( (pwcsName==NULL) || (ppstm==0) )
288 return E_INVALIDARG;
291 * Initialize the out parameter
293 *ppstm = 0;
296 * Validate the STGM flags
298 if ( FAILED( validateSTGM(grfMode) ))
299 return STG_E_INVALIDFLAG;
302 * As documented.
304 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
305 (grfMode & STGM_DELETEONRELEASE) ||
306 (grfMode & STGM_TRANSACTED) )
307 return STG_E_INVALIDFUNCTION;
310 * Create a property enumeration to search the properties
312 propertyEnumeration = IEnumSTATSTGImpl_Construct(
313 This->ancestorStorage,
314 This->rootPropertySetIndex);
317 * Search the enumeration for the property with the given name
319 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
320 propertyEnumeration,
321 pwcsName,
322 &currentProperty);
325 * Delete the property enumeration since we don't need it anymore
327 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
330 * If it was found, construct the stream object and return a pointer to it.
332 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
333 (currentProperty.propertyType==PROPTYPE_STREAM) )
335 newStream = StgStreamImpl_Construct(This, foundPropertyIndex);
337 if (newStream!=0)
339 *ppstm = (IStream*)newStream;
342 * Since we are returning a pointer to the interface, we have to
343 * nail down the reference.
345 StgStreamImpl_AddRef(*ppstm);
347 return S_OK;
350 return E_OUTOFMEMORY;
353 return STG_E_FILENOTFOUND;
356 /************************************************************************
357 * Storage32BaseImpl_OpenStorage (IStorage)
359 * This method will open a new storage object from the current storage.
361 * See Windows documentation for more details on IStorage methods.
363 HRESULT WINAPI StorageBaseImpl_OpenStorage(
364 IStorage* iface,
365 const OLECHAR* pwcsName, /* [string][unique][in] */
366 IStorage* pstgPriority, /* [unique][in] */
367 DWORD grfMode, /* [in] */
368 SNB snbExclude, /* [unique][in] */
369 DWORD reserved, /* [in] */
370 IStorage** ppstg) /* [out] */
372 ICOM_THIS(StorageBaseImpl,iface);
373 StorageInternalImpl* newStorage;
374 IEnumSTATSTGImpl* propertyEnumeration;
375 StgProperty currentProperty;
376 ULONG foundPropertyIndex;
379 * Perform a sanity check on the parameters.
381 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
382 return E_INVALIDARG;
385 * Validate the STGM flags
387 if ( FAILED( validateSTGM(grfMode) ))
388 return STG_E_INVALIDFLAG;
391 * As documented.
393 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
394 (grfMode & STGM_DELETEONRELEASE) ||
395 (grfMode & STGM_PRIORITY) )
396 return STG_E_INVALIDFUNCTION;
399 * Initialize the out parameter
401 *ppstg = 0;
404 * Create a property enumeration to search the properties
406 propertyEnumeration = IEnumSTATSTGImpl_Construct(
407 This->ancestorStorage,
408 This->rootPropertySetIndex);
411 * Search the enumeration for the property with the given name
413 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
414 propertyEnumeration,
415 pwcsName,
416 &currentProperty);
419 * Delete the property enumeration since we don't need it anymore
421 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
424 * If it was found, construct the stream object and return a pointer to it.
426 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
427 (currentProperty.propertyType==PROPTYPE_STORAGE) )
430 * Construct a new Storage object
432 newStorage = StorageInternalImpl_Construct(
433 This->ancestorStorage,
434 foundPropertyIndex);
436 if (newStorage != 0)
438 *ppstg = (IStorage*)newStorage;
441 * Since we are returning a pointer to the interface,
442 * we have to nail down the reference.
444 StorageBaseImpl_AddRef(*ppstg);
446 return S_OK;
449 return STG_E_INSUFFICIENTMEMORY;
452 return STG_E_FILENOTFOUND;
455 /************************************************************************
456 * Storage32BaseImpl_EnumElements (IStorage)
458 * This method will create an enumerator object that can be used to
459 * retrieve informatino about all the properties in the storage object.
461 * See Windows documentation for more details on IStorage methods.
463 HRESULT WINAPI StorageBaseImpl_EnumElements(
464 IStorage* iface,
465 DWORD reserved1, /* [in] */
466 void* reserved2, /* [size_is][unique][in] */
467 DWORD reserved3, /* [in] */
468 IEnumSTATSTG** ppenum) /* [out] */
470 ICOM_THIS(StorageBaseImpl,iface);
471 IEnumSTATSTGImpl* newEnum;
474 * Perform a sanity check on the parameters.
476 if ( (This==0) || (ppenum==0))
477 return E_INVALIDARG;
480 * Construct the enumerator.
482 newEnum = IEnumSTATSTGImpl_Construct(
483 This->ancestorStorage,
484 This->rootPropertySetIndex);
486 if (newEnum!=0)
488 *ppenum = (IEnumSTATSTG*)newEnum;
491 * Don't forget to nail down a reference to the new object before
492 * returning it.
494 IEnumSTATSTGImpl_AddRef(*ppenum);
496 return S_OK;
499 return E_OUTOFMEMORY;
502 /************************************************************************
503 * Storage32BaseImpl_Stat (IStorage)
505 * This method will retrieve information about this storage object.
507 * See Windows documentation for more details on IStorage methods.
509 HRESULT WINAPI StorageBaseImpl_Stat(
510 IStorage* iface,
511 STATSTG* pstatstg, /* [out] */
512 DWORD grfStatFlag) /* [in] */
514 ICOM_THIS(StorageBaseImpl,iface);
515 StgProperty curProperty;
516 BOOL readSucessful;
519 * Perform a sanity check on the parameters.
521 if ( (This==0) || (pstatstg==0))
522 return E_INVALIDARG;
525 * Read the information from the property.
527 readSucessful = StorageImpl_ReadProperty(
528 This->ancestorStorage,
529 This->rootPropertySetIndex,
530 &curProperty);
532 if (readSucessful)
534 StorageUtl_CopyPropertyToSTATSTG(
535 pstatstg,
536 &curProperty,
537 grfStatFlag);
539 return S_OK;
542 return E_FAIL;
545 /************************************************************************
546 * Storage32BaseImpl_RenameElement (IStorage)
548 * This method will rename the specified element.
550 * See Windows documentation for more details on IStorage methods.
552 * Implementation notes: The method used to rename consists of creating a clone
553 * of the deleted StgProperty object setting it with the new name and to
554 * perform a DestroyElement of the old StgProperty.
556 HRESULT WINAPI StorageBaseImpl_RenameElement(
557 IStorage* iface,
558 const OLECHAR* pwcsOldName, /* [in] */
559 const OLECHAR* pwcsNewName) /* [in] */
561 ICOM_THIS(StorageBaseImpl,iface);
562 IEnumSTATSTGImpl* propertyEnumeration;
563 StgProperty currentProperty;
564 ULONG foundPropertyIndex;
567 * Create a property enumeration to search the properties
569 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
570 This->rootPropertySetIndex);
573 * Search the enumeration for the new property name
575 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
576 pwcsNewName,
577 &currentProperty);
579 if (foundPropertyIndex != PROPERTY_NULL)
582 * There is already a property with the new name
584 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
585 return STG_E_FILEALREADYEXISTS;
588 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
591 * Search the enumeration for the old property name
593 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
594 pwcsOldName,
595 &currentProperty);
598 * Delete the property enumeration since we don't need it anymore
600 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
602 if (foundPropertyIndex != PROPERTY_NULL)
604 StgProperty renamedProperty;
605 ULONG renamedPropertyIndex;
608 * Setup a new property for the renamed property
610 renamedProperty.sizeOfNameString =
611 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
613 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
614 return STG_E_INVALIDNAME;
616 lstrcpyW(renamedProperty.name, pwcsNewName);
618 renamedProperty.propertyType = currentProperty.propertyType;
619 renamedProperty.startingBlock = currentProperty.startingBlock;
620 renamedProperty.size.LowPart = currentProperty.size.LowPart;
621 renamedProperty.size.HighPart = currentProperty.size.HighPart;
623 renamedProperty.previousProperty = PROPERTY_NULL;
624 renamedProperty.nextProperty = PROPERTY_NULL;
627 * Bring the dirProperty link in case it is a storage and in which
628 * case the renamed storage elements don't require to be reorganized.
630 renamedProperty.dirProperty = currentProperty.dirProperty;
632 /* call CoFileTime to get the current time
633 renamedProperty.timeStampS1
634 renamedProperty.timeStampD1
635 renamedProperty.timeStampS2
636 renamedProperty.timeStampD2
637 renamedProperty.propertyUniqueID
641 * Obtain a free property in the property chain
643 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
646 * Save the new property into the new property spot
648 StorageImpl_WriteProperty(
649 This->ancestorStorage,
650 renamedPropertyIndex,
651 &renamedProperty);
654 * Find a spot in the property chain for our newly created property.
656 updatePropertyChain(
657 (StorageImpl*)This,
658 renamedPropertyIndex,
659 renamedProperty);
662 * At this point the renamed property has been inserted in the tree,
663 * now, before to Destroy the old property we must zeroed it's dirProperty
664 * otherwise the DestroyProperty below will zap it all and we do not want
665 * this to happen.
666 * Also, we fake that the old property is a storage so the DestroyProperty
667 * will not do a SetSize(0) on the stream data.
669 * This means that we need to tweek the StgProperty if it is a stream or a
670 * non empty storage.
672 currentProperty.dirProperty = PROPERTY_NULL;
673 currentProperty.propertyType = PROPTYPE_STORAGE;
674 StorageImpl_WriteProperty(
675 This->ancestorStorage,
676 foundPropertyIndex,
677 &currentProperty);
680 * Invoke Destroy to get rid of the ole property and automatically redo
681 * the linking of it's previous and next members...
683 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
686 else
689 * There is no property with the old name
691 return STG_E_FILENOTFOUND;
694 return S_OK;
697 /************************************************************************
698 * Storage32BaseImpl_CreateStream (IStorage)
700 * This method will create a stream object within this storage
702 * See Windows documentation for more details on IStorage methods.
704 HRESULT WINAPI StorageBaseImpl_CreateStream(
705 IStorage* iface,
706 const OLECHAR* pwcsName, /* [string][in] */
707 DWORD grfMode, /* [in] */
708 DWORD reserved1, /* [in] */
709 DWORD reserved2, /* [in] */
710 IStream** ppstm) /* [out] */
712 ICOM_THIS(StorageBaseImpl,iface);
713 IEnumSTATSTGImpl* propertyEnumeration;
714 StgStreamImpl* newStream;
715 StgProperty currentProperty, newStreamProperty;
716 ULONG foundPropertyIndex, newPropertyIndex;
719 * Validate parameters
721 if (ppstm == 0)
722 return STG_E_INVALIDPOINTER;
724 if (pwcsName == 0)
725 return STG_E_INVALIDNAME;
728 * Validate the STGM flags
730 if ( FAILED( validateSTGM(grfMode) ))
731 return STG_E_INVALIDFLAG;
734 * As documented.
736 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
737 (grfMode & STGM_DELETEONRELEASE) ||
738 (grfMode & STGM_TRANSACTED) )
739 return STG_E_INVALIDFUNCTION;
742 * Initialize the out parameter
744 *ppstm = 0;
747 * Create a property enumeration to search the properties
749 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
750 This->rootPropertySetIndex);
752 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
753 pwcsName,
754 &currentProperty);
756 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
758 if (foundPropertyIndex != PROPERTY_NULL)
761 * An element with this name already exists
763 if (grfMode & STGM_CREATE)
764 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsName);
765 else
766 return STG_E_FILEALREADYEXISTS;
770 * memset the empty property
772 memset(&newStreamProperty, 0, sizeof(StgProperty));
774 newStreamProperty.sizeOfNameString =
775 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
777 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
778 return STG_E_INVALIDNAME;
780 lstrcpyW(newStreamProperty.name, pwcsName);
782 newStreamProperty.propertyType = PROPTYPE_STREAM;
783 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
784 newStreamProperty.size.LowPart = 0;
785 newStreamProperty.size.HighPart = 0;
787 newStreamProperty.previousProperty = PROPERTY_NULL;
788 newStreamProperty.nextProperty = PROPERTY_NULL;
789 newStreamProperty.dirProperty = PROPERTY_NULL;
791 /* call CoFileTime to get the current time
792 newStreamProperty.timeStampS1
793 newStreamProperty.timeStampD1
794 newStreamProperty.timeStampS2
795 newStreamProperty.timeStampD2
798 /* newStreamProperty.propertyUniqueID */
801 * Get a free property or create a new one
803 newPropertyIndex = getFreeProperty(This->ancestorStorage);
806 * Save the new property into the new property spot
808 StorageImpl_WriteProperty(
809 This->ancestorStorage,
810 newPropertyIndex,
811 &newStreamProperty);
814 * Find a spot in the property chain for our newly created property.
816 updatePropertyChain(
817 (StorageImpl*)This,
818 newPropertyIndex,
819 newStreamProperty);
822 * Open the stream to return it.
824 newStream = StgStreamImpl_Construct(This, newPropertyIndex);
826 if (newStream != 0)
828 *ppstm = (IStream*)newStream;
831 * Since we are returning a pointer to the interface, we have to nail down
832 * the reference.
834 StgStreamImpl_AddRef(*ppstm);
836 else
838 return STG_E_INSUFFICIENTMEMORY;
841 return S_OK;
844 /************************************************************************
845 * Storage32BaseImpl_SetClass (IStorage)
847 * This method will write the specified CLSID in the property of this
848 * storage.
850 * See Windows documentation for more details on IStorage methods.
852 HRESULT WINAPI StorageBaseImpl_SetClass(
853 IStorage* iface,
854 REFCLSID clsid) /* [in] */
856 ICOM_THIS(StorageBaseImpl,iface);
857 HRESULT hRes = E_FAIL;
858 StgProperty curProperty;
859 BOOL success;
861 success = StorageImpl_ReadProperty(This->ancestorStorage,
862 This->rootPropertySetIndex,
863 &curProperty);
864 if (success)
866 curProperty.propertyUniqueID = *clsid;
868 success = StorageImpl_WriteProperty(This->ancestorStorage,
869 This->rootPropertySetIndex,
870 &curProperty);
871 if (success)
872 hRes = S_OK;
875 return hRes;
878 /************************************************************************
879 ** Storage32Impl implementation
882 /************************************************************************
883 * Storage32Impl_CreateStorage (IStorage)
885 * This method will create the storage object within the provided storage.
887 * See Windows documentation for more details on IStorage methods.
889 HRESULT WINAPI StorageImpl_CreateStorage(
890 IStorage* iface,
891 const OLECHAR *pwcsName, /* [string][in] */
892 DWORD grfMode, /* [in] */
893 DWORD reserved1, /* [in] */
894 DWORD reserved2, /* [in] */
895 IStorage **ppstg) /* [out] */
897 StorageImpl* const This=(StorageImpl*)iface;
899 IEnumSTATSTGImpl *propertyEnumeration;
900 StgProperty currentProperty;
901 StgProperty newProperty;
902 ULONG foundPropertyIndex;
903 ULONG newPropertyIndex;
904 HRESULT hr;
908 * Validate parameters
910 if (ppstg == 0)
911 return STG_E_INVALIDPOINTER;
913 if (pwcsName == 0)
914 return STG_E_INVALIDNAME;
917 * Validate the STGM flags
919 if ( FAILED( validateSTGM(grfMode) ) ||
920 (grfMode & STGM_DELETEONRELEASE) )
921 return STG_E_INVALIDFLAG;
924 * Initialize the out parameter
926 *ppstg = 0;
929 * Create a property enumeration and search the properties
931 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
932 This->rootPropertySetIndex);
934 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
935 pwcsName,
936 &currentProperty);
937 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
939 if (foundPropertyIndex != PROPERTY_NULL)
942 * An element with this name already exists
944 if (grfMode & STGM_CREATE)
945 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsName);
946 else
947 return STG_E_FILEALREADYEXISTS;
951 * memset the empty property
953 memset(&newProperty, 0, sizeof(StgProperty));
955 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
957 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
958 return STG_E_INVALIDNAME;
960 lstrcpyW(newProperty.name, pwcsName);
962 newProperty.propertyType = PROPTYPE_STORAGE;
963 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
964 newProperty.size.LowPart = 0;
965 newProperty.size.HighPart = 0;
967 newProperty.previousProperty = PROPERTY_NULL;
968 newProperty.nextProperty = PROPERTY_NULL;
969 newProperty.dirProperty = PROPERTY_NULL;
971 /* call CoFileTime to get the current time
972 newProperty.timeStampS1
973 newProperty.timeStampD1
974 newProperty.timeStampS2
975 newProperty.timeStampD2
978 /* newStorageProperty.propertyUniqueID */
981 * Obtain a free property in the property chain
983 newPropertyIndex = getFreeProperty(This->ancestorStorage);
986 * Save the new property into the new property spot
988 StorageImpl_WriteProperty(
989 This->ancestorStorage,
990 newPropertyIndex,
991 &newProperty);
994 * Find a spot in the property chain for our newly created property.
996 updatePropertyChain(
997 This,
998 newPropertyIndex,
999 newProperty);
1002 * Open it to get a pointer to return.
1004 hr = StorageBaseImpl_OpenStorage(
1005 iface,
1006 (OLECHAR*)pwcsName,
1008 grfMode,
1011 ppstg);
1013 if( (hr != S_OK) || (*ppstg == NULL))
1015 return hr;
1019 return S_OK;
1023 /***************************************************************************
1025 * Internal Method
1027 * Get a free property or create a new one.
1029 static ULONG getFreeProperty(
1030 StorageImpl *storage)
1032 ULONG currentPropertyIndex = 0;
1033 ULONG newPropertyIndex = PROPERTY_NULL;
1034 BOOL readSucessful = TRUE;
1035 StgProperty currentProperty;
1040 * Start by reading the root property
1042 readSucessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1043 currentPropertyIndex,
1044 &currentProperty);
1045 if (readSucessful)
1047 if (currentProperty.sizeOfNameString == 0)
1050 * The property existis and is available, we found it.
1052 newPropertyIndex = currentPropertyIndex;
1055 else
1058 * We exhausted the property list, we will create more space below
1060 newPropertyIndex = currentPropertyIndex;
1062 currentPropertyIndex++;
1064 } while (newPropertyIndex == PROPERTY_NULL);
1067 * grow the property chain
1069 if (! readSucessful)
1071 StgProperty emptyProperty;
1072 ULARGE_INTEGER newSize;
1073 ULONG propertyIndex;
1074 ULONG lastProperty = 0;
1075 ULONG blockCount = 0;
1078 * obtain the new count of property blocks
1080 blockCount = BlockChainStream_GetCount(
1081 storage->ancestorStorage->rootBlockChain)+1;
1084 * initialize the size used by the property stream
1086 newSize.HighPart = 0;
1087 newSize.LowPart = storage->bigBlockSize * blockCount;
1090 * add a property block to the property chain
1092 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1095 * memset the empty property in order to initialize the unused newly
1096 * created property
1098 memset(&emptyProperty, 0, sizeof(StgProperty));
1101 * initialize them
1103 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1105 for(
1106 propertyIndex = newPropertyIndex;
1107 propertyIndex < lastProperty;
1108 propertyIndex++)
1110 StorageImpl_WriteProperty(
1111 storage->ancestorStorage,
1112 propertyIndex,
1113 &emptyProperty);
1117 return newPropertyIndex;
1120 /****************************************************************************
1122 * Internal Method
1124 * Case insensitive comparaison of StgProperty.name by first considering
1125 * their size.
1127 * Returns <0 when newPrpoerty < currentProperty
1128 * >0 when newPrpoerty > currentProperty
1129 * 0 when newPrpoerty == currentProperty
1131 static LONG propertyNameCmp(
1132 OLECHAR *newProperty,
1133 OLECHAR *currentProperty)
1135 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1137 if (diff == 0)
1140 * We compare the string themselves only when they are of the same lenght
1142 diff = lstrcmpiW( newProperty, currentProperty);
1145 return diff;
1148 /****************************************************************************
1150 * Internal Method
1152 * Properly link this new element in the property chain.
1154 static void updatePropertyChain(
1155 StorageImpl *storage,
1156 ULONG newPropertyIndex,
1157 StgProperty newProperty)
1159 StgProperty currentProperty;
1162 * Read the root property
1164 StorageImpl_ReadProperty(storage->ancestorStorage,
1165 storage->rootPropertySetIndex,
1166 &currentProperty);
1168 if (currentProperty.dirProperty != PROPERTY_NULL)
1171 * The root storage contains some element, therefore, start the research
1172 * for the appropriate location.
1174 BOOL found = 0;
1175 ULONG current, next, previous, currentPropertyId;
1178 * Keep the StgProperty sequence number of the storage first property
1180 currentPropertyId = currentProperty.dirProperty;
1183 * Read
1185 StorageImpl_ReadProperty(storage->ancestorStorage,
1186 currentProperty.dirProperty,
1187 &currentProperty);
1189 previous = currentProperty.previousProperty;
1190 next = currentProperty.nextProperty;
1191 current = currentPropertyId;
1193 while (found == 0)
1195 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1197 if (diff < 0)
1199 if (previous != PROPERTY_NULL)
1201 StorageImpl_ReadProperty(storage->ancestorStorage,
1202 previous,
1203 &currentProperty);
1204 current = previous;
1206 else
1208 currentProperty.previousProperty = newPropertyIndex;
1209 StorageImpl_WriteProperty(storage->ancestorStorage,
1210 current,
1211 &currentProperty);
1212 found = 1;
1215 else
1217 if (next != PROPERTY_NULL)
1219 StorageImpl_ReadProperty(storage->ancestorStorage,
1220 next,
1221 &currentProperty);
1222 current = next;
1224 else
1226 currentProperty.nextProperty = newPropertyIndex;
1227 StorageImpl_WriteProperty(storage->ancestorStorage,
1228 current,
1229 &currentProperty);
1230 found = 1;
1234 previous = currentProperty.previousProperty;
1235 next = currentProperty.nextProperty;
1238 else
1241 * The root storage is empty, link the new property to it's dir property
1243 currentProperty.dirProperty = newPropertyIndex;
1244 StorageImpl_WriteProperty(storage->ancestorStorage,
1245 storage->rootPropertySetIndex,
1246 &currentProperty);
1251 /*************************************************************************
1252 * CopyTo (IStorage)
1254 HRESULT WINAPI StorageImpl_CopyTo(
1255 IStorage* iface,
1256 DWORD ciidExclude, /* [in] */
1257 const IID *rgiidExclude,/* [size_is][unique][in] */
1258 SNB snbExclude, /* [unique][in] */
1259 IStorage *pstgDest) /* [unique][in] */
1261 IEnumSTATSTG *elements = 0;
1262 STATSTG curElement, strStat;
1263 HRESULT hr;
1264 IStorage *pstgTmp, *pstgChild;
1265 IStream *pstrTmp, *pstrChild;
1267 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1268 FIXME( ole, "Exclude option not implemented\n");
1271 * Perform a sanity check
1273 if ( pstgDest == 0 )
1274 return STG_E_INVALIDPOINTER;
1277 * Enumerate the elements
1279 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1281 if ( hr != S_OK )
1282 return hr;
1285 * set the class ID
1287 StorageBaseImpl_Stat( iface, &curElement, STATFLAG_NONAME);
1288 IStorage_SetClass( pstgDest, &curElement.clsid );
1293 * Obtain the next element
1295 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1297 if ( hr == S_FALSE )
1299 hr = S_OK; /* done, every element has been copied */
1300 break;
1303 if (curElement.type == STGTY_STORAGE)
1306 * open child source storage
1308 hr = StorageBaseImpl_OpenStorage( iface, curElement.pwcsName, NULL,
1309 STGM_READ|STGM_SHARE_EXCLUSIVE,
1310 NULL, 0, &pstgChild );
1312 if (hr != S_OK)
1313 break;
1316 * Check if destination storage is not a child of the source
1317 * storage, which will cause an infinite loop
1319 if (pstgChild == pstgDest)
1321 IEnumSTATSTG_Release(elements);
1323 return STG_E_ACCESSDENIED;
1327 * create a new storage in destination storage
1329 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1330 STGM_FAILIFTHERE|STGM_WRITE, 0, 0,
1331 &pstgTmp );
1333 * if it already exist, don't create a new one use this one
1335 if (hr == STG_E_FILEALREADYEXISTS)
1337 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1338 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1339 NULL, 0, &pstgTmp );
1342 if (hr != S_OK)
1343 break;
1347 * do the copy recursively
1349 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1350 snbExclude, pstgTmp );
1352 IStorage_Release( pstgTmp );
1353 IStorage_Release( pstgChild );
1355 else if (curElement.type == STGTY_STREAM)
1358 * create a new stream in destination storage. If the stream already
1359 * exist, it will be deleted and a new one will be created.
1361 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1362 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1363 0, 0, &pstrTmp );
1365 if (hr != S_OK)
1366 break;
1369 * open child stream storage
1371 hr = StorageBaseImpl_OpenStream( iface, curElement.pwcsName, NULL,
1372 STGM_READ|STGM_SHARE_EXCLUSIVE,
1373 0, &pstrChild );
1375 if (hr != S_OK)
1376 break;
1379 * Get the size of the stream
1381 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1384 * do the copy
1386 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1387 NULL, NULL );
1389 IStream_Release( pstrTmp );
1390 IStream_Release( pstrChild );
1392 else
1394 WARN(ole, "unknown element type: %ld\n", curElement.type);
1397 } while (hr == S_OK);
1400 * Clean-up
1402 IEnumSTATSTG_Release(elements);
1404 return hr;
1407 /*************************************************************************
1408 * MoveElementTo (IStorage)
1410 HRESULT WINAPI StorageImpl_MoveElementTo(
1411 IStorage* iface,
1412 const OLECHAR *pwcsName, /* [string][in] */
1413 IStorage *pstgDest, /* [unique][in] */
1414 const OLECHAR *pwcsNewName,/* [string][in] */
1415 DWORD grfFlags) /* [in] */
1417 FIXME(ole, "not implemented!\n");
1418 return E_NOTIMPL;
1421 /*************************************************************************
1422 * Commit (IStorage)
1424 HRESULT WINAPI StorageImpl_Commit(
1425 IStorage* iface,
1426 DWORD grfCommitFlags)/* [in] */
1428 FIXME(ole, "(%ld): stub!\n", grfCommitFlags);
1429 return S_OK;
1432 /*************************************************************************
1433 * Revert (IStorage)
1435 HRESULT WINAPI StorageImpl_Revert(
1436 IStorage* iface)
1438 FIXME(ole, "not implemented!\n");
1439 return E_NOTIMPL;
1442 /*************************************************************************
1443 * DestroyElement (IStorage)
1445 * Stategy: This implementation is build this way for simplicity not for speed.
1446 * I always delete the top most element of the enumeration and adjust
1447 * the deleted element pointer all the time. This takes longer to
1448 * do but allow to reinvoke DestroyElement whenever we encounter a
1449 * storage object. The optimisation reside in the usage of another
1450 * enumeration stategy that would give all the leaves of a storage
1451 * first. (postfix order)
1453 HRESULT WINAPI StorageImpl_DestroyElement(
1454 IStorage* iface,
1455 const OLECHAR *pwcsName)/* [string][in] */
1457 StorageImpl* const This=(StorageImpl*)iface;
1459 IEnumSTATSTGImpl* propertyEnumeration;
1460 HRESULT hr = S_OK;
1461 BOOL res;
1462 StgProperty propertyToDelete;
1463 StgProperty parentProperty;
1464 ULONG foundPropertyIndexToDelete;
1465 ULONG typeOfRelation;
1466 ULONG parentPropertyId;
1469 * Perform a sanity check on the parameters.
1471 if (pwcsName==NULL)
1472 return STG_E_INVALIDPOINTER;
1475 * Create a property enumeration to search the property with the given name
1477 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1478 This->ancestorStorage,
1479 This->rootPropertySetIndex);
1481 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1482 propertyEnumeration,
1483 pwcsName,
1484 &propertyToDelete);
1486 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1488 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1490 return STG_E_FILENOTFOUND;
1494 * Find the parent property of the property to delete (the one that
1495 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1496 * the parent is This. Otherwise, the parent is one of it's sibling...
1500 * First, read This's StgProperty..
1502 res = StorageImpl_ReadProperty(
1503 This->ancestorStorage,
1504 This->rootPropertySetIndex,
1505 &parentProperty);
1507 assert(res==TRUE);
1510 * Second, check to see if by any chance the actual storage (This) is not
1511 * the parent of the property to delete... We never know...
1513 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1516 * Set data as it would have been done in the else part...
1518 typeOfRelation = PROPERTY_RELATION_DIR;
1519 parentPropertyId = This->rootPropertySetIndex;
1521 else
1524 * Create a property enumeration to search the parent properties, and
1525 * delete it once done.
1527 IEnumSTATSTGImpl* propertyEnumeration2;
1529 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1530 This->ancestorStorage,
1531 This->rootPropertySetIndex);
1533 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1534 propertyEnumeration2,
1535 foundPropertyIndexToDelete,
1536 &parentProperty,
1537 &parentPropertyId);
1539 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1542 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1544 hr = deleteStorageProperty(
1545 This,
1546 propertyToDelete.name);
1548 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1550 hr = deleteStreamProperty(
1551 This,
1552 foundPropertyIndexToDelete,
1553 propertyToDelete);
1556 if (hr!=S_OK)
1557 return hr;
1560 * Adjust the property chain
1562 hr = adjustPropertyChain(
1563 This,
1564 propertyToDelete,
1565 parentProperty,
1566 parentPropertyId,
1567 typeOfRelation);
1569 return hr;
1573 /*********************************************************************
1575 * Internal Method
1577 * Perform the deletion of a complete storage node
1580 static HRESULT deleteStorageProperty(
1581 StorageImpl *parentStorage,
1582 OLECHAR *propertyToDeleteName)
1584 IEnumSTATSTG *elements = 0;
1585 IStorage *childStorage = 0;
1586 STATSTG currentElement;
1587 HRESULT hr;
1588 HRESULT destroyHr = S_OK;
1591 * Open the storage and enumerate it
1593 hr = StorageBaseImpl_OpenStorage(
1594 (IStorage*)parentStorage,
1595 propertyToDeleteName,
1597 STGM_SHARE_EXCLUSIVE,
1600 &childStorage);
1602 if (hr != S_OK)
1604 return hr;
1608 * Enumerate the elements
1610 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1615 * Obtain the next element
1617 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1618 if (hr==S_OK)
1620 destroyHr = StorageImpl_DestroyElement(
1621 (IStorage*)childStorage,
1622 (OLECHAR*)currentElement.pwcsName);
1624 CoTaskMemFree(currentElement.pwcsName);
1628 * We need to Reset the enumeration every time because we delete elements
1629 * and the enumeration could be invalid
1631 IEnumSTATSTG_Reset(elements);
1633 } while ((hr == S_OK) && (destroyHr == S_OK));
1635 IStorage_Release(childStorage);
1636 IEnumSTATSTG_Release(elements);
1638 return destroyHr;
1641 /*********************************************************************
1643 * Internal Method
1645 * Perform the deletion of a stream node
1648 static HRESULT deleteStreamProperty(
1649 StorageImpl *parentStorage,
1650 ULONG indexOfPropertyToDelete,
1651 StgProperty propertyToDelete)
1653 IStream *pis;
1654 HRESULT hr;
1655 ULARGE_INTEGER size;
1657 size.HighPart = 0;
1658 size.LowPart = 0;
1660 hr = StorageBaseImpl_OpenStream(
1661 (IStorage*)parentStorage,
1662 (OLECHAR*)propertyToDelete.name,
1663 NULL,
1664 STGM_SHARE_EXCLUSIVE,
1666 &pis);
1668 if (hr!=S_OK)
1670 return(hr);
1674 * Zap the stream
1676 hr = IStream_SetSize(pis, size);
1678 if(hr != S_OK)
1680 return hr;
1684 * Invalidate the property by zeroing it's name member.
1686 propertyToDelete.sizeOfNameString = 0;
1689 * Here we should re-read the property so we get the updated pointer
1690 * but since we are here to zap it, I don't do it...
1693 StorageImpl_WriteProperty(
1694 parentStorage->ancestorStorage,
1695 indexOfPropertyToDelete,
1696 &propertyToDelete);
1698 return S_OK;
1701 /*********************************************************************
1703 * Internal Method
1705 * Finds a placeholder for the StgProperty within the Storage
1708 static HRESULT findPlaceholder(
1709 StorageImpl *storage,
1710 ULONG propertyIndexToStore,
1711 ULONG storePropertyIndex,
1712 INT typeOfRelation)
1714 StgProperty storeProperty;
1715 HRESULT hr = S_OK;
1716 BOOL res = TRUE;
1719 * Read the storage property
1721 res = StorageImpl_ReadProperty(
1722 storage->ancestorStorage,
1723 storePropertyIndex,
1724 &storeProperty);
1726 if(! res)
1728 return E_FAIL;
1731 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1733 if (storeProperty.previousProperty != PROPERTY_NULL)
1735 return findPlaceholder(
1736 storage,
1737 propertyIndexToStore,
1738 storeProperty.previousProperty,
1739 typeOfRelation);
1741 else
1743 storeProperty.previousProperty = propertyIndexToStore;
1746 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1748 if (storeProperty.nextProperty != PROPERTY_NULL)
1750 return findPlaceholder(
1751 storage,
1752 propertyIndexToStore,
1753 storeProperty.nextProperty,
1754 typeOfRelation);
1756 else
1758 storeProperty.nextProperty = propertyIndexToStore;
1761 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1763 if (storeProperty.dirProperty != PROPERTY_NULL)
1765 return findPlaceholder(
1766 storage,
1767 propertyIndexToStore,
1768 storeProperty.dirProperty,
1769 typeOfRelation);
1771 else
1773 storeProperty.dirProperty = propertyIndexToStore;
1777 hr = StorageImpl_WriteProperty(
1778 storage->ancestorStorage,
1779 storePropertyIndex,
1780 &storeProperty);
1782 if(! hr)
1784 return E_FAIL;
1787 return S_OK;
1790 /*************************************************************************
1792 * Internal Method
1794 * This method takes the previous and the next property link of a property
1795 * to be deleted and find them a place in the Storage.
1797 static HRESULT adjustPropertyChain(
1798 StorageImpl *This,
1799 StgProperty propertyToDelete,
1800 StgProperty parentProperty,
1801 ULONG parentPropertyId,
1802 INT typeOfRelation)
1804 ULONG newLinkProperty = PROPERTY_NULL;
1805 BOOL needToFindAPlaceholder = FALSE;
1806 ULONG storeNode = PROPERTY_NULL;
1807 ULONG toStoreNode = PROPERTY_NULL;
1808 INT relationType = 0;
1809 HRESULT hr = S_OK;
1810 BOOL res = TRUE;
1812 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1814 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1817 * Set the parent previous to the property to delete previous
1819 newLinkProperty = propertyToDelete.previousProperty;
1821 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1824 * We also need to find a storage for the other link, setup variables
1825 * to do this at the end...
1827 needToFindAPlaceholder = TRUE;
1828 storeNode = propertyToDelete.previousProperty;
1829 toStoreNode = propertyToDelete.nextProperty;
1830 relationType = PROPERTY_RELATION_NEXT;
1833 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1836 * Set the parent previous to the property to delete next
1838 newLinkProperty = propertyToDelete.nextProperty;
1842 * Link it for real...
1844 parentProperty.previousProperty = newLinkProperty;
1847 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1849 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1852 * Set the parent next to the property to delete next previous
1854 newLinkProperty = propertyToDelete.previousProperty;
1856 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1859 * We also need to find a storage for the other link, setup variables
1860 * to do this at the end...
1862 needToFindAPlaceholder = TRUE;
1863 storeNode = propertyToDelete.previousProperty;
1864 toStoreNode = propertyToDelete.nextProperty;
1865 relationType = PROPERTY_RELATION_NEXT;
1868 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1871 * Set the parent next to the property to delete next
1873 newLinkProperty = propertyToDelete.nextProperty;
1877 * Link it for real...
1879 parentProperty.nextProperty = newLinkProperty;
1881 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
1883 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1886 * Set the parent dir to the property to delete previous
1888 newLinkProperty = propertyToDelete.previousProperty;
1890 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1893 * We also need to find a storage for the other link, setup variables
1894 * to do this at the end...
1896 needToFindAPlaceholder = TRUE;
1897 storeNode = propertyToDelete.previousProperty;
1898 toStoreNode = propertyToDelete.nextProperty;
1899 relationType = PROPERTY_RELATION_NEXT;
1902 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1905 * Set the parent dir to the property to delete next
1907 newLinkProperty = propertyToDelete.nextProperty;
1911 * Link it for real...
1913 parentProperty.dirProperty = newLinkProperty;
1917 * Write back the parent property
1919 res = StorageImpl_WriteProperty(
1920 This->ancestorStorage,
1921 parentPropertyId,
1922 &parentProperty);
1923 if(! res)
1925 return E_FAIL;
1929 * If a placeholder is required for the other link, then, find one and
1930 * get out of here...
1932 if (needToFindAPlaceholder)
1934 hr = findPlaceholder(
1935 This,
1936 toStoreNode,
1937 storeNode,
1938 relationType);
1941 return hr;
1945 /******************************************************************************
1946 * SetElementTimes (IStorage)
1948 HRESULT WINAPI StorageImpl_SetElementTimes(
1949 IStorage* iface,
1950 const OLECHAR *pwcsName,/* [string][in] */
1951 const FILETIME *pctime, /* [in] */
1952 const FILETIME *patime, /* [in] */
1953 const FILETIME *pmtime) /* [in] */
1955 FIXME(ole, "not implemented!\n");
1956 return E_NOTIMPL;
1959 /******************************************************************************
1960 * SetStateBits (IStorage)
1962 HRESULT WINAPI StorageImpl_SetStateBits(
1963 IStorage* iface,
1964 DWORD grfStateBits,/* [in] */
1965 DWORD grfMask) /* [in] */
1967 FIXME(ole, "not implemented!\n");
1968 return E_NOTIMPL;
1971 HRESULT StorageImpl_Construct(
1972 StorageImpl* This,
1973 HANDLE hFile,
1974 DWORD openFlags)
1976 HRESULT hr = S_OK;
1977 StgProperty currentProperty;
1978 BOOL readSucessful;
1979 ULONG currentPropertyIndex;
1981 if ( FAILED( validateSTGM(openFlags) ))
1982 return STG_E_INVALIDFLAG;
1984 memset(This, 0, sizeof(StorageImpl));
1987 * Initialize the virtual fgunction table.
1989 This->lpvtbl = &Storage32Impl_Vtbl;
1990 This->v_destructor = &StorageImpl_Destroy;
1993 * This is the top-level storage so initialize the ancester pointer
1994 * to this.
1996 This->ancestorStorage = This;
1999 * Initialize the physical support of the storage.
2001 This->hFile = hFile;
2004 * Initialize the big block cache.
2006 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2007 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2008 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2009 openFlags,
2010 This->bigBlockSize);
2012 if (This->bigBlockFile == 0)
2013 return E_FAIL;
2015 if (openFlags & STGM_CREATE)
2017 ULARGE_INTEGER size;
2018 BYTE* bigBlockBuffer;
2021 * Initialize all header variables:
2022 * - The big block depot consists of one block and it is at block 0
2023 * - The properties start at block 1
2024 * - There is no small block depot
2026 memset( This->bigBlockDepotStart,
2027 BLOCK_UNUSED,
2028 sizeof(This->bigBlockDepotStart));
2030 This->bigBlockDepotCount = 1;
2031 This->bigBlockDepotStart[0] = 0;
2032 This->rootStartBlock = 1;
2033 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2034 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2035 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2036 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2037 This->extBigBlockDepotCount = 0;
2039 StorageImpl_SaveFileHeader(This);
2042 * Add one block for the big block depot and one block for the properties
2044 size.HighPart = 0;
2045 size.LowPart = This->bigBlockSize * 3;
2046 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2049 * Initialize the big block depot
2051 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2052 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2053 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2054 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2055 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2057 else
2060 * Load the header for the file.
2062 hr = StorageImpl_LoadFileHeader(This);
2064 if (FAILED(hr))
2066 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2068 return hr;
2073 * There is no block depot cached yet.
2075 This->indexBlockDepotCached = 0xFFFFFFFF;
2078 * Start searching for free blocks with block 0.
2080 This->prevFreeBlock = 0;
2083 * Create the block chain abstractions.
2085 This->rootBlockChain =
2086 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2088 This->smallBlockDepotChain = BlockChainStream_Construct(
2089 This,
2090 &This->smallBlockDepotStart,
2091 PROPERTY_NULL);
2094 * Write the root property
2096 if (openFlags & STGM_CREATE)
2098 StgProperty rootProp;
2100 * Initialize the property chain
2102 memset(&rootProp, 0, sizeof(rootProp));
2103 lstrcpyAtoW(rootProp.name, rootPropertyName);
2105 rootProp.sizeOfNameString = (lstrlenW(rootProp.name)+1) * sizeof(WCHAR);
2106 rootProp.propertyType = PROPTYPE_ROOT;
2107 rootProp.previousProperty = PROPERTY_NULL;
2108 rootProp.nextProperty = PROPERTY_NULL;
2109 rootProp.dirProperty = PROPERTY_NULL;
2110 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2111 rootProp.size.HighPart = 0;
2112 rootProp.size.LowPart = 0;
2114 StorageImpl_WriteProperty(This, 0, &rootProp);
2118 * Find the ID of the root int he property sets.
2120 currentPropertyIndex = 0;
2124 readSucessful = StorageImpl_ReadProperty(
2125 This,
2126 currentPropertyIndex,
2127 &currentProperty);
2129 if (readSucessful)
2131 if ( (currentProperty.sizeOfNameString != 0 ) &&
2132 (currentProperty.propertyType == PROPTYPE_ROOT) )
2134 This->rootPropertySetIndex = currentPropertyIndex;
2138 currentPropertyIndex++;
2140 } while (readSucessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2142 if (!readSucessful)
2144 /* TODO CLEANUP */
2145 return E_FAIL;
2149 * Create the block chain abstraction for the small block root chain.
2151 This->smallBlockRootChain = BlockChainStream_Construct(
2152 This,
2153 NULL,
2154 This->rootPropertySetIndex);
2156 return hr;
2159 void StorageImpl_Destroy(
2160 StorageImpl* This)
2162 BlockChainStream_Destroy(This->smallBlockRootChain);
2163 BlockChainStream_Destroy(This->rootBlockChain);
2164 BlockChainStream_Destroy(This->smallBlockDepotChain);
2166 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2167 return;
2170 /******************************************************************************
2171 * Storage32Impl_GetNextFreeBigBlock
2173 * Returns the index of the next free big block.
2174 * If the big block depot is filled, this method will enlarge it.
2177 ULONG StorageImpl_GetNextFreeBigBlock(
2178 StorageImpl* This)
2180 ULONG depotBlockIndexPos;
2181 void *depotBuffer;
2182 ULONG depotBlockOffset;
2183 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2184 ULONG nextBlockIndex = BLOCK_SPECIAL;
2185 int depotIndex = 0;
2186 ULONG freeBlock = BLOCK_UNUSED;
2188 depotIndex = This->prevFreeBlock / blocksPerDepot;
2189 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2192 * Scan the entire big block depot until we find a block marked free
2194 while (nextBlockIndex != BLOCK_UNUSED)
2196 if (depotIndex < COUNT_BBDEPOTINHEADER)
2198 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2201 * Grow the primary depot.
2203 if (depotBlockIndexPos == BLOCK_UNUSED)
2205 depotBlockIndexPos = depotIndex*blocksPerDepot;
2208 * Add a block depot.
2210 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2211 This->bigBlockDepotCount++;
2212 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2215 * Flag it as a block depot.
2217 StorageImpl_SetNextBlockInChain(This,
2218 depotBlockIndexPos,
2219 BLOCK_SPECIAL);
2221 /* Save new header information.
2223 StorageImpl_SaveFileHeader(This);
2226 else
2228 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2230 if (depotBlockIndexPos == BLOCK_UNUSED)
2233 * Grow the extended depot.
2235 ULONG extIndex = BLOCK_UNUSED;
2236 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2237 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2239 if (extBlockOffset == 0)
2241 /* We need an extended block.
2243 extIndex = Storage32Impl_AddExtBlockDepot(This);
2244 This->extBigBlockDepotCount++;
2245 depotBlockIndexPos = extIndex + 1;
2247 else
2248 depotBlockIndexPos = depotIndex * blocksPerDepot;
2251 * Add a block depot and mark it in the extended block.
2253 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2254 This->bigBlockDepotCount++;
2255 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2257 /* Flag the block depot.
2259 StorageImpl_SetNextBlockInChain(This,
2260 depotBlockIndexPos,
2261 BLOCK_SPECIAL);
2263 /* If necessary, flag the extended depot block.
2265 if (extIndex != BLOCK_UNUSED)
2266 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2268 /* Save header information.
2270 StorageImpl_SaveFileHeader(This);
2274 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2276 if (depotBuffer != 0)
2278 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2279 ( nextBlockIndex != BLOCK_UNUSED))
2281 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2283 if (nextBlockIndex == BLOCK_UNUSED)
2285 freeBlock = (depotIndex * blocksPerDepot) +
2286 (depotBlockOffset/sizeof(ULONG));
2289 depotBlockOffset += sizeof(ULONG);
2292 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2295 depotIndex++;
2296 depotBlockOffset = 0;
2299 This->prevFreeBlock = freeBlock;
2301 return freeBlock;
2304 /******************************************************************************
2305 * Storage32Impl_AddBlockDepot
2307 * This will create a depot block, essentially it is a block initialized
2308 * to BLOCK_UNUSEDs.
2310 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2312 BYTE* blockBuffer;
2314 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2317 * Initialize blocks as free
2319 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2321 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2324 /******************************************************************************
2325 * Storage32Impl_GetExtDepotBlock
2327 * Returns the index of the block that corresponds to the specified depot
2328 * index. This method is only for depot indexes equal or greater than
2329 * COUNT_BBDEPOTINHEADER.
2331 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2333 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2334 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2335 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2336 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2337 ULONG blockIndex = BLOCK_UNUSED;
2338 ULONG extBlockIndex = This->extBigBlockDepotStart;
2340 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2342 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2343 return BLOCK_UNUSED;
2345 while (extBlockCount > 0)
2347 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2348 extBlockCount--;
2351 if (extBlockIndex != BLOCK_UNUSED)
2353 BYTE* depotBuffer;
2355 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2357 if (depotBuffer != 0)
2359 StorageUtl_ReadDWord(depotBuffer,
2360 extBlockOffset * sizeof(ULONG),
2361 &blockIndex);
2363 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2367 return blockIndex;
2370 /******************************************************************************
2371 * Storage32Impl_SetExtDepotBlock
2373 * Associates the specified block index to the specified depot index.
2374 * This method is only for depot indexes equal or greater than
2375 * COUNT_BBDEPOTINHEADER.
2377 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2378 ULONG depotIndex,
2379 ULONG blockIndex)
2381 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2382 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2383 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2384 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2385 ULONG extBlockIndex = This->extBigBlockDepotStart;
2387 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2389 while (extBlockCount > 0)
2391 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2392 extBlockCount--;
2395 if (extBlockIndex != BLOCK_UNUSED)
2397 BYTE* depotBuffer;
2399 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2401 if (depotBuffer != 0)
2403 StorageUtl_WriteDWord(depotBuffer,
2404 extBlockOffset * sizeof(ULONG),
2405 blockIndex);
2407 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2412 /******************************************************************************
2413 * Storage32Impl_AddExtBlockDepot
2415 * Creates an extended depot block.
2417 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2419 ULONG numExtBlocks = This->extBigBlockDepotCount;
2420 ULONG nextExtBlock = This->extBigBlockDepotStart;
2421 BYTE* depotBuffer = NULL;
2422 ULONG index = BLOCK_UNUSED;
2423 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2424 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2425 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2427 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2428 blocksPerDepotBlock;
2430 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2433 * The first extended block.
2435 This->extBigBlockDepotStart = index;
2437 else
2439 int i;
2441 * Follow the chain to the last one.
2443 for (i = 0; i < (numExtBlocks - 1); i++)
2445 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2449 * Add the new extended block to the chain.
2451 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2452 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2453 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2457 * Initialize this block.
2459 depotBuffer = StorageImpl_GetBigBlock(This, index);
2460 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2461 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2463 return index;
2466 /******************************************************************************
2467 * Storage32Impl_FreeBigBlock
2469 * This method will flag the specified block as free in the big block depot.
2471 void StorageImpl_FreeBigBlock(
2472 StorageImpl* This,
2473 ULONG blockIndex)
2475 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2477 if (blockIndex < This->prevFreeBlock)
2478 This->prevFreeBlock = blockIndex;
2481 /************************************************************************
2482 * Storage32Impl_GetNextBlockInChain
2484 * This method will retrieve the block index of the next big block in
2485 * in the chain.
2487 * Params: This - Pointer to the Storage object.
2488 * blockIndex - Index of the block to retrieve the chain
2489 * for.
2491 * Returns: This method returns the index of the next block in the chain.
2492 * It will return the constants:
2493 * BLOCK_SPECIAL - If the block given was not part of a
2494 * chain.
2495 * BLOCK_END_OF_CHAIN - If the block given was the last in
2496 * a chain.
2497 * BLOCK_UNUSED - If the block given was not past of a chain
2498 * and is available.
2499 * BLOCK_EXTBBDEPOT - This block is part of the extended
2500 * big block depot.
2502 * See Windows documentation for more details on IStorage methods.
2504 ULONG StorageImpl_GetNextBlockInChain(
2505 StorageImpl* This,
2506 ULONG blockIndex)
2508 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2509 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2510 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2511 ULONG nextBlockIndex = BLOCK_SPECIAL;
2512 void* depotBuffer;
2513 ULONG depotBlockIndexPos;
2515 assert(depotBlockCount < This->bigBlockDepotCount);
2518 * Cache the currently accessed depot block.
2520 if (depotBlockCount != This->indexBlockDepotCached)
2522 This->indexBlockDepotCached = depotBlockCount;
2524 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2526 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2528 else
2531 * We have to look in the extended depot.
2533 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2536 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2538 if (depotBuffer!=0)
2540 int index;
2542 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2544 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2545 This->blockDepotCached[index] = nextBlockIndex;
2548 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2552 nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2554 return nextBlockIndex;
2557 /******************************************************************************
2558 * Storage32Impl_GetNextExtendedBlock
2560 * Given an extended block this method will return the next extended block.
2562 * NOTES:
2563 * The last ULONG of an extended block is the block index of the next
2564 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2565 * depot.
2567 * Return values:
2568 * - The index of the next extended block
2569 * - BLOCK_UNUSED: there is no next extended block.
2570 * - Any other return values denotes failure.
2572 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2574 ULONG nextBlockIndex = BLOCK_SPECIAL;
2575 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2576 void* depotBuffer;
2578 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2580 if (depotBuffer!=0)
2582 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2584 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2587 return nextBlockIndex;
2590 /******************************************************************************
2591 * Storage32Impl_SetNextBlockInChain
2593 * This method will write the index of the specified block's next block
2594 * in the big block depot.
2596 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2597 * do the following
2599 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2600 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2601 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2604 void StorageImpl_SetNextBlockInChain(
2605 StorageImpl* This,
2606 ULONG blockIndex,
2607 ULONG nextBlock)
2609 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2610 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2611 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2612 ULONG depotBlockIndexPos;
2613 void* depotBuffer;
2615 assert(depotBlockCount < This->bigBlockDepotCount);
2616 assert(blockIndex != nextBlock);
2618 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2620 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2622 else
2625 * We have to look in the extended depot.
2627 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2630 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2632 if (depotBuffer!=0)
2634 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2635 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2639 * Update the cached block depot, if necessary.
2641 if (depotBlockCount == This->indexBlockDepotCached)
2643 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2647 /******************************************************************************
2648 * Storage32Impl_LoadFileHeader
2650 * This method will read in the file header, i.e. big block index -1.
2652 HRESULT StorageImpl_LoadFileHeader(
2653 StorageImpl* This)
2655 HRESULT hr = STG_E_FILENOTFOUND;
2656 void* headerBigBlock = NULL;
2657 int index;
2660 * Get a pointer to the big block of data containing the header.
2662 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2665 * Extract the information from the header.
2667 if (headerBigBlock!=0)
2670 * Check for the "magic number" signature and return an error if it is not
2671 * found.
2673 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2675 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2676 return STG_E_OLDFORMAT;
2679 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2681 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2682 return STG_E_INVALIDHEADER;
2685 StorageUtl_ReadWord(
2686 headerBigBlock,
2687 OFFSET_BIGBLOCKSIZEBITS,
2688 &This->bigBlockSizeBits);
2690 StorageUtl_ReadWord(
2691 headerBigBlock,
2692 OFFSET_SMALLBLOCKSIZEBITS,
2693 &This->smallBlockSizeBits);
2695 StorageUtl_ReadDWord(
2696 headerBigBlock,
2697 OFFSET_BBDEPOTCOUNT,
2698 &This->bigBlockDepotCount);
2700 StorageUtl_ReadDWord(
2701 headerBigBlock,
2702 OFFSET_ROOTSTARTBLOCK,
2703 &This->rootStartBlock);
2705 StorageUtl_ReadDWord(
2706 headerBigBlock,
2707 OFFSET_SBDEPOTSTART,
2708 &This->smallBlockDepotStart);
2710 StorageUtl_ReadDWord(
2711 headerBigBlock,
2712 OFFSET_EXTBBDEPOTSTART,
2713 &This->extBigBlockDepotStart);
2715 StorageUtl_ReadDWord(
2716 headerBigBlock,
2717 OFFSET_EXTBBDEPOTCOUNT,
2718 &This->extBigBlockDepotCount);
2720 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2722 StorageUtl_ReadDWord(
2723 headerBigBlock,
2724 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2725 &(This->bigBlockDepotStart[index]));
2729 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2731 if ((1 << 2) == 4)
2733 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2734 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2736 else
2738 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2739 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2743 * Right now, the code is making some assumptions about the size of the
2744 * blocks, just make sure they are what we're expecting.
2746 assert( (This->bigBlockSize==DEF_BIG_BLOCK_SIZE) &&
2747 (This->smallBlockSize==DEF_SMALL_BLOCK_SIZE));
2750 * Release the block.
2752 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2754 hr = S_OK;
2757 return hr;
2760 /******************************************************************************
2761 * Storage32Impl_SaveFileHeader
2763 * This method will save to the file the header, i.e. big block -1.
2765 void StorageImpl_SaveFileHeader(
2766 StorageImpl* This)
2768 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2769 int index;
2770 BOOL success;
2773 * Get a pointer to the big block of data containing the header.
2775 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2778 * If the block read failed, the file is probably new.
2780 if (!success)
2783 * Initialize for all unknown fields.
2785 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2788 * Initialize the magic number.
2790 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2793 * And a bunch of things we don't know what they mean
2795 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2796 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2797 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2798 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2799 StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
2803 * Write the information to the header.
2805 if (headerBigBlock!=0)
2807 StorageUtl_WriteWord(
2808 headerBigBlock,
2809 OFFSET_BIGBLOCKSIZEBITS,
2810 This->bigBlockSizeBits);
2812 StorageUtl_WriteWord(
2813 headerBigBlock,
2814 OFFSET_SMALLBLOCKSIZEBITS,
2815 This->smallBlockSizeBits);
2817 StorageUtl_WriteDWord(
2818 headerBigBlock,
2819 OFFSET_BBDEPOTCOUNT,
2820 This->bigBlockDepotCount);
2822 StorageUtl_WriteDWord(
2823 headerBigBlock,
2824 OFFSET_ROOTSTARTBLOCK,
2825 This->rootStartBlock);
2827 StorageUtl_WriteDWord(
2828 headerBigBlock,
2829 OFFSET_SBDEPOTSTART,
2830 This->smallBlockDepotStart);
2832 StorageUtl_WriteDWord(
2833 headerBigBlock,
2834 OFFSET_EXTBBDEPOTSTART,
2835 This->extBigBlockDepotStart);
2837 StorageUtl_WriteDWord(
2838 headerBigBlock,
2839 OFFSET_EXTBBDEPOTCOUNT,
2840 This->extBigBlockDepotCount);
2842 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2844 StorageUtl_WriteDWord(
2845 headerBigBlock,
2846 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2847 (This->bigBlockDepotStart[index]));
2852 * Write the big block back to the file.
2854 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2857 /******************************************************************************
2858 * Storage32Impl_ReadProperty
2860 * This method will read the specified property from the property chain.
2862 BOOL StorageImpl_ReadProperty(
2863 StorageImpl* This,
2864 ULONG index,
2865 StgProperty* buffer)
2867 BYTE currentProperty[PROPSET_BLOCK_SIZE];
2868 ULARGE_INTEGER offsetInPropSet;
2869 BOOL readSucessful;
2870 ULONG bytesRead;
2872 offsetInPropSet.HighPart = 0;
2873 offsetInPropSet.LowPart = index * PROPSET_BLOCK_SIZE;
2875 readSucessful = BlockChainStream_ReadAt(
2876 This->rootBlockChain,
2877 offsetInPropSet,
2878 PROPSET_BLOCK_SIZE,
2879 currentProperty,
2880 &bytesRead);
2882 if (readSucessful)
2884 memset(buffer->name, 0, sizeof(buffer->name));
2885 memcpy(
2886 buffer->name,
2887 currentProperty+OFFSET_PS_NAME,
2888 PROPERTY_NAME_BUFFER_LEN );
2890 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
2892 StorageUtl_ReadWord(
2893 currentProperty,
2894 OFFSET_PS_NAMELENGTH,
2895 &buffer->sizeOfNameString);
2897 StorageUtl_ReadDWord(
2898 currentProperty,
2899 OFFSET_PS_PREVIOUSPROP,
2900 &buffer->previousProperty);
2902 StorageUtl_ReadDWord(
2903 currentProperty,
2904 OFFSET_PS_NEXTPROP,
2905 &buffer->nextProperty);
2907 StorageUtl_ReadDWord(
2908 currentProperty,
2909 OFFSET_PS_DIRPROP,
2910 &buffer->dirProperty);
2912 StorageUtl_ReadGUID(
2913 currentProperty,
2914 OFFSET_PS_GUID,
2915 &buffer->propertyUniqueID);
2917 StorageUtl_ReadDWord(
2918 currentProperty,
2919 OFFSET_PS_TSS1,
2920 &buffer->timeStampS1);
2922 StorageUtl_ReadDWord(
2923 currentProperty,
2924 OFFSET_PS_TSD1,
2925 &buffer->timeStampD1);
2927 StorageUtl_ReadDWord(
2928 currentProperty,
2929 OFFSET_PS_TSS2,
2930 &buffer->timeStampS2);
2932 StorageUtl_ReadDWord(
2933 currentProperty,
2934 OFFSET_PS_TSD2,
2935 &buffer->timeStampD2);
2937 StorageUtl_ReadDWord(
2938 currentProperty,
2939 OFFSET_PS_STARTBLOCK,
2940 &buffer->startingBlock);
2942 StorageUtl_ReadDWord(
2943 currentProperty,
2944 OFFSET_PS_SIZE,
2945 &buffer->size.LowPart);
2947 buffer->size.HighPart = 0;
2950 return readSucessful;
2953 /*********************************************************************
2954 * Write the specified property into the property chain
2956 BOOL StorageImpl_WriteProperty(
2957 StorageImpl* This,
2958 ULONG index,
2959 StgProperty* buffer)
2961 BYTE currentProperty[PROPSET_BLOCK_SIZE];
2962 ULARGE_INTEGER offsetInPropSet;
2963 BOOL writeSucessful;
2964 ULONG bytesWritten;
2966 offsetInPropSet.HighPart = 0;
2967 offsetInPropSet.LowPart = index * PROPSET_BLOCK_SIZE;
2969 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
2971 memcpy(
2972 currentProperty + OFFSET_PS_NAME,
2973 buffer->name,
2974 PROPERTY_NAME_BUFFER_LEN );
2976 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
2979 * Reassign the size in case of mistake....
2981 buffer->sizeOfNameString = (lstrlenW(buffer->name)+1) * sizeof(WCHAR);
2983 StorageUtl_WriteWord(
2984 currentProperty,
2985 OFFSET_PS_NAMELENGTH,
2986 buffer->sizeOfNameString);
2988 StorageUtl_WriteDWord(
2989 currentProperty,
2990 OFFSET_PS_PREVIOUSPROP,
2991 buffer->previousProperty);
2993 StorageUtl_WriteDWord(
2994 currentProperty,
2995 OFFSET_PS_NEXTPROP,
2996 buffer->nextProperty);
2998 StorageUtl_WriteDWord(
2999 currentProperty,
3000 OFFSET_PS_DIRPROP,
3001 buffer->dirProperty);
3003 StorageUtl_WriteGUID(
3004 currentProperty,
3005 OFFSET_PS_GUID,
3006 &buffer->propertyUniqueID);
3008 StorageUtl_WriteDWord(
3009 currentProperty,
3010 OFFSET_PS_TSS1,
3011 buffer->timeStampS1);
3013 StorageUtl_WriteDWord(
3014 currentProperty,
3015 OFFSET_PS_TSD1,
3016 buffer->timeStampD1);
3018 StorageUtl_WriteDWord(
3019 currentProperty,
3020 OFFSET_PS_TSS2,
3021 buffer->timeStampS2);
3023 StorageUtl_WriteDWord(
3024 currentProperty,
3025 OFFSET_PS_TSD2,
3026 buffer->timeStampD2);
3028 StorageUtl_WriteDWord(
3029 currentProperty,
3030 OFFSET_PS_STARTBLOCK,
3031 buffer->startingBlock);
3033 StorageUtl_WriteDWord(
3034 currentProperty,
3035 OFFSET_PS_SIZE,
3036 buffer->size.LowPart);
3038 writeSucessful = BlockChainStream_WriteAt(This->rootBlockChain,
3039 offsetInPropSet,
3040 PROPSET_BLOCK_SIZE,
3041 currentProperty,
3042 &bytesWritten);
3043 return writeSucessful;
3046 BOOL StorageImpl_ReadBigBlock(
3047 StorageImpl* This,
3048 ULONG blockIndex,
3049 void* buffer)
3051 void* bigBlockBuffer;
3053 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3055 if (bigBlockBuffer!=0)
3057 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3059 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3061 return TRUE;
3064 return FALSE;
3067 BOOL StorageImpl_WriteBigBlock(
3068 StorageImpl* This,
3069 ULONG blockIndex,
3070 void* buffer)
3072 void* bigBlockBuffer;
3074 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3076 if (bigBlockBuffer!=0)
3078 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3080 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3082 return TRUE;
3085 return FALSE;
3088 void* StorageImpl_GetROBigBlock(
3089 StorageImpl* This,
3090 ULONG blockIndex)
3092 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3095 void* StorageImpl_GetBigBlock(
3096 StorageImpl* This,
3097 ULONG blockIndex)
3099 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3102 void StorageImpl_ReleaseBigBlock(
3103 StorageImpl* This,
3104 void* pBigBlock)
3106 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3109 /******************************************************************************
3110 * Storage32Impl_SmallBlocksToBigBlocks
3112 * This method will convert a small block chain to a big block chain.
3113 * The small block chain will be destroyed.
3115 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3116 StorageImpl* This,
3117 SmallBlockChainStream** ppsbChain)
3119 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3120 ULARGE_INTEGER size, offset;
3121 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3122 ULONG propertyIndex;
3123 BOOL successRead, successWrite;
3124 StgProperty chainProperty;
3125 BYTE buffer[DEF_SMALL_BLOCK_SIZE];
3126 BlockChainStream *bbTempChain = NULL;
3127 BlockChainStream *bigBlockChain = NULL;
3130 * Create a temporary big block chain that doesn't have
3131 * an associated property. This temporary chain will be
3132 * used to copy data from small blocks to big blocks.
3134 bbTempChain = BlockChainStream_Construct(This,
3135 &bbHeadOfChain,
3136 PROPERTY_NULL);
3139 * Grow the big block chain.
3141 size = SmallBlockChainStream_GetSize(*ppsbChain);
3142 BlockChainStream_SetSize(bbTempChain, size);
3145 * Copy the contents of the small block chain to the big block chain
3146 * by small block size increments.
3148 offset.LowPart = 0;
3149 offset.HighPart = 0;
3150 cbTotalRead = 0;
3151 cbTotalWritten = 0;
3155 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3156 offset,
3157 sizeof(buffer),
3158 buffer,
3159 &cbRead);
3160 cbTotalRead += cbRead;
3162 successWrite = BlockChainStream_WriteAt(bbTempChain,
3163 offset,
3164 cbRead,
3165 buffer,
3166 &cbWritten);
3167 cbTotalWritten += cbWritten;
3169 offset.LowPart += This->smallBlockSize;
3171 } while (successRead && successWrite);
3173 assert(cbTotalRead == cbTotalWritten);
3176 * Destroy the small block chain.
3178 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3179 size.HighPart = 0;
3180 size.LowPart = 0;
3181 SmallBlockChainStream_SetSize(*ppsbChain, size);
3182 SmallBlockChainStream_Destroy(*ppsbChain);
3183 *ppsbChain = 0;
3186 * Change the property information. This chain is now a big block chain
3187 * and it doesn't reside in the small blocks chain anymore.
3189 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3191 chainProperty.startingBlock = bbHeadOfChain;
3193 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3196 * Destroy the temporary propertyless big block chain.
3197 * Create a new big block chain associated with this property.
3199 BlockChainStream_Destroy(bbTempChain);
3200 bigBlockChain = BlockChainStream_Construct(This,
3201 NULL,
3202 propertyIndex);
3204 return bigBlockChain;
3207 /******************************************************************************
3208 ** Storage32InternalImpl implementation
3211 StorageInternalImpl* StorageInternalImpl_Construct(
3212 StorageImpl* ancestorStorage,
3213 ULONG rootPropertyIndex)
3215 StorageInternalImpl* newStorage;
3218 * Allocate space for the new storage object
3220 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3222 if (newStorage!=0)
3224 memset(newStorage, 0, sizeof(StorageInternalImpl));
3227 * Initialize the virtual function table.
3229 newStorage->lpvtbl = &Storage32InternalImpl_Vtbl;
3230 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3233 * Keep the ancestor storage pointer and nail a reference to it.
3235 newStorage->ancestorStorage = ancestorStorage;
3236 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3239 * Keep the index of the root property set for this storage,
3241 newStorage->rootPropertySetIndex = rootPropertyIndex;
3243 return newStorage;
3246 return 0;
3249 void StorageInternalImpl_Destroy(
3250 StorageInternalImpl* This)
3252 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3253 HeapFree(GetProcessHeap(), 0, This);
3256 /******************************************************************************
3258 ** Storage32InternalImpl_Commit
3260 ** The non-root storages cannot be opened in transacted mode thus this function
3261 ** does nothing.
3263 HRESULT WINAPI StorageInternalImpl_Commit(
3264 IStorage* iface,
3265 DWORD grfCommitFlags) /* [in] */
3267 return S_OK;
3270 /******************************************************************************
3272 ** Storage32InternalImpl_Revert
3274 ** The non-root storages cannot be opened in transacted mode thus this function
3275 ** does nothing.
3277 HRESULT WINAPI StorageInternalImpl_Revert(
3278 IStorage* iface)
3280 return S_OK;
3283 /******************************************************************************
3284 ** IEnumSTATSTGImpl implementation
3287 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3288 StorageImpl* parentStorage,
3289 ULONG firstPropertyNode)
3291 IEnumSTATSTGImpl* newEnumeration;
3293 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3295 if (newEnumeration!=0)
3298 * Set-up the virtual function table and reference count.
3300 newEnumeration->lpvtbl = &IEnumSTATSTGImpl_Vtbl;
3301 newEnumeration->ref = 0;
3304 * We want to nail-down the reference to the storage in case the
3305 * enumeration out-lives the storage in the client application.
3307 newEnumeration->parentStorage = parentStorage;
3308 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3310 newEnumeration->firstPropertyNode = firstPropertyNode;
3313 * Initialize the search stack
3315 newEnumeration->stackSize = 0;
3316 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3317 newEnumeration->stackToVisit =
3318 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3321 * Make sure the current node of the iterator is the first one.
3323 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3326 return newEnumeration;
3329 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3331 IStorage_Release((IStorage*)This->parentStorage);
3332 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3333 HeapFree(GetProcessHeap(), 0, This);
3336 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3337 IEnumSTATSTG* iface,
3338 REFIID riid,
3339 void** ppvObject)
3341 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3344 * Perform a sanity check on the parameters.
3346 if (ppvObject==0)
3347 return E_INVALIDARG;
3350 * Initialize the return parameter.
3352 *ppvObject = 0;
3355 * Compare the riid with the interface IDs implemented by this object.
3357 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3359 *ppvObject = (IEnumSTATSTG*)This;
3361 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3363 *ppvObject = (IEnumSTATSTG*)This;
3367 * Check that we obtained an interface.
3369 if ((*ppvObject)==0)
3370 return E_NOINTERFACE;
3373 * Query Interface always increases the reference count by one when it is
3374 * successful
3376 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3378 return S_OK;
3381 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3382 IEnumSTATSTG* iface)
3384 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3386 This->ref++;
3387 return This->ref;
3390 ULONG WINAPI IEnumSTATSTGImpl_Release(
3391 IEnumSTATSTG* iface)
3393 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3395 ULONG newRef;
3397 This->ref--;
3398 newRef = This->ref;
3401 * If the reference count goes down to 0, perform suicide.
3403 if (newRef==0)
3405 IEnumSTATSTGImpl_Destroy(This);
3408 return newRef;;
3411 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3412 IEnumSTATSTG* iface,
3413 ULONG celt,
3414 STATSTG* rgelt,
3415 ULONG* pceltFetched)
3417 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3419 StgProperty currentProperty;
3420 STATSTG* currentReturnStruct = rgelt;
3421 ULONG objectFetched = 0;
3422 ULONG currentSearchNode;
3425 * Perform a sanity check on the parameters.
3427 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3428 return E_INVALIDARG;
3431 * To avoid the special case, get another pointer to a ULONG value if
3432 * the caller didn't supply one.
3434 if (pceltFetched==0)
3435 pceltFetched = &objectFetched;
3438 * Start the iteration, we will iterate until we hit the end of the
3439 * linked list or until we hit the number of items to iterate through
3441 *pceltFetched = 0;
3444 * Start with the node at the top of the stack.
3446 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3448 while ( ( *pceltFetched < celt) &&
3449 ( currentSearchNode!=PROPERTY_NULL) )
3452 * Remove the top node from the stack
3454 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3457 * Read the property from the storage.
3459 StorageImpl_ReadProperty(This->parentStorage,
3460 currentSearchNode,
3461 &currentProperty);
3464 * Copy the information to the return buffer.
3466 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3467 &currentProperty,
3468 STATFLAG_DEFAULT);
3471 * Step to the next item in the iteration
3473 (*pceltFetched)++;
3474 currentReturnStruct++;
3477 * Push the next search node in the search stack.
3479 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3482 * continue the iteration.
3484 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3487 if (*pceltFetched == celt)
3488 return S_OK;
3490 return S_FALSE;
3494 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3495 IEnumSTATSTG* iface,
3496 ULONG celt)
3498 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3500 StgProperty currentProperty;
3501 ULONG objectFetched = 0;
3502 ULONG currentSearchNode;
3505 * Start with the node at the top of the stack.
3507 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3509 while ( (objectFetched < celt) &&
3510 (currentSearchNode!=PROPERTY_NULL) )
3513 * Remove the top node from the stack
3515 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3518 * Read the property from the storage.
3520 StorageImpl_ReadProperty(This->parentStorage,
3521 currentSearchNode,
3522 &currentProperty);
3525 * Step to the next item in the iteration
3527 objectFetched++;
3530 * Push the next search node in the search stack.
3532 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3535 * continue the iteration.
3537 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3540 if (objectFetched == celt)
3541 return S_OK;
3543 return S_FALSE;
3546 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3547 IEnumSTATSTG* iface)
3549 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3551 StgProperty rootProperty;
3552 BOOL readSucessful;
3555 * Re-initialize the search stack to an empty stack
3557 This->stackSize = 0;
3560 * Read the root property from the storage.
3562 readSucessful = StorageImpl_ReadProperty(
3563 This->parentStorage,
3564 This->firstPropertyNode,
3565 &rootProperty);
3567 if (readSucessful)
3569 assert(rootProperty.sizeOfNameString!=0);
3572 * Push the search node in the search stack.
3574 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3577 return S_OK;
3580 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3581 IEnumSTATSTG* iface,
3582 IEnumSTATSTG** ppenum)
3584 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3586 IEnumSTATSTGImpl* newClone;
3589 * Perform a sanity check on the parameters.
3591 if (ppenum==0)
3592 return E_INVALIDARG;
3594 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3595 This->firstPropertyNode);
3599 * The new clone enumeration must point to the same current node as
3600 * the ole one.
3602 newClone->stackSize = This->stackSize ;
3603 newClone->stackMaxSize = This->stackMaxSize ;
3604 newClone->stackToVisit =
3605 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3607 memcpy(
3608 newClone->stackToVisit,
3609 This->stackToVisit,
3610 sizeof(ULONG) * newClone->stackSize);
3612 *ppenum = (IEnumSTATSTG*)newClone;
3615 * Don't forget to nail down a reference to the clone before
3616 * returning it.
3618 IEnumSTATSTGImpl_AddRef(*ppenum);
3620 return S_OK;
3623 INT IEnumSTATSTGImpl_FindParentProperty(
3624 IEnumSTATSTGImpl *This,
3625 ULONG childProperty,
3626 StgProperty *currentProperty,
3627 ULONG *thisNodeId)
3629 ULONG currentSearchNode;
3630 ULONG foundNode;
3633 * To avoid the special case, get another pointer to a ULONG value if
3634 * the caller didn't supply one.
3636 if (thisNodeId==0)
3637 thisNodeId = &foundNode;
3640 * Start with the node at the top of the stack.
3642 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3645 while (currentSearchNode!=PROPERTY_NULL)
3648 * Store the current node in the returned parameters
3650 *thisNodeId = currentSearchNode;
3653 * Remove the top node from the stack
3655 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3658 * Read the property from the storage.
3660 StorageImpl_ReadProperty(
3661 This->parentStorage,
3662 currentSearchNode,
3663 currentProperty);
3665 if (currentProperty->previousProperty == childProperty)
3666 return PROPERTY_RELATION_PREVIOUS;
3668 else if (currentProperty->nextProperty == childProperty)
3669 return PROPERTY_RELATION_NEXT;
3671 else if (currentProperty->dirProperty == childProperty)
3672 return PROPERTY_RELATION_DIR;
3675 * Push the next search node in the search stack.
3677 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3680 * continue the iteration.
3682 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3685 return PROPERTY_NULL;
3688 ULONG IEnumSTATSTGImpl_FindProperty(
3689 IEnumSTATSTGImpl* This,
3690 const OLECHAR* lpszPropName,
3691 StgProperty* currentProperty)
3693 ULONG currentSearchNode;
3696 * Start with the node at the top of the stack.
3698 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3700 while (currentSearchNode!=PROPERTY_NULL)
3703 * Remove the top node from the stack
3705 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3708 * Read the property from the storage.
3710 StorageImpl_ReadProperty(This->parentStorage,
3711 currentSearchNode,
3712 currentProperty);
3714 if ( propertyNameCmp(
3715 (OLECHAR*)currentProperty->name,
3716 (OLECHAR*)lpszPropName) == 0)
3717 return currentSearchNode;
3720 * Push the next search node in the search stack.
3722 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3725 * continue the iteration.
3727 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3730 return PROPERTY_NULL;
3733 void IEnumSTATSTGImpl_PushSearchNode(
3734 IEnumSTATSTGImpl* This,
3735 ULONG nodeToPush)
3737 StgProperty rootProperty;
3738 BOOL readSucessful;
3741 * First, make sure we're not trying to push an unexisting node.
3743 if (nodeToPush==PROPERTY_NULL)
3744 return;
3747 * First push the node to the stack
3749 if (This->stackSize == This->stackMaxSize)
3751 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3753 This->stackToVisit = HeapReAlloc(
3754 GetProcessHeap(),
3756 This->stackToVisit,
3757 sizeof(ULONG) * This->stackMaxSize);
3760 This->stackToVisit[This->stackSize] = nodeToPush;
3761 This->stackSize++;
3764 * Read the root property from the storage.
3766 readSucessful = StorageImpl_ReadProperty(
3767 This->parentStorage,
3768 nodeToPush,
3769 &rootProperty);
3771 if (readSucessful)
3773 assert(rootProperty.sizeOfNameString!=0);
3776 * Push the previous search node in the search stack.
3778 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3782 ULONG IEnumSTATSTGImpl_PopSearchNode(
3783 IEnumSTATSTGImpl* This,
3784 BOOL remove)
3786 ULONG topNode;
3788 if (This->stackSize == 0)
3789 return PROPERTY_NULL;
3791 topNode = This->stackToVisit[This->stackSize-1];
3793 if (remove)
3794 This->stackSize--;
3796 return topNode;
3799 /******************************************************************************
3800 ** StorageUtl implementation
3803 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
3805 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
3808 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
3810 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
3813 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
3815 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
3818 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
3820 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
3823 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
3825 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
3826 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
3827 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
3829 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
3832 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
3834 StorageUtl_WriteDWord(buffer, offset, value->Data1);
3835 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
3836 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
3838 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
3841 void StorageUtl_CopyPropertyToSTATSTG(
3842 STATSTG* destination,
3843 StgProperty* source,
3844 int statFlags)
3847 * The copy of the string occurs only when the flag is not set
3849 if ((statFlags & STATFLAG_NONAME) != 0)
3851 destination->pwcsName = 0;
3853 else
3855 destination->pwcsName =
3856 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
3858 lstrcpyW((LPWSTR)destination->pwcsName, source->name);
3861 switch (source->propertyType)
3863 case PROPTYPE_STORAGE:
3864 case PROPTYPE_ROOT:
3865 destination->type = STGTY_STORAGE;
3866 break;
3867 case PROPTYPE_STREAM:
3868 destination->type = STGTY_STREAM;
3869 break;
3870 default:
3871 destination->type = STGTY_STREAM;
3872 break;
3875 destination->cbSize = source->size;
3877 currentReturnStruct->mtime = {0}; TODO
3878 currentReturnStruct->ctime = {0};
3879 currentReturnStruct->atime = {0};
3881 destination->grfMode = 0;
3882 destination->grfLocksSupported = 0;
3883 destination->clsid = source->propertyUniqueID;
3884 destination->grfStateBits = 0;
3885 destination->reserved = 0;
3888 /******************************************************************************
3889 ** BlockChainStream implementation
3892 BlockChainStream* BlockChainStream_Construct(
3893 StorageImpl* parentStorage,
3894 ULONG* headOfStreamPlaceHolder,
3895 ULONG propertyIndex)
3897 BlockChainStream* newStream;
3898 ULONG blockIndex;
3900 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
3902 newStream->parentStorage = parentStorage;
3903 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
3904 newStream->ownerPropertyIndex = propertyIndex;
3905 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
3906 newStream->tailIndex = BLOCK_END_OF_CHAIN;
3907 newStream->numBlocks = 0;
3909 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
3911 while (blockIndex != BLOCK_END_OF_CHAIN)
3913 newStream->numBlocks++;
3914 newStream->tailIndex = blockIndex;
3916 blockIndex = StorageImpl_GetNextBlockInChain(
3917 parentStorage,
3918 blockIndex);
3921 return newStream;
3924 void BlockChainStream_Destroy(BlockChainStream* This)
3926 HeapFree(GetProcessHeap(), 0, This);
3929 /******************************************************************************
3930 * BlockChainStream_GetHeadOfChain
3932 * Returns the head of this stream chain.
3933 * Some special chains don't have properties, their heads are kept in
3934 * This->headOfStreamPlaceHolder.
3937 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
3939 StgProperty chainProperty;
3940 BOOL readSucessful;
3942 if (This->headOfStreamPlaceHolder != 0)
3943 return *(This->headOfStreamPlaceHolder);
3945 if (This->ownerPropertyIndex != PROPERTY_NULL)
3947 readSucessful = StorageImpl_ReadProperty(
3948 This->parentStorage,
3949 This->ownerPropertyIndex,
3950 &chainProperty);
3952 if (readSucessful)
3954 return chainProperty.startingBlock;
3958 return BLOCK_END_OF_CHAIN;
3961 /******************************************************************************
3962 * BlockChainStream_GetCount
3964 * Returns the number of blocks that comprises this chain.
3965 * This is not the size of the stream as the last block may not be full!
3968 ULONG BlockChainStream_GetCount(BlockChainStream* This)
3970 ULONG blockIndex;
3971 ULONG count = 0;
3973 blockIndex = BlockChainStream_GetHeadOfChain(This);
3975 while (blockIndex != BLOCK_END_OF_CHAIN)
3977 count++;
3979 blockIndex = StorageImpl_GetNextBlockInChain(
3980 This->parentStorage,
3981 blockIndex);
3984 return count;
3987 /******************************************************************************
3988 * BlockChainStream_ReadAt
3990 * Reads a specified number of bytes from this chain at the specified offset.
3991 * bytesRead may be NULL.
3992 * Failure will be returned if the specified number of bytes has not been read.
3994 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
3995 ULARGE_INTEGER offset,
3996 ULONG size,
3997 void* buffer,
3998 ULONG* bytesRead)
4000 ULONG blockNoInSequence = offset.LowPart / This->parentStorage->bigBlockSize;
4001 ULONG offsetInBlock = offset.LowPart % This->parentStorage->bigBlockSize;
4002 ULONG bytesToReadInBuffer;
4003 ULONG blockIndex;
4004 BYTE* bufferWalker;
4005 BYTE* bigBlockBuffer;
4007 if (This->lastBlockNoInSequence == 0xFFFFFFFF)
4008 This->lastBlockNoInSequence = blockNoInSequence;
4010 * Find the first block in the stream that contains part of the buffer.
4012 if (blockNoInSequence > This->lastBlockNoInSequence)
4014 ULONG temp = blockNoInSequence;
4016 blockIndex = This->lastBlockNoInSequenceIndex;
4017 blockNoInSequence -= This->lastBlockNoInSequence;
4018 This->lastBlockNoInSequence = temp;
4020 else
4022 blockIndex = BlockChainStream_GetHeadOfChain(This);
4023 This->lastBlockNoInSequence = blockNoInSequence;
4026 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4028 blockIndex =
4029 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4031 blockNoInSequence--;
4034 This->lastBlockNoInSequenceIndex = blockIndex;
4037 * Start reading the buffer.
4039 *bytesRead = 0;
4040 bufferWalker = buffer;
4042 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4045 * Calculate how many bytes we can copy from this big block.
4047 bytesToReadInBuffer =
4048 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4051 * Copy those bytes to the buffer
4053 bigBlockBuffer =
4054 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4056 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4058 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4061 * Step to the next big block.
4063 blockIndex =
4064 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4066 bufferWalker += bytesToReadInBuffer;
4067 size -= bytesToReadInBuffer;
4068 *bytesRead += bytesToReadInBuffer;
4069 offsetInBlock = 0; /* There is no offset on the next block */
4073 return (size == 0);
4076 /******************************************************************************
4077 * BlockChainStream_WriteAt
4079 * Writes the specified number of bytes to this chain at the specified offset.
4080 * bytesWritten may be NULL.
4081 * Will fail if not all specified number of bytes have been written.
4083 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4084 ULARGE_INTEGER offset,
4085 ULONG size,
4086 const void* buffer,
4087 ULONG* bytesWritten)
4089 ULONG blockNoInSequence = offset.LowPart / This->parentStorage->bigBlockSize;
4090 ULONG offsetInBlock = offset.LowPart % This->parentStorage->bigBlockSize;
4091 ULONG bytesToWrite;
4092 ULONG blockIndex;
4093 BYTE* bufferWalker;
4094 BYTE* bigBlockBuffer;
4096 if (This->lastBlockNoInSequence == 0xFFFFFFFF)
4097 This->lastBlockNoInSequence = blockNoInSequence;
4100 * Find the first block in the stream that contains part of the buffer.
4102 if (blockNoInSequence > This->lastBlockNoInSequence)
4104 ULONG temp = blockNoInSequence;
4106 blockIndex = This->lastBlockNoInSequenceIndex;
4107 blockNoInSequence -= This->lastBlockNoInSequence;
4108 This->lastBlockNoInSequence = temp;
4110 else
4112 blockIndex = BlockChainStream_GetHeadOfChain(This);
4113 This->lastBlockNoInSequence = blockNoInSequence;
4116 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4118 blockIndex =
4119 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4121 blockNoInSequence--;
4124 This->lastBlockNoInSequenceIndex = blockIndex;
4127 * Here, I'm casting away the constness on the buffer variable
4128 * This is OK since we don't intend to modify that buffer.
4130 *bytesWritten = 0;
4131 bufferWalker = (BYTE*)buffer;
4133 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4136 * Calculate how many bytes we can copy from this big block.
4138 bytesToWrite =
4139 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4142 * Copy those bytes to the buffer
4144 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4146 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4148 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4151 * Step to the next big block.
4153 blockIndex =
4154 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4156 bufferWalker += bytesToWrite;
4157 size -= bytesToWrite;
4158 *bytesWritten += bytesToWrite;
4159 offsetInBlock = 0; /* There is no offset on the next block */
4162 return (size == 0);
4165 /******************************************************************************
4166 * BlockChainStream_Shrink
4168 * Shrinks this chain in the big block depot.
4170 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4171 ULARGE_INTEGER newSize)
4173 ULONG blockIndex, extraBlock;
4174 ULONG numBlocks;
4175 ULONG count = 1;
4178 * Figure out how many blocks are needed to contain the new size
4180 numBlocks = newSize.LowPart / This->parentStorage->bigBlockSize;
4182 if ((newSize.LowPart % This->parentStorage->bigBlockSize) != 0)
4183 numBlocks++;
4185 blockIndex = BlockChainStream_GetHeadOfChain(This);
4188 * Go to the new end of chain
4190 while (count < numBlocks)
4192 blockIndex =
4193 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4195 count++;
4198 /* Get the next block before marking the new end */
4199 extraBlock =
4200 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4202 /* Mark the new end of chain */
4203 StorageImpl_SetNextBlockInChain(
4204 This->parentStorage,
4205 blockIndex,
4206 BLOCK_END_OF_CHAIN);
4208 This->tailIndex = blockIndex;
4209 This->numBlocks = numBlocks;
4212 * Mark the extra blocks as free
4214 while (extraBlock != BLOCK_END_OF_CHAIN)
4216 blockIndex =
4217 StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4219 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4220 extraBlock = blockIndex;
4223 return TRUE;
4226 /******************************************************************************
4227 * BlockChainStream_Enlarge
4229 * Grows this chain in the big block depot.
4231 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4232 ULARGE_INTEGER newSize)
4234 ULONG blockIndex, currentBlock;
4235 ULONG newNumBlocks;
4236 ULONG oldNumBlocks = 0;
4238 blockIndex = BlockChainStream_GetHeadOfChain(This);
4241 * Empty chain. Create the head.
4243 if (blockIndex == BLOCK_END_OF_CHAIN)
4245 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4246 StorageImpl_SetNextBlockInChain(This->parentStorage,
4247 blockIndex,
4248 BLOCK_END_OF_CHAIN);
4250 if (This->headOfStreamPlaceHolder != 0)
4252 *(This->headOfStreamPlaceHolder) = blockIndex;
4254 else
4256 StgProperty chainProp;
4257 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4259 StorageImpl_ReadProperty(
4260 This->parentStorage,
4261 This->ownerPropertyIndex,
4262 &chainProp);
4264 chainProp.startingBlock = blockIndex;
4266 StorageImpl_WriteProperty(
4267 This->parentStorage,
4268 This->ownerPropertyIndex,
4269 &chainProp);
4272 This->tailIndex = blockIndex;
4273 This->numBlocks = 1;
4277 * Figure out how many blocks are needed to contain this stream
4279 newNumBlocks = newSize.LowPart / This->parentStorage->bigBlockSize;
4281 if ((newSize.LowPart % This->parentStorage->bigBlockSize) != 0)
4282 newNumBlocks++;
4285 * Go to the current end of chain
4287 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4289 currentBlock = blockIndex;
4291 while (blockIndex != BLOCK_END_OF_CHAIN)
4293 This->numBlocks++;
4294 currentBlock = blockIndex;
4296 blockIndex =
4297 StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4300 This->tailIndex = currentBlock;
4303 currentBlock = This->tailIndex;
4304 oldNumBlocks = This->numBlocks;
4307 * Add new blocks to the chain
4309 while (oldNumBlocks < newNumBlocks)
4311 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4313 StorageImpl_SetNextBlockInChain(
4314 This->parentStorage,
4315 currentBlock,
4316 blockIndex);
4318 StorageImpl_SetNextBlockInChain(
4319 This->parentStorage,
4320 blockIndex,
4321 BLOCK_END_OF_CHAIN);
4323 currentBlock = blockIndex;
4324 oldNumBlocks++;
4327 This->tailIndex = blockIndex;
4328 This->numBlocks = newNumBlocks;
4330 return TRUE;
4333 /******************************************************************************
4334 * BlockChainStream_SetSize
4336 * Sets the size of this stream. The big block depot will be updated.
4337 * The file will grow if we grow the chain.
4339 * TODO: Free the actual blocks in the file when we shrink the chain.
4340 * Currently, the blocks are still in the file. So the file size
4341 * doesn't shrink even if we shrink streams.
4343 BOOL BlockChainStream_SetSize(
4344 BlockChainStream* This,
4345 ULARGE_INTEGER newSize)
4347 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4349 if (newSize.LowPart == size.LowPart)
4350 return TRUE;
4352 if (newSize.LowPart < size.LowPart)
4354 BlockChainStream_Shrink(This, newSize);
4356 else
4358 ULARGE_INTEGER fileSize =
4359 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4361 ULONG diff = newSize.LowPart - size.LowPart;
4364 * Make sure the file stays a multiple of blocksize
4366 if ((diff % This->parentStorage->bigBlockSize) != 0)
4367 diff += (This->parentStorage->bigBlockSize -
4368 (diff % This->parentStorage->bigBlockSize) );
4370 fileSize.LowPart += diff;
4371 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4373 BlockChainStream_Enlarge(This, newSize);
4376 return TRUE;
4379 /******************************************************************************
4380 * BlockChainStream_GetSize
4382 * Returns the size of this chain.
4383 * Will return the block count if this chain doesn't have a property.
4385 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4387 StgProperty chainProperty;
4389 if(This->headOfStreamPlaceHolder == NULL)
4392 * This chain is a data stream read the property and return
4393 * the appropriate size
4395 StorageImpl_ReadProperty(
4396 This->parentStorage,
4397 This->ownerPropertyIndex,
4398 &chainProperty);
4400 return chainProperty.size;
4402 else
4405 * this chain is a chain that does not have a property, figure out the
4406 * size by making the product number of used blocks times the
4407 * size of them
4409 ULARGE_INTEGER result;
4410 result.HighPart = 0;
4412 result.LowPart =
4413 BlockChainStream_GetCount(This) *
4414 This->parentStorage->bigBlockSize;
4416 return result;
4420 /******************************************************************************
4421 ** SmallBlockChainStream implementation
4424 SmallBlockChainStream* SmallBlockChainStream_Construct(
4425 StorageImpl* parentStorage,
4426 ULONG propertyIndex)
4428 SmallBlockChainStream* newStream;
4430 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4432 newStream->parentStorage = parentStorage;
4433 newStream->ownerPropertyIndex = propertyIndex;
4435 return newStream;
4438 void SmallBlockChainStream_Destroy(
4439 SmallBlockChainStream* This)
4441 HeapFree(GetProcessHeap(), 0, This);
4444 /******************************************************************************
4445 * SmallBlockChainStream_GetHeadOfChain
4447 * Returns the head of this chain of small blocks.
4449 ULONG SmallBlockChainStream_GetHeadOfChain(
4450 SmallBlockChainStream* This)
4452 StgProperty chainProperty;
4453 BOOL readSucessful;
4455 if (This->ownerPropertyIndex)
4457 readSucessful = StorageImpl_ReadProperty(
4458 This->parentStorage,
4459 This->ownerPropertyIndex,
4460 &chainProperty);
4462 if (readSucessful)
4464 return chainProperty.startingBlock;
4469 return BLOCK_END_OF_CHAIN;
4472 /******************************************************************************
4473 * SmallBlockChainStream_GetNextBlockInChain
4475 * Returns the index of the next small block in this chain.
4477 * Return Values:
4478 * - BLOCK_END_OF_CHAIN: end of this chain
4479 * - BLOCK_UNUSED: small block 'blockIndex' is free
4481 ULONG SmallBlockChainStream_GetNextBlockInChain(
4482 SmallBlockChainStream* This,
4483 ULONG blockIndex)
4485 ULARGE_INTEGER offsetOfBlockInDepot;
4486 DWORD buffer;
4487 ULONG nextBlockInChain = BLOCK_END_OF_CHAIN;
4488 ULONG bytesRead;
4489 BOOL success;
4491 offsetOfBlockInDepot.HighPart = 0;
4492 offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
4495 * Read those bytes in the buffer from the small block file.
4497 success = BlockChainStream_ReadAt(
4498 This->parentStorage->smallBlockDepotChain,
4499 offsetOfBlockInDepot,
4500 sizeof(DWORD),
4501 &buffer,
4502 &bytesRead);
4504 if (success)
4506 StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4509 return nextBlockInChain;
4512 /******************************************************************************
4513 * SmallBlockChainStream_SetNextBlockInChain
4515 * Writes the index of the next block of the specified block in the small
4516 * block depot.
4517 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4518 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4520 void SmallBlockChainStream_SetNextBlockInChain(
4521 SmallBlockChainStream* This,
4522 ULONG blockIndex,
4523 ULONG nextBlock)
4525 ULARGE_INTEGER offsetOfBlockInDepot;
4526 DWORD buffer;
4527 ULONG bytesWritten;
4529 offsetOfBlockInDepot.HighPart = 0;
4530 offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
4532 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4535 * Read those bytes in the buffer from the small block file.
4537 BlockChainStream_WriteAt(
4538 This->parentStorage->smallBlockDepotChain,
4539 offsetOfBlockInDepot,
4540 sizeof(DWORD),
4541 &buffer,
4542 &bytesWritten);
4545 /******************************************************************************
4546 * SmallBlockChainStream_FreeBlock
4548 * Flag small block 'blockIndex' as free in the small block depot.
4550 void SmallBlockChainStream_FreeBlock(
4551 SmallBlockChainStream* This,
4552 ULONG blockIndex)
4554 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4557 /******************************************************************************
4558 * SmallBlockChainStream_GetNextFreeBlock
4560 * Returns the index of a free small block. The small block depot will be
4561 * enlarged if necessary. The small block chain will also be enlarged if
4562 * necessary.
4564 ULONG SmallBlockChainStream_GetNextFreeBlock(
4565 SmallBlockChainStream* This)
4567 ULARGE_INTEGER offsetOfBlockInDepot;
4568 DWORD buffer;
4569 ULONG bytesRead;
4570 ULONG blockIndex = 0;
4571 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4572 BOOL success = TRUE;
4573 ULONG smallBlocksPerBigBlock;
4575 offsetOfBlockInDepot.HighPart = 0;
4578 * Scan the small block depot for a free block
4580 while (nextBlockIndex != BLOCK_UNUSED)
4582 offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
4584 success = BlockChainStream_ReadAt(
4585 This->parentStorage->smallBlockDepotChain,
4586 offsetOfBlockInDepot,
4587 sizeof(DWORD),
4588 &buffer,
4589 &bytesRead);
4592 * If we run out of space for the small block depot, enlarge it
4594 if (success)
4596 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4598 if (nextBlockIndex != BLOCK_UNUSED)
4599 blockIndex++;
4601 else
4603 ULONG count =
4604 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4606 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4607 ULONG nextBlock, newsbdIndex;
4608 BYTE* smallBlockDepot;
4610 nextBlock = sbdIndex;
4611 while (nextBlock != BLOCK_END_OF_CHAIN)
4613 sbdIndex = nextBlock;
4614 nextBlock =
4615 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4618 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4619 if (sbdIndex != BLOCK_END_OF_CHAIN)
4620 StorageImpl_SetNextBlockInChain(
4621 This->parentStorage,
4622 sbdIndex,
4623 newsbdIndex);
4625 StorageImpl_SetNextBlockInChain(
4626 This->parentStorage,
4627 newsbdIndex,
4628 BLOCK_END_OF_CHAIN);
4631 * Initialize all the small blocks to free
4633 smallBlockDepot =
4634 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4636 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4637 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4639 if (count == 0)
4642 * We have just created the small block depot.
4644 StgProperty rootProp;
4645 ULONG sbStartIndex;
4648 * Save it in the header
4650 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4651 StorageImpl_SaveFileHeader(This->parentStorage);
4654 * And allocate the first big block that will contain small blocks
4656 sbStartIndex =
4657 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4659 StorageImpl_SetNextBlockInChain(
4660 This->parentStorage,
4661 sbStartIndex,
4662 BLOCK_END_OF_CHAIN);
4664 StorageImpl_ReadProperty(
4665 This->parentStorage,
4666 This->parentStorage->rootPropertySetIndex,
4667 &rootProp);
4669 rootProp.startingBlock = sbStartIndex;
4670 rootProp.size.HighPart = 0;
4671 rootProp.size.LowPart = This->parentStorage->bigBlockSize;
4673 StorageImpl_WriteProperty(
4674 This->parentStorage,
4675 This->parentStorage->rootPropertySetIndex,
4676 &rootProp);
4681 smallBlocksPerBigBlock =
4682 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4685 * Verify if we have to allocate big blocks to contain small blocks
4687 if (blockIndex % smallBlocksPerBigBlock == 0)
4689 StgProperty rootProp;
4690 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4692 StorageImpl_ReadProperty(
4693 This->parentStorage,
4694 This->parentStorage->rootPropertySetIndex,
4695 &rootProp);
4697 if (rootProp.size.LowPart <
4698 (blocksRequired * This->parentStorage->bigBlockSize))
4700 rootProp.size.LowPart += This->parentStorage->bigBlockSize;
4702 BlockChainStream_SetSize(
4703 This->parentStorage->smallBlockRootChain,
4704 rootProp.size);
4706 StorageImpl_WriteProperty(
4707 This->parentStorage,
4708 This->parentStorage->rootPropertySetIndex,
4709 &rootProp);
4713 return blockIndex;
4716 /******************************************************************************
4717 * SmallBlockChainStream_ReadAt
4719 * Reads a specified number of bytes from this chain at the specified offset.
4720 * bytesRead may be NULL.
4721 * Failure will be returned if the specified number of bytes has not been read.
4723 BOOL SmallBlockChainStream_ReadAt(
4724 SmallBlockChainStream* This,
4725 ULARGE_INTEGER offset,
4726 ULONG size,
4727 void* buffer,
4728 ULONG* bytesRead)
4730 ULARGE_INTEGER offsetInBigBlockFile;
4731 ULONG blockNoInSequence =
4732 offset.LowPart / This->parentStorage->smallBlockSize;
4734 ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize;
4735 ULONG bytesToReadInBuffer;
4736 ULONG blockIndex;
4737 ULONG bytesReadFromBigBlockFile;
4738 BYTE* bufferWalker;
4741 * This should never happen on a small block file.
4743 assert(offset.HighPart==0);
4746 * Find the first block in the stream that contains part of the buffer.
4748 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4750 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4752 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4754 blockNoInSequence--;
4758 * Start reading the buffer.
4760 *bytesRead = 0;
4761 bufferWalker = buffer;
4763 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4766 * Calculate how many bytes we can copy from this small block.
4768 bytesToReadInBuffer =
4769 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4772 * Calculate the offset of the small block in the small block file.
4774 offsetInBigBlockFile.HighPart = 0;
4775 offsetInBigBlockFile.LowPart =
4776 blockIndex * This->parentStorage->smallBlockSize;
4778 offsetInBigBlockFile.LowPart += offsetInBlock;
4781 * Read those bytes in the buffer from the small block file.
4783 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
4784 offsetInBigBlockFile,
4785 bytesToReadInBuffer,
4786 bufferWalker,
4787 &bytesReadFromBigBlockFile);
4789 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
4792 * Step to the next big block.
4794 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4795 bufferWalker += bytesToReadInBuffer;
4796 size -= bytesToReadInBuffer;
4797 *bytesRead += bytesToReadInBuffer;
4798 offsetInBlock = 0; /* There is no offset on the next block */
4801 return (size == 0);
4804 /******************************************************************************
4805 * SmallBlockChainStream_WriteAt
4807 * Writes the specified number of bytes to this chain at the specified offset.
4808 * bytesWritten may be NULL.
4809 * Will fail if not all specified number of bytes have been written.
4811 BOOL SmallBlockChainStream_WriteAt(
4812 SmallBlockChainStream* This,
4813 ULARGE_INTEGER offset,
4814 ULONG size,
4815 const void* buffer,
4816 ULONG* bytesWritten)
4818 ULARGE_INTEGER offsetInBigBlockFile;
4819 ULONG blockNoInSequence =
4820 offset.LowPart / This->parentStorage->smallBlockSize;
4822 ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize;
4823 ULONG bytesToWriteInBuffer;
4824 ULONG blockIndex;
4825 ULONG bytesWrittenFromBigBlockFile;
4826 BYTE* bufferWalker;
4829 * This should never happen on a small block file.
4831 assert(offset.HighPart==0);
4834 * Find the first block in the stream that contains part of the buffer.
4836 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4838 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4840 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4842 blockNoInSequence--;
4846 * Start writing the buffer.
4848 * Here, I'm casting away the constness on the buffer variable
4849 * This is OK since we don't intend to modify that buffer.
4851 *bytesWritten = 0;
4852 bufferWalker = (BYTE*)buffer;
4853 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4856 * Calculate how many bytes we can copy to this small block.
4858 bytesToWriteInBuffer =
4859 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4862 * Calculate the offset of the small block in the small block file.
4864 offsetInBigBlockFile.HighPart = 0;
4865 offsetInBigBlockFile.LowPart =
4866 blockIndex * This->parentStorage->smallBlockSize;
4868 offsetInBigBlockFile.LowPart += offsetInBlock;
4871 * Write those bytes in the buffer to the small block file.
4873 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
4874 offsetInBigBlockFile,
4875 bytesToWriteInBuffer,
4876 bufferWalker,
4877 &bytesWrittenFromBigBlockFile);
4879 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
4882 * Step to the next big block.
4884 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4885 bufferWalker += bytesToWriteInBuffer;
4886 size -= bytesToWriteInBuffer;
4887 *bytesWritten += bytesToWriteInBuffer;
4888 offsetInBlock = 0; /* There is no offset on the next block */
4891 return (size == 0);
4894 /******************************************************************************
4895 * SmallBlockChainStream_Shrink
4897 * Shrinks this chain in the small block depot.
4899 BOOL SmallBlockChainStream_Shrink(
4900 SmallBlockChainStream* This,
4901 ULARGE_INTEGER newSize)
4903 ULONG blockIndex, extraBlock;
4904 ULONG numBlocks;
4905 ULONG count = 1;
4907 numBlocks = newSize.LowPart / This->parentStorage->smallBlockSize;
4909 if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0)
4910 numBlocks++;
4912 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4915 * Go to the new end of chain
4917 while (count < numBlocks)
4919 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4920 count++;
4923 /* Get the next block before marking the new end */
4924 extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4926 /* Mark the new end of chain */
4927 SmallBlockChainStream_SetNextBlockInChain(
4928 This,
4929 blockIndex,
4930 BLOCK_END_OF_CHAIN);
4933 * Mark the extra blocks as free
4935 while (extraBlock != BLOCK_END_OF_CHAIN)
4937 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
4938 SmallBlockChainStream_FreeBlock(This, extraBlock);
4939 extraBlock = blockIndex;
4942 return TRUE;
4945 /******************************************************************************
4946 * SmallBlockChainStream_Enlarge
4948 * Grows this chain in the small block depot.
4950 BOOL SmallBlockChainStream_Enlarge(
4951 SmallBlockChainStream* This,
4952 ULARGE_INTEGER newSize)
4954 ULONG blockIndex, currentBlock;
4955 ULONG newNumBlocks;
4956 ULONG oldNumBlocks = 0;
4958 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4961 * Empty chain
4963 if (blockIndex == BLOCK_END_OF_CHAIN)
4965 StgProperty chainProp;
4967 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
4968 &chainProp);
4970 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
4972 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
4973 &chainProp);
4975 blockIndex = chainProp.startingBlock;
4976 SmallBlockChainStream_SetNextBlockInChain(
4977 This,
4978 blockIndex,
4979 BLOCK_END_OF_CHAIN);
4982 currentBlock = blockIndex;
4985 * Figure out how many blocks are needed to contain this stream
4987 newNumBlocks = newSize.LowPart / This->parentStorage->smallBlockSize;
4989 if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0)
4990 newNumBlocks++;
4993 * Go to the current end of chain
4995 while (blockIndex != BLOCK_END_OF_CHAIN)
4997 oldNumBlocks++;
4998 currentBlock = blockIndex;
4999 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5003 * Add new blocks to the chain
5005 while (oldNumBlocks < newNumBlocks)
5007 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5008 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5010 SmallBlockChainStream_SetNextBlockInChain(
5011 This,
5012 blockIndex,
5013 BLOCK_END_OF_CHAIN);
5015 currentBlock = blockIndex;
5016 oldNumBlocks++;
5019 return TRUE;
5022 /******************************************************************************
5023 * SmallBlockChainStream_GetCount
5025 * Returns the number of blocks that comprises this chain.
5026 * This is not the size of this chain as the last block may not be full!
5028 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5030 ULONG blockIndex;
5031 ULONG count = 0;
5033 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5035 while (blockIndex != BLOCK_END_OF_CHAIN)
5037 count++;
5039 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5042 return count;
5045 /******************************************************************************
5046 * SmallBlockChainStream_SetSize
5048 * Sets the size of this stream.
5049 * The file will grow if we grow the chain.
5051 * TODO: Free the actual blocks in the file when we shrink the chain.
5052 * Currently, the blocks are still in the file. So the file size
5053 * doesn't shrink even if we shrink streams.
5055 BOOL SmallBlockChainStream_SetSize(
5056 SmallBlockChainStream* This,
5057 ULARGE_INTEGER newSize)
5059 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5061 if (newSize.LowPart == size.LowPart)
5062 return TRUE;
5064 if (newSize.LowPart < size.LowPart)
5066 SmallBlockChainStream_Shrink(This, newSize);
5068 else
5070 SmallBlockChainStream_Enlarge(This, newSize);
5073 return TRUE;
5076 /******************************************************************************
5077 * SmallBlockChainStream_GetSize
5079 * Returns the size of this chain.
5081 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5083 StgProperty chainProperty;
5085 StorageImpl_ReadProperty(
5086 This->parentStorage,
5087 This->ownerPropertyIndex,
5088 &chainProperty);
5090 return chainProperty.size;
5093 /******************************************************************************
5094 * StgCreateDocfile32 [OLE32.144]
5096 HRESULT WINAPI StgCreateDocfile(
5097 LPCOLESTR pwcsName,
5098 DWORD grfMode,
5099 DWORD reserved,
5100 IStorage **ppstgOpen)
5102 StorageImpl* newStorage = 0;
5103 HANDLE hFile = INVALID_HANDLE_VALUE;
5104 HRESULT hr = S_OK;
5105 DWORD shareMode;
5106 DWORD accessMode;
5107 DWORD creationMode;
5108 DWORD fileAttributes;
5109 WCHAR tempFileName[MAX_PATH];
5112 * Validate the parameters
5114 if (ppstgOpen == 0)
5115 return STG_E_INVALIDPOINTER;
5118 * Validate the STGM flags
5120 if ( FAILED( validateSTGM(grfMode) ))
5121 return STG_E_INVALIDFLAG;
5124 * Generate a unique name.
5126 if (pwcsName == 0)
5128 WCHAR tempPath[MAX_PATH];
5129 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5131 memset(tempPath, 0, sizeof(tempPath));
5132 memset(tempFileName, 0, sizeof(tempFileName));
5134 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5135 tempPath[0] = '.';
5137 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5138 pwcsName = tempFileName;
5139 else
5140 return STG_E_INSUFFICIENTMEMORY;
5144 * Interpret the STGM value grfMode
5146 shareMode = GetShareModeFromSTGM(grfMode);
5147 accessMode = GetAccessModeFromSTGM(grfMode);
5148 creationMode = GetCreationModeFromSTGM(grfMode);
5150 if (grfMode & STGM_DELETEONRELEASE)
5151 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5152 else
5153 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5155 if (grfMode & STGM_TRANSACTED)
5156 FIXME(ole, "Transacted mode not implemented.\n");
5159 * Initialize the "out" parameter.
5161 *ppstgOpen = 0;
5163 hFile = CreateFileW(pwcsName,
5164 accessMode,
5165 shareMode,
5166 NULL,
5167 creationMode,
5168 fileAttributes,
5171 if (hFile == INVALID_HANDLE_VALUE)
5173 return E_FAIL;
5177 * Allocate and initialize the new IStorage32object.
5179 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5181 if (newStorage == 0)
5182 return STG_E_INSUFFICIENTMEMORY;
5184 hr = StorageImpl_Construct(
5185 newStorage,
5186 hFile,
5187 grfMode);
5189 if (FAILED(hr))
5191 HeapFree(GetProcessHeap(), 0, newStorage);
5192 return hr;
5196 * Get an "out" pointer for the caller.
5198 hr = StorageBaseImpl_QueryInterface(
5199 (IStorage*)newStorage,
5200 (REFIID)&IID_IStorage,
5201 (void**)ppstgOpen);
5203 return hr;
5206 /******************************************************************************
5207 * StgOpenStorage32 [OLE32.148]
5209 HRESULT WINAPI StgOpenStorage(
5210 const OLECHAR *pwcsName,
5211 IStorage *pstgPriority,
5212 DWORD grfMode,
5213 SNB snbExclude,
5214 DWORD reserved,
5215 IStorage **ppstgOpen)
5217 StorageImpl* newStorage = 0;
5218 HRESULT hr = S_OK;
5219 HANDLE hFile = 0;
5220 DWORD shareMode;
5221 DWORD accessMode;
5224 * Perform a sanity check
5226 if (( pwcsName == 0) || (ppstgOpen == 0) )
5227 return STG_E_INVALIDPOINTER;
5230 * Validate the STGM flags
5232 if ( FAILED( validateSTGM(grfMode) ))
5233 return STG_E_INVALIDFLAG;
5236 * Interpret the STGM value grfMode
5238 shareMode = GetShareModeFromSTGM(grfMode);
5239 accessMode = GetAccessModeFromSTGM(grfMode);
5242 * Initialize the "out" parameter.
5244 *ppstgOpen = 0;
5246 hFile = CreateFileW( pwcsName,
5247 accessMode,
5248 shareMode,
5249 NULL,
5250 OPEN_EXISTING,
5251 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5255 if (hFile==INVALID_HANDLE_VALUE)
5257 return E_FAIL;
5261 * Allocate and initialize the new IStorage32object.
5263 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5265 if (newStorage == 0)
5266 return STG_E_INSUFFICIENTMEMORY;
5268 hr = StorageImpl_Construct(
5269 newStorage,
5270 hFile,
5271 grfMode);
5273 if (FAILED(hr))
5275 HeapFree(GetProcessHeap(), 0, newStorage);
5276 return hr;
5280 * Get an "out" pointer for the caller.
5282 hr = StorageBaseImpl_QueryInterface(
5283 (IStorage*)newStorage,
5284 (REFIID)&IID_IStorage,
5285 (void**)ppstgOpen);
5287 return hr;
5290 /******************************************************************************
5291 * WriteClassStg32 [OLE32.148]
5293 * This method will store the specified CLSID in the specified storage object
5295 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5297 HRESULT hRes;
5299 assert(pStg != 0);
5301 hRes = IStorage_SetClass(pStg, rclsid);
5303 return hRes;
5306 /*******************************************************************************************
5307 * ReadClassStg
5309 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5311 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5313 STATSTG pstatstg;
5314 HRESULT hRes;
5316 TRACE(ole,"()\n");
5318 if(pclsid==NULL)
5319 return E_POINTER;
5321 * read a STATSTG structure (contains the clsid) from the storage
5323 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5325 if(SUCCEEDED(hRes))
5326 *pclsid=pstatstg.clsid;
5328 return hRes;
5331 /*************************************************************************************
5332 * OleLoadFromStream
5334 * This function loads an object from stream
5336 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5338 CLSID clsid;
5339 HRESULT res;
5341 FIXME(ole,"(),stub!\n");
5343 res=ReadClassStm(pStm,&clsid);
5345 if (SUCCEEDED(res)){
5347 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5349 if (SUCCEEDED(res))
5351 res=IPersistStream_Load((IPersistStream*)ppvObj,pStm);
5354 return res;
5357 /************************************************************************************************
5358 * OleSaveToStream
5360 * This function saves an object with the IPersistStream interface on it to the specified stream
5362 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5365 CLSID clsid;
5366 HRESULT res;
5368 TRACE(ole,"(%p,%p)\n",pPStm,pStm);
5370 res=IPersistStream_GetClassID(pPStm,&clsid);
5372 if (SUCCEEDED(res)){
5374 res=WriteClassStm(pStm,&clsid);
5376 if (SUCCEEDED(res))
5378 res=IPersistStream_Save(pPStm,pStm,FALSE);
5381 return res;
5384 /****************************************************************************
5385 * This method validate a STGM parameter that can contain the values below
5387 * STGM_DIRECT 0x00000000
5388 * STGM_TRANSACTED 0x00010000
5389 * STGM_SIMPLE 0x08000000
5391 * STGM_READ 0x00000000
5392 * STGM_WRITE 0x00000001
5393 * STGM_READWRITE 0x00000002
5395 * STGM_SHARE_DENY_NONE 0x00000040
5396 * STGM_SHARE_DENY_READ 0x00000030
5397 * STGM_SHARE_DENY_WRITE 0x00000020
5398 * STGM_SHARE_EXCLUSIVE 0x00000010
5400 * STGM_PRIORITY 0x00040000
5401 * STGM_DELETEONRELEASE 0x04000000
5403 * STGM_CREATE 0x00001000
5404 * STGM_CONVERT 0x00020000
5405 * STGM_FAILIFTHERE 0x00000000
5407 * STGM_NOSCRATCH 0x00100000
5408 * STGM_NOSNAPSHOT 0x00200000
5410 static HRESULT validateSTGM(DWORD stgm)
5412 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5413 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5414 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5416 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5417 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5418 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5420 BOOL bSTGM_SHARE_DENY_NONE =
5421 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5423 BOOL bSTGM_SHARE_DENY_READ =
5424 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5426 BOOL bSTGM_SHARE_DENY_WRITE =
5427 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5429 BOOL bSTGM_SHARE_EXCLUSIVE =
5430 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5432 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5433 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5435 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5436 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5439 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5441 if ( ! bSTGM_DIRECT )
5442 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5443 return E_FAIL;
5446 * STGM_WRITE | STGM_READWRITE | STGM_READ
5448 if ( ! bSTGM_READ )
5449 if( bSTGM_WRITE && bSTGM_READWRITE )
5450 return E_FAIL;
5453 * STGM_SHARE_DENY_NONE | others
5454 * (I assume here that DENY_READ implies DENY_WRITE)
5456 if ( bSTGM_SHARE_DENY_NONE )
5457 if ( bSTGM_SHARE_DENY_READ ||
5458 bSTGM_SHARE_DENY_WRITE ||
5459 bSTGM_SHARE_EXCLUSIVE)
5460 return E_FAIL;
5463 * STGM_CREATE | STGM_CONVERT
5464 * if both are false, STGM_FAILIFTHERE is set to TRUE
5466 if ( bSTGM_CREATE && bSTGM_CONVERT )
5467 return E_FAIL;
5470 * STGM_NOSCRATCH requires STGM_TRANSACTED
5472 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5473 return E_FAIL;
5476 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5477 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5479 if (bSTGM_NOSNAPSHOT)
5481 if ( ! ( bSTGM_TRANSACTED &&
5482 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5483 return E_FAIL;
5486 return S_OK;
5489 /****************************************************************************
5490 * GetShareModeFromSTGM
5492 * This method will return a share mode flag from a STGM value.
5493 * The STGM value is assumed valid.
5495 static DWORD GetShareModeFromSTGM(DWORD stgm)
5497 DWORD dwShareMode = 0;
5498 BOOL bSTGM_SHARE_DENY_NONE =
5499 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5501 BOOL bSTGM_SHARE_DENY_READ =
5502 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5504 BOOL bSTGM_SHARE_DENY_WRITE =
5505 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5507 BOOL bSTGM_SHARE_EXCLUSIVE =
5508 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5510 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5511 dwShareMode = 0;
5513 if (bSTGM_SHARE_DENY_NONE)
5514 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5516 if (bSTGM_SHARE_DENY_WRITE)
5517 dwShareMode = FILE_SHARE_READ;
5519 return dwShareMode;
5522 /****************************************************************************
5523 * GetAccessModeFromSTGM
5525 * This method will return an access mode flag from a STGM value.
5526 * The STGM value is assumed valid.
5528 static DWORD GetAccessModeFromSTGM(DWORD stgm)
5530 DWORD dwDesiredAccess = GENERIC_READ;
5531 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5532 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5533 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5535 if (bSTGM_READ)
5536 dwDesiredAccess = GENERIC_READ;
5538 if (bSTGM_WRITE)
5539 dwDesiredAccess |= GENERIC_WRITE;
5541 if (bSTGM_READWRITE)
5542 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5544 return dwDesiredAccess;
5547 /****************************************************************************
5548 * GetCreationModeFromSTGM
5550 * This method will return a creation mode flag from a STGM value.
5551 * The STGM value is assumed valid.
5553 static DWORD GetCreationModeFromSTGM(DWORD stgm)
5555 if ( stgm & STGM_CREATE)
5556 return CREATE_ALWAYS;
5557 if (stgm & STGM_CONVERT) {
5558 FIXME(ole, "STGM_CONVERT not implemented!\n");
5559 return CREATE_NEW;
5561 /* All other cases */
5562 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5563 FIXME(ole,"unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
5564 return CREATE_NEW;