Added TEB in init_thread request.
[wine/multimedia.git] / ole / storage32.c
blobdc41df63f20b5a161db316a49b454e5fa3daaf0a
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(storage)
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;
284 TRACE(storage, "(%p, %s, %p, %lx, %ld, %p)\n",
285 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
288 * Perform a sanity check on the parameters.
290 if ( (pwcsName==NULL) || (ppstm==0) )
291 return E_INVALIDARG;
294 * Initialize the out parameter
296 *ppstm = 0;
299 * Validate the STGM flags
301 if ( FAILED( validateSTGM(grfMode) ))
302 return STG_E_INVALIDFLAG;
305 * As documented.
307 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
308 (grfMode & STGM_DELETEONRELEASE) ||
309 (grfMode & STGM_TRANSACTED) )
310 return STG_E_INVALIDFUNCTION;
313 * Create a property enumeration to search the properties
315 propertyEnumeration = IEnumSTATSTGImpl_Construct(
316 This->ancestorStorage,
317 This->rootPropertySetIndex);
320 * Search the enumeration for the property with the given name
322 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
323 propertyEnumeration,
324 pwcsName,
325 &currentProperty);
328 * Delete the property enumeration since we don't need it anymore
330 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
333 * If it was found, construct the stream object and return a pointer to it.
335 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
336 (currentProperty.propertyType==PROPTYPE_STREAM) )
338 newStream = StgStreamImpl_Construct(This, foundPropertyIndex);
340 if (newStream!=0)
342 *ppstm = (IStream*)newStream;
345 * Since we are returning a pointer to the interface, we have to
346 * nail down the reference.
348 StgStreamImpl_AddRef(*ppstm);
350 return S_OK;
353 return E_OUTOFMEMORY;
356 return STG_E_FILENOTFOUND;
359 /************************************************************************
360 * Storage32BaseImpl_OpenStorage (IStorage)
362 * This method will open a new storage object from the current storage.
364 * See Windows documentation for more details on IStorage methods.
366 HRESULT WINAPI StorageBaseImpl_OpenStorage(
367 IStorage* iface,
368 const OLECHAR* pwcsName, /* [string][unique][in] */
369 IStorage* pstgPriority, /* [unique][in] */
370 DWORD grfMode, /* [in] */
371 SNB snbExclude, /* [unique][in] */
372 DWORD reserved, /* [in] */
373 IStorage** ppstg) /* [out] */
375 ICOM_THIS(StorageBaseImpl,iface);
376 StorageInternalImpl* newStorage;
377 IEnumSTATSTGImpl* propertyEnumeration;
378 StgProperty currentProperty;
379 ULONG foundPropertyIndex;
381 TRACE(storage, "(%p, %s, %p, %lx, %p, %ld, %p)\n",
382 iface, debugstr_w(pwcsName), pstgPriority,
383 grfMode, snbExclude, reserved, ppstg);
386 * Perform a sanity check on the parameters.
388 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
389 return E_INVALIDARG;
392 * Validate the STGM flags
394 if ( FAILED( validateSTGM(grfMode) ))
395 return STG_E_INVALIDFLAG;
398 * As documented.
400 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
401 (grfMode & STGM_DELETEONRELEASE) ||
402 (grfMode & STGM_PRIORITY) )
403 return STG_E_INVALIDFUNCTION;
406 * Initialize the out parameter
408 *ppstg = 0;
411 * Create a property enumeration to search the properties
413 propertyEnumeration = IEnumSTATSTGImpl_Construct(
414 This->ancestorStorage,
415 This->rootPropertySetIndex);
418 * Search the enumeration for the property with the given name
420 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
421 propertyEnumeration,
422 pwcsName,
423 &currentProperty);
426 * Delete the property enumeration since we don't need it anymore
428 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
431 * If it was found, construct the stream object and return a pointer to it.
433 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
434 (currentProperty.propertyType==PROPTYPE_STORAGE) )
437 * Construct a new Storage object
439 newStorage = StorageInternalImpl_Construct(
440 This->ancestorStorage,
441 foundPropertyIndex);
443 if (newStorage != 0)
445 *ppstg = (IStorage*)newStorage;
448 * Since we are returning a pointer to the interface,
449 * we have to nail down the reference.
451 StorageBaseImpl_AddRef(*ppstg);
453 return S_OK;
456 return STG_E_INSUFFICIENTMEMORY;
459 return STG_E_FILENOTFOUND;
462 /************************************************************************
463 * Storage32BaseImpl_EnumElements (IStorage)
465 * This method will create an enumerator object that can be used to
466 * retrieve informatino about all the properties in the storage object.
468 * See Windows documentation for more details on IStorage methods.
470 HRESULT WINAPI StorageBaseImpl_EnumElements(
471 IStorage* iface,
472 DWORD reserved1, /* [in] */
473 void* reserved2, /* [size_is][unique][in] */
474 DWORD reserved3, /* [in] */
475 IEnumSTATSTG** ppenum) /* [out] */
477 ICOM_THIS(StorageBaseImpl,iface);
478 IEnumSTATSTGImpl* newEnum;
480 TRACE(storage, "(%p, %ld, %p, %ld, %p)\n",
481 iface, reserved1, reserved2, reserved3, ppenum);
484 * Perform a sanity check on the parameters.
486 if ( (This==0) || (ppenum==0))
487 return E_INVALIDARG;
490 * Construct the enumerator.
492 newEnum = IEnumSTATSTGImpl_Construct(
493 This->ancestorStorage,
494 This->rootPropertySetIndex);
496 if (newEnum!=0)
498 *ppenum = (IEnumSTATSTG*)newEnum;
501 * Don't forget to nail down a reference to the new object before
502 * returning it.
504 IEnumSTATSTGImpl_AddRef(*ppenum);
506 return S_OK;
509 return E_OUTOFMEMORY;
512 /************************************************************************
513 * Storage32BaseImpl_Stat (IStorage)
515 * This method will retrieve information about this storage object.
517 * See Windows documentation for more details on IStorage methods.
519 HRESULT WINAPI StorageBaseImpl_Stat(
520 IStorage* iface,
521 STATSTG* pstatstg, /* [out] */
522 DWORD grfStatFlag) /* [in] */
524 ICOM_THIS(StorageBaseImpl,iface);
525 StgProperty curProperty;
526 BOOL readSucessful;
528 TRACE(storage, "(%p, %p, %lx)\n",
529 iface, pstatstg, grfStatFlag);
532 * Perform a sanity check on the parameters.
534 if ( (This==0) || (pstatstg==0))
535 return E_INVALIDARG;
538 * Read the information from the property.
540 readSucessful = StorageImpl_ReadProperty(
541 This->ancestorStorage,
542 This->rootPropertySetIndex,
543 &curProperty);
545 if (readSucessful)
547 StorageUtl_CopyPropertyToSTATSTG(
548 pstatstg,
549 &curProperty,
550 grfStatFlag);
552 return S_OK;
555 return E_FAIL;
558 /************************************************************************
559 * Storage32BaseImpl_RenameElement (IStorage)
561 * This method will rename the specified element.
563 * See Windows documentation for more details on IStorage methods.
565 * Implementation notes: The method used to rename consists of creating a clone
566 * of the deleted StgProperty object setting it with the new name and to
567 * perform a DestroyElement of the old StgProperty.
569 HRESULT WINAPI StorageBaseImpl_RenameElement(
570 IStorage* iface,
571 const OLECHAR* pwcsOldName, /* [in] */
572 const OLECHAR* pwcsNewName) /* [in] */
574 ICOM_THIS(StorageBaseImpl,iface);
575 IEnumSTATSTGImpl* propertyEnumeration;
576 StgProperty currentProperty;
577 ULONG foundPropertyIndex;
579 TRACE(storage, "(%p, %s, %s)\n",
580 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
583 * Create a property enumeration to search the properties
585 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
586 This->rootPropertySetIndex);
589 * Search the enumeration for the new property name
591 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
592 pwcsNewName,
593 &currentProperty);
595 if (foundPropertyIndex != PROPERTY_NULL)
598 * There is already a property with the new name
600 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
601 return STG_E_FILEALREADYEXISTS;
604 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
607 * Search the enumeration for the old property name
609 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
610 pwcsOldName,
611 &currentProperty);
614 * Delete the property enumeration since we don't need it anymore
616 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
618 if (foundPropertyIndex != PROPERTY_NULL)
620 StgProperty renamedProperty;
621 ULONG renamedPropertyIndex;
624 * Setup a new property for the renamed property
626 renamedProperty.sizeOfNameString =
627 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
629 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
630 return STG_E_INVALIDNAME;
632 lstrcpyW(renamedProperty.name, pwcsNewName);
634 renamedProperty.propertyType = currentProperty.propertyType;
635 renamedProperty.startingBlock = currentProperty.startingBlock;
636 renamedProperty.size.LowPart = currentProperty.size.LowPart;
637 renamedProperty.size.HighPart = currentProperty.size.HighPart;
639 renamedProperty.previousProperty = PROPERTY_NULL;
640 renamedProperty.nextProperty = PROPERTY_NULL;
643 * Bring the dirProperty link in case it is a storage and in which
644 * case the renamed storage elements don't require to be reorganized.
646 renamedProperty.dirProperty = currentProperty.dirProperty;
648 /* call CoFileTime to get the current time
649 renamedProperty.timeStampS1
650 renamedProperty.timeStampD1
651 renamedProperty.timeStampS2
652 renamedProperty.timeStampD2
653 renamedProperty.propertyUniqueID
657 * Obtain a free property in the property chain
659 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
662 * Save the new property into the new property spot
664 StorageImpl_WriteProperty(
665 This->ancestorStorage,
666 renamedPropertyIndex,
667 &renamedProperty);
670 * Find a spot in the property chain for our newly created property.
672 updatePropertyChain(
673 (StorageImpl*)This,
674 renamedPropertyIndex,
675 renamedProperty);
678 * At this point the renamed property has been inserted in the tree,
679 * now, before to Destroy the old property we must zeroed it's dirProperty
680 * otherwise the DestroyProperty below will zap it all and we do not want
681 * this to happen.
682 * Also, we fake that the old property is a storage so the DestroyProperty
683 * will not do a SetSize(0) on the stream data.
685 * This means that we need to tweek the StgProperty if it is a stream or a
686 * non empty storage.
688 currentProperty.dirProperty = PROPERTY_NULL;
689 currentProperty.propertyType = PROPTYPE_STORAGE;
690 StorageImpl_WriteProperty(
691 This->ancestorStorage,
692 foundPropertyIndex,
693 &currentProperty);
696 * Invoke Destroy to get rid of the ole property and automatically redo
697 * the linking of it's previous and next members...
699 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
702 else
705 * There is no property with the old name
707 return STG_E_FILENOTFOUND;
710 return S_OK;
713 /************************************************************************
714 * Storage32BaseImpl_CreateStream (IStorage)
716 * This method will create a stream object within this storage
718 * See Windows documentation for more details on IStorage methods.
720 HRESULT WINAPI StorageBaseImpl_CreateStream(
721 IStorage* iface,
722 const OLECHAR* pwcsName, /* [string][in] */
723 DWORD grfMode, /* [in] */
724 DWORD reserved1, /* [in] */
725 DWORD reserved2, /* [in] */
726 IStream** ppstm) /* [out] */
728 ICOM_THIS(StorageBaseImpl,iface);
729 IEnumSTATSTGImpl* propertyEnumeration;
730 StgStreamImpl* newStream;
731 StgProperty currentProperty, newStreamProperty;
732 ULONG foundPropertyIndex, newPropertyIndex;
734 TRACE(storage, "(%p, %s, %lx, %ld, %ld, %p)\n",
735 iface, debugstr_w(pwcsName), grfMode,
736 reserved1, reserved2, ppstm);
739 * Validate parameters
741 if (ppstm == 0)
742 return STG_E_INVALIDPOINTER;
744 if (pwcsName == 0)
745 return STG_E_INVALIDNAME;
748 * Validate the STGM flags
750 if ( FAILED( validateSTGM(grfMode) ))
751 return STG_E_INVALIDFLAG;
754 * As documented.
756 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
757 (grfMode & STGM_DELETEONRELEASE) ||
758 (grfMode & STGM_TRANSACTED) )
759 return STG_E_INVALIDFUNCTION;
762 * Initialize the out parameter
764 *ppstm = 0;
767 * Create a property enumeration to search the properties
769 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
770 This->rootPropertySetIndex);
772 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
773 pwcsName,
774 &currentProperty);
776 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
778 if (foundPropertyIndex != PROPERTY_NULL)
781 * An element with this name already exists
783 if (grfMode & STGM_CREATE)
785 IStorage_DestroyElement(iface, pwcsName);
787 else
788 return STG_E_FILEALREADYEXISTS;
792 * memset the empty property
794 memset(&newStreamProperty, 0, sizeof(StgProperty));
796 newStreamProperty.sizeOfNameString =
797 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
799 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
800 return STG_E_INVALIDNAME;
802 lstrcpyW(newStreamProperty.name, pwcsName);
804 newStreamProperty.propertyType = PROPTYPE_STREAM;
805 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
806 newStreamProperty.size.LowPart = 0;
807 newStreamProperty.size.HighPart = 0;
809 newStreamProperty.previousProperty = PROPERTY_NULL;
810 newStreamProperty.nextProperty = PROPERTY_NULL;
811 newStreamProperty.dirProperty = PROPERTY_NULL;
813 /* call CoFileTime to get the current time
814 newStreamProperty.timeStampS1
815 newStreamProperty.timeStampD1
816 newStreamProperty.timeStampS2
817 newStreamProperty.timeStampD2
820 /* newStreamProperty.propertyUniqueID */
823 * Get a free property or create a new one
825 newPropertyIndex = getFreeProperty(This->ancestorStorage);
828 * Save the new property into the new property spot
830 StorageImpl_WriteProperty(
831 This->ancestorStorage,
832 newPropertyIndex,
833 &newStreamProperty);
836 * Find a spot in the property chain for our newly created property.
838 updatePropertyChain(
839 (StorageImpl*)This,
840 newPropertyIndex,
841 newStreamProperty);
844 * Open the stream to return it.
846 newStream = StgStreamImpl_Construct(This, newPropertyIndex);
848 if (newStream != 0)
850 *ppstm = (IStream*)newStream;
853 * Since we are returning a pointer to the interface, we have to nail down
854 * the reference.
856 StgStreamImpl_AddRef(*ppstm);
858 else
860 return STG_E_INSUFFICIENTMEMORY;
863 return S_OK;
866 /************************************************************************
867 * Storage32BaseImpl_SetClass (IStorage)
869 * This method will write the specified CLSID in the property of this
870 * storage.
872 * See Windows documentation for more details on IStorage methods.
874 HRESULT WINAPI StorageBaseImpl_SetClass(
875 IStorage* iface,
876 REFCLSID clsid) /* [in] */
878 ICOM_THIS(StorageBaseImpl,iface);
879 HRESULT hRes = E_FAIL;
880 StgProperty curProperty;
881 BOOL success;
883 TRACE(storage, "(%p, %p)\n", iface, clsid);
885 success = StorageImpl_ReadProperty(This->ancestorStorage,
886 This->rootPropertySetIndex,
887 &curProperty);
888 if (success)
890 curProperty.propertyUniqueID = *clsid;
892 success = StorageImpl_WriteProperty(This->ancestorStorage,
893 This->rootPropertySetIndex,
894 &curProperty);
895 if (success)
896 hRes = S_OK;
899 return hRes;
902 /************************************************************************
903 ** Storage32Impl implementation
906 /************************************************************************
907 * Storage32Impl_CreateStorage (IStorage)
909 * This method will create the storage object within the provided storage.
911 * See Windows documentation for more details on IStorage methods.
913 HRESULT WINAPI StorageImpl_CreateStorage(
914 IStorage* iface,
915 const OLECHAR *pwcsName, /* [string][in] */
916 DWORD grfMode, /* [in] */
917 DWORD reserved1, /* [in] */
918 DWORD reserved2, /* [in] */
919 IStorage **ppstg) /* [out] */
921 StorageImpl* const This=(StorageImpl*)iface;
923 IEnumSTATSTGImpl *propertyEnumeration;
924 StgProperty currentProperty;
925 StgProperty newProperty;
926 ULONG foundPropertyIndex;
927 ULONG newPropertyIndex;
928 HRESULT hr;
930 TRACE(storage, "(%p, %s, %lx, %ld, %ld, %p)\n",
931 iface, debugstr_w(pwcsName), grfMode,
932 reserved1, reserved2, ppstg);
935 * Validate parameters
937 if (ppstg == 0)
938 return STG_E_INVALIDPOINTER;
940 if (pwcsName == 0)
941 return STG_E_INVALIDNAME;
944 * Validate the STGM flags
946 if ( FAILED( validateSTGM(grfMode) ) ||
947 (grfMode & STGM_DELETEONRELEASE) )
948 return STG_E_INVALIDFLAG;
951 * Initialize the out parameter
953 *ppstg = 0;
956 * Create a property enumeration and search the properties
958 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
959 This->rootPropertySetIndex);
961 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
962 pwcsName,
963 &currentProperty);
964 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
966 if (foundPropertyIndex != PROPERTY_NULL)
969 * An element with this name already exists
971 if (grfMode & STGM_CREATE)
972 IStorage_DestroyElement(iface, pwcsName);
973 else
974 return STG_E_FILEALREADYEXISTS;
978 * memset the empty property
980 memset(&newProperty, 0, sizeof(StgProperty));
982 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
984 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
985 return STG_E_INVALIDNAME;
987 lstrcpyW(newProperty.name, pwcsName);
989 newProperty.propertyType = PROPTYPE_STORAGE;
990 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
991 newProperty.size.LowPart = 0;
992 newProperty.size.HighPart = 0;
994 newProperty.previousProperty = PROPERTY_NULL;
995 newProperty.nextProperty = PROPERTY_NULL;
996 newProperty.dirProperty = PROPERTY_NULL;
998 /* call CoFileTime to get the current time
999 newProperty.timeStampS1
1000 newProperty.timeStampD1
1001 newProperty.timeStampS2
1002 newProperty.timeStampD2
1005 /* newStorageProperty.propertyUniqueID */
1008 * Obtain a free property in the property chain
1010 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1013 * Save the new property into the new property spot
1015 StorageImpl_WriteProperty(
1016 This->ancestorStorage,
1017 newPropertyIndex,
1018 &newProperty);
1021 * Find a spot in the property chain for our newly created property.
1023 updatePropertyChain(
1024 This,
1025 newPropertyIndex,
1026 newProperty);
1029 * Open it to get a pointer to return.
1031 hr = IStorage_OpenStorage(
1032 iface,
1033 (OLECHAR*)pwcsName,
1035 grfMode,
1038 ppstg);
1040 if( (hr != S_OK) || (*ppstg == NULL))
1042 return hr;
1046 return S_OK;
1050 /***************************************************************************
1052 * Internal Method
1054 * Get a free property or create a new one.
1056 static ULONG getFreeProperty(
1057 StorageImpl *storage)
1059 ULONG currentPropertyIndex = 0;
1060 ULONG newPropertyIndex = PROPERTY_NULL;
1061 BOOL readSucessful = TRUE;
1062 StgProperty currentProperty;
1067 * Start by reading the root property
1069 readSucessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1070 currentPropertyIndex,
1071 &currentProperty);
1072 if (readSucessful)
1074 if (currentProperty.sizeOfNameString == 0)
1077 * The property existis and is available, we found it.
1079 newPropertyIndex = currentPropertyIndex;
1082 else
1085 * We exhausted the property list, we will create more space below
1087 newPropertyIndex = currentPropertyIndex;
1089 currentPropertyIndex++;
1091 } while (newPropertyIndex == PROPERTY_NULL);
1094 * grow the property chain
1096 if (! readSucessful)
1098 StgProperty emptyProperty;
1099 ULARGE_INTEGER newSize;
1100 ULONG propertyIndex;
1101 ULONG lastProperty = 0;
1102 ULONG blockCount = 0;
1105 * obtain the new count of property blocks
1107 blockCount = BlockChainStream_GetCount(
1108 storage->ancestorStorage->rootBlockChain)+1;
1111 * initialize the size used by the property stream
1113 newSize.HighPart = 0;
1114 newSize.LowPart = storage->bigBlockSize * blockCount;
1117 * add a property block to the property chain
1119 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1122 * memset the empty property in order to initialize the unused newly
1123 * created property
1125 memset(&emptyProperty, 0, sizeof(StgProperty));
1128 * initialize them
1130 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1132 for(
1133 propertyIndex = newPropertyIndex;
1134 propertyIndex < lastProperty;
1135 propertyIndex++)
1137 StorageImpl_WriteProperty(
1138 storage->ancestorStorage,
1139 propertyIndex,
1140 &emptyProperty);
1144 return newPropertyIndex;
1147 /****************************************************************************
1149 * Internal Method
1151 * Case insensitive comparaison of StgProperty.name by first considering
1152 * their size.
1154 * Returns <0 when newPrpoerty < currentProperty
1155 * >0 when newPrpoerty > currentProperty
1156 * 0 when newPrpoerty == currentProperty
1158 static LONG propertyNameCmp(
1159 OLECHAR *newProperty,
1160 OLECHAR *currentProperty)
1162 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1164 if (diff == 0)
1167 * We compare the string themselves only when they are of the same lenght
1169 diff = lstrcmpiW( newProperty, currentProperty);
1172 return diff;
1175 /****************************************************************************
1177 * Internal Method
1179 * Properly link this new element in the property chain.
1181 static void updatePropertyChain(
1182 StorageImpl *storage,
1183 ULONG newPropertyIndex,
1184 StgProperty newProperty)
1186 StgProperty currentProperty;
1189 * Read the root property
1191 StorageImpl_ReadProperty(storage->ancestorStorage,
1192 storage->rootPropertySetIndex,
1193 &currentProperty);
1195 if (currentProperty.dirProperty != PROPERTY_NULL)
1198 * The root storage contains some element, therefore, start the research
1199 * for the appropriate location.
1201 BOOL found = 0;
1202 ULONG current, next, previous, currentPropertyId;
1205 * Keep the StgProperty sequence number of the storage first property
1207 currentPropertyId = currentProperty.dirProperty;
1210 * Read
1212 StorageImpl_ReadProperty(storage->ancestorStorage,
1213 currentProperty.dirProperty,
1214 &currentProperty);
1216 previous = currentProperty.previousProperty;
1217 next = currentProperty.nextProperty;
1218 current = currentPropertyId;
1220 while (found == 0)
1222 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1224 if (diff < 0)
1226 if (previous != PROPERTY_NULL)
1228 StorageImpl_ReadProperty(storage->ancestorStorage,
1229 previous,
1230 &currentProperty);
1231 current = previous;
1233 else
1235 currentProperty.previousProperty = newPropertyIndex;
1236 StorageImpl_WriteProperty(storage->ancestorStorage,
1237 current,
1238 &currentProperty);
1239 found = 1;
1242 else if (diff > 0)
1244 if (next != PROPERTY_NULL)
1246 StorageImpl_ReadProperty(storage->ancestorStorage,
1247 next,
1248 &currentProperty);
1249 current = next;
1251 else
1253 currentProperty.nextProperty = newPropertyIndex;
1254 StorageImpl_WriteProperty(storage->ancestorStorage,
1255 current,
1256 &currentProperty);
1257 found = 1;
1260 else
1263 * Trying to insert an item with the same name in the
1264 * subtree structure.
1266 assert(FALSE);
1269 previous = currentProperty.previousProperty;
1270 next = currentProperty.nextProperty;
1273 else
1276 * The root storage is empty, link the new property to it's dir property
1278 currentProperty.dirProperty = newPropertyIndex;
1279 StorageImpl_WriteProperty(storage->ancestorStorage,
1280 storage->rootPropertySetIndex,
1281 &currentProperty);
1286 /*************************************************************************
1287 * CopyTo (IStorage)
1289 HRESULT WINAPI StorageImpl_CopyTo(
1290 IStorage* iface,
1291 DWORD ciidExclude, /* [in] */
1292 const IID* rgiidExclude, /* [size_is][unique][in] */
1293 SNB snbExclude, /* [unique][in] */
1294 IStorage* pstgDest) /* [unique][in] */
1296 IEnumSTATSTG *elements = 0;
1297 STATSTG curElement, strStat;
1298 HRESULT hr;
1299 IStorage *pstgTmp, *pstgChild;
1300 IStream *pstrTmp, *pstrChild;
1302 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1303 FIXME( storage, "Exclude option not implemented\n");
1305 TRACE(storage, "(%p, %ld, %p, %p, %p)\n",
1306 iface, ciidExclude, rgiidExclude,
1307 snbExclude, pstgDest);
1310 * Perform a sanity check
1312 if ( pstgDest == 0 )
1313 return STG_E_INVALIDPOINTER;
1316 * Enumerate the elements
1318 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1320 if ( hr != S_OK )
1321 return hr;
1324 * set the class ID
1326 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1327 IStorage_SetClass( pstgDest, &curElement.clsid );
1332 * Obtain the next element
1334 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1336 if ( hr == S_FALSE )
1338 hr = S_OK; /* done, every element has been copied */
1339 break;
1342 if (curElement.type == STGTY_STORAGE)
1345 * open child source storage
1347 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1348 STGM_READ|STGM_SHARE_EXCLUSIVE,
1349 NULL, 0, &pstgChild );
1351 if (hr != S_OK)
1352 break;
1355 * Check if destination storage is not a child of the source
1356 * storage, which will cause an infinite loop
1358 if (pstgChild == pstgDest)
1360 IEnumSTATSTG_Release(elements);
1362 return STG_E_ACCESSDENIED;
1366 * create a new storage in destination storage
1368 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1369 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1370 0, 0,
1371 &pstgTmp );
1373 * if it already exist, don't create a new one use this one
1375 if (hr == STG_E_FILEALREADYEXISTS)
1377 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1378 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1379 NULL, 0, &pstgTmp );
1382 if (hr != S_OK)
1383 break;
1387 * do the copy recursively
1389 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1390 snbExclude, pstgTmp );
1392 IStorage_Release( pstgTmp );
1393 IStorage_Release( pstgChild );
1395 else if (curElement.type == STGTY_STREAM)
1398 * create a new stream in destination storage. If the stream already
1399 * exist, it will be deleted and a new one will be created.
1401 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1402 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1403 0, 0, &pstrTmp );
1405 if (hr != S_OK)
1406 break;
1409 * open child stream storage
1411 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1412 STGM_READ|STGM_SHARE_EXCLUSIVE,
1413 0, &pstrChild );
1415 if (hr != S_OK)
1416 break;
1419 * Get the size of the source stream
1421 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1424 * Set the size of the destination stream.
1426 IStream_SetSize(pstrTmp, strStat.cbSize);
1429 * do the copy
1431 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1432 NULL, NULL );
1434 IStream_Release( pstrTmp );
1435 IStream_Release( pstrChild );
1437 else
1439 WARN(storage, "unknown element type: %ld\n", curElement.type);
1442 } while (hr == S_OK);
1445 * Clean-up
1447 IEnumSTATSTG_Release(elements);
1449 return hr;
1452 /*************************************************************************
1453 * MoveElementTo (IStorage)
1455 HRESULT WINAPI StorageImpl_MoveElementTo(
1456 IStorage* iface,
1457 const OLECHAR *pwcsName, /* [string][in] */
1458 IStorage *pstgDest, /* [unique][in] */
1459 const OLECHAR *pwcsNewName,/* [string][in] */
1460 DWORD grfFlags) /* [in] */
1462 FIXME(storage, "not implemented!\n");
1463 return E_NOTIMPL;
1466 /*************************************************************************
1467 * Commit (IStorage)
1469 HRESULT WINAPI StorageImpl_Commit(
1470 IStorage* iface,
1471 DWORD grfCommitFlags)/* [in] */
1473 FIXME(storage, "(%ld): stub!\n", grfCommitFlags);
1474 return S_OK;
1477 /*************************************************************************
1478 * Revert (IStorage)
1480 HRESULT WINAPI StorageImpl_Revert(
1481 IStorage* iface)
1483 FIXME(storage, "not implemented!\n");
1484 return E_NOTIMPL;
1487 /*************************************************************************
1488 * DestroyElement (IStorage)
1490 * Stategy: This implementation is build this way for simplicity not for speed.
1491 * I always delete the top most element of the enumeration and adjust
1492 * the deleted element pointer all the time. This takes longer to
1493 * do but allow to reinvoke DestroyElement whenever we encounter a
1494 * storage object. The optimisation reside in the usage of another
1495 * enumeration stategy that would give all the leaves of a storage
1496 * first. (postfix order)
1498 HRESULT WINAPI StorageImpl_DestroyElement(
1499 IStorage* iface,
1500 const OLECHAR *pwcsName)/* [string][in] */
1502 StorageImpl* const This=(StorageImpl*)iface;
1504 IEnumSTATSTGImpl* propertyEnumeration;
1505 HRESULT hr = S_OK;
1506 BOOL res;
1507 StgProperty propertyToDelete;
1508 StgProperty parentProperty;
1509 ULONG foundPropertyIndexToDelete;
1510 ULONG typeOfRelation;
1511 ULONG parentPropertyId;
1513 TRACE(storage, "(%p, %s)\n",
1514 iface, debugstr_w(pwcsName));
1517 * Perform a sanity check on the parameters.
1519 if (pwcsName==NULL)
1520 return STG_E_INVALIDPOINTER;
1523 * Create a property enumeration to search the property with the given name
1525 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1526 This->ancestorStorage,
1527 This->rootPropertySetIndex);
1529 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1530 propertyEnumeration,
1531 pwcsName,
1532 &propertyToDelete);
1534 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1536 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1538 return STG_E_FILENOTFOUND;
1542 * Find the parent property of the property to delete (the one that
1543 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1544 * the parent is This. Otherwise, the parent is one of it's sibling...
1548 * First, read This's StgProperty..
1550 res = StorageImpl_ReadProperty(
1551 This->ancestorStorage,
1552 This->rootPropertySetIndex,
1553 &parentProperty);
1555 assert(res==TRUE);
1558 * Second, check to see if by any chance the actual storage (This) is not
1559 * the parent of the property to delete... We never know...
1561 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1564 * Set data as it would have been done in the else part...
1566 typeOfRelation = PROPERTY_RELATION_DIR;
1567 parentPropertyId = This->rootPropertySetIndex;
1569 else
1572 * Create a property enumeration to search the parent properties, and
1573 * delete it once done.
1575 IEnumSTATSTGImpl* propertyEnumeration2;
1577 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1578 This->ancestorStorage,
1579 This->rootPropertySetIndex);
1581 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1582 propertyEnumeration2,
1583 foundPropertyIndexToDelete,
1584 &parentProperty,
1585 &parentPropertyId);
1587 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1590 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1592 hr = deleteStorageProperty(
1593 This,
1594 propertyToDelete.name);
1596 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1598 hr = deleteStreamProperty(
1599 This,
1600 foundPropertyIndexToDelete,
1601 propertyToDelete);
1604 if (hr!=S_OK)
1605 return hr;
1608 * Adjust the property chain
1610 hr = adjustPropertyChain(
1611 This,
1612 propertyToDelete,
1613 parentProperty,
1614 parentPropertyId,
1615 typeOfRelation);
1617 return hr;
1621 /*********************************************************************
1623 * Internal Method
1625 * Perform the deletion of a complete storage node
1628 static HRESULT deleteStorageProperty(
1629 StorageImpl *parentStorage,
1630 OLECHAR *propertyToDeleteName)
1632 IEnumSTATSTG *elements = 0;
1633 IStorage *childStorage = 0;
1634 STATSTG currentElement;
1635 HRESULT hr;
1636 HRESULT destroyHr = S_OK;
1639 * Open the storage and enumerate it
1641 hr = StorageBaseImpl_OpenStorage(
1642 (IStorage*)parentStorage,
1643 propertyToDeleteName,
1645 STGM_SHARE_EXCLUSIVE,
1648 &childStorage);
1650 if (hr != S_OK)
1652 return hr;
1656 * Enumerate the elements
1658 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1663 * Obtain the next element
1665 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1666 if (hr==S_OK)
1668 destroyHr = StorageImpl_DestroyElement(
1669 (IStorage*)childStorage,
1670 (OLECHAR*)currentElement.pwcsName);
1672 CoTaskMemFree(currentElement.pwcsName);
1676 * We need to Reset the enumeration every time because we delete elements
1677 * and the enumeration could be invalid
1679 IEnumSTATSTG_Reset(elements);
1681 } while ((hr == S_OK) && (destroyHr == S_OK));
1683 IStorage_Release(childStorage);
1684 IEnumSTATSTG_Release(elements);
1686 return destroyHr;
1689 /*********************************************************************
1691 * Internal Method
1693 * Perform the deletion of a stream node
1696 static HRESULT deleteStreamProperty(
1697 StorageImpl *parentStorage,
1698 ULONG indexOfPropertyToDelete,
1699 StgProperty propertyToDelete)
1701 IStream *pis;
1702 HRESULT hr;
1703 ULARGE_INTEGER size;
1705 size.HighPart = 0;
1706 size.LowPart = 0;
1708 hr = StorageBaseImpl_OpenStream(
1709 (IStorage*)parentStorage,
1710 (OLECHAR*)propertyToDelete.name,
1711 NULL,
1712 STGM_SHARE_EXCLUSIVE,
1714 &pis);
1716 if (hr!=S_OK)
1718 return(hr);
1722 * Zap the stream
1724 hr = IStream_SetSize(pis, size);
1726 if(hr != S_OK)
1728 return hr;
1732 * Release the stream object.
1734 IStream_Release(pis);
1737 * Invalidate the property by zeroing it's name member.
1739 propertyToDelete.sizeOfNameString = 0;
1742 * Here we should re-read the property so we get the updated pointer
1743 * but since we are here to zap it, I don't do it...
1745 StorageImpl_WriteProperty(
1746 parentStorage->ancestorStorage,
1747 indexOfPropertyToDelete,
1748 &propertyToDelete);
1750 return S_OK;
1753 /*********************************************************************
1755 * Internal Method
1757 * Finds a placeholder for the StgProperty within the Storage
1760 static HRESULT findPlaceholder(
1761 StorageImpl *storage,
1762 ULONG propertyIndexToStore,
1763 ULONG storePropertyIndex,
1764 INT typeOfRelation)
1766 StgProperty storeProperty;
1767 HRESULT hr = S_OK;
1768 BOOL res = TRUE;
1771 * Read the storage property
1773 res = StorageImpl_ReadProperty(
1774 storage->ancestorStorage,
1775 storePropertyIndex,
1776 &storeProperty);
1778 if(! res)
1780 return E_FAIL;
1783 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1785 if (storeProperty.previousProperty != PROPERTY_NULL)
1787 return findPlaceholder(
1788 storage,
1789 propertyIndexToStore,
1790 storeProperty.previousProperty,
1791 typeOfRelation);
1793 else
1795 storeProperty.previousProperty = propertyIndexToStore;
1798 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1800 if (storeProperty.nextProperty != PROPERTY_NULL)
1802 return findPlaceholder(
1803 storage,
1804 propertyIndexToStore,
1805 storeProperty.nextProperty,
1806 typeOfRelation);
1808 else
1810 storeProperty.nextProperty = propertyIndexToStore;
1813 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1815 if (storeProperty.dirProperty != PROPERTY_NULL)
1817 return findPlaceholder(
1818 storage,
1819 propertyIndexToStore,
1820 storeProperty.dirProperty,
1821 typeOfRelation);
1823 else
1825 storeProperty.dirProperty = propertyIndexToStore;
1829 hr = StorageImpl_WriteProperty(
1830 storage->ancestorStorage,
1831 storePropertyIndex,
1832 &storeProperty);
1834 if(! hr)
1836 return E_FAIL;
1839 return S_OK;
1842 /*************************************************************************
1844 * Internal Method
1846 * This method takes the previous and the next property link of a property
1847 * to be deleted and find them a place in the Storage.
1849 static HRESULT adjustPropertyChain(
1850 StorageImpl *This,
1851 StgProperty propertyToDelete,
1852 StgProperty parentProperty,
1853 ULONG parentPropertyId,
1854 INT typeOfRelation)
1856 ULONG newLinkProperty = PROPERTY_NULL;
1857 BOOL needToFindAPlaceholder = FALSE;
1858 ULONG storeNode = PROPERTY_NULL;
1859 ULONG toStoreNode = PROPERTY_NULL;
1860 INT relationType = 0;
1861 HRESULT hr = S_OK;
1862 BOOL res = TRUE;
1864 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1866 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1869 * Set the parent previous to the property to delete previous
1871 newLinkProperty = propertyToDelete.previousProperty;
1873 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1876 * We also need to find a storage for the other link, setup variables
1877 * to do this at the end...
1879 needToFindAPlaceholder = TRUE;
1880 storeNode = propertyToDelete.previousProperty;
1881 toStoreNode = propertyToDelete.nextProperty;
1882 relationType = PROPERTY_RELATION_NEXT;
1885 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1888 * Set the parent previous to the property to delete next
1890 newLinkProperty = propertyToDelete.nextProperty;
1894 * Link it for real...
1896 parentProperty.previousProperty = newLinkProperty;
1899 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1901 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1904 * Set the parent next to the property to delete next previous
1906 newLinkProperty = propertyToDelete.previousProperty;
1908 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1911 * We also need to find a storage for the other link, setup variables
1912 * to do this at the end...
1914 needToFindAPlaceholder = TRUE;
1915 storeNode = propertyToDelete.previousProperty;
1916 toStoreNode = propertyToDelete.nextProperty;
1917 relationType = PROPERTY_RELATION_NEXT;
1920 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1923 * Set the parent next to the property to delete next
1925 newLinkProperty = propertyToDelete.nextProperty;
1929 * Link it for real...
1931 parentProperty.nextProperty = newLinkProperty;
1933 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
1935 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1938 * Set the parent dir to the property to delete previous
1940 newLinkProperty = propertyToDelete.previousProperty;
1942 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1945 * We also need to find a storage for the other link, setup variables
1946 * to do this at the end...
1948 needToFindAPlaceholder = TRUE;
1949 storeNode = propertyToDelete.previousProperty;
1950 toStoreNode = propertyToDelete.nextProperty;
1951 relationType = PROPERTY_RELATION_NEXT;
1954 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1957 * Set the parent dir to the property to delete next
1959 newLinkProperty = propertyToDelete.nextProperty;
1963 * Link it for real...
1965 parentProperty.dirProperty = newLinkProperty;
1969 * Write back the parent property
1971 res = StorageImpl_WriteProperty(
1972 This->ancestorStorage,
1973 parentPropertyId,
1974 &parentProperty);
1975 if(! res)
1977 return E_FAIL;
1981 * If a placeholder is required for the other link, then, find one and
1982 * get out of here...
1984 if (needToFindAPlaceholder)
1986 hr = findPlaceholder(
1987 This,
1988 toStoreNode,
1989 storeNode,
1990 relationType);
1993 return hr;
1997 /******************************************************************************
1998 * SetElementTimes (IStorage)
2000 HRESULT WINAPI StorageImpl_SetElementTimes(
2001 IStorage* iface,
2002 const OLECHAR *pwcsName,/* [string][in] */
2003 const FILETIME *pctime, /* [in] */
2004 const FILETIME *patime, /* [in] */
2005 const FILETIME *pmtime) /* [in] */
2007 FIXME(storage, "not implemented!\n");
2008 return E_NOTIMPL;
2011 /******************************************************************************
2012 * SetStateBits (IStorage)
2014 HRESULT WINAPI StorageImpl_SetStateBits(
2015 IStorage* iface,
2016 DWORD grfStateBits,/* [in] */
2017 DWORD grfMask) /* [in] */
2019 FIXME(storage, "not implemented!\n");
2020 return E_NOTIMPL;
2023 HRESULT StorageImpl_Construct(
2024 StorageImpl* This,
2025 HANDLE hFile,
2026 ILockBytes* pLkbyt,
2027 DWORD openFlags,
2028 BOOL fileBased)
2030 HRESULT hr = S_OK;
2031 StgProperty currentProperty;
2032 BOOL readSucessful;
2033 ULONG currentPropertyIndex;
2035 if ( FAILED( validateSTGM(openFlags) ))
2036 return STG_E_INVALIDFLAG;
2038 memset(This, 0, sizeof(StorageImpl));
2041 * Initialize the virtual fgunction table.
2043 This->lpvtbl = &Storage32Impl_Vtbl;
2044 This->v_destructor = &StorageImpl_Destroy;
2047 * This is the top-level storage so initialize the ancester pointer
2048 * to this.
2050 This->ancestorStorage = This;
2053 * Initialize the physical support of the storage.
2055 This->hFile = hFile;
2058 * Initialize the big block cache.
2060 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2061 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2062 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2063 pLkbyt,
2064 openFlags,
2065 This->bigBlockSize,
2066 fileBased);
2068 if (This->bigBlockFile == 0)
2069 return E_FAIL;
2071 if (openFlags & STGM_CREATE)
2073 ULARGE_INTEGER size;
2074 BYTE* bigBlockBuffer;
2077 * Initialize all header variables:
2078 * - The big block depot consists of one block and it is at block 0
2079 * - The properties start at block 1
2080 * - There is no small block depot
2082 memset( This->bigBlockDepotStart,
2083 BLOCK_UNUSED,
2084 sizeof(This->bigBlockDepotStart));
2086 This->bigBlockDepotCount = 1;
2087 This->bigBlockDepotStart[0] = 0;
2088 This->rootStartBlock = 1;
2089 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2090 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2091 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2092 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2093 This->extBigBlockDepotCount = 0;
2095 StorageImpl_SaveFileHeader(This);
2098 * Add one block for the big block depot and one block for the properties
2100 size.HighPart = 0;
2101 size.LowPart = This->bigBlockSize * 3;
2102 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2105 * Initialize the big block depot
2107 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2108 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2109 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2110 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2111 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2113 else
2116 * Load the header for the file.
2118 hr = StorageImpl_LoadFileHeader(This);
2120 if (FAILED(hr))
2122 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2124 return hr;
2129 * There is no block depot cached yet.
2131 This->indexBlockDepotCached = 0xFFFFFFFF;
2134 * Start searching for free blocks with block 0.
2136 This->prevFreeBlock = 0;
2139 * Create the block chain abstractions.
2141 This->rootBlockChain =
2142 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2144 This->smallBlockDepotChain = BlockChainStream_Construct(
2145 This,
2146 &This->smallBlockDepotStart,
2147 PROPERTY_NULL);
2150 * Write the root property
2152 if (openFlags & STGM_CREATE)
2154 StgProperty rootProp;
2156 * Initialize the property chain
2158 memset(&rootProp, 0, sizeof(rootProp));
2159 lstrcpyAtoW(rootProp.name, rootPropertyName);
2161 rootProp.sizeOfNameString = (lstrlenW(rootProp.name)+1) * sizeof(WCHAR);
2162 rootProp.propertyType = PROPTYPE_ROOT;
2163 rootProp.previousProperty = PROPERTY_NULL;
2164 rootProp.nextProperty = PROPERTY_NULL;
2165 rootProp.dirProperty = PROPERTY_NULL;
2166 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2167 rootProp.size.HighPart = 0;
2168 rootProp.size.LowPart = 0;
2170 StorageImpl_WriteProperty(This, 0, &rootProp);
2174 * Find the ID of the root int he property sets.
2176 currentPropertyIndex = 0;
2180 readSucessful = StorageImpl_ReadProperty(
2181 This,
2182 currentPropertyIndex,
2183 &currentProperty);
2185 if (readSucessful)
2187 if ( (currentProperty.sizeOfNameString != 0 ) &&
2188 (currentProperty.propertyType == PROPTYPE_ROOT) )
2190 This->rootPropertySetIndex = currentPropertyIndex;
2194 currentPropertyIndex++;
2196 } while (readSucessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2198 if (!readSucessful)
2200 /* TODO CLEANUP */
2201 return E_FAIL;
2205 * Create the block chain abstraction for the small block root chain.
2207 This->smallBlockRootChain = BlockChainStream_Construct(
2208 This,
2209 NULL,
2210 This->rootPropertySetIndex);
2212 return hr;
2215 void StorageImpl_Destroy(
2216 StorageImpl* This)
2218 TRACE(storage, "(%p)\n", This);
2220 BlockChainStream_Destroy(This->smallBlockRootChain);
2221 BlockChainStream_Destroy(This->rootBlockChain);
2222 BlockChainStream_Destroy(This->smallBlockDepotChain);
2224 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2225 return;
2228 /******************************************************************************
2229 * Storage32Impl_GetNextFreeBigBlock
2231 * Returns the index of the next free big block.
2232 * If the big block depot is filled, this method will enlarge it.
2235 ULONG StorageImpl_GetNextFreeBigBlock(
2236 StorageImpl* This)
2238 ULONG depotBlockIndexPos;
2239 void *depotBuffer;
2240 ULONG depotBlockOffset;
2241 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2242 ULONG nextBlockIndex = BLOCK_SPECIAL;
2243 int depotIndex = 0;
2244 ULONG freeBlock = BLOCK_UNUSED;
2246 depotIndex = This->prevFreeBlock / blocksPerDepot;
2247 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2250 * Scan the entire big block depot until we find a block marked free
2252 while (nextBlockIndex != BLOCK_UNUSED)
2254 if (depotIndex < COUNT_BBDEPOTINHEADER)
2256 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2259 * Grow the primary depot.
2261 if (depotBlockIndexPos == BLOCK_UNUSED)
2263 depotBlockIndexPos = depotIndex*blocksPerDepot;
2266 * Add a block depot.
2268 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2269 This->bigBlockDepotCount++;
2270 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2273 * Flag it as a block depot.
2275 StorageImpl_SetNextBlockInChain(This,
2276 depotBlockIndexPos,
2277 BLOCK_SPECIAL);
2279 /* Save new header information.
2281 StorageImpl_SaveFileHeader(This);
2284 else
2286 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2288 if (depotBlockIndexPos == BLOCK_UNUSED)
2291 * Grow the extended depot.
2293 ULONG extIndex = BLOCK_UNUSED;
2294 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2295 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2297 if (extBlockOffset == 0)
2299 /* We need an extended block.
2301 extIndex = Storage32Impl_AddExtBlockDepot(This);
2302 This->extBigBlockDepotCount++;
2303 depotBlockIndexPos = extIndex + 1;
2305 else
2306 depotBlockIndexPos = depotIndex * blocksPerDepot;
2309 * Add a block depot and mark it in the extended block.
2311 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2312 This->bigBlockDepotCount++;
2313 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2315 /* Flag the block depot.
2317 StorageImpl_SetNextBlockInChain(This,
2318 depotBlockIndexPos,
2319 BLOCK_SPECIAL);
2321 /* If necessary, flag the extended depot block.
2323 if (extIndex != BLOCK_UNUSED)
2324 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2326 /* Save header information.
2328 StorageImpl_SaveFileHeader(This);
2332 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2334 if (depotBuffer != 0)
2336 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2337 ( nextBlockIndex != BLOCK_UNUSED))
2339 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2341 if (nextBlockIndex == BLOCK_UNUSED)
2343 freeBlock = (depotIndex * blocksPerDepot) +
2344 (depotBlockOffset/sizeof(ULONG));
2347 depotBlockOffset += sizeof(ULONG);
2350 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2353 depotIndex++;
2354 depotBlockOffset = 0;
2357 This->prevFreeBlock = freeBlock;
2359 return freeBlock;
2362 /******************************************************************************
2363 * Storage32Impl_AddBlockDepot
2365 * This will create a depot block, essentially it is a block initialized
2366 * to BLOCK_UNUSEDs.
2368 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2370 BYTE* blockBuffer;
2372 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2375 * Initialize blocks as free
2377 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2379 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2382 /******************************************************************************
2383 * Storage32Impl_GetExtDepotBlock
2385 * Returns the index of the block that corresponds to the specified depot
2386 * index. This method is only for depot indexes equal or greater than
2387 * COUNT_BBDEPOTINHEADER.
2389 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2391 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2392 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2393 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2394 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2395 ULONG blockIndex = BLOCK_UNUSED;
2396 ULONG extBlockIndex = This->extBigBlockDepotStart;
2398 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2400 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2401 return BLOCK_UNUSED;
2403 while (extBlockCount > 0)
2405 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2406 extBlockCount--;
2409 if (extBlockIndex != BLOCK_UNUSED)
2411 BYTE* depotBuffer;
2413 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2415 if (depotBuffer != 0)
2417 StorageUtl_ReadDWord(depotBuffer,
2418 extBlockOffset * sizeof(ULONG),
2419 &blockIndex);
2421 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2425 return blockIndex;
2428 /******************************************************************************
2429 * Storage32Impl_SetExtDepotBlock
2431 * Associates the specified block index to the specified depot index.
2432 * This method is only for depot indexes equal or greater than
2433 * COUNT_BBDEPOTINHEADER.
2435 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2436 ULONG depotIndex,
2437 ULONG blockIndex)
2439 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2440 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2441 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2442 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2443 ULONG extBlockIndex = This->extBigBlockDepotStart;
2445 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2447 while (extBlockCount > 0)
2449 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2450 extBlockCount--;
2453 if (extBlockIndex != BLOCK_UNUSED)
2455 BYTE* depotBuffer;
2457 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2459 if (depotBuffer != 0)
2461 StorageUtl_WriteDWord(depotBuffer,
2462 extBlockOffset * sizeof(ULONG),
2463 blockIndex);
2465 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2470 /******************************************************************************
2471 * Storage32Impl_AddExtBlockDepot
2473 * Creates an extended depot block.
2475 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2477 ULONG numExtBlocks = This->extBigBlockDepotCount;
2478 ULONG nextExtBlock = This->extBigBlockDepotStart;
2479 BYTE* depotBuffer = NULL;
2480 ULONG index = BLOCK_UNUSED;
2481 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2482 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2483 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2485 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2486 blocksPerDepotBlock;
2488 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2491 * The first extended block.
2493 This->extBigBlockDepotStart = index;
2495 else
2497 int i;
2499 * Follow the chain to the last one.
2501 for (i = 0; i < (numExtBlocks - 1); i++)
2503 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2507 * Add the new extended block to the chain.
2509 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2510 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2511 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2515 * Initialize this block.
2517 depotBuffer = StorageImpl_GetBigBlock(This, index);
2518 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2519 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2521 return index;
2524 /******************************************************************************
2525 * Storage32Impl_FreeBigBlock
2527 * This method will flag the specified block as free in the big block depot.
2529 void StorageImpl_FreeBigBlock(
2530 StorageImpl* This,
2531 ULONG blockIndex)
2533 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2535 if (blockIndex < This->prevFreeBlock)
2536 This->prevFreeBlock = blockIndex;
2539 /************************************************************************
2540 * Storage32Impl_GetNextBlockInChain
2542 * This method will retrieve the block index of the next big block in
2543 * in the chain.
2545 * Params: This - Pointer to the Storage object.
2546 * blockIndex - Index of the block to retrieve the chain
2547 * for.
2549 * Returns: This method returns the index of the next block in the chain.
2550 * It will return the constants:
2551 * BLOCK_SPECIAL - If the block given was not part of a
2552 * chain.
2553 * BLOCK_END_OF_CHAIN - If the block given was the last in
2554 * a chain.
2555 * BLOCK_UNUSED - If the block given was not past of a chain
2556 * and is available.
2557 * BLOCK_EXTBBDEPOT - This block is part of the extended
2558 * big block depot.
2560 * See Windows documentation for more details on IStorage methods.
2562 ULONG StorageImpl_GetNextBlockInChain(
2563 StorageImpl* This,
2564 ULONG blockIndex)
2566 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2567 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2568 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2569 ULONG nextBlockIndex = BLOCK_SPECIAL;
2570 void* depotBuffer;
2571 ULONG depotBlockIndexPos;
2573 assert(depotBlockCount < This->bigBlockDepotCount);
2576 * Cache the currently accessed depot block.
2578 if (depotBlockCount != This->indexBlockDepotCached)
2580 This->indexBlockDepotCached = depotBlockCount;
2582 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2584 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2586 else
2589 * We have to look in the extended depot.
2591 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2594 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2596 if (depotBuffer!=0)
2598 int index;
2600 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2602 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2603 This->blockDepotCached[index] = nextBlockIndex;
2606 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2610 nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2612 return nextBlockIndex;
2615 /******************************************************************************
2616 * Storage32Impl_GetNextExtendedBlock
2618 * Given an extended block this method will return the next extended block.
2620 * NOTES:
2621 * The last ULONG of an extended block is the block index of the next
2622 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2623 * depot.
2625 * Return values:
2626 * - The index of the next extended block
2627 * - BLOCK_UNUSED: there is no next extended block.
2628 * - Any other return values denotes failure.
2630 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2632 ULONG nextBlockIndex = BLOCK_SPECIAL;
2633 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2634 void* depotBuffer;
2636 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2638 if (depotBuffer!=0)
2640 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2642 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2645 return nextBlockIndex;
2648 /******************************************************************************
2649 * Storage32Impl_SetNextBlockInChain
2651 * This method will write the index of the specified block's next block
2652 * in the big block depot.
2654 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2655 * do the following
2657 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2658 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2659 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2662 void StorageImpl_SetNextBlockInChain(
2663 StorageImpl* This,
2664 ULONG blockIndex,
2665 ULONG nextBlock)
2667 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2668 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2669 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2670 ULONG depotBlockIndexPos;
2671 void* depotBuffer;
2673 assert(depotBlockCount < This->bigBlockDepotCount);
2674 assert(blockIndex != nextBlock);
2676 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2678 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2680 else
2683 * We have to look in the extended depot.
2685 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2688 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2690 if (depotBuffer!=0)
2692 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2693 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2697 * Update the cached block depot, if necessary.
2699 if (depotBlockCount == This->indexBlockDepotCached)
2701 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2705 /******************************************************************************
2706 * Storage32Impl_LoadFileHeader
2708 * This method will read in the file header, i.e. big block index -1.
2710 HRESULT StorageImpl_LoadFileHeader(
2711 StorageImpl* This)
2713 HRESULT hr = STG_E_FILENOTFOUND;
2714 void* headerBigBlock = NULL;
2715 int index;
2718 * Get a pointer to the big block of data containing the header.
2720 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2723 * Extract the information from the header.
2725 if (headerBigBlock!=0)
2728 * Check for the "magic number" signature and return an error if it is not
2729 * found.
2731 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2733 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2734 return STG_E_OLDFORMAT;
2737 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2739 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2740 return STG_E_INVALIDHEADER;
2743 StorageUtl_ReadWord(
2744 headerBigBlock,
2745 OFFSET_BIGBLOCKSIZEBITS,
2746 &This->bigBlockSizeBits);
2748 StorageUtl_ReadWord(
2749 headerBigBlock,
2750 OFFSET_SMALLBLOCKSIZEBITS,
2751 &This->smallBlockSizeBits);
2753 StorageUtl_ReadDWord(
2754 headerBigBlock,
2755 OFFSET_BBDEPOTCOUNT,
2756 &This->bigBlockDepotCount);
2758 StorageUtl_ReadDWord(
2759 headerBigBlock,
2760 OFFSET_ROOTSTARTBLOCK,
2761 &This->rootStartBlock);
2763 StorageUtl_ReadDWord(
2764 headerBigBlock,
2765 OFFSET_SBDEPOTSTART,
2766 &This->smallBlockDepotStart);
2768 StorageUtl_ReadDWord(
2769 headerBigBlock,
2770 OFFSET_EXTBBDEPOTSTART,
2771 &This->extBigBlockDepotStart);
2773 StorageUtl_ReadDWord(
2774 headerBigBlock,
2775 OFFSET_EXTBBDEPOTCOUNT,
2776 &This->extBigBlockDepotCount);
2778 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2780 StorageUtl_ReadDWord(
2781 headerBigBlock,
2782 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2783 &(This->bigBlockDepotStart[index]));
2787 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2789 if ((1 << 2) == 4)
2791 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2792 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2794 else
2796 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2797 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2801 * Right now, the code is making some assumptions about the size of the
2802 * blocks, just make sure they are what we're expecting.
2804 assert( (This->bigBlockSize==DEF_BIG_BLOCK_SIZE) &&
2805 (This->smallBlockSize==DEF_SMALL_BLOCK_SIZE));
2808 * Release the block.
2810 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2812 hr = S_OK;
2815 return hr;
2818 /******************************************************************************
2819 * Storage32Impl_SaveFileHeader
2821 * This method will save to the file the header, i.e. big block -1.
2823 void StorageImpl_SaveFileHeader(
2824 StorageImpl* This)
2826 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2827 int index;
2828 BOOL success;
2831 * Get a pointer to the big block of data containing the header.
2833 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2836 * If the block read failed, the file is probably new.
2838 if (!success)
2841 * Initialize for all unknown fields.
2843 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2846 * Initialize the magic number.
2848 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2851 * And a bunch of things we don't know what they mean
2853 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2854 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2855 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2856 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2857 StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
2861 * Write the information to the header.
2863 if (headerBigBlock!=0)
2865 StorageUtl_WriteWord(
2866 headerBigBlock,
2867 OFFSET_BIGBLOCKSIZEBITS,
2868 This->bigBlockSizeBits);
2870 StorageUtl_WriteWord(
2871 headerBigBlock,
2872 OFFSET_SMALLBLOCKSIZEBITS,
2873 This->smallBlockSizeBits);
2875 StorageUtl_WriteDWord(
2876 headerBigBlock,
2877 OFFSET_BBDEPOTCOUNT,
2878 This->bigBlockDepotCount);
2880 StorageUtl_WriteDWord(
2881 headerBigBlock,
2882 OFFSET_ROOTSTARTBLOCK,
2883 This->rootStartBlock);
2885 StorageUtl_WriteDWord(
2886 headerBigBlock,
2887 OFFSET_SBDEPOTSTART,
2888 This->smallBlockDepotStart);
2890 StorageUtl_WriteDWord(
2891 headerBigBlock,
2892 OFFSET_EXTBBDEPOTSTART,
2893 This->extBigBlockDepotStart);
2895 StorageUtl_WriteDWord(
2896 headerBigBlock,
2897 OFFSET_EXTBBDEPOTCOUNT,
2898 This->extBigBlockDepotCount);
2900 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2902 StorageUtl_WriteDWord(
2903 headerBigBlock,
2904 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2905 (This->bigBlockDepotStart[index]));
2910 * Write the big block back to the file.
2912 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2915 /******************************************************************************
2916 * Storage32Impl_ReadProperty
2918 * This method will read the specified property from the property chain.
2920 BOOL StorageImpl_ReadProperty(
2921 StorageImpl* This,
2922 ULONG index,
2923 StgProperty* buffer)
2925 BYTE currentProperty[PROPSET_BLOCK_SIZE];
2926 ULARGE_INTEGER offsetInPropSet;
2927 BOOL readSucessful;
2928 ULONG bytesRead;
2930 offsetInPropSet.HighPart = 0;
2931 offsetInPropSet.LowPart = index * PROPSET_BLOCK_SIZE;
2933 readSucessful = BlockChainStream_ReadAt(
2934 This->rootBlockChain,
2935 offsetInPropSet,
2936 PROPSET_BLOCK_SIZE,
2937 currentProperty,
2938 &bytesRead);
2940 if (readSucessful)
2942 memset(buffer->name, 0, sizeof(buffer->name));
2943 memcpy(
2944 buffer->name,
2945 currentProperty+OFFSET_PS_NAME,
2946 PROPERTY_NAME_BUFFER_LEN );
2948 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
2950 StorageUtl_ReadWord(
2951 currentProperty,
2952 OFFSET_PS_NAMELENGTH,
2953 &buffer->sizeOfNameString);
2955 StorageUtl_ReadDWord(
2956 currentProperty,
2957 OFFSET_PS_PREVIOUSPROP,
2958 &buffer->previousProperty);
2960 StorageUtl_ReadDWord(
2961 currentProperty,
2962 OFFSET_PS_NEXTPROP,
2963 &buffer->nextProperty);
2965 StorageUtl_ReadDWord(
2966 currentProperty,
2967 OFFSET_PS_DIRPROP,
2968 &buffer->dirProperty);
2970 StorageUtl_ReadGUID(
2971 currentProperty,
2972 OFFSET_PS_GUID,
2973 &buffer->propertyUniqueID);
2975 StorageUtl_ReadDWord(
2976 currentProperty,
2977 OFFSET_PS_TSS1,
2978 &buffer->timeStampS1);
2980 StorageUtl_ReadDWord(
2981 currentProperty,
2982 OFFSET_PS_TSD1,
2983 &buffer->timeStampD1);
2985 StorageUtl_ReadDWord(
2986 currentProperty,
2987 OFFSET_PS_TSS2,
2988 &buffer->timeStampS2);
2990 StorageUtl_ReadDWord(
2991 currentProperty,
2992 OFFSET_PS_TSD2,
2993 &buffer->timeStampD2);
2995 StorageUtl_ReadDWord(
2996 currentProperty,
2997 OFFSET_PS_STARTBLOCK,
2998 &buffer->startingBlock);
3000 StorageUtl_ReadDWord(
3001 currentProperty,
3002 OFFSET_PS_SIZE,
3003 &buffer->size.LowPart);
3005 buffer->size.HighPart = 0;
3008 return readSucessful;
3011 /*********************************************************************
3012 * Write the specified property into the property chain
3014 BOOL StorageImpl_WriteProperty(
3015 StorageImpl* This,
3016 ULONG index,
3017 StgProperty* buffer)
3019 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3020 ULARGE_INTEGER offsetInPropSet;
3021 BOOL writeSucessful;
3022 ULONG bytesWritten;
3024 offsetInPropSet.HighPart = 0;
3025 offsetInPropSet.LowPart = index * PROPSET_BLOCK_SIZE;
3027 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3029 memcpy(
3030 currentProperty + OFFSET_PS_NAME,
3031 buffer->name,
3032 PROPERTY_NAME_BUFFER_LEN );
3034 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3037 * Reassign the size in case of mistake....
3039 buffer->sizeOfNameString = (lstrlenW(buffer->name)+1) * sizeof(WCHAR);
3041 StorageUtl_WriteWord(
3042 currentProperty,
3043 OFFSET_PS_NAMELENGTH,
3044 buffer->sizeOfNameString);
3046 StorageUtl_WriteDWord(
3047 currentProperty,
3048 OFFSET_PS_PREVIOUSPROP,
3049 buffer->previousProperty);
3051 StorageUtl_WriteDWord(
3052 currentProperty,
3053 OFFSET_PS_NEXTPROP,
3054 buffer->nextProperty);
3056 StorageUtl_WriteDWord(
3057 currentProperty,
3058 OFFSET_PS_DIRPROP,
3059 buffer->dirProperty);
3061 StorageUtl_WriteGUID(
3062 currentProperty,
3063 OFFSET_PS_GUID,
3064 &buffer->propertyUniqueID);
3066 StorageUtl_WriteDWord(
3067 currentProperty,
3068 OFFSET_PS_TSS1,
3069 buffer->timeStampS1);
3071 StorageUtl_WriteDWord(
3072 currentProperty,
3073 OFFSET_PS_TSD1,
3074 buffer->timeStampD1);
3076 StorageUtl_WriteDWord(
3077 currentProperty,
3078 OFFSET_PS_TSS2,
3079 buffer->timeStampS2);
3081 StorageUtl_WriteDWord(
3082 currentProperty,
3083 OFFSET_PS_TSD2,
3084 buffer->timeStampD2);
3086 StorageUtl_WriteDWord(
3087 currentProperty,
3088 OFFSET_PS_STARTBLOCK,
3089 buffer->startingBlock);
3091 StorageUtl_WriteDWord(
3092 currentProperty,
3093 OFFSET_PS_SIZE,
3094 buffer->size.LowPart);
3096 writeSucessful = BlockChainStream_WriteAt(This->rootBlockChain,
3097 offsetInPropSet,
3098 PROPSET_BLOCK_SIZE,
3099 currentProperty,
3100 &bytesWritten);
3101 return writeSucessful;
3104 BOOL StorageImpl_ReadBigBlock(
3105 StorageImpl* This,
3106 ULONG blockIndex,
3107 void* buffer)
3109 void* bigBlockBuffer;
3111 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3113 if (bigBlockBuffer!=0)
3115 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3117 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3119 return TRUE;
3122 return FALSE;
3125 BOOL StorageImpl_WriteBigBlock(
3126 StorageImpl* This,
3127 ULONG blockIndex,
3128 void* buffer)
3130 void* bigBlockBuffer;
3132 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3134 if (bigBlockBuffer!=0)
3136 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3138 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3140 return TRUE;
3143 return FALSE;
3146 void* StorageImpl_GetROBigBlock(
3147 StorageImpl* This,
3148 ULONG blockIndex)
3150 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3153 void* StorageImpl_GetBigBlock(
3154 StorageImpl* This,
3155 ULONG blockIndex)
3157 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3160 void StorageImpl_ReleaseBigBlock(
3161 StorageImpl* This,
3162 void* pBigBlock)
3164 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3167 /******************************************************************************
3168 * Storage32Impl_SmallBlocksToBigBlocks
3170 * This method will convert a small block chain to a big block chain.
3171 * The small block chain will be destroyed.
3173 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3174 StorageImpl* This,
3175 SmallBlockChainStream** ppsbChain)
3177 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3178 ULARGE_INTEGER size, offset;
3179 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3180 ULONG propertyIndex;
3181 BOOL successRead, successWrite;
3182 StgProperty chainProperty;
3183 BYTE buffer[DEF_SMALL_BLOCK_SIZE];
3184 BlockChainStream *bbTempChain = NULL;
3185 BlockChainStream *bigBlockChain = NULL;
3188 * Create a temporary big block chain that doesn't have
3189 * an associated property. This temporary chain will be
3190 * used to copy data from small blocks to big blocks.
3192 bbTempChain = BlockChainStream_Construct(This,
3193 &bbHeadOfChain,
3194 PROPERTY_NULL);
3197 * Grow the big block chain.
3199 size = SmallBlockChainStream_GetSize(*ppsbChain);
3200 BlockChainStream_SetSize(bbTempChain, size);
3203 * Copy the contents of the small block chain to the big block chain
3204 * by small block size increments.
3206 offset.LowPart = 0;
3207 offset.HighPart = 0;
3208 cbTotalRead = 0;
3209 cbTotalWritten = 0;
3213 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3214 offset,
3215 sizeof(buffer),
3216 buffer,
3217 &cbRead);
3218 cbTotalRead += cbRead;
3220 successWrite = BlockChainStream_WriteAt(bbTempChain,
3221 offset,
3222 cbRead,
3223 buffer,
3224 &cbWritten);
3225 cbTotalWritten += cbWritten;
3227 offset.LowPart += This->smallBlockSize;
3229 } while (successRead && successWrite);
3231 assert(cbTotalRead == cbTotalWritten);
3234 * Destroy the small block chain.
3236 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3237 size.HighPart = 0;
3238 size.LowPart = 0;
3239 SmallBlockChainStream_SetSize(*ppsbChain, size);
3240 SmallBlockChainStream_Destroy(*ppsbChain);
3241 *ppsbChain = 0;
3244 * Change the property information. This chain is now a big block chain
3245 * and it doesn't reside in the small blocks chain anymore.
3247 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3249 chainProperty.startingBlock = bbHeadOfChain;
3251 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3254 * Destroy the temporary propertyless big block chain.
3255 * Create a new big block chain associated with this property.
3257 BlockChainStream_Destroy(bbTempChain);
3258 bigBlockChain = BlockChainStream_Construct(This,
3259 NULL,
3260 propertyIndex);
3262 return bigBlockChain;
3265 /******************************************************************************
3266 ** Storage32InternalImpl implementation
3269 StorageInternalImpl* StorageInternalImpl_Construct(
3270 StorageImpl* ancestorStorage,
3271 ULONG rootPropertyIndex)
3273 StorageInternalImpl* newStorage;
3276 * Allocate space for the new storage object
3278 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3280 if (newStorage!=0)
3282 memset(newStorage, 0, sizeof(StorageInternalImpl));
3285 * Initialize the virtual function table.
3287 newStorage->lpvtbl = &Storage32InternalImpl_Vtbl;
3288 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3291 * Keep the ancestor storage pointer and nail a reference to it.
3293 newStorage->ancestorStorage = ancestorStorage;
3294 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3297 * Keep the index of the root property set for this storage,
3299 newStorage->rootPropertySetIndex = rootPropertyIndex;
3301 return newStorage;
3304 return 0;
3307 void StorageInternalImpl_Destroy(
3308 StorageInternalImpl* This)
3310 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3311 HeapFree(GetProcessHeap(), 0, This);
3314 /******************************************************************************
3316 ** Storage32InternalImpl_Commit
3318 ** The non-root storages cannot be opened in transacted mode thus this function
3319 ** does nothing.
3321 HRESULT WINAPI StorageInternalImpl_Commit(
3322 IStorage* iface,
3323 DWORD grfCommitFlags) /* [in] */
3325 return S_OK;
3328 /******************************************************************************
3330 ** Storage32InternalImpl_Revert
3332 ** The non-root storages cannot be opened in transacted mode thus this function
3333 ** does nothing.
3335 HRESULT WINAPI StorageInternalImpl_Revert(
3336 IStorage* iface)
3338 return S_OK;
3341 /******************************************************************************
3342 ** IEnumSTATSTGImpl implementation
3345 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3346 StorageImpl* parentStorage,
3347 ULONG firstPropertyNode)
3349 IEnumSTATSTGImpl* newEnumeration;
3351 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3353 if (newEnumeration!=0)
3356 * Set-up the virtual function table and reference count.
3358 newEnumeration->lpvtbl = &IEnumSTATSTGImpl_Vtbl;
3359 newEnumeration->ref = 0;
3362 * We want to nail-down the reference to the storage in case the
3363 * enumeration out-lives the storage in the client application.
3365 newEnumeration->parentStorage = parentStorage;
3366 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3368 newEnumeration->firstPropertyNode = firstPropertyNode;
3371 * Initialize the search stack
3373 newEnumeration->stackSize = 0;
3374 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3375 newEnumeration->stackToVisit =
3376 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3379 * Make sure the current node of the iterator is the first one.
3381 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3384 return newEnumeration;
3387 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3389 IStorage_Release((IStorage*)This->parentStorage);
3390 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3391 HeapFree(GetProcessHeap(), 0, This);
3394 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3395 IEnumSTATSTG* iface,
3396 REFIID riid,
3397 void** ppvObject)
3399 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3402 * Perform a sanity check on the parameters.
3404 if (ppvObject==0)
3405 return E_INVALIDARG;
3408 * Initialize the return parameter.
3410 *ppvObject = 0;
3413 * Compare the riid with the interface IDs implemented by this object.
3415 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3417 *ppvObject = (IEnumSTATSTG*)This;
3419 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3421 *ppvObject = (IEnumSTATSTG*)This;
3425 * Check that we obtained an interface.
3427 if ((*ppvObject)==0)
3428 return E_NOINTERFACE;
3431 * Query Interface always increases the reference count by one when it is
3432 * successful
3434 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3436 return S_OK;
3439 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3440 IEnumSTATSTG* iface)
3442 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3444 This->ref++;
3445 return This->ref;
3448 ULONG WINAPI IEnumSTATSTGImpl_Release(
3449 IEnumSTATSTG* iface)
3451 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3453 ULONG newRef;
3455 This->ref--;
3456 newRef = This->ref;
3459 * If the reference count goes down to 0, perform suicide.
3461 if (newRef==0)
3463 IEnumSTATSTGImpl_Destroy(This);
3466 return newRef;;
3469 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3470 IEnumSTATSTG* iface,
3471 ULONG celt,
3472 STATSTG* rgelt,
3473 ULONG* pceltFetched)
3475 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3477 StgProperty currentProperty;
3478 STATSTG* currentReturnStruct = rgelt;
3479 ULONG objectFetched = 0;
3480 ULONG currentSearchNode;
3483 * Perform a sanity check on the parameters.
3485 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3486 return E_INVALIDARG;
3489 * To avoid the special case, get another pointer to a ULONG value if
3490 * the caller didn't supply one.
3492 if (pceltFetched==0)
3493 pceltFetched = &objectFetched;
3496 * Start the iteration, we will iterate until we hit the end of the
3497 * linked list or until we hit the number of items to iterate through
3499 *pceltFetched = 0;
3502 * Start with the node at the top of the stack.
3504 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3506 while ( ( *pceltFetched < celt) &&
3507 ( currentSearchNode!=PROPERTY_NULL) )
3510 * Remove the top node from the stack
3512 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3515 * Read the property from the storage.
3517 StorageImpl_ReadProperty(This->parentStorage,
3518 currentSearchNode,
3519 &currentProperty);
3522 * Copy the information to the return buffer.
3524 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3525 &currentProperty,
3526 STATFLAG_DEFAULT);
3529 * Step to the next item in the iteration
3531 (*pceltFetched)++;
3532 currentReturnStruct++;
3535 * Push the next search node in the search stack.
3537 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3540 * continue the iteration.
3542 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3545 if (*pceltFetched == celt)
3546 return S_OK;
3548 return S_FALSE;
3552 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3553 IEnumSTATSTG* iface,
3554 ULONG celt)
3556 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3558 StgProperty currentProperty;
3559 ULONG objectFetched = 0;
3560 ULONG currentSearchNode;
3563 * Start with the node at the top of the stack.
3565 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3567 while ( (objectFetched < celt) &&
3568 (currentSearchNode!=PROPERTY_NULL) )
3571 * Remove the top node from the stack
3573 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3576 * Read the property from the storage.
3578 StorageImpl_ReadProperty(This->parentStorage,
3579 currentSearchNode,
3580 &currentProperty);
3583 * Step to the next item in the iteration
3585 objectFetched++;
3588 * Push the next search node in the search stack.
3590 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3593 * continue the iteration.
3595 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3598 if (objectFetched == celt)
3599 return S_OK;
3601 return S_FALSE;
3604 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3605 IEnumSTATSTG* iface)
3607 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3609 StgProperty rootProperty;
3610 BOOL readSucessful;
3613 * Re-initialize the search stack to an empty stack
3615 This->stackSize = 0;
3618 * Read the root property from the storage.
3620 readSucessful = StorageImpl_ReadProperty(
3621 This->parentStorage,
3622 This->firstPropertyNode,
3623 &rootProperty);
3625 if (readSucessful)
3627 assert(rootProperty.sizeOfNameString!=0);
3630 * Push the search node in the search stack.
3632 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3635 return S_OK;
3638 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3639 IEnumSTATSTG* iface,
3640 IEnumSTATSTG** ppenum)
3642 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3644 IEnumSTATSTGImpl* newClone;
3647 * Perform a sanity check on the parameters.
3649 if (ppenum==0)
3650 return E_INVALIDARG;
3652 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3653 This->firstPropertyNode);
3657 * The new clone enumeration must point to the same current node as
3658 * the ole one.
3660 newClone->stackSize = This->stackSize ;
3661 newClone->stackMaxSize = This->stackMaxSize ;
3662 newClone->stackToVisit =
3663 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3665 memcpy(
3666 newClone->stackToVisit,
3667 This->stackToVisit,
3668 sizeof(ULONG) * newClone->stackSize);
3670 *ppenum = (IEnumSTATSTG*)newClone;
3673 * Don't forget to nail down a reference to the clone before
3674 * returning it.
3676 IEnumSTATSTGImpl_AddRef(*ppenum);
3678 return S_OK;
3681 INT IEnumSTATSTGImpl_FindParentProperty(
3682 IEnumSTATSTGImpl *This,
3683 ULONG childProperty,
3684 StgProperty *currentProperty,
3685 ULONG *thisNodeId)
3687 ULONG currentSearchNode;
3688 ULONG foundNode;
3691 * To avoid the special case, get another pointer to a ULONG value if
3692 * the caller didn't supply one.
3694 if (thisNodeId==0)
3695 thisNodeId = &foundNode;
3698 * Start with the node at the top of the stack.
3700 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3703 while (currentSearchNode!=PROPERTY_NULL)
3706 * Store the current node in the returned parameters
3708 *thisNodeId = currentSearchNode;
3711 * Remove the top node from the stack
3713 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3716 * Read the property from the storage.
3718 StorageImpl_ReadProperty(
3719 This->parentStorage,
3720 currentSearchNode,
3721 currentProperty);
3723 if (currentProperty->previousProperty == childProperty)
3724 return PROPERTY_RELATION_PREVIOUS;
3726 else if (currentProperty->nextProperty == childProperty)
3727 return PROPERTY_RELATION_NEXT;
3729 else if (currentProperty->dirProperty == childProperty)
3730 return PROPERTY_RELATION_DIR;
3733 * Push the next search node in the search stack.
3735 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3738 * continue the iteration.
3740 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3743 return PROPERTY_NULL;
3746 ULONG IEnumSTATSTGImpl_FindProperty(
3747 IEnumSTATSTGImpl* This,
3748 const OLECHAR* lpszPropName,
3749 StgProperty* currentProperty)
3751 ULONG currentSearchNode;
3754 * Start with the node at the top of the stack.
3756 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3758 while (currentSearchNode!=PROPERTY_NULL)
3761 * Remove the top node from the stack
3763 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3766 * Read the property from the storage.
3768 StorageImpl_ReadProperty(This->parentStorage,
3769 currentSearchNode,
3770 currentProperty);
3772 if ( propertyNameCmp(
3773 (OLECHAR*)currentProperty->name,
3774 (OLECHAR*)lpszPropName) == 0)
3775 return currentSearchNode;
3778 * Push the next search node in the search stack.
3780 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3783 * continue the iteration.
3785 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3788 return PROPERTY_NULL;
3791 void IEnumSTATSTGImpl_PushSearchNode(
3792 IEnumSTATSTGImpl* This,
3793 ULONG nodeToPush)
3795 StgProperty rootProperty;
3796 BOOL readSucessful;
3799 * First, make sure we're not trying to push an unexisting node.
3801 if (nodeToPush==PROPERTY_NULL)
3802 return;
3805 * First push the node to the stack
3807 if (This->stackSize == This->stackMaxSize)
3809 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3811 This->stackToVisit = HeapReAlloc(
3812 GetProcessHeap(),
3814 This->stackToVisit,
3815 sizeof(ULONG) * This->stackMaxSize);
3818 This->stackToVisit[This->stackSize] = nodeToPush;
3819 This->stackSize++;
3822 * Read the root property from the storage.
3824 readSucessful = StorageImpl_ReadProperty(
3825 This->parentStorage,
3826 nodeToPush,
3827 &rootProperty);
3829 if (readSucessful)
3831 assert(rootProperty.sizeOfNameString!=0);
3834 * Push the previous search node in the search stack.
3836 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3840 ULONG IEnumSTATSTGImpl_PopSearchNode(
3841 IEnumSTATSTGImpl* This,
3842 BOOL remove)
3844 ULONG topNode;
3846 if (This->stackSize == 0)
3847 return PROPERTY_NULL;
3849 topNode = This->stackToVisit[This->stackSize-1];
3851 if (remove)
3852 This->stackSize--;
3854 return topNode;
3857 /******************************************************************************
3858 ** StorageUtl implementation
3861 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
3863 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
3866 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
3868 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
3871 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
3873 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
3876 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
3878 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
3881 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
3883 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
3884 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
3885 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
3887 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
3890 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
3892 StorageUtl_WriteDWord(buffer, offset, value->Data1);
3893 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
3894 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
3896 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
3899 void StorageUtl_CopyPropertyToSTATSTG(
3900 STATSTG* destination,
3901 StgProperty* source,
3902 int statFlags)
3905 * The copy of the string occurs only when the flag is not set
3907 if ((statFlags & STATFLAG_NONAME) != 0)
3909 destination->pwcsName = 0;
3911 else
3913 destination->pwcsName =
3914 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
3916 lstrcpyW((LPWSTR)destination->pwcsName, source->name);
3919 switch (source->propertyType)
3921 case PROPTYPE_STORAGE:
3922 case PROPTYPE_ROOT:
3923 destination->type = STGTY_STORAGE;
3924 break;
3925 case PROPTYPE_STREAM:
3926 destination->type = STGTY_STREAM;
3927 break;
3928 default:
3929 destination->type = STGTY_STREAM;
3930 break;
3933 destination->cbSize = source->size;
3935 currentReturnStruct->mtime = {0}; TODO
3936 currentReturnStruct->ctime = {0};
3937 currentReturnStruct->atime = {0};
3939 destination->grfMode = 0;
3940 destination->grfLocksSupported = 0;
3941 destination->clsid = source->propertyUniqueID;
3942 destination->grfStateBits = 0;
3943 destination->reserved = 0;
3946 /******************************************************************************
3947 ** BlockChainStream implementation
3950 BlockChainStream* BlockChainStream_Construct(
3951 StorageImpl* parentStorage,
3952 ULONG* headOfStreamPlaceHolder,
3953 ULONG propertyIndex)
3955 BlockChainStream* newStream;
3956 ULONG blockIndex;
3958 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
3960 newStream->parentStorage = parentStorage;
3961 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
3962 newStream->ownerPropertyIndex = propertyIndex;
3963 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
3964 newStream->tailIndex = BLOCK_END_OF_CHAIN;
3965 newStream->numBlocks = 0;
3967 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
3969 while (blockIndex != BLOCK_END_OF_CHAIN)
3971 newStream->numBlocks++;
3972 newStream->tailIndex = blockIndex;
3974 blockIndex = StorageImpl_GetNextBlockInChain(
3975 parentStorage,
3976 blockIndex);
3979 return newStream;
3982 void BlockChainStream_Destroy(BlockChainStream* This)
3984 HeapFree(GetProcessHeap(), 0, This);
3987 /******************************************************************************
3988 * BlockChainStream_GetHeadOfChain
3990 * Returns the head of this stream chain.
3991 * Some special chains don't have properties, their heads are kept in
3992 * This->headOfStreamPlaceHolder.
3995 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
3997 StgProperty chainProperty;
3998 BOOL readSucessful;
4000 if (This->headOfStreamPlaceHolder != 0)
4001 return *(This->headOfStreamPlaceHolder);
4003 if (This->ownerPropertyIndex != PROPERTY_NULL)
4005 readSucessful = StorageImpl_ReadProperty(
4006 This->parentStorage,
4007 This->ownerPropertyIndex,
4008 &chainProperty);
4010 if (readSucessful)
4012 return chainProperty.startingBlock;
4016 return BLOCK_END_OF_CHAIN;
4019 /******************************************************************************
4020 * BlockChainStream_GetCount
4022 * Returns the number of blocks that comprises this chain.
4023 * This is not the size of the stream as the last block may not be full!
4026 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4028 ULONG blockIndex;
4029 ULONG count = 0;
4031 blockIndex = BlockChainStream_GetHeadOfChain(This);
4033 while (blockIndex != BLOCK_END_OF_CHAIN)
4035 count++;
4037 blockIndex = StorageImpl_GetNextBlockInChain(
4038 This->parentStorage,
4039 blockIndex);
4042 return count;
4045 /******************************************************************************
4046 * BlockChainStream_ReadAt
4048 * Reads a specified number of bytes from this chain at the specified offset.
4049 * bytesRead may be NULL.
4050 * Failure will be returned if the specified number of bytes has not been read.
4052 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4053 ULARGE_INTEGER offset,
4054 ULONG size,
4055 void* buffer,
4056 ULONG* bytesRead)
4058 ULONG blockNoInSequence = offset.LowPart / This->parentStorage->bigBlockSize;
4059 ULONG offsetInBlock = offset.LowPart % This->parentStorage->bigBlockSize;
4060 ULONG bytesToReadInBuffer;
4061 ULONG blockIndex;
4062 BYTE* bufferWalker;
4063 BYTE* bigBlockBuffer;
4066 * Find the first block in the stream that contains part of the buffer.
4068 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4069 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4070 (blockNoInSequence < This->lastBlockNoInSequence) )
4072 blockIndex = BlockChainStream_GetHeadOfChain(This);
4073 This->lastBlockNoInSequence = blockNoInSequence;
4075 else
4077 ULONG temp = blockNoInSequence;
4079 blockIndex = This->lastBlockNoInSequenceIndex;
4080 blockNoInSequence -= This->lastBlockNoInSequence;
4081 This->lastBlockNoInSequence = temp;
4084 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4086 blockIndex =
4087 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4089 blockNoInSequence--;
4092 This->lastBlockNoInSequenceIndex = blockIndex;
4095 * Start reading the buffer.
4097 *bytesRead = 0;
4098 bufferWalker = buffer;
4100 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4103 * Calculate how many bytes we can copy from this big block.
4105 bytesToReadInBuffer =
4106 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4109 * Copy those bytes to the buffer
4111 bigBlockBuffer =
4112 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4114 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4116 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4119 * Step to the next big block.
4121 blockIndex =
4122 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4124 bufferWalker += bytesToReadInBuffer;
4125 size -= bytesToReadInBuffer;
4126 *bytesRead += bytesToReadInBuffer;
4127 offsetInBlock = 0; /* There is no offset on the next block */
4131 return (size == 0);
4134 /******************************************************************************
4135 * BlockChainStream_WriteAt
4137 * Writes the specified number of bytes to this chain at the specified offset.
4138 * bytesWritten may be NULL.
4139 * Will fail if not all specified number of bytes have been written.
4141 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4142 ULARGE_INTEGER offset,
4143 ULONG size,
4144 const void* buffer,
4145 ULONG* bytesWritten)
4147 ULONG blockNoInSequence = offset.LowPart / This->parentStorage->bigBlockSize;
4148 ULONG offsetInBlock = offset.LowPart % This->parentStorage->bigBlockSize;
4149 ULONG bytesToWrite;
4150 ULONG blockIndex;
4151 BYTE* bufferWalker;
4152 BYTE* bigBlockBuffer;
4155 * Find the first block in the stream that contains part of the buffer.
4157 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4158 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4159 (blockNoInSequence < This->lastBlockNoInSequence) )
4161 blockIndex = BlockChainStream_GetHeadOfChain(This);
4162 This->lastBlockNoInSequence = blockNoInSequence;
4164 else
4166 ULONG temp = blockNoInSequence;
4168 blockIndex = This->lastBlockNoInSequenceIndex;
4169 blockNoInSequence -= This->lastBlockNoInSequence;
4170 This->lastBlockNoInSequence = temp;
4173 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4175 blockIndex =
4176 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4178 blockNoInSequence--;
4181 This->lastBlockNoInSequenceIndex = blockIndex;
4184 * Here, I'm casting away the constness on the buffer variable
4185 * This is OK since we don't intend to modify that buffer.
4187 *bytesWritten = 0;
4188 bufferWalker = (BYTE*)buffer;
4190 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4193 * Calculate how many bytes we can copy from this big block.
4195 bytesToWrite =
4196 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4199 * Copy those bytes to the buffer
4201 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4203 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4205 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4208 * Step to the next big block.
4210 blockIndex =
4211 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4213 bufferWalker += bytesToWrite;
4214 size -= bytesToWrite;
4215 *bytesWritten += bytesToWrite;
4216 offsetInBlock = 0; /* There is no offset on the next block */
4219 return (size == 0);
4222 /******************************************************************************
4223 * BlockChainStream_Shrink
4225 * Shrinks this chain in the big block depot.
4227 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4228 ULARGE_INTEGER newSize)
4230 ULONG blockIndex, extraBlock;
4231 ULONG numBlocks;
4232 ULONG count = 1;
4235 * Reset the last accessed block cache.
4237 This->lastBlockNoInSequence = 0xFFFFFFFF;
4238 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4241 * Figure out how many blocks are needed to contain the new size
4243 numBlocks = newSize.LowPart / This->parentStorage->bigBlockSize;
4245 if ((newSize.LowPart % This->parentStorage->bigBlockSize) != 0)
4246 numBlocks++;
4248 blockIndex = BlockChainStream_GetHeadOfChain(This);
4251 * Go to the new end of chain
4253 while (count < numBlocks)
4255 blockIndex =
4256 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4258 count++;
4261 /* Get the next block before marking the new end */
4262 extraBlock =
4263 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4265 /* Mark the new end of chain */
4266 StorageImpl_SetNextBlockInChain(
4267 This->parentStorage,
4268 blockIndex,
4269 BLOCK_END_OF_CHAIN);
4271 This->tailIndex = blockIndex;
4272 This->numBlocks = numBlocks;
4275 * Mark the extra blocks as free
4277 while (extraBlock != BLOCK_END_OF_CHAIN)
4279 blockIndex =
4280 StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4282 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4283 extraBlock = blockIndex;
4286 return TRUE;
4289 /******************************************************************************
4290 * BlockChainStream_Enlarge
4292 * Grows this chain in the big block depot.
4294 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4295 ULARGE_INTEGER newSize)
4297 ULONG blockIndex, currentBlock;
4298 ULONG newNumBlocks;
4299 ULONG oldNumBlocks = 0;
4301 blockIndex = BlockChainStream_GetHeadOfChain(This);
4304 * Empty chain. Create the head.
4306 if (blockIndex == BLOCK_END_OF_CHAIN)
4308 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4309 StorageImpl_SetNextBlockInChain(This->parentStorage,
4310 blockIndex,
4311 BLOCK_END_OF_CHAIN);
4313 if (This->headOfStreamPlaceHolder != 0)
4315 *(This->headOfStreamPlaceHolder) = blockIndex;
4317 else
4319 StgProperty chainProp;
4320 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4322 StorageImpl_ReadProperty(
4323 This->parentStorage,
4324 This->ownerPropertyIndex,
4325 &chainProp);
4327 chainProp.startingBlock = blockIndex;
4329 StorageImpl_WriteProperty(
4330 This->parentStorage,
4331 This->ownerPropertyIndex,
4332 &chainProp);
4335 This->tailIndex = blockIndex;
4336 This->numBlocks = 1;
4340 * Figure out how many blocks are needed to contain this stream
4342 newNumBlocks = newSize.LowPart / This->parentStorage->bigBlockSize;
4344 if ((newSize.LowPart % This->parentStorage->bigBlockSize) != 0)
4345 newNumBlocks++;
4348 * Go to the current end of chain
4350 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4352 currentBlock = blockIndex;
4354 while (blockIndex != BLOCK_END_OF_CHAIN)
4356 This->numBlocks++;
4357 currentBlock = blockIndex;
4359 blockIndex =
4360 StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4363 This->tailIndex = currentBlock;
4366 currentBlock = This->tailIndex;
4367 oldNumBlocks = This->numBlocks;
4370 * Add new blocks to the chain
4372 if (oldNumBlocks < newNumBlocks)
4374 while (oldNumBlocks < newNumBlocks)
4376 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4378 StorageImpl_SetNextBlockInChain(
4379 This->parentStorage,
4380 currentBlock,
4381 blockIndex);
4383 StorageImpl_SetNextBlockInChain(
4384 This->parentStorage,
4385 blockIndex,
4386 BLOCK_END_OF_CHAIN);
4388 currentBlock = blockIndex;
4389 oldNumBlocks++;
4392 This->tailIndex = blockIndex;
4393 This->numBlocks = newNumBlocks;
4396 return TRUE;
4399 /******************************************************************************
4400 * BlockChainStream_SetSize
4402 * Sets the size of this stream. The big block depot will be updated.
4403 * The file will grow if we grow the chain.
4405 * TODO: Free the actual blocks in the file when we shrink the chain.
4406 * Currently, the blocks are still in the file. So the file size
4407 * doesn't shrink even if we shrink streams.
4409 BOOL BlockChainStream_SetSize(
4410 BlockChainStream* This,
4411 ULARGE_INTEGER newSize)
4413 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4415 if (newSize.LowPart == size.LowPart)
4416 return TRUE;
4418 if (newSize.LowPart < size.LowPart)
4420 BlockChainStream_Shrink(This, newSize);
4422 else
4424 ULARGE_INTEGER fileSize =
4425 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4427 ULONG diff = newSize.LowPart - size.LowPart;
4430 * Make sure the file stays a multiple of blocksize
4432 if ((diff % This->parentStorage->bigBlockSize) != 0)
4433 diff += (This->parentStorage->bigBlockSize -
4434 (diff % This->parentStorage->bigBlockSize) );
4436 fileSize.LowPart += diff;
4437 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4439 BlockChainStream_Enlarge(This, newSize);
4442 return TRUE;
4445 /******************************************************************************
4446 * BlockChainStream_GetSize
4448 * Returns the size of this chain.
4449 * Will return the block count if this chain doesn't have a property.
4451 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4453 StgProperty chainProperty;
4455 if(This->headOfStreamPlaceHolder == NULL)
4458 * This chain is a data stream read the property and return
4459 * the appropriate size
4461 StorageImpl_ReadProperty(
4462 This->parentStorage,
4463 This->ownerPropertyIndex,
4464 &chainProperty);
4466 return chainProperty.size;
4468 else
4471 * this chain is a chain that does not have a property, figure out the
4472 * size by making the product number of used blocks times the
4473 * size of them
4475 ULARGE_INTEGER result;
4476 result.HighPart = 0;
4478 result.LowPart =
4479 BlockChainStream_GetCount(This) *
4480 This->parentStorage->bigBlockSize;
4482 return result;
4486 /******************************************************************************
4487 ** SmallBlockChainStream implementation
4490 SmallBlockChainStream* SmallBlockChainStream_Construct(
4491 StorageImpl* parentStorage,
4492 ULONG propertyIndex)
4494 SmallBlockChainStream* newStream;
4496 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4498 newStream->parentStorage = parentStorage;
4499 newStream->ownerPropertyIndex = propertyIndex;
4501 return newStream;
4504 void SmallBlockChainStream_Destroy(
4505 SmallBlockChainStream* This)
4507 HeapFree(GetProcessHeap(), 0, This);
4510 /******************************************************************************
4511 * SmallBlockChainStream_GetHeadOfChain
4513 * Returns the head of this chain of small blocks.
4515 ULONG SmallBlockChainStream_GetHeadOfChain(
4516 SmallBlockChainStream* This)
4518 StgProperty chainProperty;
4519 BOOL readSucessful;
4521 if (This->ownerPropertyIndex)
4523 readSucessful = StorageImpl_ReadProperty(
4524 This->parentStorage,
4525 This->ownerPropertyIndex,
4526 &chainProperty);
4528 if (readSucessful)
4530 return chainProperty.startingBlock;
4535 return BLOCK_END_OF_CHAIN;
4538 /******************************************************************************
4539 * SmallBlockChainStream_GetNextBlockInChain
4541 * Returns the index of the next small block in this chain.
4543 * Return Values:
4544 * - BLOCK_END_OF_CHAIN: end of this chain
4545 * - BLOCK_UNUSED: small block 'blockIndex' is free
4547 ULONG SmallBlockChainStream_GetNextBlockInChain(
4548 SmallBlockChainStream* This,
4549 ULONG blockIndex)
4551 ULARGE_INTEGER offsetOfBlockInDepot;
4552 DWORD buffer;
4553 ULONG nextBlockInChain = BLOCK_END_OF_CHAIN;
4554 ULONG bytesRead;
4555 BOOL success;
4557 offsetOfBlockInDepot.HighPart = 0;
4558 offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
4561 * Read those bytes in the buffer from the small block file.
4563 success = BlockChainStream_ReadAt(
4564 This->parentStorage->smallBlockDepotChain,
4565 offsetOfBlockInDepot,
4566 sizeof(DWORD),
4567 &buffer,
4568 &bytesRead);
4570 if (success)
4572 StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4575 return nextBlockInChain;
4578 /******************************************************************************
4579 * SmallBlockChainStream_SetNextBlockInChain
4581 * Writes the index of the next block of the specified block in the small
4582 * block depot.
4583 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4584 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4586 void SmallBlockChainStream_SetNextBlockInChain(
4587 SmallBlockChainStream* This,
4588 ULONG blockIndex,
4589 ULONG nextBlock)
4591 ULARGE_INTEGER offsetOfBlockInDepot;
4592 DWORD buffer;
4593 ULONG bytesWritten;
4595 offsetOfBlockInDepot.HighPart = 0;
4596 offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
4598 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4601 * Read those bytes in the buffer from the small block file.
4603 BlockChainStream_WriteAt(
4604 This->parentStorage->smallBlockDepotChain,
4605 offsetOfBlockInDepot,
4606 sizeof(DWORD),
4607 &buffer,
4608 &bytesWritten);
4611 /******************************************************************************
4612 * SmallBlockChainStream_FreeBlock
4614 * Flag small block 'blockIndex' as free in the small block depot.
4616 void SmallBlockChainStream_FreeBlock(
4617 SmallBlockChainStream* This,
4618 ULONG blockIndex)
4620 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4623 /******************************************************************************
4624 * SmallBlockChainStream_GetNextFreeBlock
4626 * Returns the index of a free small block. The small block depot will be
4627 * enlarged if necessary. The small block chain will also be enlarged if
4628 * necessary.
4630 ULONG SmallBlockChainStream_GetNextFreeBlock(
4631 SmallBlockChainStream* This)
4633 ULARGE_INTEGER offsetOfBlockInDepot;
4634 DWORD buffer;
4635 ULONG bytesRead;
4636 ULONG blockIndex = 0;
4637 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4638 BOOL success = TRUE;
4639 ULONG smallBlocksPerBigBlock;
4641 offsetOfBlockInDepot.HighPart = 0;
4644 * Scan the small block depot for a free block
4646 while (nextBlockIndex != BLOCK_UNUSED)
4648 offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
4650 success = BlockChainStream_ReadAt(
4651 This->parentStorage->smallBlockDepotChain,
4652 offsetOfBlockInDepot,
4653 sizeof(DWORD),
4654 &buffer,
4655 &bytesRead);
4658 * If we run out of space for the small block depot, enlarge it
4660 if (success)
4662 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4664 if (nextBlockIndex != BLOCK_UNUSED)
4665 blockIndex++;
4667 else
4669 ULONG count =
4670 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4672 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4673 ULONG nextBlock, newsbdIndex;
4674 BYTE* smallBlockDepot;
4676 nextBlock = sbdIndex;
4677 while (nextBlock != BLOCK_END_OF_CHAIN)
4679 sbdIndex = nextBlock;
4680 nextBlock =
4681 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4684 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4685 if (sbdIndex != BLOCK_END_OF_CHAIN)
4686 StorageImpl_SetNextBlockInChain(
4687 This->parentStorage,
4688 sbdIndex,
4689 newsbdIndex);
4691 StorageImpl_SetNextBlockInChain(
4692 This->parentStorage,
4693 newsbdIndex,
4694 BLOCK_END_OF_CHAIN);
4697 * Initialize all the small blocks to free
4699 smallBlockDepot =
4700 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4702 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4703 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4705 if (count == 0)
4708 * We have just created the small block depot.
4710 StgProperty rootProp;
4711 ULONG sbStartIndex;
4714 * Save it in the header
4716 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4717 StorageImpl_SaveFileHeader(This->parentStorage);
4720 * And allocate the first big block that will contain small blocks
4722 sbStartIndex =
4723 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4725 StorageImpl_SetNextBlockInChain(
4726 This->parentStorage,
4727 sbStartIndex,
4728 BLOCK_END_OF_CHAIN);
4730 StorageImpl_ReadProperty(
4731 This->parentStorage,
4732 This->parentStorage->rootPropertySetIndex,
4733 &rootProp);
4735 rootProp.startingBlock = sbStartIndex;
4736 rootProp.size.HighPart = 0;
4737 rootProp.size.LowPart = This->parentStorage->bigBlockSize;
4739 StorageImpl_WriteProperty(
4740 This->parentStorage,
4741 This->parentStorage->rootPropertySetIndex,
4742 &rootProp);
4747 smallBlocksPerBigBlock =
4748 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4751 * Verify if we have to allocate big blocks to contain small blocks
4753 if (blockIndex % smallBlocksPerBigBlock == 0)
4755 StgProperty rootProp;
4756 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4758 StorageImpl_ReadProperty(
4759 This->parentStorage,
4760 This->parentStorage->rootPropertySetIndex,
4761 &rootProp);
4763 if (rootProp.size.LowPart <
4764 (blocksRequired * This->parentStorage->bigBlockSize))
4766 rootProp.size.LowPart += This->parentStorage->bigBlockSize;
4768 BlockChainStream_SetSize(
4769 This->parentStorage->smallBlockRootChain,
4770 rootProp.size);
4772 StorageImpl_WriteProperty(
4773 This->parentStorage,
4774 This->parentStorage->rootPropertySetIndex,
4775 &rootProp);
4779 return blockIndex;
4782 /******************************************************************************
4783 * SmallBlockChainStream_ReadAt
4785 * Reads a specified number of bytes from this chain at the specified offset.
4786 * bytesRead may be NULL.
4787 * Failure will be returned if the specified number of bytes has not been read.
4789 BOOL SmallBlockChainStream_ReadAt(
4790 SmallBlockChainStream* This,
4791 ULARGE_INTEGER offset,
4792 ULONG size,
4793 void* buffer,
4794 ULONG* bytesRead)
4796 ULARGE_INTEGER offsetInBigBlockFile;
4797 ULONG blockNoInSequence =
4798 offset.LowPart / This->parentStorage->smallBlockSize;
4800 ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize;
4801 ULONG bytesToReadInBuffer;
4802 ULONG blockIndex;
4803 ULONG bytesReadFromBigBlockFile;
4804 BYTE* bufferWalker;
4807 * This should never happen on a small block file.
4809 assert(offset.HighPart==0);
4812 * Find the first block in the stream that contains part of the buffer.
4814 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4816 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4818 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4820 blockNoInSequence--;
4824 * Start reading the buffer.
4826 *bytesRead = 0;
4827 bufferWalker = buffer;
4829 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4832 * Calculate how many bytes we can copy from this small block.
4834 bytesToReadInBuffer =
4835 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4838 * Calculate the offset of the small block in the small block file.
4840 offsetInBigBlockFile.HighPart = 0;
4841 offsetInBigBlockFile.LowPart =
4842 blockIndex * This->parentStorage->smallBlockSize;
4844 offsetInBigBlockFile.LowPart += offsetInBlock;
4847 * Read those bytes in the buffer from the small block file.
4849 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
4850 offsetInBigBlockFile,
4851 bytesToReadInBuffer,
4852 bufferWalker,
4853 &bytesReadFromBigBlockFile);
4855 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
4858 * Step to the next big block.
4860 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4861 bufferWalker += bytesToReadInBuffer;
4862 size -= bytesToReadInBuffer;
4863 *bytesRead += bytesToReadInBuffer;
4864 offsetInBlock = 0; /* There is no offset on the next block */
4867 return (size == 0);
4870 /******************************************************************************
4871 * SmallBlockChainStream_WriteAt
4873 * Writes the specified number of bytes to this chain at the specified offset.
4874 * bytesWritten may be NULL.
4875 * Will fail if not all specified number of bytes have been written.
4877 BOOL SmallBlockChainStream_WriteAt(
4878 SmallBlockChainStream* This,
4879 ULARGE_INTEGER offset,
4880 ULONG size,
4881 const void* buffer,
4882 ULONG* bytesWritten)
4884 ULARGE_INTEGER offsetInBigBlockFile;
4885 ULONG blockNoInSequence =
4886 offset.LowPart / This->parentStorage->smallBlockSize;
4888 ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize;
4889 ULONG bytesToWriteInBuffer;
4890 ULONG blockIndex;
4891 ULONG bytesWrittenFromBigBlockFile;
4892 BYTE* bufferWalker;
4895 * This should never happen on a small block file.
4897 assert(offset.HighPart==0);
4900 * Find the first block in the stream that contains part of the buffer.
4902 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4904 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4906 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4908 blockNoInSequence--;
4912 * Start writing the buffer.
4914 * Here, I'm casting away the constness on the buffer variable
4915 * This is OK since we don't intend to modify that buffer.
4917 *bytesWritten = 0;
4918 bufferWalker = (BYTE*)buffer;
4919 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4922 * Calculate how many bytes we can copy to this small block.
4924 bytesToWriteInBuffer =
4925 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4928 * Calculate the offset of the small block in the small block file.
4930 offsetInBigBlockFile.HighPart = 0;
4931 offsetInBigBlockFile.LowPart =
4932 blockIndex * This->parentStorage->smallBlockSize;
4934 offsetInBigBlockFile.LowPart += offsetInBlock;
4937 * Write those bytes in the buffer to the small block file.
4939 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
4940 offsetInBigBlockFile,
4941 bytesToWriteInBuffer,
4942 bufferWalker,
4943 &bytesWrittenFromBigBlockFile);
4945 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
4948 * Step to the next big block.
4950 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4951 bufferWalker += bytesToWriteInBuffer;
4952 size -= bytesToWriteInBuffer;
4953 *bytesWritten += bytesToWriteInBuffer;
4954 offsetInBlock = 0; /* There is no offset on the next block */
4957 return (size == 0);
4960 /******************************************************************************
4961 * SmallBlockChainStream_Shrink
4963 * Shrinks this chain in the small block depot.
4965 BOOL SmallBlockChainStream_Shrink(
4966 SmallBlockChainStream* This,
4967 ULARGE_INTEGER newSize)
4969 ULONG blockIndex, extraBlock;
4970 ULONG numBlocks;
4971 ULONG count = 0;
4973 numBlocks = newSize.LowPart / This->parentStorage->smallBlockSize;
4975 if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0)
4976 numBlocks++;
4978 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4981 * Go to the new end of chain
4983 while (count < numBlocks)
4985 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4986 count++;
4990 * If the count is 0, we have a special case, the head of the chain was
4991 * just freed.
4993 if (count == 0)
4995 StgProperty chainProp;
4997 StorageImpl_ReadProperty(This->parentStorage,
4998 This->ownerPropertyIndex,
4999 &chainProp);
5001 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5003 StorageImpl_WriteProperty(This->parentStorage,
5004 This->ownerPropertyIndex,
5005 &chainProp);
5008 * We start freeing the chain at the head block.
5010 extraBlock = blockIndex;
5012 else
5014 /* Get the next block before marking the new end */
5015 extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5017 /* Mark the new end of chain */
5018 SmallBlockChainStream_SetNextBlockInChain(
5019 This,
5020 blockIndex,
5021 BLOCK_END_OF_CHAIN);
5025 * Mark the extra blocks as free
5027 while (extraBlock != BLOCK_END_OF_CHAIN)
5029 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
5030 SmallBlockChainStream_FreeBlock(This, extraBlock);
5031 extraBlock = blockIndex;
5034 return TRUE;
5037 /******************************************************************************
5038 * SmallBlockChainStream_Enlarge
5040 * Grows this chain in the small block depot.
5042 BOOL SmallBlockChainStream_Enlarge(
5043 SmallBlockChainStream* This,
5044 ULARGE_INTEGER newSize)
5046 ULONG blockIndex, currentBlock;
5047 ULONG newNumBlocks;
5048 ULONG oldNumBlocks = 0;
5050 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5053 * Empty chain
5055 if (blockIndex == BLOCK_END_OF_CHAIN)
5058 StgProperty chainProp;
5060 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5061 &chainProp);
5063 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5065 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5066 &chainProp);
5068 blockIndex = chainProp.startingBlock;
5069 SmallBlockChainStream_SetNextBlockInChain(
5070 This,
5071 blockIndex,
5072 BLOCK_END_OF_CHAIN);
5075 currentBlock = blockIndex;
5078 * Figure out how many blocks are needed to contain this stream
5080 newNumBlocks = newSize.LowPart / This->parentStorage->smallBlockSize;
5082 if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0)
5083 newNumBlocks++;
5086 * Go to the current end of chain
5088 while (blockIndex != BLOCK_END_OF_CHAIN)
5090 oldNumBlocks++;
5091 currentBlock = blockIndex;
5092 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5096 * Add new blocks to the chain
5098 while (oldNumBlocks < newNumBlocks)
5100 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5101 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5103 SmallBlockChainStream_SetNextBlockInChain(
5104 This,
5105 blockIndex,
5106 BLOCK_END_OF_CHAIN);
5108 currentBlock = blockIndex;
5109 oldNumBlocks++;
5112 return TRUE;
5115 /******************************************************************************
5116 * SmallBlockChainStream_GetCount
5118 * Returns the number of blocks that comprises this chain.
5119 * This is not the size of this chain as the last block may not be full!
5121 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5123 ULONG blockIndex;
5124 ULONG count = 0;
5126 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5128 while (blockIndex != BLOCK_END_OF_CHAIN)
5130 count++;
5132 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5135 return count;
5138 /******************************************************************************
5139 * SmallBlockChainStream_SetSize
5141 * Sets the size of this stream.
5142 * The file will grow if we grow the chain.
5144 * TODO: Free the actual blocks in the file when we shrink the chain.
5145 * Currently, the blocks are still in the file. So the file size
5146 * doesn't shrink even if we shrink streams.
5148 BOOL SmallBlockChainStream_SetSize(
5149 SmallBlockChainStream* This,
5150 ULARGE_INTEGER newSize)
5152 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5154 if (newSize.LowPart == size.LowPart)
5155 return TRUE;
5157 if (newSize.LowPart < size.LowPart)
5159 SmallBlockChainStream_Shrink(This, newSize);
5161 else
5163 SmallBlockChainStream_Enlarge(This, newSize);
5166 return TRUE;
5169 /******************************************************************************
5170 * SmallBlockChainStream_GetSize
5172 * Returns the size of this chain.
5174 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5176 StgProperty chainProperty;
5178 StorageImpl_ReadProperty(
5179 This->parentStorage,
5180 This->ownerPropertyIndex,
5181 &chainProperty);
5183 return chainProperty.size;
5186 /******************************************************************************
5187 * StgCreateDocfile32 [OLE32.144]
5189 HRESULT WINAPI StgCreateDocfile(
5190 LPCOLESTR pwcsName,
5191 DWORD grfMode,
5192 DWORD reserved,
5193 IStorage **ppstgOpen)
5195 StorageImpl* newStorage = 0;
5196 HANDLE hFile = INVALID_HANDLE_VALUE;
5197 HRESULT hr = S_OK;
5198 DWORD shareMode;
5199 DWORD accessMode;
5200 DWORD creationMode;
5201 DWORD fileAttributes;
5202 WCHAR tempFileName[MAX_PATH];
5204 TRACE(storage, "(%s, %lx, %ld, %p)\n",
5205 debugstr_w(pwcsName), grfMode,
5206 reserved, ppstgOpen);
5209 * Validate the parameters
5211 if (ppstgOpen == 0)
5212 return STG_E_INVALIDPOINTER;
5215 * Validate the STGM flags
5217 if ( FAILED( validateSTGM(grfMode) ))
5218 return STG_E_INVALIDFLAG;
5221 * Generate a unique name.
5223 if (pwcsName == 0)
5225 WCHAR tempPath[MAX_PATH];
5226 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5228 memset(tempPath, 0, sizeof(tempPath));
5229 memset(tempFileName, 0, sizeof(tempFileName));
5231 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5232 tempPath[0] = '.';
5234 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5235 pwcsName = tempFileName;
5236 else
5237 return STG_E_INSUFFICIENTMEMORY;
5241 * Interpret the STGM value grfMode
5243 shareMode = GetShareModeFromSTGM(grfMode);
5244 accessMode = GetAccessModeFromSTGM(grfMode);
5245 creationMode = GetCreationModeFromSTGM(grfMode);
5247 if (grfMode & STGM_DELETEONRELEASE)
5248 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5249 else
5250 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5252 if (grfMode & STGM_TRANSACTED)
5253 FIXME(storage, "Transacted mode not implemented.\n");
5256 * Initialize the "out" parameter.
5258 *ppstgOpen = 0;
5260 hFile = CreateFileW(pwcsName,
5261 accessMode,
5262 shareMode,
5263 NULL,
5264 creationMode,
5265 fileAttributes,
5268 if (hFile == INVALID_HANDLE_VALUE)
5270 return E_FAIL;
5274 * Allocate and initialize the new IStorage32object.
5276 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5278 if (newStorage == 0)
5279 return STG_E_INSUFFICIENTMEMORY;
5281 hr = StorageImpl_Construct(
5282 newStorage,
5283 hFile,
5284 NULL,
5285 grfMode,
5286 TRUE);
5288 if (FAILED(hr))
5290 HeapFree(GetProcessHeap(), 0, newStorage);
5291 return hr;
5295 * Get an "out" pointer for the caller.
5297 hr = StorageBaseImpl_QueryInterface(
5298 (IStorage*)newStorage,
5299 (REFIID)&IID_IStorage,
5300 (void**)ppstgOpen);
5302 return hr;
5305 /******************************************************************************
5306 * StgOpenStorage32 [OLE32.148]
5308 HRESULT WINAPI StgOpenStorage(
5309 const OLECHAR *pwcsName,
5310 IStorage *pstgPriority,
5311 DWORD grfMode,
5312 SNB snbExclude,
5313 DWORD reserved,
5314 IStorage **ppstgOpen)
5316 StorageImpl* newStorage = 0;
5317 HRESULT hr = S_OK;
5318 HANDLE hFile = 0;
5319 DWORD shareMode;
5320 DWORD accessMode;
5322 TRACE(storage, "(%s, %p, %lx, %p, %ld, %p)\n",
5323 debugstr_w(pwcsName), pstgPriority, grfMode,
5324 snbExclude, reserved, ppstgOpen);
5327 * Perform a sanity check
5329 if (( pwcsName == 0) || (ppstgOpen == 0) )
5330 return STG_E_INVALIDPOINTER;
5333 * Validate the STGM flags
5335 if ( FAILED( validateSTGM(grfMode) ))
5336 return STG_E_INVALIDFLAG;
5339 * Interpret the STGM value grfMode
5341 shareMode = GetShareModeFromSTGM(grfMode);
5342 accessMode = GetAccessModeFromSTGM(grfMode);
5345 * Initialize the "out" parameter.
5347 *ppstgOpen = 0;
5349 hFile = CreateFileW( pwcsName,
5350 accessMode,
5351 shareMode,
5352 NULL,
5353 OPEN_EXISTING,
5354 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5358 if (hFile==INVALID_HANDLE_VALUE)
5360 return E_FAIL;
5364 * Allocate and initialize the new IStorage32object.
5366 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5368 if (newStorage == 0)
5369 return STG_E_INSUFFICIENTMEMORY;
5371 hr = StorageImpl_Construct(
5372 newStorage,
5373 hFile,
5374 NULL,
5375 grfMode,
5376 TRUE);
5378 if (FAILED(hr))
5380 HeapFree(GetProcessHeap(), 0, newStorage);
5381 return hr;
5385 * Get an "out" pointer for the caller.
5387 hr = StorageBaseImpl_QueryInterface(
5388 (IStorage*)newStorage,
5389 (REFIID)&IID_IStorage,
5390 (void**)ppstgOpen);
5392 return hr;
5395 /******************************************************************************
5396 * StgCreateDocfileOnILockBytes [OLE32.145]
5398 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5399 ILockBytes *plkbyt,
5400 DWORD grfMode,
5401 DWORD reserved,
5402 IStorage** ppstgOpen)
5404 StorageImpl* newStorage = 0;
5405 HRESULT hr = S_OK;
5408 * Validate the parameters
5410 if ((ppstgOpen == 0) || (plkbyt == 0))
5411 return STG_E_INVALIDPOINTER;
5414 * Allocate and initialize the new IStorage object.
5416 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5418 if (newStorage == 0)
5419 return STG_E_INSUFFICIENTMEMORY;
5421 hr = StorageImpl_Construct(
5422 newStorage,
5424 plkbyt,
5425 grfMode,
5426 FALSE);
5428 if (FAILED(hr))
5430 HeapFree(GetProcessHeap(), 0, newStorage);
5431 return hr;
5435 * Get an "out" pointer for the caller.
5437 hr = StorageBaseImpl_QueryInterface(
5438 (IStorage*)newStorage,
5439 (REFIID)&IID_IStorage,
5440 (void**)ppstgOpen);
5442 return hr;
5445 /******************************************************************************
5446 * StgOpenStorageOnILockBytes [OLE32.149]
5448 HRESULT WINAPI StgOpenStorageOnILockBytes(
5449 ILockBytes *plkbyt,
5450 IStorage *pstgPriority,
5451 DWORD grfMode,
5452 SNB snbExclude,
5453 DWORD reserved,
5454 IStorage **ppstgOpen)
5456 StorageImpl* newStorage = 0;
5457 HRESULT hr = S_OK;
5460 * Perform a sanity check
5462 if ((plkbyt == 0) || (ppstgOpen == 0))
5463 return STG_E_INVALIDPOINTER;
5466 * Validate the STGM flags
5468 if ( FAILED( validateSTGM(grfMode) ))
5469 return STG_E_INVALIDFLAG;
5472 * Initialize the "out" parameter.
5474 *ppstgOpen = 0;
5477 * Allocate and initialize the new IStorage object.
5479 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5481 if (newStorage == 0)
5482 return STG_E_INSUFFICIENTMEMORY;
5484 hr = StorageImpl_Construct(
5485 newStorage,
5487 plkbyt,
5488 grfMode,
5489 FALSE);
5491 if (FAILED(hr))
5493 HeapFree(GetProcessHeap(), 0, newStorage);
5494 return hr;
5498 * Get an "out" pointer for the caller.
5500 hr = StorageBaseImpl_QueryInterface(
5501 (IStorage*)newStorage,
5502 (REFIID)&IID_IStorage,
5503 (void**)ppstgOpen);
5505 return hr;
5508 /******************************************************************************
5509 * StgIsStorageILockBytes [OLE32.147]
5511 * Determines if the ILockBytes contains a storage object.
5513 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5515 BYTE sig[8];
5516 ULARGE_INTEGER offset;
5518 offset.HighPart = 0;
5519 offset.LowPart = 0;
5521 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5523 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5524 return S_OK;
5526 return S_FALSE;
5529 /******************************************************************************
5530 * WriteClassStg32 [OLE32.158]
5532 * This method will store the specified CLSID in the specified storage object
5534 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5536 HRESULT hRes;
5538 assert(pStg != 0);
5540 hRes = IStorage_SetClass(pStg, rclsid);
5542 return hRes;
5545 /*******************************************************************************************
5546 * ReadClassStg
5548 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5550 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5552 STATSTG pstatstg;
5553 HRESULT hRes;
5555 TRACE(storage,"()\n");
5557 if(pclsid==NULL)
5558 return E_POINTER;
5560 * read a STATSTG structure (contains the clsid) from the storage
5562 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5564 if(SUCCEEDED(hRes))
5565 *pclsid=pstatstg.clsid;
5567 return hRes;
5570 /*************************************************************************************
5571 * OleLoadFromStream
5573 * This function loads an object from stream
5575 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5577 CLSID clsid;
5578 HRESULT res;
5580 FIXME(storage,"(),stub!\n");
5582 res=ReadClassStm(pStm,&clsid);
5584 if (SUCCEEDED(res)){
5586 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5588 if (SUCCEEDED(res))
5590 res=IPersistStream_Load((IPersistStream*)ppvObj,pStm);
5593 return res;
5596 /************************************************************************************************
5597 * OleSaveToStream
5599 * This function saves an object with the IPersistStream interface on it to the specified stream
5601 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5604 CLSID clsid;
5605 HRESULT res;
5607 TRACE(storage,"(%p,%p)\n",pPStm,pStm);
5609 res=IPersistStream_GetClassID(pPStm,&clsid);
5611 if (SUCCEEDED(res)){
5613 res=WriteClassStm(pStm,&clsid);
5615 if (SUCCEEDED(res))
5617 res=IPersistStream_Save(pPStm,pStm,FALSE);
5620 return res;
5623 /****************************************************************************
5624 * This method validate a STGM parameter that can contain the values below
5626 * STGM_DIRECT 0x00000000
5627 * STGM_TRANSACTED 0x00010000
5628 * STGM_SIMPLE 0x08000000
5630 * STGM_READ 0x00000000
5631 * STGM_WRITE 0x00000001
5632 * STGM_READWRITE 0x00000002
5634 * STGM_SHARE_DENY_NONE 0x00000040
5635 * STGM_SHARE_DENY_READ 0x00000030
5636 * STGM_SHARE_DENY_WRITE 0x00000020
5637 * STGM_SHARE_EXCLUSIVE 0x00000010
5639 * STGM_PRIORITY 0x00040000
5640 * STGM_DELETEONRELEASE 0x04000000
5642 * STGM_CREATE 0x00001000
5643 * STGM_CONVERT 0x00020000
5644 * STGM_FAILIFTHERE 0x00000000
5646 * STGM_NOSCRATCH 0x00100000
5647 * STGM_NOSNAPSHOT 0x00200000
5649 static HRESULT validateSTGM(DWORD stgm)
5651 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5652 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5653 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5655 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5656 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5657 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5659 BOOL bSTGM_SHARE_DENY_NONE =
5660 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5662 BOOL bSTGM_SHARE_DENY_READ =
5663 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5665 BOOL bSTGM_SHARE_DENY_WRITE =
5666 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5668 BOOL bSTGM_SHARE_EXCLUSIVE =
5669 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5671 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5672 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5674 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5675 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5678 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5680 if ( ! bSTGM_DIRECT )
5681 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5682 return E_FAIL;
5685 * STGM_WRITE | STGM_READWRITE | STGM_READ
5687 if ( ! bSTGM_READ )
5688 if( bSTGM_WRITE && bSTGM_READWRITE )
5689 return E_FAIL;
5692 * STGM_SHARE_DENY_NONE | others
5693 * (I assume here that DENY_READ implies DENY_WRITE)
5695 if ( bSTGM_SHARE_DENY_NONE )
5696 if ( bSTGM_SHARE_DENY_READ ||
5697 bSTGM_SHARE_DENY_WRITE ||
5698 bSTGM_SHARE_EXCLUSIVE)
5699 return E_FAIL;
5702 * STGM_CREATE | STGM_CONVERT
5703 * if both are false, STGM_FAILIFTHERE is set to TRUE
5705 if ( bSTGM_CREATE && bSTGM_CONVERT )
5706 return E_FAIL;
5709 * STGM_NOSCRATCH requires STGM_TRANSACTED
5711 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5712 return E_FAIL;
5715 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5716 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5718 if (bSTGM_NOSNAPSHOT)
5720 if ( ! ( bSTGM_TRANSACTED &&
5721 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5722 return E_FAIL;
5725 return S_OK;
5728 /****************************************************************************
5729 * GetShareModeFromSTGM
5731 * This method will return a share mode flag from a STGM value.
5732 * The STGM value is assumed valid.
5734 static DWORD GetShareModeFromSTGM(DWORD stgm)
5736 DWORD dwShareMode = 0;
5737 BOOL bSTGM_SHARE_DENY_NONE =
5738 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5740 BOOL bSTGM_SHARE_DENY_READ =
5741 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5743 BOOL bSTGM_SHARE_DENY_WRITE =
5744 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5746 BOOL bSTGM_SHARE_EXCLUSIVE =
5747 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5749 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5750 dwShareMode = 0;
5752 if (bSTGM_SHARE_DENY_NONE)
5753 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5755 if (bSTGM_SHARE_DENY_WRITE)
5756 dwShareMode = FILE_SHARE_READ;
5758 return dwShareMode;
5761 /****************************************************************************
5762 * GetAccessModeFromSTGM
5764 * This method will return an access mode flag from a STGM value.
5765 * The STGM value is assumed valid.
5767 static DWORD GetAccessModeFromSTGM(DWORD stgm)
5769 DWORD dwDesiredAccess = GENERIC_READ;
5770 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5771 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5772 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5774 if (bSTGM_READ)
5775 dwDesiredAccess = GENERIC_READ;
5777 if (bSTGM_WRITE)
5778 dwDesiredAccess |= GENERIC_WRITE;
5780 if (bSTGM_READWRITE)
5781 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5783 return dwDesiredAccess;
5786 /****************************************************************************
5787 * GetCreationModeFromSTGM
5789 * This method will return a creation mode flag from a STGM value.
5790 * The STGM value is assumed valid.
5792 static DWORD GetCreationModeFromSTGM(DWORD stgm)
5794 if ( stgm & STGM_CREATE)
5795 return CREATE_ALWAYS;
5796 if (stgm & STGM_CONVERT) {
5797 FIXME(storage, "STGM_CONVERT not implemented!\n");
5798 return CREATE_NEW;
5800 /* All other cases */
5801 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5802 FIXME(storage,"unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
5803 return CREATE_NEW;