Implemented SHCreateShellFolderViewEx.
[wine.git] / ole / storage32.c
blob71c84e099d08b454f3d2b70cbe76e4ca0067685c
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 "debugtools.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 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
93 StorageBaseImpl_QueryInterface,
94 StorageBaseImpl_AddRef,
95 StorageBaseImpl_Release,
96 StorageBaseImpl_CreateStream,
97 StorageBaseImpl_OpenStream,
98 StorageImpl_CreateStorage,
99 StorageBaseImpl_OpenStorage,
100 StorageImpl_CopyTo,
101 StorageImpl_MoveElementTo,
102 StorageImpl_Commit,
103 StorageImpl_Revert,
104 StorageBaseImpl_EnumElements,
105 StorageImpl_DestroyElement,
106 StorageBaseImpl_RenameElement,
107 StorageImpl_SetElementTimes,
108 StorageBaseImpl_SetClass,
109 StorageImpl_SetStateBits,
110 StorageBaseImpl_Stat
114 * Virtual function table for the Storage32InternalImpl class.
116 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
118 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
119 StorageBaseImpl_QueryInterface,
120 StorageBaseImpl_AddRef,
121 StorageBaseImpl_Release,
122 StorageBaseImpl_CreateStream,
123 StorageBaseImpl_OpenStream,
124 StorageImpl_CreateStorage,
125 StorageBaseImpl_OpenStorage,
126 StorageImpl_CopyTo,
127 StorageImpl_MoveElementTo,
128 StorageInternalImpl_Commit,
129 StorageInternalImpl_Revert,
130 StorageBaseImpl_EnumElements,
131 StorageImpl_DestroyElement,
132 StorageBaseImpl_RenameElement,
133 StorageImpl_SetElementTimes,
134 StorageBaseImpl_SetClass,
135 StorageImpl_SetStateBits,
136 StorageBaseImpl_Stat
140 * Virtual function table for the IEnumSTATSTGImpl class.
142 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
144 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
145 IEnumSTATSTGImpl_QueryInterface,
146 IEnumSTATSTGImpl_AddRef,
147 IEnumSTATSTGImpl_Release,
148 IEnumSTATSTGImpl_Next,
149 IEnumSTATSTGImpl_Skip,
150 IEnumSTATSTGImpl_Reset,
151 IEnumSTATSTGImpl_Clone
158 /************************************************************************
159 ** Storage32BaseImpl implementatiion
162 /************************************************************************
163 * Storage32BaseImpl_QueryInterface (IUnknown)
165 * This method implements the common QueryInterface for all IStorage32
166 * implementations contained in this file.
168 * See Windows documentation for more details on IUnknown methods.
170 HRESULT WINAPI StorageBaseImpl_QueryInterface(
171 IStorage* iface,
172 REFIID riid,
173 void** ppvObject)
175 ICOM_THIS(StorageBaseImpl,iface);
177 * Perform a sanity check on the parameters.
179 if ( (This==0) || (ppvObject==0) )
180 return E_INVALIDARG;
183 * Initialize the return parameter.
185 *ppvObject = 0;
188 * Compare the riid with the interface IDs implemented by this object.
190 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
192 *ppvObject = (IStorage*)This;
194 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
196 *ppvObject = (IStorage*)This;
200 * Check that we obtained an interface.
202 if ((*ppvObject)==0)
203 return E_NOINTERFACE;
206 * Query Interface always increases the reference count by one when it is
207 * successful
209 StorageBaseImpl_AddRef(iface);
211 return S_OK;
214 /************************************************************************
215 * Storage32BaseImpl_AddRef (IUnknown)
217 * This method implements the common AddRef for all IStorage32
218 * implementations contained in this file.
220 * See Windows documentation for more details on IUnknown methods.
222 ULONG WINAPI StorageBaseImpl_AddRef(
223 IStorage* iface)
225 ICOM_THIS(StorageBaseImpl,iface);
226 This->ref++;
228 return This->ref;
231 /************************************************************************
232 * Storage32BaseImpl_Release (IUnknown)
234 * This method implements the common Release for all IStorage32
235 * implementations contained in this file.
237 * See Windows documentation for more details on IUnknown methods.
239 ULONG WINAPI StorageBaseImpl_Release(
240 IStorage* iface)
242 ICOM_THIS(StorageBaseImpl,iface);
244 * Decrease the reference count on this object.
246 This->ref--;
249 * If the reference count goes down to 0, perform suicide.
251 if (This->ref==0)
254 * Since we are using a system of base-classes, we want to call the
255 * destructor of the appropriate derived class. To do this, we are
256 * using virtual functions to implement the destructor.
258 This->v_destructor(This);
260 return 0;
263 return This->ref;
266 /************************************************************************
267 * Storage32BaseImpl_OpenStream (IStorage)
269 * This method will open the specified stream object from the current storage.
271 * See Windows documentation for more details on IStorage methods.
273 HRESULT WINAPI StorageBaseImpl_OpenStream(
274 IStorage* iface,
275 const OLECHAR* pwcsName, /* [string][in] */
276 void* reserved1, /* [unique][in] */
277 DWORD grfMode, /* [in] */
278 DWORD reserved2, /* [in] */
279 IStream** ppstm) /* [out] */
281 ICOM_THIS(StorageBaseImpl,iface);
282 IEnumSTATSTGImpl* propertyEnumeration;
283 StgStreamImpl* newStream;
284 StgProperty currentProperty;
285 ULONG foundPropertyIndex;
287 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
288 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
291 * Perform a sanity check on the parameters.
293 if ( (pwcsName==NULL) || (ppstm==0) )
294 return E_INVALIDARG;
297 * Initialize the out parameter
299 *ppstm = 0;
302 * Validate the STGM flags
304 if ( FAILED( validateSTGM(grfMode) ))
305 return STG_E_INVALIDFLAG;
308 * As documented.
310 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
311 (grfMode & STGM_DELETEONRELEASE) ||
312 (grfMode & STGM_TRANSACTED) )
313 return STG_E_INVALIDFUNCTION;
316 * Create a property enumeration to search the properties
318 propertyEnumeration = IEnumSTATSTGImpl_Construct(
319 This->ancestorStorage,
320 This->rootPropertySetIndex);
323 * Search the enumeration for the property with the given name
325 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
326 propertyEnumeration,
327 pwcsName,
328 &currentProperty);
331 * Delete the property enumeration since we don't need it anymore
333 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
336 * If it was found, construct the stream object and return a pointer to it.
338 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
339 (currentProperty.propertyType==PROPTYPE_STREAM) )
341 newStream = StgStreamImpl_Construct(This, foundPropertyIndex);
343 if (newStream!=0)
345 *ppstm = (IStream*)newStream;
348 * Since we are returning a pointer to the interface, we have to
349 * nail down the reference.
351 StgStreamImpl_AddRef(*ppstm);
353 return S_OK;
356 return E_OUTOFMEMORY;
359 return STG_E_FILENOTFOUND;
362 /************************************************************************
363 * Storage32BaseImpl_OpenStorage (IStorage)
365 * This method will open a new storage object from the current storage.
367 * See Windows documentation for more details on IStorage methods.
369 HRESULT WINAPI StorageBaseImpl_OpenStorage(
370 IStorage* iface,
371 const OLECHAR* pwcsName, /* [string][unique][in] */
372 IStorage* pstgPriority, /* [unique][in] */
373 DWORD grfMode, /* [in] */
374 SNB snbExclude, /* [unique][in] */
375 DWORD reserved, /* [in] */
376 IStorage** ppstg) /* [out] */
378 ICOM_THIS(StorageBaseImpl,iface);
379 StorageInternalImpl* newStorage;
380 IEnumSTATSTGImpl* propertyEnumeration;
381 StgProperty currentProperty;
382 ULONG foundPropertyIndex;
384 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
385 iface, debugstr_w(pwcsName), pstgPriority,
386 grfMode, snbExclude, reserved, ppstg);
389 * Perform a sanity check on the parameters.
391 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
392 return E_INVALIDARG;
395 * Validate the STGM flags
397 if ( FAILED( validateSTGM(grfMode) ))
398 return STG_E_INVALIDFLAG;
401 * As documented.
403 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
404 (grfMode & STGM_DELETEONRELEASE) ||
405 (grfMode & STGM_PRIORITY) )
406 return STG_E_INVALIDFUNCTION;
409 * Initialize the out parameter
411 *ppstg = 0;
414 * Create a property enumeration to search the properties
416 propertyEnumeration = IEnumSTATSTGImpl_Construct(
417 This->ancestorStorage,
418 This->rootPropertySetIndex);
421 * Search the enumeration for the property with the given name
423 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
424 propertyEnumeration,
425 pwcsName,
426 &currentProperty);
429 * Delete the property enumeration since we don't need it anymore
431 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
434 * If it was found, construct the stream object and return a pointer to it.
436 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
437 (currentProperty.propertyType==PROPTYPE_STORAGE) )
440 * Construct a new Storage object
442 newStorage = StorageInternalImpl_Construct(
443 This->ancestorStorage,
444 foundPropertyIndex);
446 if (newStorage != 0)
448 *ppstg = (IStorage*)newStorage;
451 * Since we are returning a pointer to the interface,
452 * we have to nail down the reference.
454 StorageBaseImpl_AddRef(*ppstg);
456 return S_OK;
459 return STG_E_INSUFFICIENTMEMORY;
462 return STG_E_FILENOTFOUND;
465 /************************************************************************
466 * Storage32BaseImpl_EnumElements (IStorage)
468 * This method will create an enumerator object that can be used to
469 * retrieve informatino about all the properties in the storage object.
471 * See Windows documentation for more details on IStorage methods.
473 HRESULT WINAPI StorageBaseImpl_EnumElements(
474 IStorage* iface,
475 DWORD reserved1, /* [in] */
476 void* reserved2, /* [size_is][unique][in] */
477 DWORD reserved3, /* [in] */
478 IEnumSTATSTG** ppenum) /* [out] */
480 ICOM_THIS(StorageBaseImpl,iface);
481 IEnumSTATSTGImpl* newEnum;
483 TRACE("(%p, %ld, %p, %ld, %p)\n",
484 iface, reserved1, reserved2, reserved3, ppenum);
487 * Perform a sanity check on the parameters.
489 if ( (This==0) || (ppenum==0))
490 return E_INVALIDARG;
493 * Construct the enumerator.
495 newEnum = IEnumSTATSTGImpl_Construct(
496 This->ancestorStorage,
497 This->rootPropertySetIndex);
499 if (newEnum!=0)
501 *ppenum = (IEnumSTATSTG*)newEnum;
504 * Don't forget to nail down a reference to the new object before
505 * returning it.
507 IEnumSTATSTGImpl_AddRef(*ppenum);
509 return S_OK;
512 return E_OUTOFMEMORY;
515 /************************************************************************
516 * Storage32BaseImpl_Stat (IStorage)
518 * This method will retrieve information about this storage object.
520 * See Windows documentation for more details on IStorage methods.
522 HRESULT WINAPI StorageBaseImpl_Stat(
523 IStorage* iface,
524 STATSTG* pstatstg, /* [out] */
525 DWORD grfStatFlag) /* [in] */
527 ICOM_THIS(StorageBaseImpl,iface);
528 StgProperty curProperty;
529 BOOL readSucessful;
531 TRACE("(%p, %p, %lx)\n",
532 iface, pstatstg, grfStatFlag);
535 * Perform a sanity check on the parameters.
537 if ( (This==0) || (pstatstg==0))
538 return E_INVALIDARG;
541 * Read the information from the property.
543 readSucessful = StorageImpl_ReadProperty(
544 This->ancestorStorage,
545 This->rootPropertySetIndex,
546 &curProperty);
548 if (readSucessful)
550 StorageUtl_CopyPropertyToSTATSTG(
551 pstatstg,
552 &curProperty,
553 grfStatFlag);
555 return S_OK;
558 return E_FAIL;
561 /************************************************************************
562 * Storage32BaseImpl_RenameElement (IStorage)
564 * This method will rename the specified element.
566 * See Windows documentation for more details on IStorage methods.
568 * Implementation notes: The method used to rename consists of creating a clone
569 * of the deleted StgProperty object setting it with the new name and to
570 * perform a DestroyElement of the old StgProperty.
572 HRESULT WINAPI StorageBaseImpl_RenameElement(
573 IStorage* iface,
574 const OLECHAR* pwcsOldName, /* [in] */
575 const OLECHAR* pwcsNewName) /* [in] */
577 ICOM_THIS(StorageBaseImpl,iface);
578 IEnumSTATSTGImpl* propertyEnumeration;
579 StgProperty currentProperty;
580 ULONG foundPropertyIndex;
582 TRACE("(%p, %s, %s)\n",
583 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
586 * Create a property enumeration to search the properties
588 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
589 This->rootPropertySetIndex);
592 * Search the enumeration for the new property name
594 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
595 pwcsNewName,
596 &currentProperty);
598 if (foundPropertyIndex != PROPERTY_NULL)
601 * There is already a property with the new name
603 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
604 return STG_E_FILEALREADYEXISTS;
607 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
610 * Search the enumeration for the old property name
612 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
613 pwcsOldName,
614 &currentProperty);
617 * Delete the property enumeration since we don't need it anymore
619 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
621 if (foundPropertyIndex != PROPERTY_NULL)
623 StgProperty renamedProperty;
624 ULONG renamedPropertyIndex;
627 * Setup a new property for the renamed property
629 renamedProperty.sizeOfNameString =
630 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
632 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
633 return STG_E_INVALIDNAME;
635 lstrcpyW(renamedProperty.name, pwcsNewName);
637 renamedProperty.propertyType = currentProperty.propertyType;
638 renamedProperty.startingBlock = currentProperty.startingBlock;
639 renamedProperty.size.LowPart = currentProperty.size.LowPart;
640 renamedProperty.size.HighPart = currentProperty.size.HighPart;
642 renamedProperty.previousProperty = PROPERTY_NULL;
643 renamedProperty.nextProperty = PROPERTY_NULL;
646 * Bring the dirProperty link in case it is a storage and in which
647 * case the renamed storage elements don't require to be reorganized.
649 renamedProperty.dirProperty = currentProperty.dirProperty;
651 /* call CoFileTime to get the current time
652 renamedProperty.timeStampS1
653 renamedProperty.timeStampD1
654 renamedProperty.timeStampS2
655 renamedProperty.timeStampD2
656 renamedProperty.propertyUniqueID
660 * Obtain a free property in the property chain
662 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
665 * Save the new property into the new property spot
667 StorageImpl_WriteProperty(
668 This->ancestorStorage,
669 renamedPropertyIndex,
670 &renamedProperty);
673 * Find a spot in the property chain for our newly created property.
675 updatePropertyChain(
676 (StorageImpl*)This,
677 renamedPropertyIndex,
678 renamedProperty);
681 * At this point the renamed property has been inserted in the tree,
682 * now, before to Destroy the old property we must zeroed it's dirProperty
683 * otherwise the DestroyProperty below will zap it all and we do not want
684 * this to happen.
685 * Also, we fake that the old property is a storage so the DestroyProperty
686 * will not do a SetSize(0) on the stream data.
688 * This means that we need to tweek the StgProperty if it is a stream or a
689 * non empty storage.
691 currentProperty.dirProperty = PROPERTY_NULL;
692 currentProperty.propertyType = PROPTYPE_STORAGE;
693 StorageImpl_WriteProperty(
694 This->ancestorStorage,
695 foundPropertyIndex,
696 &currentProperty);
699 * Invoke Destroy to get rid of the ole property and automatically redo
700 * the linking of it's previous and next members...
702 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
705 else
708 * There is no property with the old name
710 return STG_E_FILENOTFOUND;
713 return S_OK;
716 /************************************************************************
717 * Storage32BaseImpl_CreateStream (IStorage)
719 * This method will create a stream object within this storage
721 * See Windows documentation for more details on IStorage methods.
723 HRESULT WINAPI StorageBaseImpl_CreateStream(
724 IStorage* iface,
725 const OLECHAR* pwcsName, /* [string][in] */
726 DWORD grfMode, /* [in] */
727 DWORD reserved1, /* [in] */
728 DWORD reserved2, /* [in] */
729 IStream** ppstm) /* [out] */
731 ICOM_THIS(StorageBaseImpl,iface);
732 IEnumSTATSTGImpl* propertyEnumeration;
733 StgStreamImpl* newStream;
734 StgProperty currentProperty, newStreamProperty;
735 ULONG foundPropertyIndex, newPropertyIndex;
737 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
738 iface, debugstr_w(pwcsName), grfMode,
739 reserved1, reserved2, ppstm);
742 * Validate parameters
744 if (ppstm == 0)
745 return STG_E_INVALIDPOINTER;
747 if (pwcsName == 0)
748 return STG_E_INVALIDNAME;
751 * Validate the STGM flags
753 if ( FAILED( validateSTGM(grfMode) ))
754 return STG_E_INVALIDFLAG;
757 * As documented.
759 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
760 (grfMode & STGM_DELETEONRELEASE) ||
761 (grfMode & STGM_TRANSACTED) )
762 return STG_E_INVALIDFUNCTION;
765 * Initialize the out parameter
767 *ppstm = 0;
770 * Create a property enumeration to search the properties
772 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
773 This->rootPropertySetIndex);
775 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
776 pwcsName,
777 &currentProperty);
779 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
781 if (foundPropertyIndex != PROPERTY_NULL)
784 * An element with this name already exists
786 if (grfMode & STGM_CREATE)
788 IStorage_DestroyElement(iface, pwcsName);
790 else
791 return STG_E_FILEALREADYEXISTS;
795 * memset the empty property
797 memset(&newStreamProperty, 0, sizeof(StgProperty));
799 newStreamProperty.sizeOfNameString =
800 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
802 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
803 return STG_E_INVALIDNAME;
805 lstrcpyW(newStreamProperty.name, pwcsName);
807 newStreamProperty.propertyType = PROPTYPE_STREAM;
808 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
809 newStreamProperty.size.LowPart = 0;
810 newStreamProperty.size.HighPart = 0;
812 newStreamProperty.previousProperty = PROPERTY_NULL;
813 newStreamProperty.nextProperty = PROPERTY_NULL;
814 newStreamProperty.dirProperty = PROPERTY_NULL;
816 /* call CoFileTime to get the current time
817 newStreamProperty.timeStampS1
818 newStreamProperty.timeStampD1
819 newStreamProperty.timeStampS2
820 newStreamProperty.timeStampD2
823 /* newStreamProperty.propertyUniqueID */
826 * Get a free property or create a new one
828 newPropertyIndex = getFreeProperty(This->ancestorStorage);
831 * Save the new property into the new property spot
833 StorageImpl_WriteProperty(
834 This->ancestorStorage,
835 newPropertyIndex,
836 &newStreamProperty);
839 * Find a spot in the property chain for our newly created property.
841 updatePropertyChain(
842 (StorageImpl*)This,
843 newPropertyIndex,
844 newStreamProperty);
847 * Open the stream to return it.
849 newStream = StgStreamImpl_Construct(This, newPropertyIndex);
851 if (newStream != 0)
853 *ppstm = (IStream*)newStream;
856 * Since we are returning a pointer to the interface, we have to nail down
857 * the reference.
859 StgStreamImpl_AddRef(*ppstm);
861 else
863 return STG_E_INSUFFICIENTMEMORY;
866 return S_OK;
869 /************************************************************************
870 * Storage32BaseImpl_SetClass (IStorage)
872 * This method will write the specified CLSID in the property of this
873 * storage.
875 * See Windows documentation for more details on IStorage methods.
877 HRESULT WINAPI StorageBaseImpl_SetClass(
878 IStorage* iface,
879 REFCLSID clsid) /* [in] */
881 ICOM_THIS(StorageBaseImpl,iface);
882 HRESULT hRes = E_FAIL;
883 StgProperty curProperty;
884 BOOL success;
886 TRACE("(%p, %p)\n", iface, clsid);
888 success = StorageImpl_ReadProperty(This->ancestorStorage,
889 This->rootPropertySetIndex,
890 &curProperty);
891 if (success)
893 curProperty.propertyUniqueID = *clsid;
895 success = StorageImpl_WriteProperty(This->ancestorStorage,
896 This->rootPropertySetIndex,
897 &curProperty);
898 if (success)
899 hRes = S_OK;
902 return hRes;
905 /************************************************************************
906 ** Storage32Impl implementation
909 /************************************************************************
910 * Storage32Impl_CreateStorage (IStorage)
912 * This method will create the storage object within the provided storage.
914 * See Windows documentation for more details on IStorage methods.
916 HRESULT WINAPI StorageImpl_CreateStorage(
917 IStorage* iface,
918 const OLECHAR *pwcsName, /* [string][in] */
919 DWORD grfMode, /* [in] */
920 DWORD reserved1, /* [in] */
921 DWORD reserved2, /* [in] */
922 IStorage **ppstg) /* [out] */
924 StorageImpl* const This=(StorageImpl*)iface;
926 IEnumSTATSTGImpl *propertyEnumeration;
927 StgProperty currentProperty;
928 StgProperty newProperty;
929 ULONG foundPropertyIndex;
930 ULONG newPropertyIndex;
931 HRESULT hr;
933 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
934 iface, debugstr_w(pwcsName), grfMode,
935 reserved1, reserved2, ppstg);
938 * Validate parameters
940 if (ppstg == 0)
941 return STG_E_INVALIDPOINTER;
943 if (pwcsName == 0)
944 return STG_E_INVALIDNAME;
947 * Validate the STGM flags
949 if ( FAILED( validateSTGM(grfMode) ) ||
950 (grfMode & STGM_DELETEONRELEASE) )
951 return STG_E_INVALIDFLAG;
954 * Initialize the out parameter
956 *ppstg = 0;
959 * Create a property enumeration and search the properties
961 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
962 This->rootPropertySetIndex);
964 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
965 pwcsName,
966 &currentProperty);
967 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
969 if (foundPropertyIndex != PROPERTY_NULL)
972 * An element with this name already exists
974 if (grfMode & STGM_CREATE)
975 IStorage_DestroyElement(iface, pwcsName);
976 else
977 return STG_E_FILEALREADYEXISTS;
981 * memset the empty property
983 memset(&newProperty, 0, sizeof(StgProperty));
985 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
987 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
988 return STG_E_INVALIDNAME;
990 lstrcpyW(newProperty.name, pwcsName);
992 newProperty.propertyType = PROPTYPE_STORAGE;
993 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
994 newProperty.size.LowPart = 0;
995 newProperty.size.HighPart = 0;
997 newProperty.previousProperty = PROPERTY_NULL;
998 newProperty.nextProperty = PROPERTY_NULL;
999 newProperty.dirProperty = PROPERTY_NULL;
1001 /* call CoFileTime to get the current time
1002 newProperty.timeStampS1
1003 newProperty.timeStampD1
1004 newProperty.timeStampS2
1005 newProperty.timeStampD2
1008 /* newStorageProperty.propertyUniqueID */
1011 * Obtain a free property in the property chain
1013 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1016 * Save the new property into the new property spot
1018 StorageImpl_WriteProperty(
1019 This->ancestorStorage,
1020 newPropertyIndex,
1021 &newProperty);
1024 * Find a spot in the property chain for our newly created property.
1026 updatePropertyChain(
1027 This,
1028 newPropertyIndex,
1029 newProperty);
1032 * Open it to get a pointer to return.
1034 hr = IStorage_OpenStorage(
1035 iface,
1036 (OLECHAR*)pwcsName,
1038 grfMode,
1041 ppstg);
1043 if( (hr != S_OK) || (*ppstg == NULL))
1045 return hr;
1049 return S_OK;
1053 /***************************************************************************
1055 * Internal Method
1057 * Get a free property or create a new one.
1059 static ULONG getFreeProperty(
1060 StorageImpl *storage)
1062 ULONG currentPropertyIndex = 0;
1063 ULONG newPropertyIndex = PROPERTY_NULL;
1064 BOOL readSucessful = TRUE;
1065 StgProperty currentProperty;
1070 * Start by reading the root property
1072 readSucessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1073 currentPropertyIndex,
1074 &currentProperty);
1075 if (readSucessful)
1077 if (currentProperty.sizeOfNameString == 0)
1080 * The property existis and is available, we found it.
1082 newPropertyIndex = currentPropertyIndex;
1085 else
1088 * We exhausted the property list, we will create more space below
1090 newPropertyIndex = currentPropertyIndex;
1092 currentPropertyIndex++;
1094 } while (newPropertyIndex == PROPERTY_NULL);
1097 * grow the property chain
1099 if (! readSucessful)
1101 StgProperty emptyProperty;
1102 ULARGE_INTEGER newSize;
1103 ULONG propertyIndex;
1104 ULONG lastProperty = 0;
1105 ULONG blockCount = 0;
1108 * obtain the new count of property blocks
1110 blockCount = BlockChainStream_GetCount(
1111 storage->ancestorStorage->rootBlockChain)+1;
1114 * initialize the size used by the property stream
1116 newSize.HighPart = 0;
1117 newSize.LowPart = storage->bigBlockSize * blockCount;
1120 * add a property block to the property chain
1122 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1125 * memset the empty property in order to initialize the unused newly
1126 * created property
1128 memset(&emptyProperty, 0, sizeof(StgProperty));
1131 * initialize them
1133 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1135 for(
1136 propertyIndex = newPropertyIndex;
1137 propertyIndex < lastProperty;
1138 propertyIndex++)
1140 StorageImpl_WriteProperty(
1141 storage->ancestorStorage,
1142 propertyIndex,
1143 &emptyProperty);
1147 return newPropertyIndex;
1150 /****************************************************************************
1152 * Internal Method
1154 * Case insensitive comparaison of StgProperty.name by first considering
1155 * their size.
1157 * Returns <0 when newPrpoerty < currentProperty
1158 * >0 when newPrpoerty > currentProperty
1159 * 0 when newPrpoerty == currentProperty
1161 static LONG propertyNameCmp(
1162 OLECHAR *newProperty,
1163 OLECHAR *currentProperty)
1165 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1167 if (diff == 0)
1170 * We compare the string themselves only when they are of the same lenght
1172 diff = lstrcmpiW( newProperty, currentProperty);
1175 return diff;
1178 /****************************************************************************
1180 * Internal Method
1182 * Properly link this new element in the property chain.
1184 static void updatePropertyChain(
1185 StorageImpl *storage,
1186 ULONG newPropertyIndex,
1187 StgProperty newProperty)
1189 StgProperty currentProperty;
1192 * Read the root property
1194 StorageImpl_ReadProperty(storage->ancestorStorage,
1195 storage->rootPropertySetIndex,
1196 &currentProperty);
1198 if (currentProperty.dirProperty != PROPERTY_NULL)
1201 * The root storage contains some element, therefore, start the research
1202 * for the appropriate location.
1204 BOOL found = 0;
1205 ULONG current, next, previous, currentPropertyId;
1208 * Keep the StgProperty sequence number of the storage first property
1210 currentPropertyId = currentProperty.dirProperty;
1213 * Read
1215 StorageImpl_ReadProperty(storage->ancestorStorage,
1216 currentProperty.dirProperty,
1217 &currentProperty);
1219 previous = currentProperty.previousProperty;
1220 next = currentProperty.nextProperty;
1221 current = currentPropertyId;
1223 while (found == 0)
1225 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1227 if (diff < 0)
1229 if (previous != PROPERTY_NULL)
1231 StorageImpl_ReadProperty(storage->ancestorStorage,
1232 previous,
1233 &currentProperty);
1234 current = previous;
1236 else
1238 currentProperty.previousProperty = newPropertyIndex;
1239 StorageImpl_WriteProperty(storage->ancestorStorage,
1240 current,
1241 &currentProperty);
1242 found = 1;
1245 else if (diff > 0)
1247 if (next != PROPERTY_NULL)
1249 StorageImpl_ReadProperty(storage->ancestorStorage,
1250 next,
1251 &currentProperty);
1252 current = next;
1254 else
1256 currentProperty.nextProperty = newPropertyIndex;
1257 StorageImpl_WriteProperty(storage->ancestorStorage,
1258 current,
1259 &currentProperty);
1260 found = 1;
1263 else
1266 * Trying to insert an item with the same name in the
1267 * subtree structure.
1269 assert(FALSE);
1272 previous = currentProperty.previousProperty;
1273 next = currentProperty.nextProperty;
1276 else
1279 * The root storage is empty, link the new property to it's dir property
1281 currentProperty.dirProperty = newPropertyIndex;
1282 StorageImpl_WriteProperty(storage->ancestorStorage,
1283 storage->rootPropertySetIndex,
1284 &currentProperty);
1289 /*************************************************************************
1290 * CopyTo (IStorage)
1292 HRESULT WINAPI StorageImpl_CopyTo(
1293 IStorage* iface,
1294 DWORD ciidExclude, /* [in] */
1295 const IID* rgiidExclude, /* [size_is][unique][in] */
1296 SNB snbExclude, /* [unique][in] */
1297 IStorage* pstgDest) /* [unique][in] */
1299 IEnumSTATSTG *elements = 0;
1300 STATSTG curElement, strStat;
1301 HRESULT hr;
1302 IStorage *pstgTmp, *pstgChild;
1303 IStream *pstrTmp, *pstrChild;
1305 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1306 FIXME("Exclude option not implemented\n");
1308 TRACE("(%p, %ld, %p, %p, %p)\n",
1309 iface, ciidExclude, rgiidExclude,
1310 snbExclude, pstgDest);
1313 * Perform a sanity check
1315 if ( pstgDest == 0 )
1316 return STG_E_INVALIDPOINTER;
1319 * Enumerate the elements
1321 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1323 if ( hr != S_OK )
1324 return hr;
1327 * set the class ID
1329 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1330 IStorage_SetClass( pstgDest, &curElement.clsid );
1335 * Obtain the next element
1337 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1339 if ( hr == S_FALSE )
1341 hr = S_OK; /* done, every element has been copied */
1342 break;
1345 if (curElement.type == STGTY_STORAGE)
1348 * open child source storage
1350 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1351 STGM_READ|STGM_SHARE_EXCLUSIVE,
1352 NULL, 0, &pstgChild );
1354 if (hr != S_OK)
1355 break;
1358 * Check if destination storage is not a child of the source
1359 * storage, which will cause an infinite loop
1361 if (pstgChild == pstgDest)
1363 IEnumSTATSTG_Release(elements);
1365 return STG_E_ACCESSDENIED;
1369 * create a new storage in destination storage
1371 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1372 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1373 0, 0,
1374 &pstgTmp );
1376 * if it already exist, don't create a new one use this one
1378 if (hr == STG_E_FILEALREADYEXISTS)
1380 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1381 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1382 NULL, 0, &pstgTmp );
1385 if (hr != S_OK)
1386 break;
1390 * do the copy recursively
1392 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1393 snbExclude, pstgTmp );
1395 IStorage_Release( pstgTmp );
1396 IStorage_Release( pstgChild );
1398 else if (curElement.type == STGTY_STREAM)
1401 * create a new stream in destination storage. If the stream already
1402 * exist, it will be deleted and a new one will be created.
1404 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1405 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1406 0, 0, &pstrTmp );
1408 if (hr != S_OK)
1409 break;
1412 * open child stream storage
1414 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1415 STGM_READ|STGM_SHARE_EXCLUSIVE,
1416 0, &pstrChild );
1418 if (hr != S_OK)
1419 break;
1422 * Get the size of the source stream
1424 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1427 * Set the size of the destination stream.
1429 IStream_SetSize(pstrTmp, strStat.cbSize);
1432 * do the copy
1434 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1435 NULL, NULL );
1437 IStream_Release( pstrTmp );
1438 IStream_Release( pstrChild );
1440 else
1442 WARN("unknown element type: %ld\n", curElement.type);
1445 } while (hr == S_OK);
1448 * Clean-up
1450 IEnumSTATSTG_Release(elements);
1452 return hr;
1455 /*************************************************************************
1456 * MoveElementTo (IStorage)
1458 HRESULT WINAPI StorageImpl_MoveElementTo(
1459 IStorage* iface,
1460 const OLECHAR *pwcsName, /* [string][in] */
1461 IStorage *pstgDest, /* [unique][in] */
1462 const OLECHAR *pwcsNewName,/* [string][in] */
1463 DWORD grfFlags) /* [in] */
1465 FIXME("not implemented!\n");
1466 return E_NOTIMPL;
1469 /*************************************************************************
1470 * Commit (IStorage)
1472 HRESULT WINAPI StorageImpl_Commit(
1473 IStorage* iface,
1474 DWORD grfCommitFlags)/* [in] */
1476 FIXME("(%ld): stub!\n", grfCommitFlags);
1477 return S_OK;
1480 /*************************************************************************
1481 * Revert (IStorage)
1483 HRESULT WINAPI StorageImpl_Revert(
1484 IStorage* iface)
1486 FIXME("not implemented!\n");
1487 return E_NOTIMPL;
1490 /*************************************************************************
1491 * DestroyElement (IStorage)
1493 * Stategy: This implementation is build this way for simplicity not for speed.
1494 * I always delete the top most element of the enumeration and adjust
1495 * the deleted element pointer all the time. This takes longer to
1496 * do but allow to reinvoke DestroyElement whenever we encounter a
1497 * storage object. The optimisation reside in the usage of another
1498 * enumeration stategy that would give all the leaves of a storage
1499 * first. (postfix order)
1501 HRESULT WINAPI StorageImpl_DestroyElement(
1502 IStorage* iface,
1503 const OLECHAR *pwcsName)/* [string][in] */
1505 StorageImpl* const This=(StorageImpl*)iface;
1507 IEnumSTATSTGImpl* propertyEnumeration;
1508 HRESULT hr = S_OK;
1509 BOOL res;
1510 StgProperty propertyToDelete;
1511 StgProperty parentProperty;
1512 ULONG foundPropertyIndexToDelete;
1513 ULONG typeOfRelation;
1514 ULONG parentPropertyId;
1516 TRACE("(%p, %s)\n",
1517 iface, debugstr_w(pwcsName));
1520 * Perform a sanity check on the parameters.
1522 if (pwcsName==NULL)
1523 return STG_E_INVALIDPOINTER;
1526 * Create a property enumeration to search the property with the given name
1528 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1529 This->ancestorStorage,
1530 This->rootPropertySetIndex);
1532 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1533 propertyEnumeration,
1534 pwcsName,
1535 &propertyToDelete);
1537 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1539 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1541 return STG_E_FILENOTFOUND;
1545 * Find the parent property of the property to delete (the one that
1546 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1547 * the parent is This. Otherwise, the parent is one of it's sibling...
1551 * First, read This's StgProperty..
1553 res = StorageImpl_ReadProperty(
1554 This->ancestorStorage,
1555 This->rootPropertySetIndex,
1556 &parentProperty);
1558 assert(res==TRUE);
1561 * Second, check to see if by any chance the actual storage (This) is not
1562 * the parent of the property to delete... We never know...
1564 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1567 * Set data as it would have been done in the else part...
1569 typeOfRelation = PROPERTY_RELATION_DIR;
1570 parentPropertyId = This->rootPropertySetIndex;
1572 else
1575 * Create a property enumeration to search the parent properties, and
1576 * delete it once done.
1578 IEnumSTATSTGImpl* propertyEnumeration2;
1580 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1581 This->ancestorStorage,
1582 This->rootPropertySetIndex);
1584 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1585 propertyEnumeration2,
1586 foundPropertyIndexToDelete,
1587 &parentProperty,
1588 &parentPropertyId);
1590 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1593 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1595 hr = deleteStorageProperty(
1596 This,
1597 propertyToDelete.name);
1599 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1601 hr = deleteStreamProperty(
1602 This,
1603 foundPropertyIndexToDelete,
1604 propertyToDelete);
1607 if (hr!=S_OK)
1608 return hr;
1611 * Adjust the property chain
1613 hr = adjustPropertyChain(
1614 This,
1615 propertyToDelete,
1616 parentProperty,
1617 parentPropertyId,
1618 typeOfRelation);
1620 return hr;
1624 /*********************************************************************
1626 * Internal Method
1628 * Perform the deletion of a complete storage node
1631 static HRESULT deleteStorageProperty(
1632 StorageImpl *parentStorage,
1633 OLECHAR *propertyToDeleteName)
1635 IEnumSTATSTG *elements = 0;
1636 IStorage *childStorage = 0;
1637 STATSTG currentElement;
1638 HRESULT hr;
1639 HRESULT destroyHr = S_OK;
1642 * Open the storage and enumerate it
1644 hr = StorageBaseImpl_OpenStorage(
1645 (IStorage*)parentStorage,
1646 propertyToDeleteName,
1648 STGM_SHARE_EXCLUSIVE,
1651 &childStorage);
1653 if (hr != S_OK)
1655 return hr;
1659 * Enumerate the elements
1661 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1666 * Obtain the next element
1668 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1669 if (hr==S_OK)
1671 destroyHr = StorageImpl_DestroyElement(
1672 (IStorage*)childStorage,
1673 (OLECHAR*)currentElement.pwcsName);
1675 CoTaskMemFree(currentElement.pwcsName);
1679 * We need to Reset the enumeration every time because we delete elements
1680 * and the enumeration could be invalid
1682 IEnumSTATSTG_Reset(elements);
1684 } while ((hr == S_OK) && (destroyHr == S_OK));
1686 IStorage_Release(childStorage);
1687 IEnumSTATSTG_Release(elements);
1689 return destroyHr;
1692 /*********************************************************************
1694 * Internal Method
1696 * Perform the deletion of a stream node
1699 static HRESULT deleteStreamProperty(
1700 StorageImpl *parentStorage,
1701 ULONG indexOfPropertyToDelete,
1702 StgProperty propertyToDelete)
1704 IStream *pis;
1705 HRESULT hr;
1706 ULARGE_INTEGER size;
1708 size.HighPart = 0;
1709 size.LowPart = 0;
1711 hr = StorageBaseImpl_OpenStream(
1712 (IStorage*)parentStorage,
1713 (OLECHAR*)propertyToDelete.name,
1714 NULL,
1715 STGM_SHARE_EXCLUSIVE,
1717 &pis);
1719 if (hr!=S_OK)
1721 return(hr);
1725 * Zap the stream
1727 hr = IStream_SetSize(pis, size);
1729 if(hr != S_OK)
1731 return hr;
1735 * Release the stream object.
1737 IStream_Release(pis);
1740 * Invalidate the property by zeroing it's name member.
1742 propertyToDelete.sizeOfNameString = 0;
1745 * Here we should re-read the property so we get the updated pointer
1746 * but since we are here to zap it, I don't do it...
1748 StorageImpl_WriteProperty(
1749 parentStorage->ancestorStorage,
1750 indexOfPropertyToDelete,
1751 &propertyToDelete);
1753 return S_OK;
1756 /*********************************************************************
1758 * Internal Method
1760 * Finds a placeholder for the StgProperty within the Storage
1763 static HRESULT findPlaceholder(
1764 StorageImpl *storage,
1765 ULONG propertyIndexToStore,
1766 ULONG storePropertyIndex,
1767 INT typeOfRelation)
1769 StgProperty storeProperty;
1770 HRESULT hr = S_OK;
1771 BOOL res = TRUE;
1774 * Read the storage property
1776 res = StorageImpl_ReadProperty(
1777 storage->ancestorStorage,
1778 storePropertyIndex,
1779 &storeProperty);
1781 if(! res)
1783 return E_FAIL;
1786 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1788 if (storeProperty.previousProperty != PROPERTY_NULL)
1790 return findPlaceholder(
1791 storage,
1792 propertyIndexToStore,
1793 storeProperty.previousProperty,
1794 typeOfRelation);
1796 else
1798 storeProperty.previousProperty = propertyIndexToStore;
1801 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1803 if (storeProperty.nextProperty != PROPERTY_NULL)
1805 return findPlaceholder(
1806 storage,
1807 propertyIndexToStore,
1808 storeProperty.nextProperty,
1809 typeOfRelation);
1811 else
1813 storeProperty.nextProperty = propertyIndexToStore;
1816 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1818 if (storeProperty.dirProperty != PROPERTY_NULL)
1820 return findPlaceholder(
1821 storage,
1822 propertyIndexToStore,
1823 storeProperty.dirProperty,
1824 typeOfRelation);
1826 else
1828 storeProperty.dirProperty = propertyIndexToStore;
1832 hr = StorageImpl_WriteProperty(
1833 storage->ancestorStorage,
1834 storePropertyIndex,
1835 &storeProperty);
1837 if(! hr)
1839 return E_FAIL;
1842 return S_OK;
1845 /*************************************************************************
1847 * Internal Method
1849 * This method takes the previous and the next property link of a property
1850 * to be deleted and find them a place in the Storage.
1852 static HRESULT adjustPropertyChain(
1853 StorageImpl *This,
1854 StgProperty propertyToDelete,
1855 StgProperty parentProperty,
1856 ULONG parentPropertyId,
1857 INT typeOfRelation)
1859 ULONG newLinkProperty = PROPERTY_NULL;
1860 BOOL needToFindAPlaceholder = FALSE;
1861 ULONG storeNode = PROPERTY_NULL;
1862 ULONG toStoreNode = PROPERTY_NULL;
1863 INT relationType = 0;
1864 HRESULT hr = S_OK;
1865 BOOL res = TRUE;
1867 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1869 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1872 * Set the parent previous to the property to delete previous
1874 newLinkProperty = propertyToDelete.previousProperty;
1876 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1879 * We also need to find a storage for the other link, setup variables
1880 * to do this at the end...
1882 needToFindAPlaceholder = TRUE;
1883 storeNode = propertyToDelete.previousProperty;
1884 toStoreNode = propertyToDelete.nextProperty;
1885 relationType = PROPERTY_RELATION_NEXT;
1888 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1891 * Set the parent previous to the property to delete next
1893 newLinkProperty = propertyToDelete.nextProperty;
1897 * Link it for real...
1899 parentProperty.previousProperty = newLinkProperty;
1902 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1904 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1907 * Set the parent next to the property to delete next previous
1909 newLinkProperty = propertyToDelete.previousProperty;
1911 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1914 * We also need to find a storage for the other link, setup variables
1915 * to do this at the end...
1917 needToFindAPlaceholder = TRUE;
1918 storeNode = propertyToDelete.previousProperty;
1919 toStoreNode = propertyToDelete.nextProperty;
1920 relationType = PROPERTY_RELATION_NEXT;
1923 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1926 * Set the parent next to the property to delete next
1928 newLinkProperty = propertyToDelete.nextProperty;
1932 * Link it for real...
1934 parentProperty.nextProperty = newLinkProperty;
1936 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
1938 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1941 * Set the parent dir to the property to delete previous
1943 newLinkProperty = propertyToDelete.previousProperty;
1945 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1948 * We also need to find a storage for the other link, setup variables
1949 * to do this at the end...
1951 needToFindAPlaceholder = TRUE;
1952 storeNode = propertyToDelete.previousProperty;
1953 toStoreNode = propertyToDelete.nextProperty;
1954 relationType = PROPERTY_RELATION_NEXT;
1957 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1960 * Set the parent dir to the property to delete next
1962 newLinkProperty = propertyToDelete.nextProperty;
1966 * Link it for real...
1968 parentProperty.dirProperty = newLinkProperty;
1972 * Write back the parent property
1974 res = StorageImpl_WriteProperty(
1975 This->ancestorStorage,
1976 parentPropertyId,
1977 &parentProperty);
1978 if(! res)
1980 return E_FAIL;
1984 * If a placeholder is required for the other link, then, find one and
1985 * get out of here...
1987 if (needToFindAPlaceholder)
1989 hr = findPlaceholder(
1990 This,
1991 toStoreNode,
1992 storeNode,
1993 relationType);
1996 return hr;
2000 /******************************************************************************
2001 * SetElementTimes (IStorage)
2003 HRESULT WINAPI StorageImpl_SetElementTimes(
2004 IStorage* iface,
2005 const OLECHAR *pwcsName,/* [string][in] */
2006 const FILETIME *pctime, /* [in] */
2007 const FILETIME *patime, /* [in] */
2008 const FILETIME *pmtime) /* [in] */
2010 FIXME("not implemented!\n");
2011 return E_NOTIMPL;
2014 /******************************************************************************
2015 * SetStateBits (IStorage)
2017 HRESULT WINAPI StorageImpl_SetStateBits(
2018 IStorage* iface,
2019 DWORD grfStateBits,/* [in] */
2020 DWORD grfMask) /* [in] */
2022 FIXME("not implemented!\n");
2023 return E_NOTIMPL;
2026 HRESULT StorageImpl_Construct(
2027 StorageImpl* This,
2028 HANDLE hFile,
2029 ILockBytes* pLkbyt,
2030 DWORD openFlags,
2031 BOOL fileBased)
2033 HRESULT hr = S_OK;
2034 StgProperty currentProperty;
2035 BOOL readSucessful;
2036 ULONG currentPropertyIndex;
2038 if ( FAILED( validateSTGM(openFlags) ))
2039 return STG_E_INVALIDFLAG;
2041 memset(This, 0, sizeof(StorageImpl));
2044 * Initialize the virtual fgunction table.
2046 This->lpvtbl = &Storage32Impl_Vtbl;
2047 This->v_destructor = &StorageImpl_Destroy;
2050 * This is the top-level storage so initialize the ancester pointer
2051 * to this.
2053 This->ancestorStorage = This;
2056 * Initialize the physical support of the storage.
2058 This->hFile = hFile;
2061 * Initialize the big block cache.
2063 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2064 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2065 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2066 pLkbyt,
2067 openFlags,
2068 This->bigBlockSize,
2069 fileBased);
2071 if (This->bigBlockFile == 0)
2072 return E_FAIL;
2074 if (openFlags & STGM_CREATE)
2076 ULARGE_INTEGER size;
2077 BYTE* bigBlockBuffer;
2080 * Initialize all header variables:
2081 * - The big block depot consists of one block and it is at block 0
2082 * - The properties start at block 1
2083 * - There is no small block depot
2085 memset( This->bigBlockDepotStart,
2086 BLOCK_UNUSED,
2087 sizeof(This->bigBlockDepotStart));
2089 This->bigBlockDepotCount = 1;
2090 This->bigBlockDepotStart[0] = 0;
2091 This->rootStartBlock = 1;
2092 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2093 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2094 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2095 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2096 This->extBigBlockDepotCount = 0;
2098 StorageImpl_SaveFileHeader(This);
2101 * Add one block for the big block depot and one block for the properties
2103 size.HighPart = 0;
2104 size.LowPart = This->bigBlockSize * 3;
2105 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2108 * Initialize the big block depot
2110 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2111 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2112 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2113 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2114 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2116 else
2119 * Load the header for the file.
2121 hr = StorageImpl_LoadFileHeader(This);
2123 if (FAILED(hr))
2125 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2127 return hr;
2132 * There is no block depot cached yet.
2134 This->indexBlockDepotCached = 0xFFFFFFFF;
2137 * Start searching for free blocks with block 0.
2139 This->prevFreeBlock = 0;
2142 * Create the block chain abstractions.
2144 This->rootBlockChain =
2145 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2147 This->smallBlockDepotChain = BlockChainStream_Construct(
2148 This,
2149 &This->smallBlockDepotStart,
2150 PROPERTY_NULL);
2153 * Write the root property
2155 if (openFlags & STGM_CREATE)
2157 StgProperty rootProp;
2159 * Initialize the property chain
2161 memset(&rootProp, 0, sizeof(rootProp));
2162 lstrcpyAtoW(rootProp.name, rootPropertyName);
2164 rootProp.sizeOfNameString = (lstrlenW(rootProp.name)+1) * sizeof(WCHAR);
2165 rootProp.propertyType = PROPTYPE_ROOT;
2166 rootProp.previousProperty = PROPERTY_NULL;
2167 rootProp.nextProperty = PROPERTY_NULL;
2168 rootProp.dirProperty = PROPERTY_NULL;
2169 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2170 rootProp.size.HighPart = 0;
2171 rootProp.size.LowPart = 0;
2173 StorageImpl_WriteProperty(This, 0, &rootProp);
2177 * Find the ID of the root int he property sets.
2179 currentPropertyIndex = 0;
2183 readSucessful = StorageImpl_ReadProperty(
2184 This,
2185 currentPropertyIndex,
2186 &currentProperty);
2188 if (readSucessful)
2190 if ( (currentProperty.sizeOfNameString != 0 ) &&
2191 (currentProperty.propertyType == PROPTYPE_ROOT) )
2193 This->rootPropertySetIndex = currentPropertyIndex;
2197 currentPropertyIndex++;
2199 } while (readSucessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2201 if (!readSucessful)
2203 /* TODO CLEANUP */
2204 return E_FAIL;
2208 * Create the block chain abstraction for the small block root chain.
2210 This->smallBlockRootChain = BlockChainStream_Construct(
2211 This,
2212 NULL,
2213 This->rootPropertySetIndex);
2215 return hr;
2218 void StorageImpl_Destroy(
2219 StorageImpl* This)
2221 TRACE("(%p)\n", This);
2223 BlockChainStream_Destroy(This->smallBlockRootChain);
2224 BlockChainStream_Destroy(This->rootBlockChain);
2225 BlockChainStream_Destroy(This->smallBlockDepotChain);
2227 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2228 return;
2231 /******************************************************************************
2232 * Storage32Impl_GetNextFreeBigBlock
2234 * Returns the index of the next free big block.
2235 * If the big block depot is filled, this method will enlarge it.
2238 ULONG StorageImpl_GetNextFreeBigBlock(
2239 StorageImpl* This)
2241 ULONG depotBlockIndexPos;
2242 void *depotBuffer;
2243 ULONG depotBlockOffset;
2244 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2245 ULONG nextBlockIndex = BLOCK_SPECIAL;
2246 int depotIndex = 0;
2247 ULONG freeBlock = BLOCK_UNUSED;
2249 depotIndex = This->prevFreeBlock / blocksPerDepot;
2250 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2253 * Scan the entire big block depot until we find a block marked free
2255 while (nextBlockIndex != BLOCK_UNUSED)
2257 if (depotIndex < COUNT_BBDEPOTINHEADER)
2259 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2262 * Grow the primary depot.
2264 if (depotBlockIndexPos == BLOCK_UNUSED)
2266 depotBlockIndexPos = depotIndex*blocksPerDepot;
2269 * Add a block depot.
2271 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2272 This->bigBlockDepotCount++;
2273 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2276 * Flag it as a block depot.
2278 StorageImpl_SetNextBlockInChain(This,
2279 depotBlockIndexPos,
2280 BLOCK_SPECIAL);
2282 /* Save new header information.
2284 StorageImpl_SaveFileHeader(This);
2287 else
2289 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2291 if (depotBlockIndexPos == BLOCK_UNUSED)
2294 * Grow the extended depot.
2296 ULONG extIndex = BLOCK_UNUSED;
2297 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2298 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2300 if (extBlockOffset == 0)
2302 /* We need an extended block.
2304 extIndex = Storage32Impl_AddExtBlockDepot(This);
2305 This->extBigBlockDepotCount++;
2306 depotBlockIndexPos = extIndex + 1;
2308 else
2309 depotBlockIndexPos = depotIndex * blocksPerDepot;
2312 * Add a block depot and mark it in the extended block.
2314 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2315 This->bigBlockDepotCount++;
2316 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2318 /* Flag the block depot.
2320 StorageImpl_SetNextBlockInChain(This,
2321 depotBlockIndexPos,
2322 BLOCK_SPECIAL);
2324 /* If necessary, flag the extended depot block.
2326 if (extIndex != BLOCK_UNUSED)
2327 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2329 /* Save header information.
2331 StorageImpl_SaveFileHeader(This);
2335 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2337 if (depotBuffer != 0)
2339 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2340 ( nextBlockIndex != BLOCK_UNUSED))
2342 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2344 if (nextBlockIndex == BLOCK_UNUSED)
2346 freeBlock = (depotIndex * blocksPerDepot) +
2347 (depotBlockOffset/sizeof(ULONG));
2350 depotBlockOffset += sizeof(ULONG);
2353 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2356 depotIndex++;
2357 depotBlockOffset = 0;
2360 This->prevFreeBlock = freeBlock;
2362 return freeBlock;
2365 /******************************************************************************
2366 * Storage32Impl_AddBlockDepot
2368 * This will create a depot block, essentially it is a block initialized
2369 * to BLOCK_UNUSEDs.
2371 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2373 BYTE* blockBuffer;
2375 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2378 * Initialize blocks as free
2380 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2382 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2385 /******************************************************************************
2386 * Storage32Impl_GetExtDepotBlock
2388 * Returns the index of the block that corresponds to the specified depot
2389 * index. This method is only for depot indexes equal or greater than
2390 * COUNT_BBDEPOTINHEADER.
2392 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2394 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2395 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2396 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2397 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2398 ULONG blockIndex = BLOCK_UNUSED;
2399 ULONG extBlockIndex = This->extBigBlockDepotStart;
2401 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2403 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2404 return BLOCK_UNUSED;
2406 while (extBlockCount > 0)
2408 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2409 extBlockCount--;
2412 if (extBlockIndex != BLOCK_UNUSED)
2414 BYTE* depotBuffer;
2416 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2418 if (depotBuffer != 0)
2420 StorageUtl_ReadDWord(depotBuffer,
2421 extBlockOffset * sizeof(ULONG),
2422 &blockIndex);
2424 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2428 return blockIndex;
2431 /******************************************************************************
2432 * Storage32Impl_SetExtDepotBlock
2434 * Associates the specified block index to the specified depot index.
2435 * This method is only for depot indexes equal or greater than
2436 * COUNT_BBDEPOTINHEADER.
2438 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2439 ULONG depotIndex,
2440 ULONG blockIndex)
2442 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2443 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2444 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2445 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2446 ULONG extBlockIndex = This->extBigBlockDepotStart;
2448 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2450 while (extBlockCount > 0)
2452 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2453 extBlockCount--;
2456 if (extBlockIndex != BLOCK_UNUSED)
2458 BYTE* depotBuffer;
2460 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2462 if (depotBuffer != 0)
2464 StorageUtl_WriteDWord(depotBuffer,
2465 extBlockOffset * sizeof(ULONG),
2466 blockIndex);
2468 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2473 /******************************************************************************
2474 * Storage32Impl_AddExtBlockDepot
2476 * Creates an extended depot block.
2478 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2480 ULONG numExtBlocks = This->extBigBlockDepotCount;
2481 ULONG nextExtBlock = This->extBigBlockDepotStart;
2482 BYTE* depotBuffer = NULL;
2483 ULONG index = BLOCK_UNUSED;
2484 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2485 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2486 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2488 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2489 blocksPerDepotBlock;
2491 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2494 * The first extended block.
2496 This->extBigBlockDepotStart = index;
2498 else
2500 int i;
2502 * Follow the chain to the last one.
2504 for (i = 0; i < (numExtBlocks - 1); i++)
2506 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2510 * Add the new extended block to the chain.
2512 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2513 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2514 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2518 * Initialize this block.
2520 depotBuffer = StorageImpl_GetBigBlock(This, index);
2521 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2522 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2524 return index;
2527 /******************************************************************************
2528 * Storage32Impl_FreeBigBlock
2530 * This method will flag the specified block as free in the big block depot.
2532 void StorageImpl_FreeBigBlock(
2533 StorageImpl* This,
2534 ULONG blockIndex)
2536 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2538 if (blockIndex < This->prevFreeBlock)
2539 This->prevFreeBlock = blockIndex;
2542 /************************************************************************
2543 * Storage32Impl_GetNextBlockInChain
2545 * This method will retrieve the block index of the next big block in
2546 * in the chain.
2548 * Params: This - Pointer to the Storage object.
2549 * blockIndex - Index of the block to retrieve the chain
2550 * for.
2552 * Returns: This method returns the index of the next block in the chain.
2553 * It will return the constants:
2554 * BLOCK_SPECIAL - If the block given was not part of a
2555 * chain.
2556 * BLOCK_END_OF_CHAIN - If the block given was the last in
2557 * a chain.
2558 * BLOCK_UNUSED - If the block given was not past of a chain
2559 * and is available.
2560 * BLOCK_EXTBBDEPOT - This block is part of the extended
2561 * big block depot.
2563 * See Windows documentation for more details on IStorage methods.
2565 ULONG StorageImpl_GetNextBlockInChain(
2566 StorageImpl* This,
2567 ULONG blockIndex)
2569 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2570 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2571 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2572 ULONG nextBlockIndex = BLOCK_SPECIAL;
2573 void* depotBuffer;
2574 ULONG depotBlockIndexPos;
2576 assert(depotBlockCount < This->bigBlockDepotCount);
2579 * Cache the currently accessed depot block.
2581 if (depotBlockCount != This->indexBlockDepotCached)
2583 This->indexBlockDepotCached = depotBlockCount;
2585 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2587 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2589 else
2592 * We have to look in the extended depot.
2594 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2597 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2599 if (depotBuffer!=0)
2601 int index;
2603 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2605 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2606 This->blockDepotCached[index] = nextBlockIndex;
2609 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2613 nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2615 return nextBlockIndex;
2618 /******************************************************************************
2619 * Storage32Impl_GetNextExtendedBlock
2621 * Given an extended block this method will return the next extended block.
2623 * NOTES:
2624 * The last ULONG of an extended block is the block index of the next
2625 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2626 * depot.
2628 * Return values:
2629 * - The index of the next extended block
2630 * - BLOCK_UNUSED: there is no next extended block.
2631 * - Any other return values denotes failure.
2633 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2635 ULONG nextBlockIndex = BLOCK_SPECIAL;
2636 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2637 void* depotBuffer;
2639 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2641 if (depotBuffer!=0)
2643 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2645 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2648 return nextBlockIndex;
2651 /******************************************************************************
2652 * Storage32Impl_SetNextBlockInChain
2654 * This method will write the index of the specified block's next block
2655 * in the big block depot.
2657 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2658 * do the following
2660 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2661 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2662 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2665 void StorageImpl_SetNextBlockInChain(
2666 StorageImpl* This,
2667 ULONG blockIndex,
2668 ULONG nextBlock)
2670 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2671 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2672 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2673 ULONG depotBlockIndexPos;
2674 void* depotBuffer;
2676 assert(depotBlockCount < This->bigBlockDepotCount);
2677 assert(blockIndex != nextBlock);
2679 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2681 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2683 else
2686 * We have to look in the extended depot.
2688 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2691 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2693 if (depotBuffer!=0)
2695 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2696 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2700 * Update the cached block depot, if necessary.
2702 if (depotBlockCount == This->indexBlockDepotCached)
2704 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2708 /******************************************************************************
2709 * Storage32Impl_LoadFileHeader
2711 * This method will read in the file header, i.e. big block index -1.
2713 HRESULT StorageImpl_LoadFileHeader(
2714 StorageImpl* This)
2716 HRESULT hr = STG_E_FILENOTFOUND;
2717 void* headerBigBlock = NULL;
2718 int index;
2721 * Get a pointer to the big block of data containing the header.
2723 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2726 * Extract the information from the header.
2728 if (headerBigBlock!=0)
2731 * Check for the "magic number" signature and return an error if it is not
2732 * found.
2734 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2736 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2737 return STG_E_OLDFORMAT;
2740 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2742 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2743 return STG_E_INVALIDHEADER;
2746 StorageUtl_ReadWord(
2747 headerBigBlock,
2748 OFFSET_BIGBLOCKSIZEBITS,
2749 &This->bigBlockSizeBits);
2751 StorageUtl_ReadWord(
2752 headerBigBlock,
2753 OFFSET_SMALLBLOCKSIZEBITS,
2754 &This->smallBlockSizeBits);
2756 StorageUtl_ReadDWord(
2757 headerBigBlock,
2758 OFFSET_BBDEPOTCOUNT,
2759 &This->bigBlockDepotCount);
2761 StorageUtl_ReadDWord(
2762 headerBigBlock,
2763 OFFSET_ROOTSTARTBLOCK,
2764 &This->rootStartBlock);
2766 StorageUtl_ReadDWord(
2767 headerBigBlock,
2768 OFFSET_SBDEPOTSTART,
2769 &This->smallBlockDepotStart);
2771 StorageUtl_ReadDWord(
2772 headerBigBlock,
2773 OFFSET_EXTBBDEPOTSTART,
2774 &This->extBigBlockDepotStart);
2776 StorageUtl_ReadDWord(
2777 headerBigBlock,
2778 OFFSET_EXTBBDEPOTCOUNT,
2779 &This->extBigBlockDepotCount);
2781 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2783 StorageUtl_ReadDWord(
2784 headerBigBlock,
2785 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2786 &(This->bigBlockDepotStart[index]));
2790 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2792 if ((1 << 2) == 4)
2794 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2795 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2797 else
2799 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2800 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2804 * Right now, the code is making some assumptions about the size of the
2805 * blocks, just make sure they are what we're expecting.
2807 assert( (This->bigBlockSize==DEF_BIG_BLOCK_SIZE) &&
2808 (This->smallBlockSize==DEF_SMALL_BLOCK_SIZE));
2811 * Release the block.
2813 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2815 hr = S_OK;
2818 return hr;
2821 /******************************************************************************
2822 * Storage32Impl_SaveFileHeader
2824 * This method will save to the file the header, i.e. big block -1.
2826 void StorageImpl_SaveFileHeader(
2827 StorageImpl* This)
2829 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2830 int index;
2831 BOOL success;
2834 * Get a pointer to the big block of data containing the header.
2836 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2839 * If the block read failed, the file is probably new.
2841 if (!success)
2844 * Initialize for all unknown fields.
2846 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2849 * Initialize the magic number.
2851 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2854 * And a bunch of things we don't know what they mean
2856 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2857 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2858 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2859 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2860 StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
2864 * Write the information to the header.
2866 if (headerBigBlock!=0)
2868 StorageUtl_WriteWord(
2869 headerBigBlock,
2870 OFFSET_BIGBLOCKSIZEBITS,
2871 This->bigBlockSizeBits);
2873 StorageUtl_WriteWord(
2874 headerBigBlock,
2875 OFFSET_SMALLBLOCKSIZEBITS,
2876 This->smallBlockSizeBits);
2878 StorageUtl_WriteDWord(
2879 headerBigBlock,
2880 OFFSET_BBDEPOTCOUNT,
2881 This->bigBlockDepotCount);
2883 StorageUtl_WriteDWord(
2884 headerBigBlock,
2885 OFFSET_ROOTSTARTBLOCK,
2886 This->rootStartBlock);
2888 StorageUtl_WriteDWord(
2889 headerBigBlock,
2890 OFFSET_SBDEPOTSTART,
2891 This->smallBlockDepotStart);
2893 StorageUtl_WriteDWord(
2894 headerBigBlock,
2895 OFFSET_EXTBBDEPOTSTART,
2896 This->extBigBlockDepotStart);
2898 StorageUtl_WriteDWord(
2899 headerBigBlock,
2900 OFFSET_EXTBBDEPOTCOUNT,
2901 This->extBigBlockDepotCount);
2903 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2905 StorageUtl_WriteDWord(
2906 headerBigBlock,
2907 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2908 (This->bigBlockDepotStart[index]));
2913 * Write the big block back to the file.
2915 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2918 /******************************************************************************
2919 * Storage32Impl_ReadProperty
2921 * This method will read the specified property from the property chain.
2923 BOOL StorageImpl_ReadProperty(
2924 StorageImpl* This,
2925 ULONG index,
2926 StgProperty* buffer)
2928 BYTE currentProperty[PROPSET_BLOCK_SIZE];
2929 ULARGE_INTEGER offsetInPropSet;
2930 BOOL readSucessful;
2931 ULONG bytesRead;
2933 offsetInPropSet.HighPart = 0;
2934 offsetInPropSet.LowPart = index * PROPSET_BLOCK_SIZE;
2936 readSucessful = BlockChainStream_ReadAt(
2937 This->rootBlockChain,
2938 offsetInPropSet,
2939 PROPSET_BLOCK_SIZE,
2940 currentProperty,
2941 &bytesRead);
2943 if (readSucessful)
2945 memset(buffer->name, 0, sizeof(buffer->name));
2946 memcpy(
2947 buffer->name,
2948 currentProperty+OFFSET_PS_NAME,
2949 PROPERTY_NAME_BUFFER_LEN );
2951 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
2953 StorageUtl_ReadWord(
2954 currentProperty,
2955 OFFSET_PS_NAMELENGTH,
2956 &buffer->sizeOfNameString);
2958 StorageUtl_ReadDWord(
2959 currentProperty,
2960 OFFSET_PS_PREVIOUSPROP,
2961 &buffer->previousProperty);
2963 StorageUtl_ReadDWord(
2964 currentProperty,
2965 OFFSET_PS_NEXTPROP,
2966 &buffer->nextProperty);
2968 StorageUtl_ReadDWord(
2969 currentProperty,
2970 OFFSET_PS_DIRPROP,
2971 &buffer->dirProperty);
2973 StorageUtl_ReadGUID(
2974 currentProperty,
2975 OFFSET_PS_GUID,
2976 &buffer->propertyUniqueID);
2978 StorageUtl_ReadDWord(
2979 currentProperty,
2980 OFFSET_PS_TSS1,
2981 &buffer->timeStampS1);
2983 StorageUtl_ReadDWord(
2984 currentProperty,
2985 OFFSET_PS_TSD1,
2986 &buffer->timeStampD1);
2988 StorageUtl_ReadDWord(
2989 currentProperty,
2990 OFFSET_PS_TSS2,
2991 &buffer->timeStampS2);
2993 StorageUtl_ReadDWord(
2994 currentProperty,
2995 OFFSET_PS_TSD2,
2996 &buffer->timeStampD2);
2998 StorageUtl_ReadDWord(
2999 currentProperty,
3000 OFFSET_PS_STARTBLOCK,
3001 &buffer->startingBlock);
3003 StorageUtl_ReadDWord(
3004 currentProperty,
3005 OFFSET_PS_SIZE,
3006 &buffer->size.LowPart);
3008 buffer->size.HighPart = 0;
3011 return readSucessful;
3014 /*********************************************************************
3015 * Write the specified property into the property chain
3017 BOOL StorageImpl_WriteProperty(
3018 StorageImpl* This,
3019 ULONG index,
3020 StgProperty* buffer)
3022 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3023 ULARGE_INTEGER offsetInPropSet;
3024 BOOL writeSucessful;
3025 ULONG bytesWritten;
3027 offsetInPropSet.HighPart = 0;
3028 offsetInPropSet.LowPart = index * PROPSET_BLOCK_SIZE;
3030 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3032 memcpy(
3033 currentProperty + OFFSET_PS_NAME,
3034 buffer->name,
3035 PROPERTY_NAME_BUFFER_LEN );
3037 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3040 * Reassign the size in case of mistake....
3042 buffer->sizeOfNameString = (lstrlenW(buffer->name)+1) * sizeof(WCHAR);
3044 StorageUtl_WriteWord(
3045 currentProperty,
3046 OFFSET_PS_NAMELENGTH,
3047 buffer->sizeOfNameString);
3049 StorageUtl_WriteDWord(
3050 currentProperty,
3051 OFFSET_PS_PREVIOUSPROP,
3052 buffer->previousProperty);
3054 StorageUtl_WriteDWord(
3055 currentProperty,
3056 OFFSET_PS_NEXTPROP,
3057 buffer->nextProperty);
3059 StorageUtl_WriteDWord(
3060 currentProperty,
3061 OFFSET_PS_DIRPROP,
3062 buffer->dirProperty);
3064 StorageUtl_WriteGUID(
3065 currentProperty,
3066 OFFSET_PS_GUID,
3067 &buffer->propertyUniqueID);
3069 StorageUtl_WriteDWord(
3070 currentProperty,
3071 OFFSET_PS_TSS1,
3072 buffer->timeStampS1);
3074 StorageUtl_WriteDWord(
3075 currentProperty,
3076 OFFSET_PS_TSD1,
3077 buffer->timeStampD1);
3079 StorageUtl_WriteDWord(
3080 currentProperty,
3081 OFFSET_PS_TSS2,
3082 buffer->timeStampS2);
3084 StorageUtl_WriteDWord(
3085 currentProperty,
3086 OFFSET_PS_TSD2,
3087 buffer->timeStampD2);
3089 StorageUtl_WriteDWord(
3090 currentProperty,
3091 OFFSET_PS_STARTBLOCK,
3092 buffer->startingBlock);
3094 StorageUtl_WriteDWord(
3095 currentProperty,
3096 OFFSET_PS_SIZE,
3097 buffer->size.LowPart);
3099 writeSucessful = BlockChainStream_WriteAt(This->rootBlockChain,
3100 offsetInPropSet,
3101 PROPSET_BLOCK_SIZE,
3102 currentProperty,
3103 &bytesWritten);
3104 return writeSucessful;
3107 BOOL StorageImpl_ReadBigBlock(
3108 StorageImpl* This,
3109 ULONG blockIndex,
3110 void* buffer)
3112 void* bigBlockBuffer;
3114 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3116 if (bigBlockBuffer!=0)
3118 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3120 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3122 return TRUE;
3125 return FALSE;
3128 BOOL StorageImpl_WriteBigBlock(
3129 StorageImpl* This,
3130 ULONG blockIndex,
3131 void* buffer)
3133 void* bigBlockBuffer;
3135 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3137 if (bigBlockBuffer!=0)
3139 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3141 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3143 return TRUE;
3146 return FALSE;
3149 void* StorageImpl_GetROBigBlock(
3150 StorageImpl* This,
3151 ULONG blockIndex)
3153 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3156 void* StorageImpl_GetBigBlock(
3157 StorageImpl* This,
3158 ULONG blockIndex)
3160 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3163 void StorageImpl_ReleaseBigBlock(
3164 StorageImpl* This,
3165 void* pBigBlock)
3167 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3170 /******************************************************************************
3171 * Storage32Impl_SmallBlocksToBigBlocks
3173 * This method will convert a small block chain to a big block chain.
3174 * The small block chain will be destroyed.
3176 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3177 StorageImpl* This,
3178 SmallBlockChainStream** ppsbChain)
3180 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3181 ULARGE_INTEGER size, offset;
3182 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3183 ULONG propertyIndex;
3184 BOOL successRead, successWrite;
3185 StgProperty chainProperty;
3186 BYTE buffer[DEF_SMALL_BLOCK_SIZE];
3187 BlockChainStream *bbTempChain = NULL;
3188 BlockChainStream *bigBlockChain = NULL;
3191 * Create a temporary big block chain that doesn't have
3192 * an associated property. This temporary chain will be
3193 * used to copy data from small blocks to big blocks.
3195 bbTempChain = BlockChainStream_Construct(This,
3196 &bbHeadOfChain,
3197 PROPERTY_NULL);
3200 * Grow the big block chain.
3202 size = SmallBlockChainStream_GetSize(*ppsbChain);
3203 BlockChainStream_SetSize(bbTempChain, size);
3206 * Copy the contents of the small block chain to the big block chain
3207 * by small block size increments.
3209 offset.LowPart = 0;
3210 offset.HighPart = 0;
3211 cbTotalRead = 0;
3212 cbTotalWritten = 0;
3216 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3217 offset,
3218 sizeof(buffer),
3219 buffer,
3220 &cbRead);
3221 cbTotalRead += cbRead;
3223 successWrite = BlockChainStream_WriteAt(bbTempChain,
3224 offset,
3225 cbRead,
3226 buffer,
3227 &cbWritten);
3228 cbTotalWritten += cbWritten;
3230 offset.LowPart += This->smallBlockSize;
3232 } while (successRead && successWrite);
3234 assert(cbTotalRead == cbTotalWritten);
3237 * Destroy the small block chain.
3239 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3240 size.HighPart = 0;
3241 size.LowPart = 0;
3242 SmallBlockChainStream_SetSize(*ppsbChain, size);
3243 SmallBlockChainStream_Destroy(*ppsbChain);
3244 *ppsbChain = 0;
3247 * Change the property information. This chain is now a big block chain
3248 * and it doesn't reside in the small blocks chain anymore.
3250 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3252 chainProperty.startingBlock = bbHeadOfChain;
3254 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3257 * Destroy the temporary propertyless big block chain.
3258 * Create a new big block chain associated with this property.
3260 BlockChainStream_Destroy(bbTempChain);
3261 bigBlockChain = BlockChainStream_Construct(This,
3262 NULL,
3263 propertyIndex);
3265 return bigBlockChain;
3268 /******************************************************************************
3269 ** Storage32InternalImpl implementation
3272 StorageInternalImpl* StorageInternalImpl_Construct(
3273 StorageImpl* ancestorStorage,
3274 ULONG rootPropertyIndex)
3276 StorageInternalImpl* newStorage;
3279 * Allocate space for the new storage object
3281 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3283 if (newStorage!=0)
3285 memset(newStorage, 0, sizeof(StorageInternalImpl));
3288 * Initialize the virtual function table.
3290 newStorage->lpvtbl = &Storage32InternalImpl_Vtbl;
3291 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3294 * Keep the ancestor storage pointer and nail a reference to it.
3296 newStorage->ancestorStorage = ancestorStorage;
3297 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3300 * Keep the index of the root property set for this storage,
3302 newStorage->rootPropertySetIndex = rootPropertyIndex;
3304 return newStorage;
3307 return 0;
3310 void StorageInternalImpl_Destroy(
3311 StorageInternalImpl* This)
3313 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3314 HeapFree(GetProcessHeap(), 0, This);
3317 /******************************************************************************
3319 ** Storage32InternalImpl_Commit
3321 ** The non-root storages cannot be opened in transacted mode thus this function
3322 ** does nothing.
3324 HRESULT WINAPI StorageInternalImpl_Commit(
3325 IStorage* iface,
3326 DWORD grfCommitFlags) /* [in] */
3328 return S_OK;
3331 /******************************************************************************
3333 ** Storage32InternalImpl_Revert
3335 ** The non-root storages cannot be opened in transacted mode thus this function
3336 ** does nothing.
3338 HRESULT WINAPI StorageInternalImpl_Revert(
3339 IStorage* iface)
3341 return S_OK;
3344 /******************************************************************************
3345 ** IEnumSTATSTGImpl implementation
3348 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3349 StorageImpl* parentStorage,
3350 ULONG firstPropertyNode)
3352 IEnumSTATSTGImpl* newEnumeration;
3354 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3356 if (newEnumeration!=0)
3359 * Set-up the virtual function table and reference count.
3361 newEnumeration->lpvtbl = &IEnumSTATSTGImpl_Vtbl;
3362 newEnumeration->ref = 0;
3365 * We want to nail-down the reference to the storage in case the
3366 * enumeration out-lives the storage in the client application.
3368 newEnumeration->parentStorage = parentStorage;
3369 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3371 newEnumeration->firstPropertyNode = firstPropertyNode;
3374 * Initialize the search stack
3376 newEnumeration->stackSize = 0;
3377 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3378 newEnumeration->stackToVisit =
3379 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3382 * Make sure the current node of the iterator is the first one.
3384 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3387 return newEnumeration;
3390 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3392 IStorage_Release((IStorage*)This->parentStorage);
3393 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3394 HeapFree(GetProcessHeap(), 0, This);
3397 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3398 IEnumSTATSTG* iface,
3399 REFIID riid,
3400 void** ppvObject)
3402 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3405 * Perform a sanity check on the parameters.
3407 if (ppvObject==0)
3408 return E_INVALIDARG;
3411 * Initialize the return parameter.
3413 *ppvObject = 0;
3416 * Compare the riid with the interface IDs implemented by this object.
3418 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3420 *ppvObject = (IEnumSTATSTG*)This;
3422 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3424 *ppvObject = (IEnumSTATSTG*)This;
3428 * Check that we obtained an interface.
3430 if ((*ppvObject)==0)
3431 return E_NOINTERFACE;
3434 * Query Interface always increases the reference count by one when it is
3435 * successful
3437 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3439 return S_OK;
3442 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3443 IEnumSTATSTG* iface)
3445 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3447 This->ref++;
3448 return This->ref;
3451 ULONG WINAPI IEnumSTATSTGImpl_Release(
3452 IEnumSTATSTG* iface)
3454 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3456 ULONG newRef;
3458 This->ref--;
3459 newRef = This->ref;
3462 * If the reference count goes down to 0, perform suicide.
3464 if (newRef==0)
3466 IEnumSTATSTGImpl_Destroy(This);
3469 return newRef;;
3472 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3473 IEnumSTATSTG* iface,
3474 ULONG celt,
3475 STATSTG* rgelt,
3476 ULONG* pceltFetched)
3478 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3480 StgProperty currentProperty;
3481 STATSTG* currentReturnStruct = rgelt;
3482 ULONG objectFetched = 0;
3483 ULONG currentSearchNode;
3486 * Perform a sanity check on the parameters.
3488 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3489 return E_INVALIDARG;
3492 * To avoid the special case, get another pointer to a ULONG value if
3493 * the caller didn't supply one.
3495 if (pceltFetched==0)
3496 pceltFetched = &objectFetched;
3499 * Start the iteration, we will iterate until we hit the end of the
3500 * linked list or until we hit the number of items to iterate through
3502 *pceltFetched = 0;
3505 * Start with the node at the top of the stack.
3507 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3509 while ( ( *pceltFetched < celt) &&
3510 ( currentSearchNode!=PROPERTY_NULL) )
3513 * Remove the top node from the stack
3515 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3518 * Read the property from the storage.
3520 StorageImpl_ReadProperty(This->parentStorage,
3521 currentSearchNode,
3522 &currentProperty);
3525 * Copy the information to the return buffer.
3527 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3528 &currentProperty,
3529 STATFLAG_DEFAULT);
3532 * Step to the next item in the iteration
3534 (*pceltFetched)++;
3535 currentReturnStruct++;
3538 * Push the next search node in the search stack.
3540 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3543 * continue the iteration.
3545 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3548 if (*pceltFetched == celt)
3549 return S_OK;
3551 return S_FALSE;
3555 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3556 IEnumSTATSTG* iface,
3557 ULONG celt)
3559 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3561 StgProperty currentProperty;
3562 ULONG objectFetched = 0;
3563 ULONG currentSearchNode;
3566 * Start with the node at the top of the stack.
3568 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3570 while ( (objectFetched < celt) &&
3571 (currentSearchNode!=PROPERTY_NULL) )
3574 * Remove the top node from the stack
3576 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3579 * Read the property from the storage.
3581 StorageImpl_ReadProperty(This->parentStorage,
3582 currentSearchNode,
3583 &currentProperty);
3586 * Step to the next item in the iteration
3588 objectFetched++;
3591 * Push the next search node in the search stack.
3593 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3596 * continue the iteration.
3598 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3601 if (objectFetched == celt)
3602 return S_OK;
3604 return S_FALSE;
3607 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3608 IEnumSTATSTG* iface)
3610 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3612 StgProperty rootProperty;
3613 BOOL readSucessful;
3616 * Re-initialize the search stack to an empty stack
3618 This->stackSize = 0;
3621 * Read the root property from the storage.
3623 readSucessful = StorageImpl_ReadProperty(
3624 This->parentStorage,
3625 This->firstPropertyNode,
3626 &rootProperty);
3628 if (readSucessful)
3630 assert(rootProperty.sizeOfNameString!=0);
3633 * Push the search node in the search stack.
3635 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3638 return S_OK;
3641 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3642 IEnumSTATSTG* iface,
3643 IEnumSTATSTG** ppenum)
3645 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3647 IEnumSTATSTGImpl* newClone;
3650 * Perform a sanity check on the parameters.
3652 if (ppenum==0)
3653 return E_INVALIDARG;
3655 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3656 This->firstPropertyNode);
3660 * The new clone enumeration must point to the same current node as
3661 * the ole one.
3663 newClone->stackSize = This->stackSize ;
3664 newClone->stackMaxSize = This->stackMaxSize ;
3665 newClone->stackToVisit =
3666 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3668 memcpy(
3669 newClone->stackToVisit,
3670 This->stackToVisit,
3671 sizeof(ULONG) * newClone->stackSize);
3673 *ppenum = (IEnumSTATSTG*)newClone;
3676 * Don't forget to nail down a reference to the clone before
3677 * returning it.
3679 IEnumSTATSTGImpl_AddRef(*ppenum);
3681 return S_OK;
3684 INT IEnumSTATSTGImpl_FindParentProperty(
3685 IEnumSTATSTGImpl *This,
3686 ULONG childProperty,
3687 StgProperty *currentProperty,
3688 ULONG *thisNodeId)
3690 ULONG currentSearchNode;
3691 ULONG foundNode;
3694 * To avoid the special case, get another pointer to a ULONG value if
3695 * the caller didn't supply one.
3697 if (thisNodeId==0)
3698 thisNodeId = &foundNode;
3701 * Start with the node at the top of the stack.
3703 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3706 while (currentSearchNode!=PROPERTY_NULL)
3709 * Store the current node in the returned parameters
3711 *thisNodeId = currentSearchNode;
3714 * Remove the top node from the stack
3716 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3719 * Read the property from the storage.
3721 StorageImpl_ReadProperty(
3722 This->parentStorage,
3723 currentSearchNode,
3724 currentProperty);
3726 if (currentProperty->previousProperty == childProperty)
3727 return PROPERTY_RELATION_PREVIOUS;
3729 else if (currentProperty->nextProperty == childProperty)
3730 return PROPERTY_RELATION_NEXT;
3732 else if (currentProperty->dirProperty == childProperty)
3733 return PROPERTY_RELATION_DIR;
3736 * Push the next search node in the search stack.
3738 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3741 * continue the iteration.
3743 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3746 return PROPERTY_NULL;
3749 ULONG IEnumSTATSTGImpl_FindProperty(
3750 IEnumSTATSTGImpl* This,
3751 const OLECHAR* lpszPropName,
3752 StgProperty* currentProperty)
3754 ULONG currentSearchNode;
3757 * Start with the node at the top of the stack.
3759 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3761 while (currentSearchNode!=PROPERTY_NULL)
3764 * Remove the top node from the stack
3766 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3769 * Read the property from the storage.
3771 StorageImpl_ReadProperty(This->parentStorage,
3772 currentSearchNode,
3773 currentProperty);
3775 if ( propertyNameCmp(
3776 (OLECHAR*)currentProperty->name,
3777 (OLECHAR*)lpszPropName) == 0)
3778 return currentSearchNode;
3781 * Push the next search node in the search stack.
3783 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3786 * continue the iteration.
3788 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3791 return PROPERTY_NULL;
3794 void IEnumSTATSTGImpl_PushSearchNode(
3795 IEnumSTATSTGImpl* This,
3796 ULONG nodeToPush)
3798 StgProperty rootProperty;
3799 BOOL readSucessful;
3802 * First, make sure we're not trying to push an unexisting node.
3804 if (nodeToPush==PROPERTY_NULL)
3805 return;
3808 * First push the node to the stack
3810 if (This->stackSize == This->stackMaxSize)
3812 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3814 This->stackToVisit = HeapReAlloc(
3815 GetProcessHeap(),
3817 This->stackToVisit,
3818 sizeof(ULONG) * This->stackMaxSize);
3821 This->stackToVisit[This->stackSize] = nodeToPush;
3822 This->stackSize++;
3825 * Read the root property from the storage.
3827 readSucessful = StorageImpl_ReadProperty(
3828 This->parentStorage,
3829 nodeToPush,
3830 &rootProperty);
3832 if (readSucessful)
3834 assert(rootProperty.sizeOfNameString!=0);
3837 * Push the previous search node in the search stack.
3839 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3843 ULONG IEnumSTATSTGImpl_PopSearchNode(
3844 IEnumSTATSTGImpl* This,
3845 BOOL remove)
3847 ULONG topNode;
3849 if (This->stackSize == 0)
3850 return PROPERTY_NULL;
3852 topNode = This->stackToVisit[This->stackSize-1];
3854 if (remove)
3855 This->stackSize--;
3857 return topNode;
3860 /******************************************************************************
3861 ** StorageUtl implementation
3864 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
3866 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
3869 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
3871 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
3874 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
3876 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
3879 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
3881 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
3884 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
3886 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
3887 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
3888 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
3890 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
3893 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
3895 StorageUtl_WriteDWord(buffer, offset, value->Data1);
3896 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
3897 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
3899 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
3902 void StorageUtl_CopyPropertyToSTATSTG(
3903 STATSTG* destination,
3904 StgProperty* source,
3905 int statFlags)
3908 * The copy of the string occurs only when the flag is not set
3910 if ((statFlags & STATFLAG_NONAME) != 0)
3912 destination->pwcsName = 0;
3914 else
3916 destination->pwcsName =
3917 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
3919 lstrcpyW((LPWSTR)destination->pwcsName, source->name);
3922 switch (source->propertyType)
3924 case PROPTYPE_STORAGE:
3925 case PROPTYPE_ROOT:
3926 destination->type = STGTY_STORAGE;
3927 break;
3928 case PROPTYPE_STREAM:
3929 destination->type = STGTY_STREAM;
3930 break;
3931 default:
3932 destination->type = STGTY_STREAM;
3933 break;
3936 destination->cbSize = source->size;
3938 currentReturnStruct->mtime = {0}; TODO
3939 currentReturnStruct->ctime = {0};
3940 currentReturnStruct->atime = {0};
3942 destination->grfMode = 0;
3943 destination->grfLocksSupported = 0;
3944 destination->clsid = source->propertyUniqueID;
3945 destination->grfStateBits = 0;
3946 destination->reserved = 0;
3949 /******************************************************************************
3950 ** BlockChainStream implementation
3953 BlockChainStream* BlockChainStream_Construct(
3954 StorageImpl* parentStorage,
3955 ULONG* headOfStreamPlaceHolder,
3956 ULONG propertyIndex)
3958 BlockChainStream* newStream;
3959 ULONG blockIndex;
3961 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
3963 newStream->parentStorage = parentStorage;
3964 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
3965 newStream->ownerPropertyIndex = propertyIndex;
3966 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
3967 newStream->tailIndex = BLOCK_END_OF_CHAIN;
3968 newStream->numBlocks = 0;
3970 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
3972 while (blockIndex != BLOCK_END_OF_CHAIN)
3974 newStream->numBlocks++;
3975 newStream->tailIndex = blockIndex;
3977 blockIndex = StorageImpl_GetNextBlockInChain(
3978 parentStorage,
3979 blockIndex);
3982 return newStream;
3985 void BlockChainStream_Destroy(BlockChainStream* This)
3987 HeapFree(GetProcessHeap(), 0, This);
3990 /******************************************************************************
3991 * BlockChainStream_GetHeadOfChain
3993 * Returns the head of this stream chain.
3994 * Some special chains don't have properties, their heads are kept in
3995 * This->headOfStreamPlaceHolder.
3998 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4000 StgProperty chainProperty;
4001 BOOL readSucessful;
4003 if (This->headOfStreamPlaceHolder != 0)
4004 return *(This->headOfStreamPlaceHolder);
4006 if (This->ownerPropertyIndex != PROPERTY_NULL)
4008 readSucessful = StorageImpl_ReadProperty(
4009 This->parentStorage,
4010 This->ownerPropertyIndex,
4011 &chainProperty);
4013 if (readSucessful)
4015 return chainProperty.startingBlock;
4019 return BLOCK_END_OF_CHAIN;
4022 /******************************************************************************
4023 * BlockChainStream_GetCount
4025 * Returns the number of blocks that comprises this chain.
4026 * This is not the size of the stream as the last block may not be full!
4029 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4031 ULONG blockIndex;
4032 ULONG count = 0;
4034 blockIndex = BlockChainStream_GetHeadOfChain(This);
4036 while (blockIndex != BLOCK_END_OF_CHAIN)
4038 count++;
4040 blockIndex = StorageImpl_GetNextBlockInChain(
4041 This->parentStorage,
4042 blockIndex);
4045 return count;
4048 /******************************************************************************
4049 * BlockChainStream_ReadAt
4051 * Reads a specified number of bytes from this chain at the specified offset.
4052 * bytesRead may be NULL.
4053 * Failure will be returned if the specified number of bytes has not been read.
4055 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4056 ULARGE_INTEGER offset,
4057 ULONG size,
4058 void* buffer,
4059 ULONG* bytesRead)
4061 ULONG blockNoInSequence = offset.LowPart / This->parentStorage->bigBlockSize;
4062 ULONG offsetInBlock = offset.LowPart % This->parentStorage->bigBlockSize;
4063 ULONG bytesToReadInBuffer;
4064 ULONG blockIndex;
4065 BYTE* bufferWalker;
4066 BYTE* bigBlockBuffer;
4069 * Find the first block in the stream that contains part of the buffer.
4071 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4072 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4073 (blockNoInSequence < This->lastBlockNoInSequence) )
4075 blockIndex = BlockChainStream_GetHeadOfChain(This);
4076 This->lastBlockNoInSequence = blockNoInSequence;
4078 else
4080 ULONG temp = blockNoInSequence;
4082 blockIndex = This->lastBlockNoInSequenceIndex;
4083 blockNoInSequence -= This->lastBlockNoInSequence;
4084 This->lastBlockNoInSequence = temp;
4087 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4089 blockIndex =
4090 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4092 blockNoInSequence--;
4095 This->lastBlockNoInSequenceIndex = blockIndex;
4098 * Start reading the buffer.
4100 *bytesRead = 0;
4101 bufferWalker = buffer;
4103 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4106 * Calculate how many bytes we can copy from this big block.
4108 bytesToReadInBuffer =
4109 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4112 * Copy those bytes to the buffer
4114 bigBlockBuffer =
4115 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4117 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4119 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4122 * Step to the next big block.
4124 blockIndex =
4125 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4127 bufferWalker += bytesToReadInBuffer;
4128 size -= bytesToReadInBuffer;
4129 *bytesRead += bytesToReadInBuffer;
4130 offsetInBlock = 0; /* There is no offset on the next block */
4134 return (size == 0);
4137 /******************************************************************************
4138 * BlockChainStream_WriteAt
4140 * Writes the specified number of bytes to this chain at the specified offset.
4141 * bytesWritten may be NULL.
4142 * Will fail if not all specified number of bytes have been written.
4144 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4145 ULARGE_INTEGER offset,
4146 ULONG size,
4147 const void* buffer,
4148 ULONG* bytesWritten)
4150 ULONG blockNoInSequence = offset.LowPart / This->parentStorage->bigBlockSize;
4151 ULONG offsetInBlock = offset.LowPart % This->parentStorage->bigBlockSize;
4152 ULONG bytesToWrite;
4153 ULONG blockIndex;
4154 BYTE* bufferWalker;
4155 BYTE* bigBlockBuffer;
4158 * Find the first block in the stream that contains part of the buffer.
4160 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4161 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4162 (blockNoInSequence < This->lastBlockNoInSequence) )
4164 blockIndex = BlockChainStream_GetHeadOfChain(This);
4165 This->lastBlockNoInSequence = blockNoInSequence;
4167 else
4169 ULONG temp = blockNoInSequence;
4171 blockIndex = This->lastBlockNoInSequenceIndex;
4172 blockNoInSequence -= This->lastBlockNoInSequence;
4173 This->lastBlockNoInSequence = temp;
4176 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4178 blockIndex =
4179 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4181 blockNoInSequence--;
4184 This->lastBlockNoInSequenceIndex = blockIndex;
4187 * Here, I'm casting away the constness on the buffer variable
4188 * This is OK since we don't intend to modify that buffer.
4190 *bytesWritten = 0;
4191 bufferWalker = (BYTE*)buffer;
4193 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4196 * Calculate how many bytes we can copy from this big block.
4198 bytesToWrite =
4199 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4202 * Copy those bytes to the buffer
4204 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4206 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4208 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4211 * Step to the next big block.
4213 blockIndex =
4214 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4216 bufferWalker += bytesToWrite;
4217 size -= bytesToWrite;
4218 *bytesWritten += bytesToWrite;
4219 offsetInBlock = 0; /* There is no offset on the next block */
4222 return (size == 0);
4225 /******************************************************************************
4226 * BlockChainStream_Shrink
4228 * Shrinks this chain in the big block depot.
4230 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4231 ULARGE_INTEGER newSize)
4233 ULONG blockIndex, extraBlock;
4234 ULONG numBlocks;
4235 ULONG count = 1;
4238 * Reset the last accessed block cache.
4240 This->lastBlockNoInSequence = 0xFFFFFFFF;
4241 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4244 * Figure out how many blocks are needed to contain the new size
4246 numBlocks = newSize.LowPart / This->parentStorage->bigBlockSize;
4248 if ((newSize.LowPart % This->parentStorage->bigBlockSize) != 0)
4249 numBlocks++;
4251 blockIndex = BlockChainStream_GetHeadOfChain(This);
4254 * Go to the new end of chain
4256 while (count < numBlocks)
4258 blockIndex =
4259 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4261 count++;
4264 /* Get the next block before marking the new end */
4265 extraBlock =
4266 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4268 /* Mark the new end of chain */
4269 StorageImpl_SetNextBlockInChain(
4270 This->parentStorage,
4271 blockIndex,
4272 BLOCK_END_OF_CHAIN);
4274 This->tailIndex = blockIndex;
4275 This->numBlocks = numBlocks;
4278 * Mark the extra blocks as free
4280 while (extraBlock != BLOCK_END_OF_CHAIN)
4282 blockIndex =
4283 StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4285 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4286 extraBlock = blockIndex;
4289 return TRUE;
4292 /******************************************************************************
4293 * BlockChainStream_Enlarge
4295 * Grows this chain in the big block depot.
4297 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4298 ULARGE_INTEGER newSize)
4300 ULONG blockIndex, currentBlock;
4301 ULONG newNumBlocks;
4302 ULONG oldNumBlocks = 0;
4304 blockIndex = BlockChainStream_GetHeadOfChain(This);
4307 * Empty chain. Create the head.
4309 if (blockIndex == BLOCK_END_OF_CHAIN)
4311 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4312 StorageImpl_SetNextBlockInChain(This->parentStorage,
4313 blockIndex,
4314 BLOCK_END_OF_CHAIN);
4316 if (This->headOfStreamPlaceHolder != 0)
4318 *(This->headOfStreamPlaceHolder) = blockIndex;
4320 else
4322 StgProperty chainProp;
4323 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4325 StorageImpl_ReadProperty(
4326 This->parentStorage,
4327 This->ownerPropertyIndex,
4328 &chainProp);
4330 chainProp.startingBlock = blockIndex;
4332 StorageImpl_WriteProperty(
4333 This->parentStorage,
4334 This->ownerPropertyIndex,
4335 &chainProp);
4338 This->tailIndex = blockIndex;
4339 This->numBlocks = 1;
4343 * Figure out how many blocks are needed to contain this stream
4345 newNumBlocks = newSize.LowPart / This->parentStorage->bigBlockSize;
4347 if ((newSize.LowPart % This->parentStorage->bigBlockSize) != 0)
4348 newNumBlocks++;
4351 * Go to the current end of chain
4353 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4355 currentBlock = blockIndex;
4357 while (blockIndex != BLOCK_END_OF_CHAIN)
4359 This->numBlocks++;
4360 currentBlock = blockIndex;
4362 blockIndex =
4363 StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4366 This->tailIndex = currentBlock;
4369 currentBlock = This->tailIndex;
4370 oldNumBlocks = This->numBlocks;
4373 * Add new blocks to the chain
4375 if (oldNumBlocks < newNumBlocks)
4377 while (oldNumBlocks < newNumBlocks)
4379 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4381 StorageImpl_SetNextBlockInChain(
4382 This->parentStorage,
4383 currentBlock,
4384 blockIndex);
4386 StorageImpl_SetNextBlockInChain(
4387 This->parentStorage,
4388 blockIndex,
4389 BLOCK_END_OF_CHAIN);
4391 currentBlock = blockIndex;
4392 oldNumBlocks++;
4395 This->tailIndex = blockIndex;
4396 This->numBlocks = newNumBlocks;
4399 return TRUE;
4402 /******************************************************************************
4403 * BlockChainStream_SetSize
4405 * Sets the size of this stream. The big block depot will be updated.
4406 * The file will grow if we grow the chain.
4408 * TODO: Free the actual blocks in the file when we shrink the chain.
4409 * Currently, the blocks are still in the file. So the file size
4410 * doesn't shrink even if we shrink streams.
4412 BOOL BlockChainStream_SetSize(
4413 BlockChainStream* This,
4414 ULARGE_INTEGER newSize)
4416 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4418 if (newSize.LowPart == size.LowPart)
4419 return TRUE;
4421 if (newSize.LowPart < size.LowPart)
4423 BlockChainStream_Shrink(This, newSize);
4425 else
4427 ULARGE_INTEGER fileSize =
4428 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4430 ULONG diff = newSize.LowPart - size.LowPart;
4433 * Make sure the file stays a multiple of blocksize
4435 if ((diff % This->parentStorage->bigBlockSize) != 0)
4436 diff += (This->parentStorage->bigBlockSize -
4437 (diff % This->parentStorage->bigBlockSize) );
4439 fileSize.LowPart += diff;
4440 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4442 BlockChainStream_Enlarge(This, newSize);
4445 return TRUE;
4448 /******************************************************************************
4449 * BlockChainStream_GetSize
4451 * Returns the size of this chain.
4452 * Will return the block count if this chain doesn't have a property.
4454 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4456 StgProperty chainProperty;
4458 if(This->headOfStreamPlaceHolder == NULL)
4461 * This chain is a data stream read the property and return
4462 * the appropriate size
4464 StorageImpl_ReadProperty(
4465 This->parentStorage,
4466 This->ownerPropertyIndex,
4467 &chainProperty);
4469 return chainProperty.size;
4471 else
4474 * this chain is a chain that does not have a property, figure out the
4475 * size by making the product number of used blocks times the
4476 * size of them
4478 ULARGE_INTEGER result;
4479 result.HighPart = 0;
4481 result.LowPart =
4482 BlockChainStream_GetCount(This) *
4483 This->parentStorage->bigBlockSize;
4485 return result;
4489 /******************************************************************************
4490 ** SmallBlockChainStream implementation
4493 SmallBlockChainStream* SmallBlockChainStream_Construct(
4494 StorageImpl* parentStorage,
4495 ULONG propertyIndex)
4497 SmallBlockChainStream* newStream;
4499 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4501 newStream->parentStorage = parentStorage;
4502 newStream->ownerPropertyIndex = propertyIndex;
4504 return newStream;
4507 void SmallBlockChainStream_Destroy(
4508 SmallBlockChainStream* This)
4510 HeapFree(GetProcessHeap(), 0, This);
4513 /******************************************************************************
4514 * SmallBlockChainStream_GetHeadOfChain
4516 * Returns the head of this chain of small blocks.
4518 ULONG SmallBlockChainStream_GetHeadOfChain(
4519 SmallBlockChainStream* This)
4521 StgProperty chainProperty;
4522 BOOL readSucessful;
4524 if (This->ownerPropertyIndex)
4526 readSucessful = StorageImpl_ReadProperty(
4527 This->parentStorage,
4528 This->ownerPropertyIndex,
4529 &chainProperty);
4531 if (readSucessful)
4533 return chainProperty.startingBlock;
4538 return BLOCK_END_OF_CHAIN;
4541 /******************************************************************************
4542 * SmallBlockChainStream_GetNextBlockInChain
4544 * Returns the index of the next small block in this chain.
4546 * Return Values:
4547 * - BLOCK_END_OF_CHAIN: end of this chain
4548 * - BLOCK_UNUSED: small block 'blockIndex' is free
4550 ULONG SmallBlockChainStream_GetNextBlockInChain(
4551 SmallBlockChainStream* This,
4552 ULONG blockIndex)
4554 ULARGE_INTEGER offsetOfBlockInDepot;
4555 DWORD buffer;
4556 ULONG nextBlockInChain = BLOCK_END_OF_CHAIN;
4557 ULONG bytesRead;
4558 BOOL success;
4560 offsetOfBlockInDepot.HighPart = 0;
4561 offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
4564 * Read those bytes in the buffer from the small block file.
4566 success = BlockChainStream_ReadAt(
4567 This->parentStorage->smallBlockDepotChain,
4568 offsetOfBlockInDepot,
4569 sizeof(DWORD),
4570 &buffer,
4571 &bytesRead);
4573 if (success)
4575 StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4578 return nextBlockInChain;
4581 /******************************************************************************
4582 * SmallBlockChainStream_SetNextBlockInChain
4584 * Writes the index of the next block of the specified block in the small
4585 * block depot.
4586 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4587 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4589 void SmallBlockChainStream_SetNextBlockInChain(
4590 SmallBlockChainStream* This,
4591 ULONG blockIndex,
4592 ULONG nextBlock)
4594 ULARGE_INTEGER offsetOfBlockInDepot;
4595 DWORD buffer;
4596 ULONG bytesWritten;
4598 offsetOfBlockInDepot.HighPart = 0;
4599 offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
4601 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4604 * Read those bytes in the buffer from the small block file.
4606 BlockChainStream_WriteAt(
4607 This->parentStorage->smallBlockDepotChain,
4608 offsetOfBlockInDepot,
4609 sizeof(DWORD),
4610 &buffer,
4611 &bytesWritten);
4614 /******************************************************************************
4615 * SmallBlockChainStream_FreeBlock
4617 * Flag small block 'blockIndex' as free in the small block depot.
4619 void SmallBlockChainStream_FreeBlock(
4620 SmallBlockChainStream* This,
4621 ULONG blockIndex)
4623 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4626 /******************************************************************************
4627 * SmallBlockChainStream_GetNextFreeBlock
4629 * Returns the index of a free small block. The small block depot will be
4630 * enlarged if necessary. The small block chain will also be enlarged if
4631 * necessary.
4633 ULONG SmallBlockChainStream_GetNextFreeBlock(
4634 SmallBlockChainStream* This)
4636 ULARGE_INTEGER offsetOfBlockInDepot;
4637 DWORD buffer;
4638 ULONG bytesRead;
4639 ULONG blockIndex = 0;
4640 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4641 BOOL success = TRUE;
4642 ULONG smallBlocksPerBigBlock;
4644 offsetOfBlockInDepot.HighPart = 0;
4647 * Scan the small block depot for a free block
4649 while (nextBlockIndex != BLOCK_UNUSED)
4651 offsetOfBlockInDepot.LowPart = blockIndex * sizeof(ULONG);
4653 success = BlockChainStream_ReadAt(
4654 This->parentStorage->smallBlockDepotChain,
4655 offsetOfBlockInDepot,
4656 sizeof(DWORD),
4657 &buffer,
4658 &bytesRead);
4661 * If we run out of space for the small block depot, enlarge it
4663 if (success)
4665 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4667 if (nextBlockIndex != BLOCK_UNUSED)
4668 blockIndex++;
4670 else
4672 ULONG count =
4673 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4675 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4676 ULONG nextBlock, newsbdIndex;
4677 BYTE* smallBlockDepot;
4679 nextBlock = sbdIndex;
4680 while (nextBlock != BLOCK_END_OF_CHAIN)
4682 sbdIndex = nextBlock;
4683 nextBlock =
4684 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4687 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4688 if (sbdIndex != BLOCK_END_OF_CHAIN)
4689 StorageImpl_SetNextBlockInChain(
4690 This->parentStorage,
4691 sbdIndex,
4692 newsbdIndex);
4694 StorageImpl_SetNextBlockInChain(
4695 This->parentStorage,
4696 newsbdIndex,
4697 BLOCK_END_OF_CHAIN);
4700 * Initialize all the small blocks to free
4702 smallBlockDepot =
4703 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4705 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4706 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4708 if (count == 0)
4711 * We have just created the small block depot.
4713 StgProperty rootProp;
4714 ULONG sbStartIndex;
4717 * Save it in the header
4719 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4720 StorageImpl_SaveFileHeader(This->parentStorage);
4723 * And allocate the first big block that will contain small blocks
4725 sbStartIndex =
4726 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4728 StorageImpl_SetNextBlockInChain(
4729 This->parentStorage,
4730 sbStartIndex,
4731 BLOCK_END_OF_CHAIN);
4733 StorageImpl_ReadProperty(
4734 This->parentStorage,
4735 This->parentStorage->rootPropertySetIndex,
4736 &rootProp);
4738 rootProp.startingBlock = sbStartIndex;
4739 rootProp.size.HighPart = 0;
4740 rootProp.size.LowPart = This->parentStorage->bigBlockSize;
4742 StorageImpl_WriteProperty(
4743 This->parentStorage,
4744 This->parentStorage->rootPropertySetIndex,
4745 &rootProp);
4750 smallBlocksPerBigBlock =
4751 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4754 * Verify if we have to allocate big blocks to contain small blocks
4756 if (blockIndex % smallBlocksPerBigBlock == 0)
4758 StgProperty rootProp;
4759 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4761 StorageImpl_ReadProperty(
4762 This->parentStorage,
4763 This->parentStorage->rootPropertySetIndex,
4764 &rootProp);
4766 if (rootProp.size.LowPart <
4767 (blocksRequired * This->parentStorage->bigBlockSize))
4769 rootProp.size.LowPart += This->parentStorage->bigBlockSize;
4771 BlockChainStream_SetSize(
4772 This->parentStorage->smallBlockRootChain,
4773 rootProp.size);
4775 StorageImpl_WriteProperty(
4776 This->parentStorage,
4777 This->parentStorage->rootPropertySetIndex,
4778 &rootProp);
4782 return blockIndex;
4785 /******************************************************************************
4786 * SmallBlockChainStream_ReadAt
4788 * Reads a specified number of bytes from this chain at the specified offset.
4789 * bytesRead may be NULL.
4790 * Failure will be returned if the specified number of bytes has not been read.
4792 BOOL SmallBlockChainStream_ReadAt(
4793 SmallBlockChainStream* This,
4794 ULARGE_INTEGER offset,
4795 ULONG size,
4796 void* buffer,
4797 ULONG* bytesRead)
4799 ULARGE_INTEGER offsetInBigBlockFile;
4800 ULONG blockNoInSequence =
4801 offset.LowPart / This->parentStorage->smallBlockSize;
4803 ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize;
4804 ULONG bytesToReadInBuffer;
4805 ULONG blockIndex;
4806 ULONG bytesReadFromBigBlockFile;
4807 BYTE* bufferWalker;
4810 * This should never happen on a small block file.
4812 assert(offset.HighPart==0);
4815 * Find the first block in the stream that contains part of the buffer.
4817 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4819 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4821 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4823 blockNoInSequence--;
4827 * Start reading the buffer.
4829 *bytesRead = 0;
4830 bufferWalker = buffer;
4832 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4835 * Calculate how many bytes we can copy from this small block.
4837 bytesToReadInBuffer =
4838 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4841 * Calculate the offset of the small block in the small block file.
4843 offsetInBigBlockFile.HighPart = 0;
4844 offsetInBigBlockFile.LowPart =
4845 blockIndex * This->parentStorage->smallBlockSize;
4847 offsetInBigBlockFile.LowPart += offsetInBlock;
4850 * Read those bytes in the buffer from the small block file.
4852 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
4853 offsetInBigBlockFile,
4854 bytesToReadInBuffer,
4855 bufferWalker,
4856 &bytesReadFromBigBlockFile);
4858 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
4861 * Step to the next big block.
4863 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4864 bufferWalker += bytesToReadInBuffer;
4865 size -= bytesToReadInBuffer;
4866 *bytesRead += bytesToReadInBuffer;
4867 offsetInBlock = 0; /* There is no offset on the next block */
4870 return (size == 0);
4873 /******************************************************************************
4874 * SmallBlockChainStream_WriteAt
4876 * Writes the specified number of bytes to this chain at the specified offset.
4877 * bytesWritten may be NULL.
4878 * Will fail if not all specified number of bytes have been written.
4880 BOOL SmallBlockChainStream_WriteAt(
4881 SmallBlockChainStream* This,
4882 ULARGE_INTEGER offset,
4883 ULONG size,
4884 const void* buffer,
4885 ULONG* bytesWritten)
4887 ULARGE_INTEGER offsetInBigBlockFile;
4888 ULONG blockNoInSequence =
4889 offset.LowPart / This->parentStorage->smallBlockSize;
4891 ULONG offsetInBlock = offset.LowPart % This->parentStorage->smallBlockSize;
4892 ULONG bytesToWriteInBuffer;
4893 ULONG blockIndex;
4894 ULONG bytesWrittenFromBigBlockFile;
4895 BYTE* bufferWalker;
4898 * This should never happen on a small block file.
4900 assert(offset.HighPart==0);
4903 * Find the first block in the stream that contains part of the buffer.
4905 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4907 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4909 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4911 blockNoInSequence--;
4915 * Start writing the buffer.
4917 * Here, I'm casting away the constness on the buffer variable
4918 * This is OK since we don't intend to modify that buffer.
4920 *bytesWritten = 0;
4921 bufferWalker = (BYTE*)buffer;
4922 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4925 * Calculate how many bytes we can copy to this small block.
4927 bytesToWriteInBuffer =
4928 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4931 * Calculate the offset of the small block in the small block file.
4933 offsetInBigBlockFile.HighPart = 0;
4934 offsetInBigBlockFile.LowPart =
4935 blockIndex * This->parentStorage->smallBlockSize;
4937 offsetInBigBlockFile.LowPart += offsetInBlock;
4940 * Write those bytes in the buffer to the small block file.
4942 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
4943 offsetInBigBlockFile,
4944 bytesToWriteInBuffer,
4945 bufferWalker,
4946 &bytesWrittenFromBigBlockFile);
4948 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
4951 * Step to the next big block.
4953 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4954 bufferWalker += bytesToWriteInBuffer;
4955 size -= bytesToWriteInBuffer;
4956 *bytesWritten += bytesToWriteInBuffer;
4957 offsetInBlock = 0; /* There is no offset on the next block */
4960 return (size == 0);
4963 /******************************************************************************
4964 * SmallBlockChainStream_Shrink
4966 * Shrinks this chain in the small block depot.
4968 BOOL SmallBlockChainStream_Shrink(
4969 SmallBlockChainStream* This,
4970 ULARGE_INTEGER newSize)
4972 ULONG blockIndex, extraBlock;
4973 ULONG numBlocks;
4974 ULONG count = 0;
4976 numBlocks = newSize.LowPart / This->parentStorage->smallBlockSize;
4978 if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0)
4979 numBlocks++;
4981 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4984 * Go to the new end of chain
4986 while (count < numBlocks)
4988 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4989 count++;
4993 * If the count is 0, we have a special case, the head of the chain was
4994 * just freed.
4996 if (count == 0)
4998 StgProperty chainProp;
5000 StorageImpl_ReadProperty(This->parentStorage,
5001 This->ownerPropertyIndex,
5002 &chainProp);
5004 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5006 StorageImpl_WriteProperty(This->parentStorage,
5007 This->ownerPropertyIndex,
5008 &chainProp);
5011 * We start freeing the chain at the head block.
5013 extraBlock = blockIndex;
5015 else
5017 /* Get the next block before marking the new end */
5018 extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5020 /* Mark the new end of chain */
5021 SmallBlockChainStream_SetNextBlockInChain(
5022 This,
5023 blockIndex,
5024 BLOCK_END_OF_CHAIN);
5028 * Mark the extra blocks as free
5030 while (extraBlock != BLOCK_END_OF_CHAIN)
5032 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
5033 SmallBlockChainStream_FreeBlock(This, extraBlock);
5034 extraBlock = blockIndex;
5037 return TRUE;
5040 /******************************************************************************
5041 * SmallBlockChainStream_Enlarge
5043 * Grows this chain in the small block depot.
5045 BOOL SmallBlockChainStream_Enlarge(
5046 SmallBlockChainStream* This,
5047 ULARGE_INTEGER newSize)
5049 ULONG blockIndex, currentBlock;
5050 ULONG newNumBlocks;
5051 ULONG oldNumBlocks = 0;
5053 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5056 * Empty chain
5058 if (blockIndex == BLOCK_END_OF_CHAIN)
5061 StgProperty chainProp;
5063 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5064 &chainProp);
5066 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5068 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5069 &chainProp);
5071 blockIndex = chainProp.startingBlock;
5072 SmallBlockChainStream_SetNextBlockInChain(
5073 This,
5074 blockIndex,
5075 BLOCK_END_OF_CHAIN);
5078 currentBlock = blockIndex;
5081 * Figure out how many blocks are needed to contain this stream
5083 newNumBlocks = newSize.LowPart / This->parentStorage->smallBlockSize;
5085 if ((newSize.LowPart % This->parentStorage->smallBlockSize) != 0)
5086 newNumBlocks++;
5089 * Go to the current end of chain
5091 while (blockIndex != BLOCK_END_OF_CHAIN)
5093 oldNumBlocks++;
5094 currentBlock = blockIndex;
5095 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5099 * Add new blocks to the chain
5101 while (oldNumBlocks < newNumBlocks)
5103 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5104 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5106 SmallBlockChainStream_SetNextBlockInChain(
5107 This,
5108 blockIndex,
5109 BLOCK_END_OF_CHAIN);
5111 currentBlock = blockIndex;
5112 oldNumBlocks++;
5115 return TRUE;
5118 /******************************************************************************
5119 * SmallBlockChainStream_GetCount
5121 * Returns the number of blocks that comprises this chain.
5122 * This is not the size of this chain as the last block may not be full!
5124 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5126 ULONG blockIndex;
5127 ULONG count = 0;
5129 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5131 while (blockIndex != BLOCK_END_OF_CHAIN)
5133 count++;
5135 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5138 return count;
5141 /******************************************************************************
5142 * SmallBlockChainStream_SetSize
5144 * Sets the size of this stream.
5145 * The file will grow if we grow the chain.
5147 * TODO: Free the actual blocks in the file when we shrink the chain.
5148 * Currently, the blocks are still in the file. So the file size
5149 * doesn't shrink even if we shrink streams.
5151 BOOL SmallBlockChainStream_SetSize(
5152 SmallBlockChainStream* This,
5153 ULARGE_INTEGER newSize)
5155 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5157 if (newSize.LowPart == size.LowPart)
5158 return TRUE;
5160 if (newSize.LowPart < size.LowPart)
5162 SmallBlockChainStream_Shrink(This, newSize);
5164 else
5166 SmallBlockChainStream_Enlarge(This, newSize);
5169 return TRUE;
5172 /******************************************************************************
5173 * SmallBlockChainStream_GetSize
5175 * Returns the size of this chain.
5177 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5179 StgProperty chainProperty;
5181 StorageImpl_ReadProperty(
5182 This->parentStorage,
5183 This->ownerPropertyIndex,
5184 &chainProperty);
5186 return chainProperty.size;
5189 /******************************************************************************
5190 * StgCreateDocfile32 [OLE32.144]
5192 HRESULT WINAPI StgCreateDocfile(
5193 LPCOLESTR pwcsName,
5194 DWORD grfMode,
5195 DWORD reserved,
5196 IStorage **ppstgOpen)
5198 StorageImpl* newStorage = 0;
5199 HANDLE hFile = INVALID_HANDLE_VALUE;
5200 HRESULT hr = S_OK;
5201 DWORD shareMode;
5202 DWORD accessMode;
5203 DWORD creationMode;
5204 DWORD fileAttributes;
5205 WCHAR tempFileName[MAX_PATH];
5207 TRACE("(%s, %lx, %ld, %p)\n",
5208 debugstr_w(pwcsName), grfMode,
5209 reserved, ppstgOpen);
5212 * Validate the parameters
5214 if (ppstgOpen == 0)
5215 return STG_E_INVALIDPOINTER;
5218 * Validate the STGM flags
5220 if ( FAILED( validateSTGM(grfMode) ))
5221 return STG_E_INVALIDFLAG;
5224 * Generate a unique name.
5226 if (pwcsName == 0)
5228 WCHAR tempPath[MAX_PATH];
5229 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5231 memset(tempPath, 0, sizeof(tempPath));
5232 memset(tempFileName, 0, sizeof(tempFileName));
5234 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5235 tempPath[0] = '.';
5237 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5238 pwcsName = tempFileName;
5239 else
5240 return STG_E_INSUFFICIENTMEMORY;
5244 * Interpret the STGM value grfMode
5246 shareMode = GetShareModeFromSTGM(grfMode);
5247 accessMode = GetAccessModeFromSTGM(grfMode);
5248 creationMode = GetCreationModeFromSTGM(grfMode);
5250 if (grfMode & STGM_DELETEONRELEASE)
5251 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5252 else
5253 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5255 if (grfMode & STGM_TRANSACTED)
5256 FIXME("Transacted mode not implemented.\n");
5259 * Initialize the "out" parameter.
5261 *ppstgOpen = 0;
5263 hFile = CreateFileW(pwcsName,
5264 accessMode,
5265 shareMode,
5266 NULL,
5267 creationMode,
5268 fileAttributes,
5271 if (hFile == INVALID_HANDLE_VALUE)
5273 return E_FAIL;
5277 * Allocate and initialize the new IStorage32object.
5279 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5281 if (newStorage == 0)
5282 return STG_E_INSUFFICIENTMEMORY;
5284 hr = StorageImpl_Construct(
5285 newStorage,
5286 hFile,
5287 NULL,
5288 grfMode,
5289 TRUE);
5291 if (FAILED(hr))
5293 HeapFree(GetProcessHeap(), 0, newStorage);
5294 return hr;
5298 * Get an "out" pointer for the caller.
5300 hr = StorageBaseImpl_QueryInterface(
5301 (IStorage*)newStorage,
5302 (REFIID)&IID_IStorage,
5303 (void**)ppstgOpen);
5305 return hr;
5308 /******************************************************************************
5309 * StgOpenStorage32 [OLE32.148]
5311 HRESULT WINAPI StgOpenStorage(
5312 const OLECHAR *pwcsName,
5313 IStorage *pstgPriority,
5314 DWORD grfMode,
5315 SNB snbExclude,
5316 DWORD reserved,
5317 IStorage **ppstgOpen)
5319 StorageImpl* newStorage = 0;
5320 HRESULT hr = S_OK;
5321 HANDLE hFile = 0;
5322 DWORD shareMode;
5323 DWORD accessMode;
5325 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5326 debugstr_w(pwcsName), pstgPriority, grfMode,
5327 snbExclude, reserved, ppstgOpen);
5330 * Perform a sanity check
5332 if (( pwcsName == 0) || (ppstgOpen == 0) )
5333 return STG_E_INVALIDPOINTER;
5336 * Validate the STGM flags
5338 if ( FAILED( validateSTGM(grfMode) ))
5339 return STG_E_INVALIDFLAG;
5342 * Interpret the STGM value grfMode
5344 shareMode = GetShareModeFromSTGM(grfMode);
5345 accessMode = GetAccessModeFromSTGM(grfMode);
5348 * Initialize the "out" parameter.
5350 *ppstgOpen = 0;
5352 hFile = CreateFileW( pwcsName,
5353 accessMode,
5354 shareMode,
5355 NULL,
5356 OPEN_EXISTING,
5357 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5361 if (hFile==INVALID_HANDLE_VALUE)
5363 return E_FAIL;
5367 * Allocate and initialize the new IStorage32object.
5369 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5371 if (newStorage == 0)
5372 return STG_E_INSUFFICIENTMEMORY;
5374 hr = StorageImpl_Construct(
5375 newStorage,
5376 hFile,
5377 NULL,
5378 grfMode,
5379 TRUE);
5381 if (FAILED(hr))
5383 HeapFree(GetProcessHeap(), 0, newStorage);
5384 return hr;
5388 * Get an "out" pointer for the caller.
5390 hr = StorageBaseImpl_QueryInterface(
5391 (IStorage*)newStorage,
5392 (REFIID)&IID_IStorage,
5393 (void**)ppstgOpen);
5395 return hr;
5398 /******************************************************************************
5399 * StgCreateDocfileOnILockBytes [OLE32.145]
5401 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5402 ILockBytes *plkbyt,
5403 DWORD grfMode,
5404 DWORD reserved,
5405 IStorage** ppstgOpen)
5407 StorageImpl* newStorage = 0;
5408 HRESULT hr = S_OK;
5411 * Validate the parameters
5413 if ((ppstgOpen == 0) || (plkbyt == 0))
5414 return STG_E_INVALIDPOINTER;
5417 * Allocate and initialize the new IStorage object.
5419 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5421 if (newStorage == 0)
5422 return STG_E_INSUFFICIENTMEMORY;
5424 hr = StorageImpl_Construct(
5425 newStorage,
5427 plkbyt,
5428 grfMode,
5429 FALSE);
5431 if (FAILED(hr))
5433 HeapFree(GetProcessHeap(), 0, newStorage);
5434 return hr;
5438 * Get an "out" pointer for the caller.
5440 hr = StorageBaseImpl_QueryInterface(
5441 (IStorage*)newStorage,
5442 (REFIID)&IID_IStorage,
5443 (void**)ppstgOpen);
5445 return hr;
5448 /******************************************************************************
5449 * StgOpenStorageOnILockBytes [OLE32.149]
5451 HRESULT WINAPI StgOpenStorageOnILockBytes(
5452 ILockBytes *plkbyt,
5453 IStorage *pstgPriority,
5454 DWORD grfMode,
5455 SNB snbExclude,
5456 DWORD reserved,
5457 IStorage **ppstgOpen)
5459 StorageImpl* newStorage = 0;
5460 HRESULT hr = S_OK;
5463 * Perform a sanity check
5465 if ((plkbyt == 0) || (ppstgOpen == 0))
5466 return STG_E_INVALIDPOINTER;
5469 * Validate the STGM flags
5471 if ( FAILED( validateSTGM(grfMode) ))
5472 return STG_E_INVALIDFLAG;
5475 * Initialize the "out" parameter.
5477 *ppstgOpen = 0;
5480 * Allocate and initialize the new IStorage object.
5482 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5484 if (newStorage == 0)
5485 return STG_E_INSUFFICIENTMEMORY;
5487 hr = StorageImpl_Construct(
5488 newStorage,
5490 plkbyt,
5491 grfMode,
5492 FALSE);
5494 if (FAILED(hr))
5496 HeapFree(GetProcessHeap(), 0, newStorage);
5497 return hr;
5501 * Get an "out" pointer for the caller.
5503 hr = StorageBaseImpl_QueryInterface(
5504 (IStorage*)newStorage,
5505 (REFIID)&IID_IStorage,
5506 (void**)ppstgOpen);
5508 return hr;
5511 /******************************************************************************
5512 * StgIsStorageILockBytes [OLE32.147]
5514 * Determines if the ILockBytes contains a storage object.
5516 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5518 BYTE sig[8];
5519 ULARGE_INTEGER offset;
5521 offset.HighPart = 0;
5522 offset.LowPart = 0;
5524 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5526 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5527 return S_OK;
5529 return S_FALSE;
5532 /******************************************************************************
5533 * WriteClassStg32 [OLE32.158]
5535 * This method will store the specified CLSID in the specified storage object
5537 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5539 HRESULT hRes;
5541 assert(pStg != 0);
5543 hRes = IStorage_SetClass(pStg, rclsid);
5545 return hRes;
5548 /*******************************************************************************************
5549 * ReadClassStg
5551 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5553 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5555 STATSTG pstatstg;
5556 HRESULT hRes;
5558 TRACE("()\n");
5560 if(pclsid==NULL)
5561 return E_POINTER;
5563 * read a STATSTG structure (contains the clsid) from the storage
5565 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5567 if(SUCCEEDED(hRes))
5568 *pclsid=pstatstg.clsid;
5570 return hRes;
5573 /*************************************************************************************
5574 * OleLoadFromStream
5576 * This function loads an object from stream
5578 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5580 CLSID clsid;
5581 HRESULT res;
5583 FIXME("(),stub!\n");
5585 res=ReadClassStm(pStm,&clsid);
5587 if (SUCCEEDED(res)){
5589 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5591 if (SUCCEEDED(res))
5593 res=IPersistStream_Load((IPersistStream*)ppvObj,pStm);
5596 return res;
5599 /************************************************************************************************
5600 * OleSaveToStream
5602 * This function saves an object with the IPersistStream interface on it to the specified stream
5604 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5607 CLSID clsid;
5608 HRESULT res;
5610 TRACE("(%p,%p)\n",pPStm,pStm);
5612 res=IPersistStream_GetClassID(pPStm,&clsid);
5614 if (SUCCEEDED(res)){
5616 res=WriteClassStm(pStm,&clsid);
5618 if (SUCCEEDED(res))
5620 res=IPersistStream_Save(pPStm,pStm,FALSE);
5623 return res;
5626 /****************************************************************************
5627 * This method validate a STGM parameter that can contain the values below
5629 * STGM_DIRECT 0x00000000
5630 * STGM_TRANSACTED 0x00010000
5631 * STGM_SIMPLE 0x08000000
5633 * STGM_READ 0x00000000
5634 * STGM_WRITE 0x00000001
5635 * STGM_READWRITE 0x00000002
5637 * STGM_SHARE_DENY_NONE 0x00000040
5638 * STGM_SHARE_DENY_READ 0x00000030
5639 * STGM_SHARE_DENY_WRITE 0x00000020
5640 * STGM_SHARE_EXCLUSIVE 0x00000010
5642 * STGM_PRIORITY 0x00040000
5643 * STGM_DELETEONRELEASE 0x04000000
5645 * STGM_CREATE 0x00001000
5646 * STGM_CONVERT 0x00020000
5647 * STGM_FAILIFTHERE 0x00000000
5649 * STGM_NOSCRATCH 0x00100000
5650 * STGM_NOSNAPSHOT 0x00200000
5652 static HRESULT validateSTGM(DWORD stgm)
5654 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5655 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5656 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5658 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5659 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5660 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5662 BOOL bSTGM_SHARE_DENY_NONE =
5663 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5665 BOOL bSTGM_SHARE_DENY_READ =
5666 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5668 BOOL bSTGM_SHARE_DENY_WRITE =
5669 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5671 BOOL bSTGM_SHARE_EXCLUSIVE =
5672 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5674 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5675 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5677 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5678 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5681 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5683 if ( ! bSTGM_DIRECT )
5684 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5685 return E_FAIL;
5688 * STGM_WRITE | STGM_READWRITE | STGM_READ
5690 if ( ! bSTGM_READ )
5691 if( bSTGM_WRITE && bSTGM_READWRITE )
5692 return E_FAIL;
5695 * STGM_SHARE_DENY_NONE | others
5696 * (I assume here that DENY_READ implies DENY_WRITE)
5698 if ( bSTGM_SHARE_DENY_NONE )
5699 if ( bSTGM_SHARE_DENY_READ ||
5700 bSTGM_SHARE_DENY_WRITE ||
5701 bSTGM_SHARE_EXCLUSIVE)
5702 return E_FAIL;
5705 * STGM_CREATE | STGM_CONVERT
5706 * if both are false, STGM_FAILIFTHERE is set to TRUE
5708 if ( bSTGM_CREATE && bSTGM_CONVERT )
5709 return E_FAIL;
5712 * STGM_NOSCRATCH requires STGM_TRANSACTED
5714 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5715 return E_FAIL;
5718 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5719 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5721 if (bSTGM_NOSNAPSHOT)
5723 if ( ! ( bSTGM_TRANSACTED &&
5724 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5725 return E_FAIL;
5728 return S_OK;
5731 /****************************************************************************
5732 * GetShareModeFromSTGM
5734 * This method will return a share mode flag from a STGM value.
5735 * The STGM value is assumed valid.
5737 static DWORD GetShareModeFromSTGM(DWORD stgm)
5739 DWORD dwShareMode = 0;
5740 BOOL bSTGM_SHARE_DENY_NONE =
5741 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5743 BOOL bSTGM_SHARE_DENY_READ =
5744 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5746 BOOL bSTGM_SHARE_DENY_WRITE =
5747 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5749 BOOL bSTGM_SHARE_EXCLUSIVE =
5750 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5752 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5753 dwShareMode = 0;
5755 if (bSTGM_SHARE_DENY_NONE)
5756 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5758 if (bSTGM_SHARE_DENY_WRITE)
5759 dwShareMode = FILE_SHARE_READ;
5761 return dwShareMode;
5764 /****************************************************************************
5765 * GetAccessModeFromSTGM
5767 * This method will return an access mode flag from a STGM value.
5768 * The STGM value is assumed valid.
5770 static DWORD GetAccessModeFromSTGM(DWORD stgm)
5772 DWORD dwDesiredAccess = GENERIC_READ;
5773 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5774 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5775 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5777 if (bSTGM_READ)
5778 dwDesiredAccess = GENERIC_READ;
5780 if (bSTGM_WRITE)
5781 dwDesiredAccess |= GENERIC_WRITE;
5783 if (bSTGM_READWRITE)
5784 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5786 return dwDesiredAccess;
5789 /****************************************************************************
5790 * GetCreationModeFromSTGM
5792 * This method will return a creation mode flag from a STGM value.
5793 * The STGM value is assumed valid.
5795 static DWORD GetCreationModeFromSTGM(DWORD stgm)
5797 if ( stgm & STGM_CREATE)
5798 return CREATE_ALWAYS;
5799 if (stgm & STGM_CONVERT) {
5800 FIXME("STGM_CONVERT not implemented!\n");
5801 return CREATE_NEW;
5803 /* All other cases */
5804 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5805 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
5806 return CREATE_NEW;