Added support for WODM_BREAKLOOP message.
[wine.git] / dlls / ole32 / storage32.c
blobf8a42dee926a1cd469f95b65243f8d9cee23303b
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" /* for lstrlenW() and the likes */
19 #include "debugtools.h"
21 #include "storage32.h"
22 #include "ole2.h" /* For Write/ReadClassStm */
24 DEFAULT_DEBUG_CHANNEL(storage)
26 #define FILE_BEGIN 0
28 static const char rootPropertyName[] = "Root Entry";
30 /***********************************************************************
31 * Forward declaration of internal functions used by the method DestroyElement
33 static HRESULT deleteStorageProperty(
34 StorageImpl *parentStorage,
35 OLECHAR *propertyToDeleteName);
37 static HRESULT deleteStreamProperty(
38 StorageImpl *parentStorage,
39 ULONG foundPropertyIndexToDelete,
40 StgProperty propertyToDelete);
42 static HRESULT findPlaceholder(
43 StorageImpl *storage,
44 ULONG propertyIndexToStore,
45 ULONG storagePropertyIndex,
46 INT typeOfRelation);
48 static HRESULT adjustPropertyChain(
49 StorageImpl *This,
50 StgProperty propertyToDelete,
51 StgProperty parentProperty,
52 ULONG parentPropertyId,
53 INT typeOfRelation);
55 /***********************************************************************
56 * Declaration of the functions used to manipulate StgProperty
59 static ULONG getFreeProperty(
60 StorageImpl *storage);
62 static void updatePropertyChain(
63 StorageImpl *storage,
64 ULONG newPropertyIndex,
65 StgProperty newProperty);
67 static LONG propertyNameCmp(
68 OLECHAR *newProperty,
69 OLECHAR *currentProperty);
72 /***********************************************************************
73 * Declaration of miscellaneous functions...
75 static HRESULT validateSTGM(DWORD stgmValue);
77 static DWORD GetShareModeFromSTGM(DWORD stgm);
78 static DWORD GetAccessModeFromSTGM(DWORD stgm);
79 static DWORD GetCreationModeFromSTGM(DWORD stgm);
82 * Virtual function table for the IStorage32Impl class.
84 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
86 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
87 StorageBaseImpl_QueryInterface,
88 StorageBaseImpl_AddRef,
89 StorageBaseImpl_Release,
90 StorageBaseImpl_CreateStream,
91 StorageBaseImpl_OpenStream,
92 StorageImpl_CreateStorage,
93 StorageBaseImpl_OpenStorage,
94 StorageImpl_CopyTo,
95 StorageImpl_MoveElementTo,
96 StorageImpl_Commit,
97 StorageImpl_Revert,
98 StorageBaseImpl_EnumElements,
99 StorageImpl_DestroyElement,
100 StorageBaseImpl_RenameElement,
101 StorageImpl_SetElementTimes,
102 StorageBaseImpl_SetClass,
103 StorageImpl_SetStateBits,
104 StorageBaseImpl_Stat
108 * Virtual function table for the Storage32InternalImpl class.
110 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
112 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
113 StorageBaseImpl_QueryInterface,
114 StorageBaseImpl_AddRef,
115 StorageBaseImpl_Release,
116 StorageBaseImpl_CreateStream,
117 StorageBaseImpl_OpenStream,
118 StorageImpl_CreateStorage,
119 StorageBaseImpl_OpenStorage,
120 StorageImpl_CopyTo,
121 StorageImpl_MoveElementTo,
122 StorageInternalImpl_Commit,
123 StorageInternalImpl_Revert,
124 StorageBaseImpl_EnumElements,
125 StorageImpl_DestroyElement,
126 StorageBaseImpl_RenameElement,
127 StorageImpl_SetElementTimes,
128 StorageBaseImpl_SetClass,
129 StorageImpl_SetStateBits,
130 StorageBaseImpl_Stat
134 * Virtual function table for the IEnumSTATSTGImpl class.
136 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
138 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
139 IEnumSTATSTGImpl_QueryInterface,
140 IEnumSTATSTGImpl_AddRef,
141 IEnumSTATSTGImpl_Release,
142 IEnumSTATSTGImpl_Next,
143 IEnumSTATSTGImpl_Skip,
144 IEnumSTATSTGImpl_Reset,
145 IEnumSTATSTGImpl_Clone
152 /************************************************************************
153 ** Storage32BaseImpl implementatiion
156 /************************************************************************
157 * Storage32BaseImpl_QueryInterface (IUnknown)
159 * This method implements the common QueryInterface for all IStorage32
160 * implementations contained in this file.
162 * See Windows documentation for more details on IUnknown methods.
164 HRESULT WINAPI StorageBaseImpl_QueryInterface(
165 IStorage* iface,
166 REFIID riid,
167 void** ppvObject)
169 ICOM_THIS(StorageBaseImpl,iface);
171 * Perform a sanity check on the parameters.
173 if ( (This==0) || (ppvObject==0) )
174 return E_INVALIDARG;
177 * Initialize the return parameter.
179 *ppvObject = 0;
182 * Compare the riid with the interface IDs implemented by this object.
184 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
186 *ppvObject = (IStorage*)This;
188 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
190 *ppvObject = (IStorage*)This;
194 * Check that we obtained an interface.
196 if ((*ppvObject)==0)
197 return E_NOINTERFACE;
200 * Query Interface always increases the reference count by one when it is
201 * successful
203 StorageBaseImpl_AddRef(iface);
205 return S_OK;
208 /************************************************************************
209 * Storage32BaseImpl_AddRef (IUnknown)
211 * This method implements the common AddRef for all IStorage32
212 * implementations contained in this file.
214 * See Windows documentation for more details on IUnknown methods.
216 ULONG WINAPI StorageBaseImpl_AddRef(
217 IStorage* iface)
219 ICOM_THIS(StorageBaseImpl,iface);
220 This->ref++;
222 return This->ref;
225 /************************************************************************
226 * Storage32BaseImpl_Release (IUnknown)
228 * This method implements the common Release for all IStorage32
229 * implementations contained in this file.
231 * See Windows documentation for more details on IUnknown methods.
233 ULONG WINAPI StorageBaseImpl_Release(
234 IStorage* iface)
236 ICOM_THIS(StorageBaseImpl,iface);
238 * Decrease the reference count on this object.
240 This->ref--;
243 * If the reference count goes down to 0, perform suicide.
245 if (This->ref==0)
248 * Since we are using a system of base-classes, we want to call the
249 * destructor of the appropriate derived class. To do this, we are
250 * using virtual functions to implement the destructor.
252 This->v_destructor(This);
254 return 0;
257 return This->ref;
260 /************************************************************************
261 * Storage32BaseImpl_OpenStream (IStorage)
263 * This method will open the specified stream object from the current storage.
265 * See Windows documentation for more details on IStorage methods.
267 HRESULT WINAPI StorageBaseImpl_OpenStream(
268 IStorage* iface,
269 const OLECHAR* pwcsName, /* [string][in] */
270 void* reserved1, /* [unique][in] */
271 DWORD grfMode, /* [in] */
272 DWORD reserved2, /* [in] */
273 IStream** ppstm) /* [out] */
275 ICOM_THIS(StorageBaseImpl,iface);
276 IEnumSTATSTGImpl* propertyEnumeration;
277 StgStreamImpl* newStream;
278 StgProperty currentProperty;
279 ULONG foundPropertyIndex;
281 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
282 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
285 * Perform a sanity check on the parameters.
287 if ( (pwcsName==NULL) || (ppstm==0) )
288 return E_INVALIDARG;
291 * Initialize the out parameter
293 *ppstm = 0;
296 * Validate the STGM flags
298 if ( FAILED( validateSTGM(grfMode) ))
299 return STG_E_INVALIDFLAG;
302 * As documented.
304 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
305 (grfMode & STGM_DELETEONRELEASE) ||
306 (grfMode & STGM_TRANSACTED) )
307 return STG_E_INVALIDFUNCTION;
310 * Create a property enumeration to search the properties
312 propertyEnumeration = IEnumSTATSTGImpl_Construct(
313 This->ancestorStorage,
314 This->rootPropertySetIndex);
317 * Search the enumeration for the property with the given name
319 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
320 propertyEnumeration,
321 pwcsName,
322 &currentProperty);
325 * Delete the property enumeration since we don't need it anymore
327 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
330 * If it was found, construct the stream object and return a pointer to it.
332 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
333 (currentProperty.propertyType==PROPTYPE_STREAM) )
335 newStream = StgStreamImpl_Construct(This, foundPropertyIndex);
337 if (newStream!=0)
339 *ppstm = (IStream*)newStream;
342 * Since we are returning a pointer to the interface, we have to
343 * nail down the reference.
345 StgStreamImpl_AddRef(*ppstm);
347 return S_OK;
350 return E_OUTOFMEMORY;
353 return STG_E_FILENOTFOUND;
356 /************************************************************************
357 * Storage32BaseImpl_OpenStorage (IStorage)
359 * This method will open a new storage object from the current storage.
361 * See Windows documentation for more details on IStorage methods.
363 HRESULT WINAPI StorageBaseImpl_OpenStorage(
364 IStorage* iface,
365 const OLECHAR* pwcsName, /* [string][unique][in] */
366 IStorage* pstgPriority, /* [unique][in] */
367 DWORD grfMode, /* [in] */
368 SNB snbExclude, /* [unique][in] */
369 DWORD reserved, /* [in] */
370 IStorage** ppstg) /* [out] */
372 ICOM_THIS(StorageBaseImpl,iface);
373 StorageInternalImpl* newStorage;
374 IEnumSTATSTGImpl* propertyEnumeration;
375 StgProperty currentProperty;
376 ULONG foundPropertyIndex;
378 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
379 iface, debugstr_w(pwcsName), pstgPriority,
380 grfMode, snbExclude, reserved, ppstg);
383 * Perform a sanity check on the parameters.
385 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
386 return E_INVALIDARG;
389 * Validate the STGM flags
391 if ( FAILED( validateSTGM(grfMode) ))
392 return STG_E_INVALIDFLAG;
395 * As documented.
397 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
398 (grfMode & STGM_DELETEONRELEASE) ||
399 (grfMode & STGM_PRIORITY) )
400 return STG_E_INVALIDFUNCTION;
403 * Initialize the out parameter
405 *ppstg = 0;
408 * Create a property enumeration to search the properties
410 propertyEnumeration = IEnumSTATSTGImpl_Construct(
411 This->ancestorStorage,
412 This->rootPropertySetIndex);
415 * Search the enumeration for the property with the given name
417 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
418 propertyEnumeration,
419 pwcsName,
420 &currentProperty);
423 * Delete the property enumeration since we don't need it anymore
425 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
428 * If it was found, construct the stream object and return a pointer to it.
430 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
431 (currentProperty.propertyType==PROPTYPE_STORAGE) )
434 * Construct a new Storage object
436 newStorage = StorageInternalImpl_Construct(
437 This->ancestorStorage,
438 foundPropertyIndex);
440 if (newStorage != 0)
442 *ppstg = (IStorage*)newStorage;
445 * Since we are returning a pointer to the interface,
446 * we have to nail down the reference.
448 StorageBaseImpl_AddRef(*ppstg);
450 return S_OK;
453 return STG_E_INSUFFICIENTMEMORY;
456 return STG_E_FILENOTFOUND;
459 /************************************************************************
460 * Storage32BaseImpl_EnumElements (IStorage)
462 * This method will create an enumerator object that can be used to
463 * retrieve informatino about all the properties in the storage object.
465 * See Windows documentation for more details on IStorage methods.
467 HRESULT WINAPI StorageBaseImpl_EnumElements(
468 IStorage* iface,
469 DWORD reserved1, /* [in] */
470 void* reserved2, /* [size_is][unique][in] */
471 DWORD reserved3, /* [in] */
472 IEnumSTATSTG** ppenum) /* [out] */
474 ICOM_THIS(StorageBaseImpl,iface);
475 IEnumSTATSTGImpl* newEnum;
477 TRACE("(%p, %ld, %p, %ld, %p)\n",
478 iface, reserved1, reserved2, reserved3, ppenum);
481 * Perform a sanity check on the parameters.
483 if ( (This==0) || (ppenum==0))
484 return E_INVALIDARG;
487 * Construct the enumerator.
489 newEnum = IEnumSTATSTGImpl_Construct(
490 This->ancestorStorage,
491 This->rootPropertySetIndex);
493 if (newEnum!=0)
495 *ppenum = (IEnumSTATSTG*)newEnum;
498 * Don't forget to nail down a reference to the new object before
499 * returning it.
501 IEnumSTATSTGImpl_AddRef(*ppenum);
503 return S_OK;
506 return E_OUTOFMEMORY;
509 /************************************************************************
510 * Storage32BaseImpl_Stat (IStorage)
512 * This method will retrieve information about this storage object.
514 * See Windows documentation for more details on IStorage methods.
516 HRESULT WINAPI StorageBaseImpl_Stat(
517 IStorage* iface,
518 STATSTG* pstatstg, /* [out] */
519 DWORD grfStatFlag) /* [in] */
521 ICOM_THIS(StorageBaseImpl,iface);
522 StgProperty curProperty;
523 BOOL readSucessful;
525 TRACE("(%p, %p, %lx)\n",
526 iface, pstatstg, grfStatFlag);
529 * Perform a sanity check on the parameters.
531 if ( (This==0) || (pstatstg==0))
532 return E_INVALIDARG;
535 * Read the information from the property.
537 readSucessful = StorageImpl_ReadProperty(
538 This->ancestorStorage,
539 This->rootPropertySetIndex,
540 &curProperty);
542 if (readSucessful)
544 StorageUtl_CopyPropertyToSTATSTG(
545 pstatstg,
546 &curProperty,
547 grfStatFlag);
549 return S_OK;
552 return E_FAIL;
555 /************************************************************************
556 * Storage32BaseImpl_RenameElement (IStorage)
558 * This method will rename the specified element.
560 * See Windows documentation for more details on IStorage methods.
562 * Implementation notes: The method used to rename consists of creating a clone
563 * of the deleted StgProperty object setting it with the new name and to
564 * perform a DestroyElement of the old StgProperty.
566 HRESULT WINAPI StorageBaseImpl_RenameElement(
567 IStorage* iface,
568 const OLECHAR* pwcsOldName, /* [in] */
569 const OLECHAR* pwcsNewName) /* [in] */
571 ICOM_THIS(StorageBaseImpl,iface);
572 IEnumSTATSTGImpl* propertyEnumeration;
573 StgProperty currentProperty;
574 ULONG foundPropertyIndex;
576 TRACE("(%p, %s, %s)\n",
577 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
580 * Create a property enumeration to search the properties
582 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
583 This->rootPropertySetIndex);
586 * Search the enumeration for the new property name
588 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
589 pwcsNewName,
590 &currentProperty);
592 if (foundPropertyIndex != PROPERTY_NULL)
595 * There is already a property with the new name
597 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
598 return STG_E_FILEALREADYEXISTS;
601 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
604 * Search the enumeration for the old property name
606 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
607 pwcsOldName,
608 &currentProperty);
611 * Delete the property enumeration since we don't need it anymore
613 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
615 if (foundPropertyIndex != PROPERTY_NULL)
617 StgProperty renamedProperty;
618 ULONG renamedPropertyIndex;
621 * Setup a new property for the renamed property
623 renamedProperty.sizeOfNameString =
624 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
626 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
627 return STG_E_INVALIDNAME;
629 lstrcpyW(renamedProperty.name, pwcsNewName);
631 renamedProperty.propertyType = currentProperty.propertyType;
632 renamedProperty.startingBlock = currentProperty.startingBlock;
633 renamedProperty.size.s.LowPart = currentProperty.size.s.LowPart;
634 renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
636 renamedProperty.previousProperty = PROPERTY_NULL;
637 renamedProperty.nextProperty = PROPERTY_NULL;
640 * Bring the dirProperty link in case it is a storage and in which
641 * case the renamed storage elements don't require to be reorganized.
643 renamedProperty.dirProperty = currentProperty.dirProperty;
645 /* call CoFileTime to get the current time
646 renamedProperty.timeStampS1
647 renamedProperty.timeStampD1
648 renamedProperty.timeStampS2
649 renamedProperty.timeStampD2
650 renamedProperty.propertyUniqueID
654 * Obtain a free property in the property chain
656 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
659 * Save the new property into the new property spot
661 StorageImpl_WriteProperty(
662 This->ancestorStorage,
663 renamedPropertyIndex,
664 &renamedProperty);
667 * Find a spot in the property chain for our newly created property.
669 updatePropertyChain(
670 (StorageImpl*)This,
671 renamedPropertyIndex,
672 renamedProperty);
675 * At this point the renamed property has been inserted in the tree,
676 * now, before to Destroy the old property we must zeroed it's dirProperty
677 * otherwise the DestroyProperty below will zap it all and we do not want
678 * this to happen.
679 * Also, we fake that the old property is a storage so the DestroyProperty
680 * will not do a SetSize(0) on the stream data.
682 * This means that we need to tweek the StgProperty if it is a stream or a
683 * non empty storage.
685 currentProperty.dirProperty = PROPERTY_NULL;
686 currentProperty.propertyType = PROPTYPE_STORAGE;
687 StorageImpl_WriteProperty(
688 This->ancestorStorage,
689 foundPropertyIndex,
690 &currentProperty);
693 * Invoke Destroy to get rid of the ole property and automatically redo
694 * the linking of it's previous and next members...
696 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
699 else
702 * There is no property with the old name
704 return STG_E_FILENOTFOUND;
707 return S_OK;
710 /************************************************************************
711 * Storage32BaseImpl_CreateStream (IStorage)
713 * This method will create a stream object within this storage
715 * See Windows documentation for more details on IStorage methods.
717 HRESULT WINAPI StorageBaseImpl_CreateStream(
718 IStorage* iface,
719 const OLECHAR* pwcsName, /* [string][in] */
720 DWORD grfMode, /* [in] */
721 DWORD reserved1, /* [in] */
722 DWORD reserved2, /* [in] */
723 IStream** ppstm) /* [out] */
725 ICOM_THIS(StorageBaseImpl,iface);
726 IEnumSTATSTGImpl* propertyEnumeration;
727 StgStreamImpl* newStream;
728 StgProperty currentProperty, newStreamProperty;
729 ULONG foundPropertyIndex, newPropertyIndex;
731 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
732 iface, debugstr_w(pwcsName), grfMode,
733 reserved1, reserved2, ppstm);
736 * Validate parameters
738 if (ppstm == 0)
739 return STG_E_INVALIDPOINTER;
741 if (pwcsName == 0)
742 return STG_E_INVALIDNAME;
745 * Validate the STGM flags
747 if ( FAILED( validateSTGM(grfMode) ))
748 return STG_E_INVALIDFLAG;
751 * As documented.
753 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
754 (grfMode & STGM_DELETEONRELEASE) ||
755 (grfMode & STGM_TRANSACTED) )
756 return STG_E_INVALIDFUNCTION;
759 * Initialize the out parameter
761 *ppstm = 0;
764 * Create a property enumeration to search the properties
766 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
767 This->rootPropertySetIndex);
769 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
770 pwcsName,
771 &currentProperty);
773 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
775 if (foundPropertyIndex != PROPERTY_NULL)
778 * An element with this name already exists
780 if (grfMode & STGM_CREATE)
782 IStorage_DestroyElement(iface, pwcsName);
784 else
785 return STG_E_FILEALREADYEXISTS;
789 * memset the empty property
791 memset(&newStreamProperty, 0, sizeof(StgProperty));
793 newStreamProperty.sizeOfNameString =
794 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
796 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
797 return STG_E_INVALIDNAME;
799 lstrcpyW(newStreamProperty.name, pwcsName);
801 newStreamProperty.propertyType = PROPTYPE_STREAM;
802 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
803 newStreamProperty.size.s.LowPart = 0;
804 newStreamProperty.size.s.HighPart = 0;
806 newStreamProperty.previousProperty = PROPERTY_NULL;
807 newStreamProperty.nextProperty = PROPERTY_NULL;
808 newStreamProperty.dirProperty = PROPERTY_NULL;
810 /* call CoFileTime to get the current time
811 newStreamProperty.timeStampS1
812 newStreamProperty.timeStampD1
813 newStreamProperty.timeStampS2
814 newStreamProperty.timeStampD2
817 /* newStreamProperty.propertyUniqueID */
820 * Get a free property or create a new one
822 newPropertyIndex = getFreeProperty(This->ancestorStorage);
825 * Save the new property into the new property spot
827 StorageImpl_WriteProperty(
828 This->ancestorStorage,
829 newPropertyIndex,
830 &newStreamProperty);
833 * Find a spot in the property chain for our newly created property.
835 updatePropertyChain(
836 (StorageImpl*)This,
837 newPropertyIndex,
838 newStreamProperty);
841 * Open the stream to return it.
843 newStream = StgStreamImpl_Construct(This, newPropertyIndex);
845 if (newStream != 0)
847 *ppstm = (IStream*)newStream;
850 * Since we are returning a pointer to the interface, we have to nail down
851 * the reference.
853 StgStreamImpl_AddRef(*ppstm);
855 else
857 return STG_E_INSUFFICIENTMEMORY;
860 return S_OK;
863 /************************************************************************
864 * Storage32BaseImpl_SetClass (IStorage)
866 * This method will write the specified CLSID in the property of this
867 * storage.
869 * See Windows documentation for more details on IStorage methods.
871 HRESULT WINAPI StorageBaseImpl_SetClass(
872 IStorage* iface,
873 REFCLSID clsid) /* [in] */
875 ICOM_THIS(StorageBaseImpl,iface);
876 HRESULT hRes = E_FAIL;
877 StgProperty curProperty;
878 BOOL success;
880 TRACE("(%p, %p)\n", iface, clsid);
882 success = StorageImpl_ReadProperty(This->ancestorStorage,
883 This->rootPropertySetIndex,
884 &curProperty);
885 if (success)
887 curProperty.propertyUniqueID = *clsid;
889 success = StorageImpl_WriteProperty(This->ancestorStorage,
890 This->rootPropertySetIndex,
891 &curProperty);
892 if (success)
893 hRes = S_OK;
896 return hRes;
899 /************************************************************************
900 ** Storage32Impl implementation
903 /************************************************************************
904 * Storage32Impl_CreateStorage (IStorage)
906 * This method will create the storage object within the provided storage.
908 * See Windows documentation for more details on IStorage methods.
910 HRESULT WINAPI StorageImpl_CreateStorage(
911 IStorage* iface,
912 const OLECHAR *pwcsName, /* [string][in] */
913 DWORD grfMode, /* [in] */
914 DWORD reserved1, /* [in] */
915 DWORD reserved2, /* [in] */
916 IStorage **ppstg) /* [out] */
918 StorageImpl* const This=(StorageImpl*)iface;
920 IEnumSTATSTGImpl *propertyEnumeration;
921 StgProperty currentProperty;
922 StgProperty newProperty;
923 ULONG foundPropertyIndex;
924 ULONG newPropertyIndex;
925 HRESULT hr;
927 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
928 iface, debugstr_w(pwcsName), grfMode,
929 reserved1, reserved2, ppstg);
932 * Validate parameters
934 if (ppstg == 0)
935 return STG_E_INVALIDPOINTER;
937 if (pwcsName == 0)
938 return STG_E_INVALIDNAME;
941 * Validate the STGM flags
943 if ( FAILED( validateSTGM(grfMode) ) ||
944 (grfMode & STGM_DELETEONRELEASE) )
945 return STG_E_INVALIDFLAG;
948 * Initialize the out parameter
950 *ppstg = 0;
953 * Create a property enumeration and search the properties
955 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
956 This->rootPropertySetIndex);
958 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
959 pwcsName,
960 &currentProperty);
961 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
963 if (foundPropertyIndex != PROPERTY_NULL)
966 * An element with this name already exists
968 if (grfMode & STGM_CREATE)
969 IStorage_DestroyElement(iface, pwcsName);
970 else
971 return STG_E_FILEALREADYEXISTS;
975 * memset the empty property
977 memset(&newProperty, 0, sizeof(StgProperty));
979 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
981 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
982 return STG_E_INVALIDNAME;
984 lstrcpyW(newProperty.name, pwcsName);
986 newProperty.propertyType = PROPTYPE_STORAGE;
987 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
988 newProperty.size.s.LowPart = 0;
989 newProperty.size.s.HighPart = 0;
991 newProperty.previousProperty = PROPERTY_NULL;
992 newProperty.nextProperty = PROPERTY_NULL;
993 newProperty.dirProperty = PROPERTY_NULL;
995 /* call CoFileTime to get the current time
996 newProperty.timeStampS1
997 newProperty.timeStampD1
998 newProperty.timeStampS2
999 newProperty.timeStampD2
1002 /* newStorageProperty.propertyUniqueID */
1005 * Obtain a free property in the property chain
1007 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1010 * Save the new property into the new property spot
1012 StorageImpl_WriteProperty(
1013 This->ancestorStorage,
1014 newPropertyIndex,
1015 &newProperty);
1018 * Find a spot in the property chain for our newly created property.
1020 updatePropertyChain(
1021 This,
1022 newPropertyIndex,
1023 newProperty);
1026 * Open it to get a pointer to return.
1028 hr = IStorage_OpenStorage(
1029 iface,
1030 (OLECHAR*)pwcsName,
1032 grfMode,
1035 ppstg);
1037 if( (hr != S_OK) || (*ppstg == NULL))
1039 return hr;
1043 return S_OK;
1047 /***************************************************************************
1049 * Internal Method
1051 * Get a free property or create a new one.
1053 static ULONG getFreeProperty(
1054 StorageImpl *storage)
1056 ULONG currentPropertyIndex = 0;
1057 ULONG newPropertyIndex = PROPERTY_NULL;
1058 BOOL readSucessful = TRUE;
1059 StgProperty currentProperty;
1064 * Start by reading the root property
1066 readSucessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1067 currentPropertyIndex,
1068 &currentProperty);
1069 if (readSucessful)
1071 if (currentProperty.sizeOfNameString == 0)
1074 * The property existis and is available, we found it.
1076 newPropertyIndex = currentPropertyIndex;
1079 else
1082 * We exhausted the property list, we will create more space below
1084 newPropertyIndex = currentPropertyIndex;
1086 currentPropertyIndex++;
1088 } while (newPropertyIndex == PROPERTY_NULL);
1091 * grow the property chain
1093 if (! readSucessful)
1095 StgProperty emptyProperty;
1096 ULARGE_INTEGER newSize;
1097 ULONG propertyIndex;
1098 ULONG lastProperty = 0;
1099 ULONG blockCount = 0;
1102 * obtain the new count of property blocks
1104 blockCount = BlockChainStream_GetCount(
1105 storage->ancestorStorage->rootBlockChain)+1;
1108 * initialize the size used by the property stream
1110 newSize.s.HighPart = 0;
1111 newSize.s.LowPart = storage->bigBlockSize * blockCount;
1114 * add a property block to the property chain
1116 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1119 * memset the empty property in order to initialize the unused newly
1120 * created property
1122 memset(&emptyProperty, 0, sizeof(StgProperty));
1125 * initialize them
1127 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1129 for(
1130 propertyIndex = newPropertyIndex;
1131 propertyIndex < lastProperty;
1132 propertyIndex++)
1134 StorageImpl_WriteProperty(
1135 storage->ancestorStorage,
1136 propertyIndex,
1137 &emptyProperty);
1141 return newPropertyIndex;
1144 /****************************************************************************
1146 * Internal Method
1148 * Case insensitive comparaison of StgProperty.name by first considering
1149 * their size.
1151 * Returns <0 when newPrpoerty < currentProperty
1152 * >0 when newPrpoerty > currentProperty
1153 * 0 when newPrpoerty == currentProperty
1155 static LONG propertyNameCmp(
1156 OLECHAR *newProperty,
1157 OLECHAR *currentProperty)
1159 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1161 if (diff == 0)
1164 * We compare the string themselves only when they are of the same lenght
1166 diff = lstrcmpiW( newProperty, currentProperty);
1169 return diff;
1172 /****************************************************************************
1174 * Internal Method
1176 * Properly link this new element in the property chain.
1178 static void updatePropertyChain(
1179 StorageImpl *storage,
1180 ULONG newPropertyIndex,
1181 StgProperty newProperty)
1183 StgProperty currentProperty;
1186 * Read the root property
1188 StorageImpl_ReadProperty(storage->ancestorStorage,
1189 storage->rootPropertySetIndex,
1190 &currentProperty);
1192 if (currentProperty.dirProperty != PROPERTY_NULL)
1195 * The root storage contains some element, therefore, start the research
1196 * for the appropriate location.
1198 BOOL found = 0;
1199 ULONG current, next, previous, currentPropertyId;
1202 * Keep the StgProperty sequence number of the storage first property
1204 currentPropertyId = currentProperty.dirProperty;
1207 * Read
1209 StorageImpl_ReadProperty(storage->ancestorStorage,
1210 currentProperty.dirProperty,
1211 &currentProperty);
1213 previous = currentProperty.previousProperty;
1214 next = currentProperty.nextProperty;
1215 current = currentPropertyId;
1217 while (found == 0)
1219 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1221 if (diff < 0)
1223 if (previous != PROPERTY_NULL)
1225 StorageImpl_ReadProperty(storage->ancestorStorage,
1226 previous,
1227 &currentProperty);
1228 current = previous;
1230 else
1232 currentProperty.previousProperty = newPropertyIndex;
1233 StorageImpl_WriteProperty(storage->ancestorStorage,
1234 current,
1235 &currentProperty);
1236 found = 1;
1239 else if (diff > 0)
1241 if (next != PROPERTY_NULL)
1243 StorageImpl_ReadProperty(storage->ancestorStorage,
1244 next,
1245 &currentProperty);
1246 current = next;
1248 else
1250 currentProperty.nextProperty = newPropertyIndex;
1251 StorageImpl_WriteProperty(storage->ancestorStorage,
1252 current,
1253 &currentProperty);
1254 found = 1;
1257 else
1260 * Trying to insert an item with the same name in the
1261 * subtree structure.
1263 assert(FALSE);
1266 previous = currentProperty.previousProperty;
1267 next = currentProperty.nextProperty;
1270 else
1273 * The root storage is empty, link the new property to it's dir property
1275 currentProperty.dirProperty = newPropertyIndex;
1276 StorageImpl_WriteProperty(storage->ancestorStorage,
1277 storage->rootPropertySetIndex,
1278 &currentProperty);
1283 /*************************************************************************
1284 * CopyTo (IStorage)
1286 HRESULT WINAPI StorageImpl_CopyTo(
1287 IStorage* iface,
1288 DWORD ciidExclude, /* [in] */
1289 const IID* rgiidExclude, /* [size_is][unique][in] */
1290 SNB snbExclude, /* [unique][in] */
1291 IStorage* pstgDest) /* [unique][in] */
1293 IEnumSTATSTG *elements = 0;
1294 STATSTG curElement, strStat;
1295 HRESULT hr;
1296 IStorage *pstgTmp, *pstgChild;
1297 IStream *pstrTmp, *pstrChild;
1299 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1300 FIXME("Exclude option not implemented\n");
1302 TRACE("(%p, %ld, %p, %p, %p)\n",
1303 iface, ciidExclude, rgiidExclude,
1304 snbExclude, pstgDest);
1307 * Perform a sanity check
1309 if ( pstgDest == 0 )
1310 return STG_E_INVALIDPOINTER;
1313 * Enumerate the elements
1315 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1317 if ( hr != S_OK )
1318 return hr;
1321 * set the class ID
1323 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1324 IStorage_SetClass( pstgDest, &curElement.clsid );
1329 * Obtain the next element
1331 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1333 if ( hr == S_FALSE )
1335 hr = S_OK; /* done, every element has been copied */
1336 break;
1339 if (curElement.type == STGTY_STORAGE)
1342 * open child source storage
1344 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1345 STGM_READ|STGM_SHARE_EXCLUSIVE,
1346 NULL, 0, &pstgChild );
1348 if (hr != S_OK)
1349 break;
1352 * Check if destination storage is not a child of the source
1353 * storage, which will cause an infinite loop
1355 if (pstgChild == pstgDest)
1357 IEnumSTATSTG_Release(elements);
1359 return STG_E_ACCESSDENIED;
1363 * create a new storage in destination storage
1365 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1366 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1367 0, 0,
1368 &pstgTmp );
1370 * if it already exist, don't create a new one use this one
1372 if (hr == STG_E_FILEALREADYEXISTS)
1374 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1375 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1376 NULL, 0, &pstgTmp );
1379 if (hr != S_OK)
1380 break;
1384 * do the copy recursively
1386 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1387 snbExclude, pstgTmp );
1389 IStorage_Release( pstgTmp );
1390 IStorage_Release( pstgChild );
1392 else if (curElement.type == STGTY_STREAM)
1395 * create a new stream in destination storage. If the stream already
1396 * exist, it will be deleted and a new one will be created.
1398 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1399 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1400 0, 0, &pstrTmp );
1402 if (hr != S_OK)
1403 break;
1406 * open child stream storage
1408 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1409 STGM_READ|STGM_SHARE_EXCLUSIVE,
1410 0, &pstrChild );
1412 if (hr != S_OK)
1413 break;
1416 * Get the size of the source stream
1418 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1421 * Set the size of the destination stream.
1423 IStream_SetSize(pstrTmp, strStat.cbSize);
1426 * do the copy
1428 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1429 NULL, NULL );
1431 IStream_Release( pstrTmp );
1432 IStream_Release( pstrChild );
1434 else
1436 WARN("unknown element type: %ld\n", curElement.type);
1439 } while (hr == S_OK);
1442 * Clean-up
1444 IEnumSTATSTG_Release(elements);
1446 return hr;
1449 /*************************************************************************
1450 * MoveElementTo (IStorage)
1452 HRESULT WINAPI StorageImpl_MoveElementTo(
1453 IStorage* iface,
1454 const OLECHAR *pwcsName, /* [string][in] */
1455 IStorage *pstgDest, /* [unique][in] */
1456 const OLECHAR *pwcsNewName,/* [string][in] */
1457 DWORD grfFlags) /* [in] */
1459 FIXME("not implemented!\n");
1460 return E_NOTIMPL;
1463 /*************************************************************************
1464 * Commit (IStorage)
1466 HRESULT WINAPI StorageImpl_Commit(
1467 IStorage* iface,
1468 DWORD grfCommitFlags)/* [in] */
1470 FIXME("(%ld): stub!\n", grfCommitFlags);
1471 return S_OK;
1474 /*************************************************************************
1475 * Revert (IStorage)
1477 HRESULT WINAPI StorageImpl_Revert(
1478 IStorage* iface)
1480 FIXME("not implemented!\n");
1481 return E_NOTIMPL;
1484 /*************************************************************************
1485 * DestroyElement (IStorage)
1487 * Stategy: This implementation is build this way for simplicity not for speed.
1488 * I always delete the top most element of the enumeration and adjust
1489 * the deleted element pointer all the time. This takes longer to
1490 * do but allow to reinvoke DestroyElement whenever we encounter a
1491 * storage object. The optimisation reside in the usage of another
1492 * enumeration stategy that would give all the leaves of a storage
1493 * first. (postfix order)
1495 HRESULT WINAPI StorageImpl_DestroyElement(
1496 IStorage* iface,
1497 const OLECHAR *pwcsName)/* [string][in] */
1499 StorageImpl* const This=(StorageImpl*)iface;
1501 IEnumSTATSTGImpl* propertyEnumeration;
1502 HRESULT hr = S_OK;
1503 BOOL res;
1504 StgProperty propertyToDelete;
1505 StgProperty parentProperty;
1506 ULONG foundPropertyIndexToDelete;
1507 ULONG typeOfRelation;
1508 ULONG parentPropertyId;
1510 TRACE("(%p, %s)\n",
1511 iface, debugstr_w(pwcsName));
1514 * Perform a sanity check on the parameters.
1516 if (pwcsName==NULL)
1517 return STG_E_INVALIDPOINTER;
1520 * Create a property enumeration to search the property with the given name
1522 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1523 This->ancestorStorage,
1524 This->rootPropertySetIndex);
1526 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1527 propertyEnumeration,
1528 pwcsName,
1529 &propertyToDelete);
1531 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1533 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1535 return STG_E_FILENOTFOUND;
1539 * Find the parent property of the property to delete (the one that
1540 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1541 * the parent is This. Otherwise, the parent is one of it's sibling...
1545 * First, read This's StgProperty..
1547 res = StorageImpl_ReadProperty(
1548 This->ancestorStorage,
1549 This->rootPropertySetIndex,
1550 &parentProperty);
1552 assert(res==TRUE);
1555 * Second, check to see if by any chance the actual storage (This) is not
1556 * the parent of the property to delete... We never know...
1558 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1561 * Set data as it would have been done in the else part...
1563 typeOfRelation = PROPERTY_RELATION_DIR;
1564 parentPropertyId = This->rootPropertySetIndex;
1566 else
1569 * Create a property enumeration to search the parent properties, and
1570 * delete it once done.
1572 IEnumSTATSTGImpl* propertyEnumeration2;
1574 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1575 This->ancestorStorage,
1576 This->rootPropertySetIndex);
1578 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1579 propertyEnumeration2,
1580 foundPropertyIndexToDelete,
1581 &parentProperty,
1582 &parentPropertyId);
1584 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1587 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1589 hr = deleteStorageProperty(
1590 This,
1591 propertyToDelete.name);
1593 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1595 hr = deleteStreamProperty(
1596 This,
1597 foundPropertyIndexToDelete,
1598 propertyToDelete);
1601 if (hr!=S_OK)
1602 return hr;
1605 * Adjust the property chain
1607 hr = adjustPropertyChain(
1608 This,
1609 propertyToDelete,
1610 parentProperty,
1611 parentPropertyId,
1612 typeOfRelation);
1614 return hr;
1618 /*********************************************************************
1620 * Internal Method
1622 * Perform the deletion of a complete storage node
1625 static HRESULT deleteStorageProperty(
1626 StorageImpl *parentStorage,
1627 OLECHAR *propertyToDeleteName)
1629 IEnumSTATSTG *elements = 0;
1630 IStorage *childStorage = 0;
1631 STATSTG currentElement;
1632 HRESULT hr;
1633 HRESULT destroyHr = S_OK;
1636 * Open the storage and enumerate it
1638 hr = StorageBaseImpl_OpenStorage(
1639 (IStorage*)parentStorage,
1640 propertyToDeleteName,
1642 STGM_SHARE_EXCLUSIVE,
1645 &childStorage);
1647 if (hr != S_OK)
1649 return hr;
1653 * Enumerate the elements
1655 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1660 * Obtain the next element
1662 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1663 if (hr==S_OK)
1665 destroyHr = StorageImpl_DestroyElement(
1666 (IStorage*)childStorage,
1667 (OLECHAR*)currentElement.pwcsName);
1669 CoTaskMemFree(currentElement.pwcsName);
1673 * We need to Reset the enumeration every time because we delete elements
1674 * and the enumeration could be invalid
1676 IEnumSTATSTG_Reset(elements);
1678 } while ((hr == S_OK) && (destroyHr == S_OK));
1680 IStorage_Release(childStorage);
1681 IEnumSTATSTG_Release(elements);
1683 return destroyHr;
1686 /*********************************************************************
1688 * Internal Method
1690 * Perform the deletion of a stream node
1693 static HRESULT deleteStreamProperty(
1694 StorageImpl *parentStorage,
1695 ULONG indexOfPropertyToDelete,
1696 StgProperty propertyToDelete)
1698 IStream *pis;
1699 HRESULT hr;
1700 ULARGE_INTEGER size;
1702 size.s.HighPart = 0;
1703 size.s.LowPart = 0;
1705 hr = StorageBaseImpl_OpenStream(
1706 (IStorage*)parentStorage,
1707 (OLECHAR*)propertyToDelete.name,
1708 NULL,
1709 STGM_SHARE_EXCLUSIVE,
1711 &pis);
1713 if (hr!=S_OK)
1715 return(hr);
1719 * Zap the stream
1721 hr = IStream_SetSize(pis, size);
1723 if(hr != S_OK)
1725 return hr;
1729 * Release the stream object.
1731 IStream_Release(pis);
1734 * Invalidate the property by zeroing it's name member.
1736 propertyToDelete.sizeOfNameString = 0;
1739 * Here we should re-read the property so we get the updated pointer
1740 * but since we are here to zap it, I don't do it...
1742 StorageImpl_WriteProperty(
1743 parentStorage->ancestorStorage,
1744 indexOfPropertyToDelete,
1745 &propertyToDelete);
1747 return S_OK;
1750 /*********************************************************************
1752 * Internal Method
1754 * Finds a placeholder for the StgProperty within the Storage
1757 static HRESULT findPlaceholder(
1758 StorageImpl *storage,
1759 ULONG propertyIndexToStore,
1760 ULONG storePropertyIndex,
1761 INT typeOfRelation)
1763 StgProperty storeProperty;
1764 HRESULT hr = S_OK;
1765 BOOL res = TRUE;
1768 * Read the storage property
1770 res = StorageImpl_ReadProperty(
1771 storage->ancestorStorage,
1772 storePropertyIndex,
1773 &storeProperty);
1775 if(! res)
1777 return E_FAIL;
1780 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1782 if (storeProperty.previousProperty != PROPERTY_NULL)
1784 return findPlaceholder(
1785 storage,
1786 propertyIndexToStore,
1787 storeProperty.previousProperty,
1788 typeOfRelation);
1790 else
1792 storeProperty.previousProperty = propertyIndexToStore;
1795 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1797 if (storeProperty.nextProperty != PROPERTY_NULL)
1799 return findPlaceholder(
1800 storage,
1801 propertyIndexToStore,
1802 storeProperty.nextProperty,
1803 typeOfRelation);
1805 else
1807 storeProperty.nextProperty = propertyIndexToStore;
1810 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1812 if (storeProperty.dirProperty != PROPERTY_NULL)
1814 return findPlaceholder(
1815 storage,
1816 propertyIndexToStore,
1817 storeProperty.dirProperty,
1818 typeOfRelation);
1820 else
1822 storeProperty.dirProperty = propertyIndexToStore;
1826 hr = StorageImpl_WriteProperty(
1827 storage->ancestorStorage,
1828 storePropertyIndex,
1829 &storeProperty);
1831 if(! hr)
1833 return E_FAIL;
1836 return S_OK;
1839 /*************************************************************************
1841 * Internal Method
1843 * This method takes the previous and the next property link of a property
1844 * to be deleted and find them a place in the Storage.
1846 static HRESULT adjustPropertyChain(
1847 StorageImpl *This,
1848 StgProperty propertyToDelete,
1849 StgProperty parentProperty,
1850 ULONG parentPropertyId,
1851 INT typeOfRelation)
1853 ULONG newLinkProperty = PROPERTY_NULL;
1854 BOOL needToFindAPlaceholder = FALSE;
1855 ULONG storeNode = PROPERTY_NULL;
1856 ULONG toStoreNode = PROPERTY_NULL;
1857 INT relationType = 0;
1858 HRESULT hr = S_OK;
1859 BOOL res = TRUE;
1861 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1863 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1866 * Set the parent previous to the property to delete previous
1868 newLinkProperty = propertyToDelete.previousProperty;
1870 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1873 * We also need to find a storage for the other link, setup variables
1874 * to do this at the end...
1876 needToFindAPlaceholder = TRUE;
1877 storeNode = propertyToDelete.previousProperty;
1878 toStoreNode = propertyToDelete.nextProperty;
1879 relationType = PROPERTY_RELATION_NEXT;
1882 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1885 * Set the parent previous to the property to delete next
1887 newLinkProperty = propertyToDelete.nextProperty;
1891 * Link it for real...
1893 parentProperty.previousProperty = newLinkProperty;
1896 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1898 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1901 * Set the parent next to the property to delete next previous
1903 newLinkProperty = propertyToDelete.previousProperty;
1905 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1908 * We also need to find a storage for the other link, setup variables
1909 * to do this at the end...
1911 needToFindAPlaceholder = TRUE;
1912 storeNode = propertyToDelete.previousProperty;
1913 toStoreNode = propertyToDelete.nextProperty;
1914 relationType = PROPERTY_RELATION_NEXT;
1917 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1920 * Set the parent next to the property to delete next
1922 newLinkProperty = propertyToDelete.nextProperty;
1926 * Link it for real...
1928 parentProperty.nextProperty = newLinkProperty;
1930 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
1932 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1935 * Set the parent dir to the property to delete previous
1937 newLinkProperty = propertyToDelete.previousProperty;
1939 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1942 * We also need to find a storage for the other link, setup variables
1943 * to do this at the end...
1945 needToFindAPlaceholder = TRUE;
1946 storeNode = propertyToDelete.previousProperty;
1947 toStoreNode = propertyToDelete.nextProperty;
1948 relationType = PROPERTY_RELATION_NEXT;
1951 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1954 * Set the parent dir to the property to delete next
1956 newLinkProperty = propertyToDelete.nextProperty;
1960 * Link it for real...
1962 parentProperty.dirProperty = newLinkProperty;
1966 * Write back the parent property
1968 res = StorageImpl_WriteProperty(
1969 This->ancestorStorage,
1970 parentPropertyId,
1971 &parentProperty);
1972 if(! res)
1974 return E_FAIL;
1978 * If a placeholder is required for the other link, then, find one and
1979 * get out of here...
1981 if (needToFindAPlaceholder)
1983 hr = findPlaceholder(
1984 This,
1985 toStoreNode,
1986 storeNode,
1987 relationType);
1990 return hr;
1994 /******************************************************************************
1995 * SetElementTimes (IStorage)
1997 HRESULT WINAPI StorageImpl_SetElementTimes(
1998 IStorage* iface,
1999 const OLECHAR *pwcsName,/* [string][in] */
2000 const FILETIME *pctime, /* [in] */
2001 const FILETIME *patime, /* [in] */
2002 const FILETIME *pmtime) /* [in] */
2004 FIXME("not implemented!\n");
2005 return E_NOTIMPL;
2008 /******************************************************************************
2009 * SetStateBits (IStorage)
2011 HRESULT WINAPI StorageImpl_SetStateBits(
2012 IStorage* iface,
2013 DWORD grfStateBits,/* [in] */
2014 DWORD grfMask) /* [in] */
2016 FIXME("not implemented!\n");
2017 return E_NOTIMPL;
2020 HRESULT StorageImpl_Construct(
2021 StorageImpl* This,
2022 HANDLE hFile,
2023 ILockBytes* pLkbyt,
2024 DWORD openFlags,
2025 BOOL fileBased)
2027 HRESULT hr = S_OK;
2028 StgProperty currentProperty;
2029 BOOL readSucessful;
2030 ULONG currentPropertyIndex;
2032 if ( FAILED( validateSTGM(openFlags) ))
2033 return STG_E_INVALIDFLAG;
2035 memset(This, 0, sizeof(StorageImpl));
2038 * Initialize the virtual fgunction table.
2040 ICOM_VTBL(This) = &Storage32Impl_Vtbl;
2041 This->v_destructor = &StorageImpl_Destroy;
2044 * This is the top-level storage so initialize the ancester pointer
2045 * to this.
2047 This->ancestorStorage = This;
2050 * Initialize the physical support of the storage.
2052 This->hFile = hFile;
2055 * Initialize the big block cache.
2057 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2058 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2059 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2060 pLkbyt,
2061 openFlags,
2062 This->bigBlockSize,
2063 fileBased);
2065 if (This->bigBlockFile == 0)
2066 return E_FAIL;
2068 if (openFlags & STGM_CREATE)
2070 ULARGE_INTEGER size;
2071 BYTE* bigBlockBuffer;
2074 * Initialize all header variables:
2075 * - The big block depot consists of one block and it is at block 0
2076 * - The properties start at block 1
2077 * - There is no small block depot
2079 memset( This->bigBlockDepotStart,
2080 BLOCK_UNUSED,
2081 sizeof(This->bigBlockDepotStart));
2083 This->bigBlockDepotCount = 1;
2084 This->bigBlockDepotStart[0] = 0;
2085 This->rootStartBlock = 1;
2086 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2087 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2088 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2089 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2090 This->extBigBlockDepotCount = 0;
2092 StorageImpl_SaveFileHeader(This);
2095 * Add one block for the big block depot and one block for the properties
2097 size.s.HighPart = 0;
2098 size.s.LowPart = This->bigBlockSize * 3;
2099 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2102 * Initialize the big block depot
2104 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2105 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2106 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2107 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2108 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2110 else
2113 * Load the header for the file.
2115 hr = StorageImpl_LoadFileHeader(This);
2117 if (FAILED(hr))
2119 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2121 return hr;
2126 * There is no block depot cached yet.
2128 This->indexBlockDepotCached = 0xFFFFFFFF;
2131 * Start searching for free blocks with block 0.
2133 This->prevFreeBlock = 0;
2136 * Create the block chain abstractions.
2138 This->rootBlockChain =
2139 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2141 This->smallBlockDepotChain = BlockChainStream_Construct(
2142 This,
2143 &This->smallBlockDepotStart,
2144 PROPERTY_NULL);
2147 * Write the root property
2149 if (openFlags & STGM_CREATE)
2151 StgProperty rootProp;
2153 * Initialize the property chain
2155 memset(&rootProp, 0, sizeof(rootProp));
2156 lstrcpyAtoW(rootProp.name, rootPropertyName);
2158 rootProp.sizeOfNameString = (lstrlenW(rootProp.name)+1) * sizeof(WCHAR);
2159 rootProp.propertyType = PROPTYPE_ROOT;
2160 rootProp.previousProperty = PROPERTY_NULL;
2161 rootProp.nextProperty = PROPERTY_NULL;
2162 rootProp.dirProperty = PROPERTY_NULL;
2163 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2164 rootProp.size.s.HighPart = 0;
2165 rootProp.size.s.LowPart = 0;
2167 StorageImpl_WriteProperty(This, 0, &rootProp);
2171 * Find the ID of the root int he property sets.
2173 currentPropertyIndex = 0;
2177 readSucessful = StorageImpl_ReadProperty(
2178 This,
2179 currentPropertyIndex,
2180 &currentProperty);
2182 if (readSucessful)
2184 if ( (currentProperty.sizeOfNameString != 0 ) &&
2185 (currentProperty.propertyType == PROPTYPE_ROOT) )
2187 This->rootPropertySetIndex = currentPropertyIndex;
2191 currentPropertyIndex++;
2193 } while (readSucessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2195 if (!readSucessful)
2197 /* TODO CLEANUP */
2198 return E_FAIL;
2202 * Create the block chain abstraction for the small block root chain.
2204 This->smallBlockRootChain = BlockChainStream_Construct(
2205 This,
2206 NULL,
2207 This->rootPropertySetIndex);
2209 return hr;
2212 void StorageImpl_Destroy(
2213 StorageImpl* This)
2215 TRACE("(%p)\n", This);
2217 BlockChainStream_Destroy(This->smallBlockRootChain);
2218 BlockChainStream_Destroy(This->rootBlockChain);
2219 BlockChainStream_Destroy(This->smallBlockDepotChain);
2221 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2222 return;
2225 /******************************************************************************
2226 * Storage32Impl_GetNextFreeBigBlock
2228 * Returns the index of the next free big block.
2229 * If the big block depot is filled, this method will enlarge it.
2232 ULONG StorageImpl_GetNextFreeBigBlock(
2233 StorageImpl* This)
2235 ULONG depotBlockIndexPos;
2236 void *depotBuffer;
2237 ULONG depotBlockOffset;
2238 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2239 ULONG nextBlockIndex = BLOCK_SPECIAL;
2240 int depotIndex = 0;
2241 ULONG freeBlock = BLOCK_UNUSED;
2243 depotIndex = This->prevFreeBlock / blocksPerDepot;
2244 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2247 * Scan the entire big block depot until we find a block marked free
2249 while (nextBlockIndex != BLOCK_UNUSED)
2251 if (depotIndex < COUNT_BBDEPOTINHEADER)
2253 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2256 * Grow the primary depot.
2258 if (depotBlockIndexPos == BLOCK_UNUSED)
2260 depotBlockIndexPos = depotIndex*blocksPerDepot;
2263 * Add a block depot.
2265 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2266 This->bigBlockDepotCount++;
2267 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2270 * Flag it as a block depot.
2272 StorageImpl_SetNextBlockInChain(This,
2273 depotBlockIndexPos,
2274 BLOCK_SPECIAL);
2276 /* Save new header information.
2278 StorageImpl_SaveFileHeader(This);
2281 else
2283 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2285 if (depotBlockIndexPos == BLOCK_UNUSED)
2288 * Grow the extended depot.
2290 ULONG extIndex = BLOCK_UNUSED;
2291 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2292 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2294 if (extBlockOffset == 0)
2296 /* We need an extended block.
2298 extIndex = Storage32Impl_AddExtBlockDepot(This);
2299 This->extBigBlockDepotCount++;
2300 depotBlockIndexPos = extIndex + 1;
2302 else
2303 depotBlockIndexPos = depotIndex * blocksPerDepot;
2306 * Add a block depot and mark it in the extended block.
2308 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2309 This->bigBlockDepotCount++;
2310 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2312 /* Flag the block depot.
2314 StorageImpl_SetNextBlockInChain(This,
2315 depotBlockIndexPos,
2316 BLOCK_SPECIAL);
2318 /* If necessary, flag the extended depot block.
2320 if (extIndex != BLOCK_UNUSED)
2321 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2323 /* Save header information.
2325 StorageImpl_SaveFileHeader(This);
2329 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2331 if (depotBuffer != 0)
2333 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2334 ( nextBlockIndex != BLOCK_UNUSED))
2336 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2338 if (nextBlockIndex == BLOCK_UNUSED)
2340 freeBlock = (depotIndex * blocksPerDepot) +
2341 (depotBlockOffset/sizeof(ULONG));
2344 depotBlockOffset += sizeof(ULONG);
2347 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2350 depotIndex++;
2351 depotBlockOffset = 0;
2354 This->prevFreeBlock = freeBlock;
2356 return freeBlock;
2359 /******************************************************************************
2360 * Storage32Impl_AddBlockDepot
2362 * This will create a depot block, essentially it is a block initialized
2363 * to BLOCK_UNUSEDs.
2365 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2367 BYTE* blockBuffer;
2369 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2372 * Initialize blocks as free
2374 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2376 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2379 /******************************************************************************
2380 * Storage32Impl_GetExtDepotBlock
2382 * Returns the index of the block that corresponds to the specified depot
2383 * index. This method is only for depot indexes equal or greater than
2384 * COUNT_BBDEPOTINHEADER.
2386 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2388 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2389 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2390 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2391 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2392 ULONG blockIndex = BLOCK_UNUSED;
2393 ULONG extBlockIndex = This->extBigBlockDepotStart;
2395 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2397 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2398 return BLOCK_UNUSED;
2400 while (extBlockCount > 0)
2402 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2403 extBlockCount--;
2406 if (extBlockIndex != BLOCK_UNUSED)
2408 BYTE* depotBuffer;
2410 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2412 if (depotBuffer != 0)
2414 StorageUtl_ReadDWord(depotBuffer,
2415 extBlockOffset * sizeof(ULONG),
2416 &blockIndex);
2418 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2422 return blockIndex;
2425 /******************************************************************************
2426 * Storage32Impl_SetExtDepotBlock
2428 * Associates the specified block index to the specified depot index.
2429 * This method is only for depot indexes equal or greater than
2430 * COUNT_BBDEPOTINHEADER.
2432 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2433 ULONG depotIndex,
2434 ULONG blockIndex)
2436 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2437 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2438 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2439 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2440 ULONG extBlockIndex = This->extBigBlockDepotStart;
2442 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2444 while (extBlockCount > 0)
2446 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2447 extBlockCount--;
2450 if (extBlockIndex != BLOCK_UNUSED)
2452 BYTE* depotBuffer;
2454 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2456 if (depotBuffer != 0)
2458 StorageUtl_WriteDWord(depotBuffer,
2459 extBlockOffset * sizeof(ULONG),
2460 blockIndex);
2462 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2467 /******************************************************************************
2468 * Storage32Impl_AddExtBlockDepot
2470 * Creates an extended depot block.
2472 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2474 ULONG numExtBlocks = This->extBigBlockDepotCount;
2475 ULONG nextExtBlock = This->extBigBlockDepotStart;
2476 BYTE* depotBuffer = NULL;
2477 ULONG index = BLOCK_UNUSED;
2478 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2479 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2480 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2482 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2483 blocksPerDepotBlock;
2485 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2488 * The first extended block.
2490 This->extBigBlockDepotStart = index;
2492 else
2494 int i;
2496 * Follow the chain to the last one.
2498 for (i = 0; i < (numExtBlocks - 1); i++)
2500 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2504 * Add the new extended block to the chain.
2506 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2507 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2508 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2512 * Initialize this block.
2514 depotBuffer = StorageImpl_GetBigBlock(This, index);
2515 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2516 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2518 return index;
2521 /******************************************************************************
2522 * Storage32Impl_FreeBigBlock
2524 * This method will flag the specified block as free in the big block depot.
2526 void StorageImpl_FreeBigBlock(
2527 StorageImpl* This,
2528 ULONG blockIndex)
2530 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2532 if (blockIndex < This->prevFreeBlock)
2533 This->prevFreeBlock = blockIndex;
2536 /************************************************************************
2537 * Storage32Impl_GetNextBlockInChain
2539 * This method will retrieve the block index of the next big block in
2540 * in the chain.
2542 * Params: This - Pointer to the Storage object.
2543 * blockIndex - Index of the block to retrieve the chain
2544 * for.
2546 * Returns: This method returns the index of the next block in the chain.
2547 * It will return the constants:
2548 * BLOCK_SPECIAL - If the block given was not part of a
2549 * chain.
2550 * BLOCK_END_OF_CHAIN - If the block given was the last in
2551 * a chain.
2552 * BLOCK_UNUSED - If the block given was not past of a chain
2553 * and is available.
2554 * BLOCK_EXTBBDEPOT - This block is part of the extended
2555 * big block depot.
2557 * See Windows documentation for more details on IStorage methods.
2559 ULONG StorageImpl_GetNextBlockInChain(
2560 StorageImpl* This,
2561 ULONG blockIndex)
2563 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2564 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2565 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2566 ULONG nextBlockIndex = BLOCK_SPECIAL;
2567 void* depotBuffer;
2568 ULONG depotBlockIndexPos;
2570 assert(depotBlockCount < This->bigBlockDepotCount);
2573 * Cache the currently accessed depot block.
2575 if (depotBlockCount != This->indexBlockDepotCached)
2577 This->indexBlockDepotCached = depotBlockCount;
2579 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2581 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2583 else
2586 * We have to look in the extended depot.
2588 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2591 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2593 if (depotBuffer!=0)
2595 int index;
2597 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2599 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2600 This->blockDepotCached[index] = nextBlockIndex;
2603 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2607 nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2609 return nextBlockIndex;
2612 /******************************************************************************
2613 * Storage32Impl_GetNextExtendedBlock
2615 * Given an extended block this method will return the next extended block.
2617 * NOTES:
2618 * The last ULONG of an extended block is the block index of the next
2619 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2620 * depot.
2622 * Return values:
2623 * - The index of the next extended block
2624 * - BLOCK_UNUSED: there is no next extended block.
2625 * - Any other return values denotes failure.
2627 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2629 ULONG nextBlockIndex = BLOCK_SPECIAL;
2630 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2631 void* depotBuffer;
2633 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2635 if (depotBuffer!=0)
2637 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2639 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2642 return nextBlockIndex;
2645 /******************************************************************************
2646 * Storage32Impl_SetNextBlockInChain
2648 * This method will write the index of the specified block's next block
2649 * in the big block depot.
2651 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2652 * do the following
2654 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2655 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2656 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2659 void StorageImpl_SetNextBlockInChain(
2660 StorageImpl* This,
2661 ULONG blockIndex,
2662 ULONG nextBlock)
2664 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2665 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2666 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2667 ULONG depotBlockIndexPos;
2668 void* depotBuffer;
2670 assert(depotBlockCount < This->bigBlockDepotCount);
2671 assert(blockIndex != nextBlock);
2673 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2675 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2677 else
2680 * We have to look in the extended depot.
2682 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2685 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2687 if (depotBuffer!=0)
2689 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2690 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2694 * Update the cached block depot, if necessary.
2696 if (depotBlockCount == This->indexBlockDepotCached)
2698 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2702 /******************************************************************************
2703 * Storage32Impl_LoadFileHeader
2705 * This method will read in the file header, i.e. big block index -1.
2707 HRESULT StorageImpl_LoadFileHeader(
2708 StorageImpl* This)
2710 HRESULT hr = STG_E_FILENOTFOUND;
2711 void* headerBigBlock = NULL;
2712 int index;
2715 * Get a pointer to the big block of data containing the header.
2717 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2720 * Extract the information from the header.
2722 if (headerBigBlock!=0)
2725 * Check for the "magic number" signature and return an error if it is not
2726 * found.
2728 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2730 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2731 return STG_E_OLDFORMAT;
2734 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2736 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2737 return STG_E_INVALIDHEADER;
2740 StorageUtl_ReadWord(
2741 headerBigBlock,
2742 OFFSET_BIGBLOCKSIZEBITS,
2743 &This->bigBlockSizeBits);
2745 StorageUtl_ReadWord(
2746 headerBigBlock,
2747 OFFSET_SMALLBLOCKSIZEBITS,
2748 &This->smallBlockSizeBits);
2750 StorageUtl_ReadDWord(
2751 headerBigBlock,
2752 OFFSET_BBDEPOTCOUNT,
2753 &This->bigBlockDepotCount);
2755 StorageUtl_ReadDWord(
2756 headerBigBlock,
2757 OFFSET_ROOTSTARTBLOCK,
2758 &This->rootStartBlock);
2760 StorageUtl_ReadDWord(
2761 headerBigBlock,
2762 OFFSET_SBDEPOTSTART,
2763 &This->smallBlockDepotStart);
2765 StorageUtl_ReadDWord(
2766 headerBigBlock,
2767 OFFSET_EXTBBDEPOTSTART,
2768 &This->extBigBlockDepotStart);
2770 StorageUtl_ReadDWord(
2771 headerBigBlock,
2772 OFFSET_EXTBBDEPOTCOUNT,
2773 &This->extBigBlockDepotCount);
2775 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2777 StorageUtl_ReadDWord(
2778 headerBigBlock,
2779 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2780 &(This->bigBlockDepotStart[index]));
2784 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2786 if ((1 << 2) == 4)
2788 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2789 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2791 else
2793 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2794 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2798 * Right now, the code is making some assumptions about the size of the
2799 * blocks, just make sure they are what we're expecting.
2801 assert( (This->bigBlockSize==DEF_BIG_BLOCK_SIZE) &&
2802 (This->smallBlockSize==DEF_SMALL_BLOCK_SIZE));
2805 * Release the block.
2807 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2809 hr = S_OK;
2812 return hr;
2815 /******************************************************************************
2816 * Storage32Impl_SaveFileHeader
2818 * This method will save to the file the header, i.e. big block -1.
2820 void StorageImpl_SaveFileHeader(
2821 StorageImpl* This)
2823 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2824 int index;
2825 BOOL success;
2828 * Get a pointer to the big block of data containing the header.
2830 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2833 * If the block read failed, the file is probably new.
2835 if (!success)
2838 * Initialize for all unknown fields.
2840 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2843 * Initialize the magic number.
2845 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2848 * And a bunch of things we don't know what they mean
2850 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2851 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2852 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2853 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2854 StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
2858 * Write the information to the header.
2860 if (headerBigBlock!=0)
2862 StorageUtl_WriteWord(
2863 headerBigBlock,
2864 OFFSET_BIGBLOCKSIZEBITS,
2865 This->bigBlockSizeBits);
2867 StorageUtl_WriteWord(
2868 headerBigBlock,
2869 OFFSET_SMALLBLOCKSIZEBITS,
2870 This->smallBlockSizeBits);
2872 StorageUtl_WriteDWord(
2873 headerBigBlock,
2874 OFFSET_BBDEPOTCOUNT,
2875 This->bigBlockDepotCount);
2877 StorageUtl_WriteDWord(
2878 headerBigBlock,
2879 OFFSET_ROOTSTARTBLOCK,
2880 This->rootStartBlock);
2882 StorageUtl_WriteDWord(
2883 headerBigBlock,
2884 OFFSET_SBDEPOTSTART,
2885 This->smallBlockDepotStart);
2887 StorageUtl_WriteDWord(
2888 headerBigBlock,
2889 OFFSET_EXTBBDEPOTSTART,
2890 This->extBigBlockDepotStart);
2892 StorageUtl_WriteDWord(
2893 headerBigBlock,
2894 OFFSET_EXTBBDEPOTCOUNT,
2895 This->extBigBlockDepotCount);
2897 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2899 StorageUtl_WriteDWord(
2900 headerBigBlock,
2901 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2902 (This->bigBlockDepotStart[index]));
2907 * Write the big block back to the file.
2909 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2912 /******************************************************************************
2913 * Storage32Impl_ReadProperty
2915 * This method will read the specified property from the property chain.
2917 BOOL StorageImpl_ReadProperty(
2918 StorageImpl* This,
2919 ULONG index,
2920 StgProperty* buffer)
2922 BYTE currentProperty[PROPSET_BLOCK_SIZE];
2923 ULARGE_INTEGER offsetInPropSet;
2924 BOOL readSucessful;
2925 ULONG bytesRead;
2927 offsetInPropSet.s.HighPart = 0;
2928 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
2930 readSucessful = BlockChainStream_ReadAt(
2931 This->rootBlockChain,
2932 offsetInPropSet,
2933 PROPSET_BLOCK_SIZE,
2934 currentProperty,
2935 &bytesRead);
2937 if (readSucessful)
2939 memset(buffer->name, 0, sizeof(buffer->name));
2940 memcpy(
2941 buffer->name,
2942 currentProperty+OFFSET_PS_NAME,
2943 PROPERTY_NAME_BUFFER_LEN );
2945 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
2947 StorageUtl_ReadWord(
2948 currentProperty,
2949 OFFSET_PS_NAMELENGTH,
2950 &buffer->sizeOfNameString);
2952 StorageUtl_ReadDWord(
2953 currentProperty,
2954 OFFSET_PS_PREVIOUSPROP,
2955 &buffer->previousProperty);
2957 StorageUtl_ReadDWord(
2958 currentProperty,
2959 OFFSET_PS_NEXTPROP,
2960 &buffer->nextProperty);
2962 StorageUtl_ReadDWord(
2963 currentProperty,
2964 OFFSET_PS_DIRPROP,
2965 &buffer->dirProperty);
2967 StorageUtl_ReadGUID(
2968 currentProperty,
2969 OFFSET_PS_GUID,
2970 &buffer->propertyUniqueID);
2972 StorageUtl_ReadDWord(
2973 currentProperty,
2974 OFFSET_PS_TSS1,
2975 &buffer->timeStampS1);
2977 StorageUtl_ReadDWord(
2978 currentProperty,
2979 OFFSET_PS_TSD1,
2980 &buffer->timeStampD1);
2982 StorageUtl_ReadDWord(
2983 currentProperty,
2984 OFFSET_PS_TSS2,
2985 &buffer->timeStampS2);
2987 StorageUtl_ReadDWord(
2988 currentProperty,
2989 OFFSET_PS_TSD2,
2990 &buffer->timeStampD2);
2992 StorageUtl_ReadDWord(
2993 currentProperty,
2994 OFFSET_PS_STARTBLOCK,
2995 &buffer->startingBlock);
2997 StorageUtl_ReadDWord(
2998 currentProperty,
2999 OFFSET_PS_SIZE,
3000 &buffer->size.s.LowPart);
3002 buffer->size.s.HighPart = 0;
3005 return readSucessful;
3008 /*********************************************************************
3009 * Write the specified property into the property chain
3011 BOOL StorageImpl_WriteProperty(
3012 StorageImpl* This,
3013 ULONG index,
3014 StgProperty* buffer)
3016 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3017 ULARGE_INTEGER offsetInPropSet;
3018 BOOL writeSucessful;
3019 ULONG bytesWritten;
3021 offsetInPropSet.s.HighPart = 0;
3022 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3024 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3026 memcpy(
3027 currentProperty + OFFSET_PS_NAME,
3028 buffer->name,
3029 PROPERTY_NAME_BUFFER_LEN );
3031 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3034 * Reassign the size in case of mistake....
3036 buffer->sizeOfNameString = (lstrlenW(buffer->name)+1) * sizeof(WCHAR);
3038 StorageUtl_WriteWord(
3039 currentProperty,
3040 OFFSET_PS_NAMELENGTH,
3041 buffer->sizeOfNameString);
3043 StorageUtl_WriteDWord(
3044 currentProperty,
3045 OFFSET_PS_PREVIOUSPROP,
3046 buffer->previousProperty);
3048 StorageUtl_WriteDWord(
3049 currentProperty,
3050 OFFSET_PS_NEXTPROP,
3051 buffer->nextProperty);
3053 StorageUtl_WriteDWord(
3054 currentProperty,
3055 OFFSET_PS_DIRPROP,
3056 buffer->dirProperty);
3058 StorageUtl_WriteGUID(
3059 currentProperty,
3060 OFFSET_PS_GUID,
3061 &buffer->propertyUniqueID);
3063 StorageUtl_WriteDWord(
3064 currentProperty,
3065 OFFSET_PS_TSS1,
3066 buffer->timeStampS1);
3068 StorageUtl_WriteDWord(
3069 currentProperty,
3070 OFFSET_PS_TSD1,
3071 buffer->timeStampD1);
3073 StorageUtl_WriteDWord(
3074 currentProperty,
3075 OFFSET_PS_TSS2,
3076 buffer->timeStampS2);
3078 StorageUtl_WriteDWord(
3079 currentProperty,
3080 OFFSET_PS_TSD2,
3081 buffer->timeStampD2);
3083 StorageUtl_WriteDWord(
3084 currentProperty,
3085 OFFSET_PS_STARTBLOCK,
3086 buffer->startingBlock);
3088 StorageUtl_WriteDWord(
3089 currentProperty,
3090 OFFSET_PS_SIZE,
3091 buffer->size.s.LowPart);
3093 writeSucessful = BlockChainStream_WriteAt(This->rootBlockChain,
3094 offsetInPropSet,
3095 PROPSET_BLOCK_SIZE,
3096 currentProperty,
3097 &bytesWritten);
3098 return writeSucessful;
3101 BOOL StorageImpl_ReadBigBlock(
3102 StorageImpl* This,
3103 ULONG blockIndex,
3104 void* buffer)
3106 void* bigBlockBuffer;
3108 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3110 if (bigBlockBuffer!=0)
3112 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3114 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3116 return TRUE;
3119 return FALSE;
3122 BOOL StorageImpl_WriteBigBlock(
3123 StorageImpl* This,
3124 ULONG blockIndex,
3125 void* buffer)
3127 void* bigBlockBuffer;
3129 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3131 if (bigBlockBuffer!=0)
3133 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3135 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3137 return TRUE;
3140 return FALSE;
3143 void* StorageImpl_GetROBigBlock(
3144 StorageImpl* This,
3145 ULONG blockIndex)
3147 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3150 void* StorageImpl_GetBigBlock(
3151 StorageImpl* This,
3152 ULONG blockIndex)
3154 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3157 void StorageImpl_ReleaseBigBlock(
3158 StorageImpl* This,
3159 void* pBigBlock)
3161 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3164 /******************************************************************************
3165 * Storage32Impl_SmallBlocksToBigBlocks
3167 * This method will convert a small block chain to a big block chain.
3168 * The small block chain will be destroyed.
3170 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3171 StorageImpl* This,
3172 SmallBlockChainStream** ppsbChain)
3174 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3175 ULARGE_INTEGER size, offset;
3176 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3177 ULONG propertyIndex;
3178 BOOL successRead, successWrite;
3179 StgProperty chainProperty;
3180 BYTE *buffer;
3181 BlockChainStream *bbTempChain = NULL;
3182 BlockChainStream *bigBlockChain = NULL;
3185 * Create a temporary big block chain that doesn't have
3186 * an associated property. This temporary chain will be
3187 * used to copy data from small blocks to big blocks.
3189 bbTempChain = BlockChainStream_Construct(This,
3190 &bbHeadOfChain,
3191 PROPERTY_NULL);
3194 * Grow the big block chain.
3196 size = SmallBlockChainStream_GetSize(*ppsbChain);
3197 BlockChainStream_SetSize(bbTempChain, size);
3200 * Copy the contents of the small block chain to the big block chain
3201 * by small block size increments.
3203 offset.s.LowPart = 0;
3204 offset.s.HighPart = 0;
3205 cbTotalRead = 0;
3206 cbTotalWritten = 0;
3208 buffer = (BYTE *) malloc(DEF_SMALL_BLOCK_SIZE);
3211 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3212 offset,
3213 DEF_SMALL_BLOCK_SIZE,
3214 buffer,
3215 &cbRead);
3216 cbTotalRead += cbRead;
3218 successWrite = BlockChainStream_WriteAt(bbTempChain,
3219 offset,
3220 cbRead,
3221 buffer,
3222 &cbWritten);
3223 cbTotalWritten += cbWritten;
3225 offset.s.LowPart += This->smallBlockSize;
3227 } while (successRead && successWrite);
3228 free(buffer);
3230 assert(cbTotalRead == cbTotalWritten);
3233 * Destroy the small block chain.
3235 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3236 size.s.HighPart = 0;
3237 size.s.LowPart = 0;
3238 SmallBlockChainStream_SetSize(*ppsbChain, size);
3239 SmallBlockChainStream_Destroy(*ppsbChain);
3240 *ppsbChain = 0;
3243 * Change the property information. This chain is now a big block chain
3244 * and it doesn't reside in the small blocks chain anymore.
3246 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3248 chainProperty.startingBlock = bbHeadOfChain;
3250 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3253 * Destroy the temporary propertyless big block chain.
3254 * Create a new big block chain associated with this property.
3256 BlockChainStream_Destroy(bbTempChain);
3257 bigBlockChain = BlockChainStream_Construct(This,
3258 NULL,
3259 propertyIndex);
3261 return bigBlockChain;
3264 /******************************************************************************
3265 ** Storage32InternalImpl implementation
3268 StorageInternalImpl* StorageInternalImpl_Construct(
3269 StorageImpl* ancestorStorage,
3270 ULONG rootPropertyIndex)
3272 StorageInternalImpl* newStorage;
3275 * Allocate space for the new storage object
3277 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3279 if (newStorage!=0)
3281 memset(newStorage, 0, sizeof(StorageInternalImpl));
3284 * Initialize the virtual function table.
3286 ICOM_VTBL(newStorage) = &Storage32InternalImpl_Vtbl;
3287 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3290 * Keep the ancestor storage pointer and nail a reference to it.
3292 newStorage->ancestorStorage = ancestorStorage;
3293 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3296 * Keep the index of the root property set for this storage,
3298 newStorage->rootPropertySetIndex = rootPropertyIndex;
3300 return newStorage;
3303 return 0;
3306 void StorageInternalImpl_Destroy(
3307 StorageInternalImpl* This)
3309 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3310 HeapFree(GetProcessHeap(), 0, This);
3313 /******************************************************************************
3315 ** Storage32InternalImpl_Commit
3317 ** The non-root storages cannot be opened in transacted mode thus this function
3318 ** does nothing.
3320 HRESULT WINAPI StorageInternalImpl_Commit(
3321 IStorage* iface,
3322 DWORD grfCommitFlags) /* [in] */
3324 return S_OK;
3327 /******************************************************************************
3329 ** Storage32InternalImpl_Revert
3331 ** The non-root storages cannot be opened in transacted mode thus this function
3332 ** does nothing.
3334 HRESULT WINAPI StorageInternalImpl_Revert(
3335 IStorage* iface)
3337 return S_OK;
3340 /******************************************************************************
3341 ** IEnumSTATSTGImpl implementation
3344 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3345 StorageImpl* parentStorage,
3346 ULONG firstPropertyNode)
3348 IEnumSTATSTGImpl* newEnumeration;
3350 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3352 if (newEnumeration!=0)
3355 * Set-up the virtual function table and reference count.
3357 ICOM_VTBL(newEnumeration) = &IEnumSTATSTGImpl_Vtbl;
3358 newEnumeration->ref = 0;
3361 * We want to nail-down the reference to the storage in case the
3362 * enumeration out-lives the storage in the client application.
3364 newEnumeration->parentStorage = parentStorage;
3365 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3367 newEnumeration->firstPropertyNode = firstPropertyNode;
3370 * Initialize the search stack
3372 newEnumeration->stackSize = 0;
3373 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3374 newEnumeration->stackToVisit =
3375 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3378 * Make sure the current node of the iterator is the first one.
3380 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3383 return newEnumeration;
3386 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3388 IStorage_Release((IStorage*)This->parentStorage);
3389 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3390 HeapFree(GetProcessHeap(), 0, This);
3393 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3394 IEnumSTATSTG* iface,
3395 REFIID riid,
3396 void** ppvObject)
3398 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3401 * Perform a sanity check on the parameters.
3403 if (ppvObject==0)
3404 return E_INVALIDARG;
3407 * Initialize the return parameter.
3409 *ppvObject = 0;
3412 * Compare the riid with the interface IDs implemented by this object.
3414 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3416 *ppvObject = (IEnumSTATSTG*)This;
3418 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3420 *ppvObject = (IEnumSTATSTG*)This;
3424 * Check that we obtained an interface.
3426 if ((*ppvObject)==0)
3427 return E_NOINTERFACE;
3430 * Query Interface always increases the reference count by one when it is
3431 * successful
3433 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3435 return S_OK;
3438 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3439 IEnumSTATSTG* iface)
3441 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3443 This->ref++;
3444 return This->ref;
3447 ULONG WINAPI IEnumSTATSTGImpl_Release(
3448 IEnumSTATSTG* iface)
3450 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3452 ULONG newRef;
3454 This->ref--;
3455 newRef = This->ref;
3458 * If the reference count goes down to 0, perform suicide.
3460 if (newRef==0)
3462 IEnumSTATSTGImpl_Destroy(This);
3465 return newRef;;
3468 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3469 IEnumSTATSTG* iface,
3470 ULONG celt,
3471 STATSTG* rgelt,
3472 ULONG* pceltFetched)
3474 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3476 StgProperty currentProperty;
3477 STATSTG* currentReturnStruct = rgelt;
3478 ULONG objectFetched = 0;
3479 ULONG currentSearchNode;
3482 * Perform a sanity check on the parameters.
3484 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3485 return E_INVALIDARG;
3488 * To avoid the special case, get another pointer to a ULONG value if
3489 * the caller didn't supply one.
3491 if (pceltFetched==0)
3492 pceltFetched = &objectFetched;
3495 * Start the iteration, we will iterate until we hit the end of the
3496 * linked list or until we hit the number of items to iterate through
3498 *pceltFetched = 0;
3501 * Start with the node at the top of the stack.
3503 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3505 while ( ( *pceltFetched < celt) &&
3506 ( currentSearchNode!=PROPERTY_NULL) )
3509 * Remove the top node from the stack
3511 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3514 * Read the property from the storage.
3516 StorageImpl_ReadProperty(This->parentStorage,
3517 currentSearchNode,
3518 &currentProperty);
3521 * Copy the information to the return buffer.
3523 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3524 &currentProperty,
3525 STATFLAG_DEFAULT);
3528 * Step to the next item in the iteration
3530 (*pceltFetched)++;
3531 currentReturnStruct++;
3534 * Push the next search node in the search stack.
3536 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3539 * continue the iteration.
3541 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3544 if (*pceltFetched == celt)
3545 return S_OK;
3547 return S_FALSE;
3551 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3552 IEnumSTATSTG* iface,
3553 ULONG celt)
3555 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3557 StgProperty currentProperty;
3558 ULONG objectFetched = 0;
3559 ULONG currentSearchNode;
3562 * Start with the node at the top of the stack.
3564 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3566 while ( (objectFetched < celt) &&
3567 (currentSearchNode!=PROPERTY_NULL) )
3570 * Remove the top node from the stack
3572 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3575 * Read the property from the storage.
3577 StorageImpl_ReadProperty(This->parentStorage,
3578 currentSearchNode,
3579 &currentProperty);
3582 * Step to the next item in the iteration
3584 objectFetched++;
3587 * Push the next search node in the search stack.
3589 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3592 * continue the iteration.
3594 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3597 if (objectFetched == celt)
3598 return S_OK;
3600 return S_FALSE;
3603 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3604 IEnumSTATSTG* iface)
3606 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3608 StgProperty rootProperty;
3609 BOOL readSucessful;
3612 * Re-initialize the search stack to an empty stack
3614 This->stackSize = 0;
3617 * Read the root property from the storage.
3619 readSucessful = StorageImpl_ReadProperty(
3620 This->parentStorage,
3621 This->firstPropertyNode,
3622 &rootProperty);
3624 if (readSucessful)
3626 assert(rootProperty.sizeOfNameString!=0);
3629 * Push the search node in the search stack.
3631 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3634 return S_OK;
3637 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3638 IEnumSTATSTG* iface,
3639 IEnumSTATSTG** ppenum)
3641 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3643 IEnumSTATSTGImpl* newClone;
3646 * Perform a sanity check on the parameters.
3648 if (ppenum==0)
3649 return E_INVALIDARG;
3651 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3652 This->firstPropertyNode);
3656 * The new clone enumeration must point to the same current node as
3657 * the ole one.
3659 newClone->stackSize = This->stackSize ;
3660 newClone->stackMaxSize = This->stackMaxSize ;
3661 newClone->stackToVisit =
3662 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3664 memcpy(
3665 newClone->stackToVisit,
3666 This->stackToVisit,
3667 sizeof(ULONG) * newClone->stackSize);
3669 *ppenum = (IEnumSTATSTG*)newClone;
3672 * Don't forget to nail down a reference to the clone before
3673 * returning it.
3675 IEnumSTATSTGImpl_AddRef(*ppenum);
3677 return S_OK;
3680 INT IEnumSTATSTGImpl_FindParentProperty(
3681 IEnumSTATSTGImpl *This,
3682 ULONG childProperty,
3683 StgProperty *currentProperty,
3684 ULONG *thisNodeId)
3686 ULONG currentSearchNode;
3687 ULONG foundNode;
3690 * To avoid the special case, get another pointer to a ULONG value if
3691 * the caller didn't supply one.
3693 if (thisNodeId==0)
3694 thisNodeId = &foundNode;
3697 * Start with the node at the top of the stack.
3699 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3702 while (currentSearchNode!=PROPERTY_NULL)
3705 * Store the current node in the returned parameters
3707 *thisNodeId = currentSearchNode;
3710 * Remove the top node from the stack
3712 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3715 * Read the property from the storage.
3717 StorageImpl_ReadProperty(
3718 This->parentStorage,
3719 currentSearchNode,
3720 currentProperty);
3722 if (currentProperty->previousProperty == childProperty)
3723 return PROPERTY_RELATION_PREVIOUS;
3725 else if (currentProperty->nextProperty == childProperty)
3726 return PROPERTY_RELATION_NEXT;
3728 else if (currentProperty->dirProperty == childProperty)
3729 return PROPERTY_RELATION_DIR;
3732 * Push the next search node in the search stack.
3734 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3737 * continue the iteration.
3739 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3742 return PROPERTY_NULL;
3745 ULONG IEnumSTATSTGImpl_FindProperty(
3746 IEnumSTATSTGImpl* This,
3747 const OLECHAR* lpszPropName,
3748 StgProperty* currentProperty)
3750 ULONG currentSearchNode;
3753 * Start with the node at the top of the stack.
3755 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3757 while (currentSearchNode!=PROPERTY_NULL)
3760 * Remove the top node from the stack
3762 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3765 * Read the property from the storage.
3767 StorageImpl_ReadProperty(This->parentStorage,
3768 currentSearchNode,
3769 currentProperty);
3771 if ( propertyNameCmp(
3772 (OLECHAR*)currentProperty->name,
3773 (OLECHAR*)lpszPropName) == 0)
3774 return currentSearchNode;
3777 * Push the next search node in the search stack.
3779 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3782 * continue the iteration.
3784 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3787 return PROPERTY_NULL;
3790 void IEnumSTATSTGImpl_PushSearchNode(
3791 IEnumSTATSTGImpl* This,
3792 ULONG nodeToPush)
3794 StgProperty rootProperty;
3795 BOOL readSucessful;
3798 * First, make sure we're not trying to push an unexisting node.
3800 if (nodeToPush==PROPERTY_NULL)
3801 return;
3804 * First push the node to the stack
3806 if (This->stackSize == This->stackMaxSize)
3808 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3810 This->stackToVisit = HeapReAlloc(
3811 GetProcessHeap(),
3813 This->stackToVisit,
3814 sizeof(ULONG) * This->stackMaxSize);
3817 This->stackToVisit[This->stackSize] = nodeToPush;
3818 This->stackSize++;
3821 * Read the root property from the storage.
3823 readSucessful = StorageImpl_ReadProperty(
3824 This->parentStorage,
3825 nodeToPush,
3826 &rootProperty);
3828 if (readSucessful)
3830 assert(rootProperty.sizeOfNameString!=0);
3833 * Push the previous search node in the search stack.
3835 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3839 ULONG IEnumSTATSTGImpl_PopSearchNode(
3840 IEnumSTATSTGImpl* This,
3841 BOOL remove)
3843 ULONG topNode;
3845 if (This->stackSize == 0)
3846 return PROPERTY_NULL;
3848 topNode = This->stackToVisit[This->stackSize-1];
3850 if (remove)
3851 This->stackSize--;
3853 return topNode;
3856 /******************************************************************************
3857 ** StorageUtl implementation
3860 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
3862 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
3865 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
3867 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
3870 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
3872 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
3875 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
3877 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
3880 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
3882 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
3883 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
3884 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
3886 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
3889 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
3891 StorageUtl_WriteDWord(buffer, offset, value->Data1);
3892 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
3893 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
3895 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
3898 void StorageUtl_CopyPropertyToSTATSTG(
3899 STATSTG* destination,
3900 StgProperty* source,
3901 int statFlags)
3904 * The copy of the string occurs only when the flag is not set
3906 if ((statFlags & STATFLAG_NONAME) != 0)
3908 destination->pwcsName = 0;
3910 else
3912 destination->pwcsName =
3913 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
3915 lstrcpyW((LPWSTR)destination->pwcsName, source->name);
3918 switch (source->propertyType)
3920 case PROPTYPE_STORAGE:
3921 case PROPTYPE_ROOT:
3922 destination->type = STGTY_STORAGE;
3923 break;
3924 case PROPTYPE_STREAM:
3925 destination->type = STGTY_STREAM;
3926 break;
3927 default:
3928 destination->type = STGTY_STREAM;
3929 break;
3932 destination->cbSize = source->size;
3934 currentReturnStruct->mtime = {0}; TODO
3935 currentReturnStruct->ctime = {0};
3936 currentReturnStruct->atime = {0};
3938 destination->grfMode = 0;
3939 destination->grfLocksSupported = 0;
3940 destination->clsid = source->propertyUniqueID;
3941 destination->grfStateBits = 0;
3942 destination->reserved = 0;
3945 /******************************************************************************
3946 ** BlockChainStream implementation
3949 BlockChainStream* BlockChainStream_Construct(
3950 StorageImpl* parentStorage,
3951 ULONG* headOfStreamPlaceHolder,
3952 ULONG propertyIndex)
3954 BlockChainStream* newStream;
3955 ULONG blockIndex;
3957 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
3959 newStream->parentStorage = parentStorage;
3960 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
3961 newStream->ownerPropertyIndex = propertyIndex;
3962 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
3963 newStream->tailIndex = BLOCK_END_OF_CHAIN;
3964 newStream->numBlocks = 0;
3966 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
3968 while (blockIndex != BLOCK_END_OF_CHAIN)
3970 newStream->numBlocks++;
3971 newStream->tailIndex = blockIndex;
3973 blockIndex = StorageImpl_GetNextBlockInChain(
3974 parentStorage,
3975 blockIndex);
3978 return newStream;
3981 void BlockChainStream_Destroy(BlockChainStream* This)
3983 HeapFree(GetProcessHeap(), 0, This);
3986 /******************************************************************************
3987 * BlockChainStream_GetHeadOfChain
3989 * Returns the head of this stream chain.
3990 * Some special chains don't have properties, their heads are kept in
3991 * This->headOfStreamPlaceHolder.
3994 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
3996 StgProperty chainProperty;
3997 BOOL readSucessful;
3999 if (This->headOfStreamPlaceHolder != 0)
4000 return *(This->headOfStreamPlaceHolder);
4002 if (This->ownerPropertyIndex != PROPERTY_NULL)
4004 readSucessful = StorageImpl_ReadProperty(
4005 This->parentStorage,
4006 This->ownerPropertyIndex,
4007 &chainProperty);
4009 if (readSucessful)
4011 return chainProperty.startingBlock;
4015 return BLOCK_END_OF_CHAIN;
4018 /******************************************************************************
4019 * BlockChainStream_GetCount
4021 * Returns the number of blocks that comprises this chain.
4022 * This is not the size of the stream as the last block may not be full!
4025 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4027 ULONG blockIndex;
4028 ULONG count = 0;
4030 blockIndex = BlockChainStream_GetHeadOfChain(This);
4032 while (blockIndex != BLOCK_END_OF_CHAIN)
4034 count++;
4036 blockIndex = StorageImpl_GetNextBlockInChain(
4037 This->parentStorage,
4038 blockIndex);
4041 return count;
4044 /******************************************************************************
4045 * BlockChainStream_ReadAt
4047 * Reads a specified number of bytes from this chain at the specified offset.
4048 * bytesRead may be NULL.
4049 * Failure will be returned if the specified number of bytes has not been read.
4051 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4052 ULARGE_INTEGER offset,
4053 ULONG size,
4054 void* buffer,
4055 ULONG* bytesRead)
4057 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4058 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4059 ULONG bytesToReadInBuffer;
4060 ULONG blockIndex;
4061 BYTE* bufferWalker;
4062 BYTE* bigBlockBuffer;
4065 * Find the first block in the stream that contains part of the buffer.
4067 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4068 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4069 (blockNoInSequence < This->lastBlockNoInSequence) )
4071 blockIndex = BlockChainStream_GetHeadOfChain(This);
4072 This->lastBlockNoInSequence = blockNoInSequence;
4074 else
4076 ULONG temp = blockNoInSequence;
4078 blockIndex = This->lastBlockNoInSequenceIndex;
4079 blockNoInSequence -= This->lastBlockNoInSequence;
4080 This->lastBlockNoInSequence = temp;
4083 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4085 blockIndex =
4086 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4088 blockNoInSequence--;
4091 This->lastBlockNoInSequenceIndex = blockIndex;
4094 * Start reading the buffer.
4096 *bytesRead = 0;
4097 bufferWalker = buffer;
4099 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4102 * Calculate how many bytes we can copy from this big block.
4104 bytesToReadInBuffer =
4105 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4108 * Copy those bytes to the buffer
4110 bigBlockBuffer =
4111 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4113 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4115 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4118 * Step to the next big block.
4120 blockIndex =
4121 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4123 bufferWalker += bytesToReadInBuffer;
4124 size -= bytesToReadInBuffer;
4125 *bytesRead += bytesToReadInBuffer;
4126 offsetInBlock = 0; /* There is no offset on the next block */
4130 return (size == 0);
4133 /******************************************************************************
4134 * BlockChainStream_WriteAt
4136 * Writes the specified number of bytes to this chain at the specified offset.
4137 * bytesWritten may be NULL.
4138 * Will fail if not all specified number of bytes have been written.
4140 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4141 ULARGE_INTEGER offset,
4142 ULONG size,
4143 const void* buffer,
4144 ULONG* bytesWritten)
4146 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4147 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4148 ULONG bytesToWrite;
4149 ULONG blockIndex;
4150 BYTE* bufferWalker;
4151 BYTE* bigBlockBuffer;
4154 * Find the first block in the stream that contains part of the buffer.
4156 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4157 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4158 (blockNoInSequence < This->lastBlockNoInSequence) )
4160 blockIndex = BlockChainStream_GetHeadOfChain(This);
4161 This->lastBlockNoInSequence = blockNoInSequence;
4163 else
4165 ULONG temp = blockNoInSequence;
4167 blockIndex = This->lastBlockNoInSequenceIndex;
4168 blockNoInSequence -= This->lastBlockNoInSequence;
4169 This->lastBlockNoInSequence = temp;
4172 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4174 blockIndex =
4175 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4177 blockNoInSequence--;
4180 This->lastBlockNoInSequenceIndex = blockIndex;
4183 * Here, I'm casting away the constness on the buffer variable
4184 * This is OK since we don't intend to modify that buffer.
4186 *bytesWritten = 0;
4187 bufferWalker = (BYTE*)buffer;
4189 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4192 * Calculate how many bytes we can copy from this big block.
4194 bytesToWrite =
4195 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4198 * Copy those bytes to the buffer
4200 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4202 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4204 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4207 * Step to the next big block.
4209 blockIndex =
4210 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4212 bufferWalker += bytesToWrite;
4213 size -= bytesToWrite;
4214 *bytesWritten += bytesToWrite;
4215 offsetInBlock = 0; /* There is no offset on the next block */
4218 return (size == 0);
4221 /******************************************************************************
4222 * BlockChainStream_Shrink
4224 * Shrinks this chain in the big block depot.
4226 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4227 ULARGE_INTEGER newSize)
4229 ULONG blockIndex, extraBlock;
4230 ULONG numBlocks;
4231 ULONG count = 1;
4234 * Reset the last accessed block cache.
4236 This->lastBlockNoInSequence = 0xFFFFFFFF;
4237 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4240 * Figure out how many blocks are needed to contain the new size
4242 numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4244 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4245 numBlocks++;
4247 blockIndex = BlockChainStream_GetHeadOfChain(This);
4250 * Go to the new end of chain
4252 while (count < numBlocks)
4254 blockIndex =
4255 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4257 count++;
4260 /* Get the next block before marking the new end */
4261 extraBlock =
4262 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4264 /* Mark the new end of chain */
4265 StorageImpl_SetNextBlockInChain(
4266 This->parentStorage,
4267 blockIndex,
4268 BLOCK_END_OF_CHAIN);
4270 This->tailIndex = blockIndex;
4271 This->numBlocks = numBlocks;
4274 * Mark the extra blocks as free
4276 while (extraBlock != BLOCK_END_OF_CHAIN)
4278 blockIndex =
4279 StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4281 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4282 extraBlock = blockIndex;
4285 return TRUE;
4288 /******************************************************************************
4289 * BlockChainStream_Enlarge
4291 * Grows this chain in the big block depot.
4293 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4294 ULARGE_INTEGER newSize)
4296 ULONG blockIndex, currentBlock;
4297 ULONG newNumBlocks;
4298 ULONG oldNumBlocks = 0;
4300 blockIndex = BlockChainStream_GetHeadOfChain(This);
4303 * Empty chain. Create the head.
4305 if (blockIndex == BLOCK_END_OF_CHAIN)
4307 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4308 StorageImpl_SetNextBlockInChain(This->parentStorage,
4309 blockIndex,
4310 BLOCK_END_OF_CHAIN);
4312 if (This->headOfStreamPlaceHolder != 0)
4314 *(This->headOfStreamPlaceHolder) = blockIndex;
4316 else
4318 StgProperty chainProp;
4319 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4321 StorageImpl_ReadProperty(
4322 This->parentStorage,
4323 This->ownerPropertyIndex,
4324 &chainProp);
4326 chainProp.startingBlock = blockIndex;
4328 StorageImpl_WriteProperty(
4329 This->parentStorage,
4330 This->ownerPropertyIndex,
4331 &chainProp);
4334 This->tailIndex = blockIndex;
4335 This->numBlocks = 1;
4339 * Figure out how many blocks are needed to contain this stream
4341 newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4343 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4344 newNumBlocks++;
4347 * Go to the current end of chain
4349 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4351 currentBlock = blockIndex;
4353 while (blockIndex != BLOCK_END_OF_CHAIN)
4355 This->numBlocks++;
4356 currentBlock = blockIndex;
4358 blockIndex =
4359 StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4362 This->tailIndex = currentBlock;
4365 currentBlock = This->tailIndex;
4366 oldNumBlocks = This->numBlocks;
4369 * Add new blocks to the chain
4371 if (oldNumBlocks < newNumBlocks)
4373 while (oldNumBlocks < newNumBlocks)
4375 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4377 StorageImpl_SetNextBlockInChain(
4378 This->parentStorage,
4379 currentBlock,
4380 blockIndex);
4382 StorageImpl_SetNextBlockInChain(
4383 This->parentStorage,
4384 blockIndex,
4385 BLOCK_END_OF_CHAIN);
4387 currentBlock = blockIndex;
4388 oldNumBlocks++;
4391 This->tailIndex = blockIndex;
4392 This->numBlocks = newNumBlocks;
4395 return TRUE;
4398 /******************************************************************************
4399 * BlockChainStream_SetSize
4401 * Sets the size of this stream. The big block depot will be updated.
4402 * The file will grow if we grow the chain.
4404 * TODO: Free the actual blocks in the file when we shrink the chain.
4405 * Currently, the blocks are still in the file. So the file size
4406 * doesn't shrink even if we shrink streams.
4408 BOOL BlockChainStream_SetSize(
4409 BlockChainStream* This,
4410 ULARGE_INTEGER newSize)
4412 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4414 if (newSize.s.LowPart == size.s.LowPart)
4415 return TRUE;
4417 if (newSize.s.LowPart < size.s.LowPart)
4419 BlockChainStream_Shrink(This, newSize);
4421 else
4423 ULARGE_INTEGER fileSize =
4424 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4426 ULONG diff = newSize.s.LowPart - size.s.LowPart;
4429 * Make sure the file stays a multiple of blocksize
4431 if ((diff % This->parentStorage->bigBlockSize) != 0)
4432 diff += (This->parentStorage->bigBlockSize -
4433 (diff % This->parentStorage->bigBlockSize) );
4435 fileSize.s.LowPart += diff;
4436 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4438 BlockChainStream_Enlarge(This, newSize);
4441 return TRUE;
4444 /******************************************************************************
4445 * BlockChainStream_GetSize
4447 * Returns the size of this chain.
4448 * Will return the block count if this chain doesn't have a property.
4450 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4452 StgProperty chainProperty;
4454 if(This->headOfStreamPlaceHolder == NULL)
4457 * This chain is a data stream read the property and return
4458 * the appropriate size
4460 StorageImpl_ReadProperty(
4461 This->parentStorage,
4462 This->ownerPropertyIndex,
4463 &chainProperty);
4465 return chainProperty.size;
4467 else
4470 * this chain is a chain that does not have a property, figure out the
4471 * size by making the product number of used blocks times the
4472 * size of them
4474 ULARGE_INTEGER result;
4475 result.s.HighPart = 0;
4477 result.s.LowPart =
4478 BlockChainStream_GetCount(This) *
4479 This->parentStorage->bigBlockSize;
4481 return result;
4485 /******************************************************************************
4486 ** SmallBlockChainStream implementation
4489 SmallBlockChainStream* SmallBlockChainStream_Construct(
4490 StorageImpl* parentStorage,
4491 ULONG propertyIndex)
4493 SmallBlockChainStream* newStream;
4495 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4497 newStream->parentStorage = parentStorage;
4498 newStream->ownerPropertyIndex = propertyIndex;
4500 return newStream;
4503 void SmallBlockChainStream_Destroy(
4504 SmallBlockChainStream* This)
4506 HeapFree(GetProcessHeap(), 0, This);
4509 /******************************************************************************
4510 * SmallBlockChainStream_GetHeadOfChain
4512 * Returns the head of this chain of small blocks.
4514 ULONG SmallBlockChainStream_GetHeadOfChain(
4515 SmallBlockChainStream* This)
4517 StgProperty chainProperty;
4518 BOOL readSucessful;
4520 if (This->ownerPropertyIndex)
4522 readSucessful = StorageImpl_ReadProperty(
4523 This->parentStorage,
4524 This->ownerPropertyIndex,
4525 &chainProperty);
4527 if (readSucessful)
4529 return chainProperty.startingBlock;
4534 return BLOCK_END_OF_CHAIN;
4537 /******************************************************************************
4538 * SmallBlockChainStream_GetNextBlockInChain
4540 * Returns the index of the next small block in this chain.
4542 * Return Values:
4543 * - BLOCK_END_OF_CHAIN: end of this chain
4544 * - BLOCK_UNUSED: small block 'blockIndex' is free
4546 ULONG SmallBlockChainStream_GetNextBlockInChain(
4547 SmallBlockChainStream* This,
4548 ULONG blockIndex)
4550 ULARGE_INTEGER offsetOfBlockInDepot;
4551 DWORD buffer;
4552 ULONG nextBlockInChain = BLOCK_END_OF_CHAIN;
4553 ULONG bytesRead;
4554 BOOL success;
4556 offsetOfBlockInDepot.s.HighPart = 0;
4557 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4560 * Read those bytes in the buffer from the small block file.
4562 success = BlockChainStream_ReadAt(
4563 This->parentStorage->smallBlockDepotChain,
4564 offsetOfBlockInDepot,
4565 sizeof(DWORD),
4566 &buffer,
4567 &bytesRead);
4569 if (success)
4571 StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4574 return nextBlockInChain;
4577 /******************************************************************************
4578 * SmallBlockChainStream_SetNextBlockInChain
4580 * Writes the index of the next block of the specified block in the small
4581 * block depot.
4582 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4583 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4585 void SmallBlockChainStream_SetNextBlockInChain(
4586 SmallBlockChainStream* This,
4587 ULONG blockIndex,
4588 ULONG nextBlock)
4590 ULARGE_INTEGER offsetOfBlockInDepot;
4591 DWORD buffer;
4592 ULONG bytesWritten;
4594 offsetOfBlockInDepot.s.HighPart = 0;
4595 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4597 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4600 * Read those bytes in the buffer from the small block file.
4602 BlockChainStream_WriteAt(
4603 This->parentStorage->smallBlockDepotChain,
4604 offsetOfBlockInDepot,
4605 sizeof(DWORD),
4606 &buffer,
4607 &bytesWritten);
4610 /******************************************************************************
4611 * SmallBlockChainStream_FreeBlock
4613 * Flag small block 'blockIndex' as free in the small block depot.
4615 void SmallBlockChainStream_FreeBlock(
4616 SmallBlockChainStream* This,
4617 ULONG blockIndex)
4619 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4622 /******************************************************************************
4623 * SmallBlockChainStream_GetNextFreeBlock
4625 * Returns the index of a free small block. The small block depot will be
4626 * enlarged if necessary. The small block chain will also be enlarged if
4627 * necessary.
4629 ULONG SmallBlockChainStream_GetNextFreeBlock(
4630 SmallBlockChainStream* This)
4632 ULARGE_INTEGER offsetOfBlockInDepot;
4633 DWORD buffer;
4634 ULONG bytesRead;
4635 ULONG blockIndex = 0;
4636 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4637 BOOL success = TRUE;
4638 ULONG smallBlocksPerBigBlock;
4640 offsetOfBlockInDepot.s.HighPart = 0;
4643 * Scan the small block depot for a free block
4645 while (nextBlockIndex != BLOCK_UNUSED)
4647 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4649 success = BlockChainStream_ReadAt(
4650 This->parentStorage->smallBlockDepotChain,
4651 offsetOfBlockInDepot,
4652 sizeof(DWORD),
4653 &buffer,
4654 &bytesRead);
4657 * If we run out of space for the small block depot, enlarge it
4659 if (success)
4661 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4663 if (nextBlockIndex != BLOCK_UNUSED)
4664 blockIndex++;
4666 else
4668 ULONG count =
4669 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4671 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4672 ULONG nextBlock, newsbdIndex;
4673 BYTE* smallBlockDepot;
4675 nextBlock = sbdIndex;
4676 while (nextBlock != BLOCK_END_OF_CHAIN)
4678 sbdIndex = nextBlock;
4679 nextBlock =
4680 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4683 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4684 if (sbdIndex != BLOCK_END_OF_CHAIN)
4685 StorageImpl_SetNextBlockInChain(
4686 This->parentStorage,
4687 sbdIndex,
4688 newsbdIndex);
4690 StorageImpl_SetNextBlockInChain(
4691 This->parentStorage,
4692 newsbdIndex,
4693 BLOCK_END_OF_CHAIN);
4696 * Initialize all the small blocks to free
4698 smallBlockDepot =
4699 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4701 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4702 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4704 if (count == 0)
4707 * We have just created the small block depot.
4709 StgProperty rootProp;
4710 ULONG sbStartIndex;
4713 * Save it in the header
4715 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4716 StorageImpl_SaveFileHeader(This->parentStorage);
4719 * And allocate the first big block that will contain small blocks
4721 sbStartIndex =
4722 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4724 StorageImpl_SetNextBlockInChain(
4725 This->parentStorage,
4726 sbStartIndex,
4727 BLOCK_END_OF_CHAIN);
4729 StorageImpl_ReadProperty(
4730 This->parentStorage,
4731 This->parentStorage->rootPropertySetIndex,
4732 &rootProp);
4734 rootProp.startingBlock = sbStartIndex;
4735 rootProp.size.s.HighPart = 0;
4736 rootProp.size.s.LowPart = This->parentStorage->bigBlockSize;
4738 StorageImpl_WriteProperty(
4739 This->parentStorage,
4740 This->parentStorage->rootPropertySetIndex,
4741 &rootProp);
4746 smallBlocksPerBigBlock =
4747 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4750 * Verify if we have to allocate big blocks to contain small blocks
4752 if (blockIndex % smallBlocksPerBigBlock == 0)
4754 StgProperty rootProp;
4755 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4757 StorageImpl_ReadProperty(
4758 This->parentStorage,
4759 This->parentStorage->rootPropertySetIndex,
4760 &rootProp);
4762 if (rootProp.size.s.LowPart <
4763 (blocksRequired * This->parentStorage->bigBlockSize))
4765 rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4767 BlockChainStream_SetSize(
4768 This->parentStorage->smallBlockRootChain,
4769 rootProp.size);
4771 StorageImpl_WriteProperty(
4772 This->parentStorage,
4773 This->parentStorage->rootPropertySetIndex,
4774 &rootProp);
4778 return blockIndex;
4781 /******************************************************************************
4782 * SmallBlockChainStream_ReadAt
4784 * Reads a specified number of bytes from this chain at the specified offset.
4785 * bytesRead may be NULL.
4786 * Failure will be returned if the specified number of bytes has not been read.
4788 BOOL SmallBlockChainStream_ReadAt(
4789 SmallBlockChainStream* This,
4790 ULARGE_INTEGER offset,
4791 ULONG size,
4792 void* buffer,
4793 ULONG* bytesRead)
4795 ULARGE_INTEGER offsetInBigBlockFile;
4796 ULONG blockNoInSequence =
4797 offset.s.LowPart / This->parentStorage->smallBlockSize;
4799 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4800 ULONG bytesToReadInBuffer;
4801 ULONG blockIndex;
4802 ULONG bytesReadFromBigBlockFile;
4803 BYTE* bufferWalker;
4806 * This should never happen on a small block file.
4808 assert(offset.s.HighPart==0);
4811 * Find the first block in the stream that contains part of the buffer.
4813 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4815 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4817 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4819 blockNoInSequence--;
4823 * Start reading the buffer.
4825 *bytesRead = 0;
4826 bufferWalker = buffer;
4828 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4831 * Calculate how many bytes we can copy from this small block.
4833 bytesToReadInBuffer =
4834 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4837 * Calculate the offset of the small block in the small block file.
4839 offsetInBigBlockFile.s.HighPart = 0;
4840 offsetInBigBlockFile.s.LowPart =
4841 blockIndex * This->parentStorage->smallBlockSize;
4843 offsetInBigBlockFile.s.LowPart += offsetInBlock;
4846 * Read those bytes in the buffer from the small block file.
4848 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
4849 offsetInBigBlockFile,
4850 bytesToReadInBuffer,
4851 bufferWalker,
4852 &bytesReadFromBigBlockFile);
4854 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
4857 * Step to the next big block.
4859 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4860 bufferWalker += bytesToReadInBuffer;
4861 size -= bytesToReadInBuffer;
4862 *bytesRead += bytesToReadInBuffer;
4863 offsetInBlock = 0; /* There is no offset on the next block */
4866 return (size == 0);
4869 /******************************************************************************
4870 * SmallBlockChainStream_WriteAt
4872 * Writes the specified number of bytes to this chain at the specified offset.
4873 * bytesWritten may be NULL.
4874 * Will fail if not all specified number of bytes have been written.
4876 BOOL SmallBlockChainStream_WriteAt(
4877 SmallBlockChainStream* This,
4878 ULARGE_INTEGER offset,
4879 ULONG size,
4880 const void* buffer,
4881 ULONG* bytesWritten)
4883 ULARGE_INTEGER offsetInBigBlockFile;
4884 ULONG blockNoInSequence =
4885 offset.s.LowPart / This->parentStorage->smallBlockSize;
4887 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4888 ULONG bytesToWriteInBuffer;
4889 ULONG blockIndex;
4890 ULONG bytesWrittenFromBigBlockFile;
4891 BYTE* bufferWalker;
4894 * This should never happen on a small block file.
4896 assert(offset.s.HighPart==0);
4899 * Find the first block in the stream that contains part of the buffer.
4901 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4903 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4905 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4907 blockNoInSequence--;
4911 * Start writing the buffer.
4913 * Here, I'm casting away the constness on the buffer variable
4914 * This is OK since we don't intend to modify that buffer.
4916 *bytesWritten = 0;
4917 bufferWalker = (BYTE*)buffer;
4918 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4921 * Calculate how many bytes we can copy to this small block.
4923 bytesToWriteInBuffer =
4924 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4927 * Calculate the offset of the small block in the small block file.
4929 offsetInBigBlockFile.s.HighPart = 0;
4930 offsetInBigBlockFile.s.LowPart =
4931 blockIndex * This->parentStorage->smallBlockSize;
4933 offsetInBigBlockFile.s.LowPart += offsetInBlock;
4936 * Write those bytes in the buffer to the small block file.
4938 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
4939 offsetInBigBlockFile,
4940 bytesToWriteInBuffer,
4941 bufferWalker,
4942 &bytesWrittenFromBigBlockFile);
4944 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
4947 * Step to the next big block.
4949 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4950 bufferWalker += bytesToWriteInBuffer;
4951 size -= bytesToWriteInBuffer;
4952 *bytesWritten += bytesToWriteInBuffer;
4953 offsetInBlock = 0; /* There is no offset on the next block */
4956 return (size == 0);
4959 /******************************************************************************
4960 * SmallBlockChainStream_Shrink
4962 * Shrinks this chain in the small block depot.
4964 BOOL SmallBlockChainStream_Shrink(
4965 SmallBlockChainStream* This,
4966 ULARGE_INTEGER newSize)
4968 ULONG blockIndex, extraBlock;
4969 ULONG numBlocks;
4970 ULONG count = 0;
4972 numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
4974 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
4975 numBlocks++;
4977 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4980 * Go to the new end of chain
4982 while (count < numBlocks)
4984 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4985 count++;
4989 * If the count is 0, we have a special case, the head of the chain was
4990 * just freed.
4992 if (count == 0)
4994 StgProperty chainProp;
4996 StorageImpl_ReadProperty(This->parentStorage,
4997 This->ownerPropertyIndex,
4998 &chainProp);
5000 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5002 StorageImpl_WriteProperty(This->parentStorage,
5003 This->ownerPropertyIndex,
5004 &chainProp);
5007 * We start freeing the chain at the head block.
5009 extraBlock = blockIndex;
5011 else
5013 /* Get the next block before marking the new end */
5014 extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5016 /* Mark the new end of chain */
5017 SmallBlockChainStream_SetNextBlockInChain(
5018 This,
5019 blockIndex,
5020 BLOCK_END_OF_CHAIN);
5024 * Mark the extra blocks as free
5026 while (extraBlock != BLOCK_END_OF_CHAIN)
5028 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
5029 SmallBlockChainStream_FreeBlock(This, extraBlock);
5030 extraBlock = blockIndex;
5033 return TRUE;
5036 /******************************************************************************
5037 * SmallBlockChainStream_Enlarge
5039 * Grows this chain in the small block depot.
5041 BOOL SmallBlockChainStream_Enlarge(
5042 SmallBlockChainStream* This,
5043 ULARGE_INTEGER newSize)
5045 ULONG blockIndex, currentBlock;
5046 ULONG newNumBlocks;
5047 ULONG oldNumBlocks = 0;
5049 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5052 * Empty chain
5054 if (blockIndex == BLOCK_END_OF_CHAIN)
5057 StgProperty chainProp;
5059 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5060 &chainProp);
5062 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5064 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5065 &chainProp);
5067 blockIndex = chainProp.startingBlock;
5068 SmallBlockChainStream_SetNextBlockInChain(
5069 This,
5070 blockIndex,
5071 BLOCK_END_OF_CHAIN);
5074 currentBlock = blockIndex;
5077 * Figure out how many blocks are needed to contain this stream
5079 newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5081 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5082 newNumBlocks++;
5085 * Go to the current end of chain
5087 while (blockIndex != BLOCK_END_OF_CHAIN)
5089 oldNumBlocks++;
5090 currentBlock = blockIndex;
5091 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5095 * Add new blocks to the chain
5097 while (oldNumBlocks < newNumBlocks)
5099 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5100 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5102 SmallBlockChainStream_SetNextBlockInChain(
5103 This,
5104 blockIndex,
5105 BLOCK_END_OF_CHAIN);
5107 currentBlock = blockIndex;
5108 oldNumBlocks++;
5111 return TRUE;
5114 /******************************************************************************
5115 * SmallBlockChainStream_GetCount
5117 * Returns the number of blocks that comprises this chain.
5118 * This is not the size of this chain as the last block may not be full!
5120 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5122 ULONG blockIndex;
5123 ULONG count = 0;
5125 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5127 while (blockIndex != BLOCK_END_OF_CHAIN)
5129 count++;
5131 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5134 return count;
5137 /******************************************************************************
5138 * SmallBlockChainStream_SetSize
5140 * Sets the size of this stream.
5141 * The file will grow if we grow the chain.
5143 * TODO: Free the actual blocks in the file when we shrink the chain.
5144 * Currently, the blocks are still in the file. So the file size
5145 * doesn't shrink even if we shrink streams.
5147 BOOL SmallBlockChainStream_SetSize(
5148 SmallBlockChainStream* This,
5149 ULARGE_INTEGER newSize)
5151 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5153 if (newSize.s.LowPart == size.s.LowPart)
5154 return TRUE;
5156 if (newSize.s.LowPart < size.s.LowPart)
5158 SmallBlockChainStream_Shrink(This, newSize);
5160 else
5162 SmallBlockChainStream_Enlarge(This, newSize);
5165 return TRUE;
5168 /******************************************************************************
5169 * SmallBlockChainStream_GetSize
5171 * Returns the size of this chain.
5173 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5175 StgProperty chainProperty;
5177 StorageImpl_ReadProperty(
5178 This->parentStorage,
5179 This->ownerPropertyIndex,
5180 &chainProperty);
5182 return chainProperty.size;
5185 /******************************************************************************
5186 * StgCreateDocfile32 [OLE32.144]
5188 HRESULT WINAPI StgCreateDocfile(
5189 LPCOLESTR pwcsName,
5190 DWORD grfMode,
5191 DWORD reserved,
5192 IStorage **ppstgOpen)
5194 StorageImpl* newStorage = 0;
5195 HANDLE hFile = INVALID_HANDLE_VALUE;
5196 HRESULT hr = S_OK;
5197 DWORD shareMode;
5198 DWORD accessMode;
5199 DWORD creationMode;
5200 DWORD fileAttributes;
5201 WCHAR tempFileName[MAX_PATH];
5203 TRACE("(%s, %lx, %ld, %p)\n",
5204 debugstr_w(pwcsName), grfMode,
5205 reserved, ppstgOpen);
5208 * Validate the parameters
5210 if (ppstgOpen == 0)
5211 return STG_E_INVALIDPOINTER;
5214 * Validate the STGM flags
5216 if ( FAILED( validateSTGM(grfMode) ))
5217 return STG_E_INVALIDFLAG;
5220 * Generate a unique name.
5222 if (pwcsName == 0)
5224 WCHAR tempPath[MAX_PATH];
5225 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5227 memset(tempPath, 0, sizeof(tempPath));
5228 memset(tempFileName, 0, sizeof(tempFileName));
5230 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5231 tempPath[0] = '.';
5233 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5234 pwcsName = tempFileName;
5235 else
5236 return STG_E_INSUFFICIENTMEMORY;
5240 * Interpret the STGM value grfMode
5242 shareMode = GetShareModeFromSTGM(grfMode);
5243 accessMode = GetAccessModeFromSTGM(grfMode);
5244 creationMode = GetCreationModeFromSTGM(grfMode);
5246 if (grfMode & STGM_DELETEONRELEASE)
5247 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5248 else
5249 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5251 if (grfMode & STGM_TRANSACTED)
5252 FIXME("Transacted mode not implemented.\n");
5255 * Initialize the "out" parameter.
5257 *ppstgOpen = 0;
5259 hFile = CreateFileW(pwcsName,
5260 accessMode,
5261 shareMode,
5262 NULL,
5263 creationMode,
5264 fileAttributes,
5267 if (hFile == INVALID_HANDLE_VALUE)
5269 return E_FAIL;
5273 * Allocate and initialize the new IStorage32object.
5275 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5277 if (newStorage == 0)
5278 return STG_E_INSUFFICIENTMEMORY;
5280 hr = StorageImpl_Construct(
5281 newStorage,
5282 hFile,
5283 NULL,
5284 grfMode,
5285 TRUE);
5287 if (FAILED(hr))
5289 HeapFree(GetProcessHeap(), 0, newStorage);
5290 return hr;
5294 * Get an "out" pointer for the caller.
5296 hr = StorageBaseImpl_QueryInterface(
5297 (IStorage*)newStorage,
5298 (REFIID)&IID_IStorage,
5299 (void**)ppstgOpen);
5301 return hr;
5304 /******************************************************************************
5305 * StgOpenStorage32 [OLE32.148]
5307 HRESULT WINAPI StgOpenStorage(
5308 const OLECHAR *pwcsName,
5309 IStorage *pstgPriority,
5310 DWORD grfMode,
5311 SNB snbExclude,
5312 DWORD reserved,
5313 IStorage **ppstgOpen)
5315 StorageImpl* newStorage = 0;
5316 HRESULT hr = S_OK;
5317 HANDLE hFile = 0;
5318 DWORD shareMode;
5319 DWORD accessMode;
5321 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5322 debugstr_w(pwcsName), pstgPriority, grfMode,
5323 snbExclude, reserved, ppstgOpen);
5326 * Perform a sanity check
5328 if (( pwcsName == 0) || (ppstgOpen == 0) )
5329 return STG_E_INVALIDPOINTER;
5332 * Validate the STGM flags
5334 if ( FAILED( validateSTGM(grfMode) ))
5335 return STG_E_INVALIDFLAG;
5338 * Interpret the STGM value grfMode
5340 shareMode = GetShareModeFromSTGM(grfMode);
5341 accessMode = GetAccessModeFromSTGM(grfMode);
5344 * Initialize the "out" parameter.
5346 *ppstgOpen = 0;
5348 hFile = CreateFileW( pwcsName,
5349 accessMode,
5350 shareMode,
5351 NULL,
5352 OPEN_EXISTING,
5353 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5357 if (hFile==INVALID_HANDLE_VALUE)
5359 return E_FAIL;
5363 * Allocate and initialize the new IStorage32object.
5365 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5367 if (newStorage == 0)
5368 return STG_E_INSUFFICIENTMEMORY;
5370 hr = StorageImpl_Construct(
5371 newStorage,
5372 hFile,
5373 NULL,
5374 grfMode,
5375 TRUE);
5377 if (FAILED(hr))
5379 HeapFree(GetProcessHeap(), 0, newStorage);
5380 return hr;
5384 * Get an "out" pointer for the caller.
5386 hr = StorageBaseImpl_QueryInterface(
5387 (IStorage*)newStorage,
5388 (REFIID)&IID_IStorage,
5389 (void**)ppstgOpen);
5391 return hr;
5394 /******************************************************************************
5395 * StgCreateDocfileOnILockBytes [OLE32.145]
5397 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5398 ILockBytes *plkbyt,
5399 DWORD grfMode,
5400 DWORD reserved,
5401 IStorage** ppstgOpen)
5403 StorageImpl* newStorage = 0;
5404 HRESULT hr = S_OK;
5407 * Validate the parameters
5409 if ((ppstgOpen == 0) || (plkbyt == 0))
5410 return STG_E_INVALIDPOINTER;
5413 * Allocate and initialize the new IStorage object.
5415 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5417 if (newStorage == 0)
5418 return STG_E_INSUFFICIENTMEMORY;
5420 hr = StorageImpl_Construct(
5421 newStorage,
5423 plkbyt,
5424 grfMode,
5425 FALSE);
5427 if (FAILED(hr))
5429 HeapFree(GetProcessHeap(), 0, newStorage);
5430 return hr;
5434 * Get an "out" pointer for the caller.
5436 hr = StorageBaseImpl_QueryInterface(
5437 (IStorage*)newStorage,
5438 (REFIID)&IID_IStorage,
5439 (void**)ppstgOpen);
5441 return hr;
5444 /******************************************************************************
5445 * StgOpenStorageOnILockBytes [OLE32.149]
5447 HRESULT WINAPI StgOpenStorageOnILockBytes(
5448 ILockBytes *plkbyt,
5449 IStorage *pstgPriority,
5450 DWORD grfMode,
5451 SNB snbExclude,
5452 DWORD reserved,
5453 IStorage **ppstgOpen)
5455 StorageImpl* newStorage = 0;
5456 HRESULT hr = S_OK;
5459 * Perform a sanity check
5461 if ((plkbyt == 0) || (ppstgOpen == 0))
5462 return STG_E_INVALIDPOINTER;
5465 * Validate the STGM flags
5467 if ( FAILED( validateSTGM(grfMode) ))
5468 return STG_E_INVALIDFLAG;
5471 * Initialize the "out" parameter.
5473 *ppstgOpen = 0;
5476 * Allocate and initialize the new IStorage object.
5478 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5480 if (newStorage == 0)
5481 return STG_E_INSUFFICIENTMEMORY;
5483 hr = StorageImpl_Construct(
5484 newStorage,
5486 plkbyt,
5487 grfMode,
5488 FALSE);
5490 if (FAILED(hr))
5492 HeapFree(GetProcessHeap(), 0, newStorage);
5493 return hr;
5497 * Get an "out" pointer for the caller.
5499 hr = StorageBaseImpl_QueryInterface(
5500 (IStorage*)newStorage,
5501 (REFIID)&IID_IStorage,
5502 (void**)ppstgOpen);
5504 return hr;
5507 /******************************************************************************
5508 * StgSetTimes [ole32.150]
5512 HRESULT WINAPI StgSetTimes(WCHAR * str, FILETIME * a, FILETIME * b, FILETIME *c )
5515 FIXME("(%p, %p, %p, %p),stub!\n", str, a, b, c);
5516 return FALSE;
5519 /******************************************************************************
5520 * StgIsStorageILockBytes [OLE32.147]
5522 * Determines if the ILockBytes contains a storage object.
5524 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5526 BYTE sig[8];
5527 ULARGE_INTEGER offset;
5529 offset.s.HighPart = 0;
5530 offset.s.LowPart = 0;
5532 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5534 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5535 return S_OK;
5537 return S_FALSE;
5540 /******************************************************************************
5541 * WriteClassStg32 [OLE32.158]
5543 * This method will store the specified CLSID in the specified storage object
5545 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5547 HRESULT hRes;
5549 assert(pStg != 0);
5551 hRes = IStorage_SetClass(pStg, rclsid);
5553 return hRes;
5556 /*******************************************************************************************
5557 * ReadClassStg
5559 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5561 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5563 STATSTG pstatstg;
5564 HRESULT hRes;
5566 TRACE("()\n");
5568 if(pclsid==NULL)
5569 return E_POINTER;
5571 * read a STATSTG structure (contains the clsid) from the storage
5573 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5575 if(SUCCEEDED(hRes))
5576 *pclsid=pstatstg.clsid;
5578 return hRes;
5581 /*************************************************************************************
5582 * OleLoadFromStream
5584 * This function loads an object from stream
5586 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5588 CLSID clsid;
5589 HRESULT res;
5591 FIXME("(),stub!\n");
5593 res=ReadClassStm(pStm,&clsid);
5595 if (SUCCEEDED(res)){
5597 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5599 if (SUCCEEDED(res))
5601 res=IPersistStream_Load((IPersistStream*)ppvObj,pStm);
5604 return res;
5607 /************************************************************************************************
5608 * OleSaveToStream
5610 * This function saves an object with the IPersistStream interface on it to the specified stream
5612 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5615 CLSID clsid;
5616 HRESULT res;
5618 TRACE("(%p,%p)\n",pPStm,pStm);
5620 res=IPersistStream_GetClassID(pPStm,&clsid);
5622 if (SUCCEEDED(res)){
5624 res=WriteClassStm(pStm,&clsid);
5626 if (SUCCEEDED(res))
5628 res=IPersistStream_Save(pPStm,pStm,FALSE);
5631 return res;
5634 /****************************************************************************
5635 * This method validate a STGM parameter that can contain the values below
5637 * STGM_DIRECT 0x00000000
5638 * STGM_TRANSACTED 0x00010000
5639 * STGM_SIMPLE 0x08000000
5641 * STGM_READ 0x00000000
5642 * STGM_WRITE 0x00000001
5643 * STGM_READWRITE 0x00000002
5645 * STGM_SHARE_DENY_NONE 0x00000040
5646 * STGM_SHARE_DENY_READ 0x00000030
5647 * STGM_SHARE_DENY_WRITE 0x00000020
5648 * STGM_SHARE_EXCLUSIVE 0x00000010
5650 * STGM_PRIORITY 0x00040000
5651 * STGM_DELETEONRELEASE 0x04000000
5653 * STGM_CREATE 0x00001000
5654 * STGM_CONVERT 0x00020000
5655 * STGM_FAILIFTHERE 0x00000000
5657 * STGM_NOSCRATCH 0x00100000
5658 * STGM_NOSNAPSHOT 0x00200000
5660 static HRESULT validateSTGM(DWORD stgm)
5662 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5663 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5664 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5666 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5667 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5668 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5670 BOOL bSTGM_SHARE_DENY_NONE =
5671 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5673 BOOL bSTGM_SHARE_DENY_READ =
5674 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5676 BOOL bSTGM_SHARE_DENY_WRITE =
5677 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5679 BOOL bSTGM_SHARE_EXCLUSIVE =
5680 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5682 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5683 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5685 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5686 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5689 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5691 if ( ! bSTGM_DIRECT )
5692 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5693 return E_FAIL;
5696 * STGM_WRITE | STGM_READWRITE | STGM_READ
5698 if ( ! bSTGM_READ )
5699 if( bSTGM_WRITE && bSTGM_READWRITE )
5700 return E_FAIL;
5703 * STGM_SHARE_DENY_NONE | others
5704 * (I assume here that DENY_READ implies DENY_WRITE)
5706 if ( bSTGM_SHARE_DENY_NONE )
5707 if ( bSTGM_SHARE_DENY_READ ||
5708 bSTGM_SHARE_DENY_WRITE ||
5709 bSTGM_SHARE_EXCLUSIVE)
5710 return E_FAIL;
5713 * STGM_CREATE | STGM_CONVERT
5714 * if both are false, STGM_FAILIFTHERE is set to TRUE
5716 if ( bSTGM_CREATE && bSTGM_CONVERT )
5717 return E_FAIL;
5720 * STGM_NOSCRATCH requires STGM_TRANSACTED
5722 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5723 return E_FAIL;
5726 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5727 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5729 if (bSTGM_NOSNAPSHOT)
5731 if ( ! ( bSTGM_TRANSACTED &&
5732 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5733 return E_FAIL;
5736 return S_OK;
5739 /****************************************************************************
5740 * GetShareModeFromSTGM
5742 * This method will return a share mode flag from a STGM value.
5743 * The STGM value is assumed valid.
5745 static DWORD GetShareModeFromSTGM(DWORD stgm)
5747 DWORD dwShareMode = 0;
5748 BOOL bSTGM_SHARE_DENY_NONE =
5749 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5751 BOOL bSTGM_SHARE_DENY_READ =
5752 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5754 BOOL bSTGM_SHARE_DENY_WRITE =
5755 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5757 BOOL bSTGM_SHARE_EXCLUSIVE =
5758 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5760 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5761 dwShareMode = 0;
5763 if (bSTGM_SHARE_DENY_NONE)
5764 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5766 if (bSTGM_SHARE_DENY_WRITE)
5767 dwShareMode = FILE_SHARE_READ;
5769 return dwShareMode;
5772 /****************************************************************************
5773 * GetAccessModeFromSTGM
5775 * This method will return an access mode flag from a STGM value.
5776 * The STGM value is assumed valid.
5778 static DWORD GetAccessModeFromSTGM(DWORD stgm)
5780 DWORD dwDesiredAccess = GENERIC_READ;
5781 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5782 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5783 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5785 if (bSTGM_READ)
5786 dwDesiredAccess = GENERIC_READ;
5788 if (bSTGM_WRITE)
5789 dwDesiredAccess |= GENERIC_WRITE;
5791 if (bSTGM_READWRITE)
5792 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5794 return dwDesiredAccess;
5797 /****************************************************************************
5798 * GetCreationModeFromSTGM
5800 * This method will return a creation mode flag from a STGM value.
5801 * The STGM value is assumed valid.
5803 static DWORD GetCreationModeFromSTGM(DWORD stgm)
5805 if ( stgm & STGM_CREATE)
5806 return CREATE_ALWAYS;
5807 if (stgm & STGM_CONVERT) {
5808 FIXME("STGM_CONVERT not implemented!\n");
5809 return CREATE_NEW;
5811 /* All other cases */
5812 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5813 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
5814 return CREATE_NEW;