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
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)
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(
44 ULONG propertyIndexToStore,
45 ULONG storagePropertyIndex,
48 static HRESULT adjustPropertyChain(
50 StgProperty propertyToDelete,
51 StgProperty parentProperty,
52 ULONG parentPropertyId,
55 /***********************************************************************
56 * Declaration of the functions used to manipulate StgProperty
59 static ULONG getFreeProperty(
60 StorageImpl *storage);
62 static void updatePropertyChain(
64 ULONG newPropertyIndex,
65 StgProperty newProperty);
67 static LONG propertyNameCmp(
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,
95 StorageImpl_MoveElementTo,
98 StorageBaseImpl_EnumElements,
99 StorageImpl_DestroyElement,
100 StorageBaseImpl_RenameElement,
101 StorageImpl_SetElementTimes,
102 StorageBaseImpl_SetClass,
103 StorageImpl_SetStateBits,
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,
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,
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(
169 ICOM_THIS(StorageBaseImpl,iface);
171 * Perform a sanity check on the parameters.
173 if ( (This==0) || (ppvObject==0) )
177 * Initialize the return parameter.
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.
197 return E_NOINTERFACE;
200 * Query Interface always increases the reference count by one when it is
203 StorageBaseImpl_AddRef(iface);
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(
219 ICOM_THIS(StorageBaseImpl,iface);
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(
236 ICOM_THIS(StorageBaseImpl,iface);
238 * Decrease the reference count on this object.
243 * If the reference count goes down to 0, perform suicide.
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);
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(
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) )
291 * Initialize the out parameter
296 * Validate the STGM flags
298 if ( FAILED( validateSTGM(grfMode) ))
299 return STG_E_INVALIDFLAG;
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(
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);
339 newStream->grfMode = grfMode;
340 *ppstm = (IStream*)newStream;
343 * Since we are returning a pointer to the interface, we have to
344 * nail down the reference.
346 StgStreamImpl_AddRef(*ppstm);
351 return E_OUTOFMEMORY;
354 return STG_E_FILENOTFOUND;
357 /************************************************************************
358 * Storage32BaseImpl_OpenStorage (IStorage)
360 * This method will open a new storage object from the current storage.
362 * See Windows documentation for more details on IStorage methods.
364 HRESULT WINAPI StorageBaseImpl_OpenStorage(
366 const OLECHAR* pwcsName, /* [string][unique][in] */
367 IStorage* pstgPriority, /* [unique][in] */
368 DWORD grfMode, /* [in] */
369 SNB snbExclude, /* [unique][in] */
370 DWORD reserved, /* [in] */
371 IStorage** ppstg) /* [out] */
373 ICOM_THIS(StorageBaseImpl,iface);
374 StorageInternalImpl* newStorage;
375 IEnumSTATSTGImpl* propertyEnumeration;
376 StgProperty currentProperty;
377 ULONG foundPropertyIndex;
379 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
380 iface, debugstr_w(pwcsName), pstgPriority,
381 grfMode, snbExclude, reserved, ppstg);
384 * Perform a sanity check on the parameters.
386 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
390 * Validate the STGM flags
392 if ( FAILED( validateSTGM(grfMode) ))
393 return STG_E_INVALIDFLAG;
398 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
399 (grfMode & STGM_DELETEONRELEASE) ||
400 (grfMode & STGM_PRIORITY) )
401 return STG_E_INVALIDFUNCTION;
404 * Initialize the out parameter
409 * Create a property enumeration to search the properties
411 propertyEnumeration = IEnumSTATSTGImpl_Construct(
412 This->ancestorStorage,
413 This->rootPropertySetIndex);
416 * Search the enumeration for the property with the given name
418 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
424 * Delete the property enumeration since we don't need it anymore
426 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
429 * If it was found, construct the stream object and return a pointer to it.
431 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
432 (currentProperty.propertyType==PROPTYPE_STORAGE) )
435 * Construct a new Storage object
437 newStorage = StorageInternalImpl_Construct(
438 This->ancestorStorage,
443 *ppstg = (IStorage*)newStorage;
446 * Since we are returning a pointer to the interface,
447 * we have to nail down the reference.
449 StorageBaseImpl_AddRef(*ppstg);
454 return STG_E_INSUFFICIENTMEMORY;
457 return STG_E_FILENOTFOUND;
460 /************************************************************************
461 * Storage32BaseImpl_EnumElements (IStorage)
463 * This method will create an enumerator object that can be used to
464 * retrieve informatino about all the properties in the storage object.
466 * See Windows documentation for more details on IStorage methods.
468 HRESULT WINAPI StorageBaseImpl_EnumElements(
470 DWORD reserved1, /* [in] */
471 void* reserved2, /* [size_is][unique][in] */
472 DWORD reserved3, /* [in] */
473 IEnumSTATSTG** ppenum) /* [out] */
475 ICOM_THIS(StorageBaseImpl,iface);
476 IEnumSTATSTGImpl* newEnum;
478 TRACE("(%p, %ld, %p, %ld, %p)\n",
479 iface, reserved1, reserved2, reserved3, ppenum);
482 * Perform a sanity check on the parameters.
484 if ( (This==0) || (ppenum==0))
488 * Construct the enumerator.
490 newEnum = IEnumSTATSTGImpl_Construct(
491 This->ancestorStorage,
492 This->rootPropertySetIndex);
496 *ppenum = (IEnumSTATSTG*)newEnum;
499 * Don't forget to nail down a reference to the new object before
502 IEnumSTATSTGImpl_AddRef(*ppenum);
507 return E_OUTOFMEMORY;
510 /************************************************************************
511 * Storage32BaseImpl_Stat (IStorage)
513 * This method will retrieve information about this storage object.
515 * See Windows documentation for more details on IStorage methods.
517 HRESULT WINAPI StorageBaseImpl_Stat(
519 STATSTG* pstatstg, /* [out] */
520 DWORD grfStatFlag) /* [in] */
522 ICOM_THIS(StorageBaseImpl,iface);
523 StgProperty curProperty;
526 TRACE("(%p, %p, %lx)\n",
527 iface, pstatstg, grfStatFlag);
530 * Perform a sanity check on the parameters.
532 if ( (This==0) || (pstatstg==0))
536 * Read the information from the property.
538 readSuccessful = StorageImpl_ReadProperty(
539 This->ancestorStorage,
540 This->rootPropertySetIndex,
545 StorageUtl_CopyPropertyToSTATSTG(
556 /************************************************************************
557 * Storage32BaseImpl_RenameElement (IStorage)
559 * This method will rename the specified element.
561 * See Windows documentation for more details on IStorage methods.
563 * Implementation notes: The method used to rename consists of creating a clone
564 * of the deleted StgProperty object setting it with the new name and to
565 * perform a DestroyElement of the old StgProperty.
567 HRESULT WINAPI StorageBaseImpl_RenameElement(
569 const OLECHAR* pwcsOldName, /* [in] */
570 const OLECHAR* pwcsNewName) /* [in] */
572 ICOM_THIS(StorageBaseImpl,iface);
573 IEnumSTATSTGImpl* propertyEnumeration;
574 StgProperty currentProperty;
575 ULONG foundPropertyIndex;
577 TRACE("(%p, %s, %s)\n",
578 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
581 * Create a property enumeration to search the properties
583 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
584 This->rootPropertySetIndex);
587 * Search the enumeration for the new property name
589 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
593 if (foundPropertyIndex != PROPERTY_NULL)
596 * There is already a property with the new name
598 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
599 return STG_E_FILEALREADYEXISTS;
602 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
605 * Search the enumeration for the old property name
607 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
612 * Delete the property enumeration since we don't need it anymore
614 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
616 if (foundPropertyIndex != PROPERTY_NULL)
618 StgProperty renamedProperty;
619 ULONG renamedPropertyIndex;
622 * Setup a new property for the renamed property
624 renamedProperty.sizeOfNameString =
625 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
627 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
628 return STG_E_INVALIDNAME;
630 lstrcpyW(renamedProperty.name, pwcsNewName);
632 renamedProperty.propertyType = currentProperty.propertyType;
633 renamedProperty.startingBlock = currentProperty.startingBlock;
634 renamedProperty.size.s.LowPart = currentProperty.size.s.LowPart;
635 renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
637 renamedProperty.previousProperty = PROPERTY_NULL;
638 renamedProperty.nextProperty = PROPERTY_NULL;
641 * Bring the dirProperty link in case it is a storage and in which
642 * case the renamed storage elements don't require to be reorganized.
644 renamedProperty.dirProperty = currentProperty.dirProperty;
646 /* call CoFileTime to get the current time
647 renamedProperty.timeStampS1
648 renamedProperty.timeStampD1
649 renamedProperty.timeStampS2
650 renamedProperty.timeStampD2
651 renamedProperty.propertyUniqueID
655 * Obtain a free property in the property chain
657 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
660 * Save the new property into the new property spot
662 StorageImpl_WriteProperty(
663 This->ancestorStorage,
664 renamedPropertyIndex,
668 * Find a spot in the property chain for our newly created property.
672 renamedPropertyIndex,
676 * At this point the renamed property has been inserted in the tree,
677 * now, before to Destroy the old property we must zeroed it's dirProperty
678 * otherwise the DestroyProperty below will zap it all and we do not want
680 * Also, we fake that the old property is a storage so the DestroyProperty
681 * will not do a SetSize(0) on the stream data.
683 * This means that we need to tweek the StgProperty if it is a stream or a
686 currentProperty.dirProperty = PROPERTY_NULL;
687 currentProperty.propertyType = PROPTYPE_STORAGE;
688 StorageImpl_WriteProperty(
689 This->ancestorStorage,
694 * Invoke Destroy to get rid of the ole property and automatically redo
695 * the linking of it's previous and next members...
697 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
703 * There is no property with the old name
705 return STG_E_FILENOTFOUND;
711 /************************************************************************
712 * Storage32BaseImpl_CreateStream (IStorage)
714 * This method will create a stream object within this storage
716 * See Windows documentation for more details on IStorage methods.
718 HRESULT WINAPI StorageBaseImpl_CreateStream(
720 const OLECHAR* pwcsName, /* [string][in] */
721 DWORD grfMode, /* [in] */
722 DWORD reserved1, /* [in] */
723 DWORD reserved2, /* [in] */
724 IStream** ppstm) /* [out] */
726 ICOM_THIS(StorageBaseImpl,iface);
727 IEnumSTATSTGImpl* propertyEnumeration;
728 StgStreamImpl* newStream;
729 StgProperty currentProperty, newStreamProperty;
730 ULONG foundPropertyIndex, newPropertyIndex;
732 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
733 iface, debugstr_w(pwcsName), grfMode,
734 reserved1, reserved2, ppstm);
737 * Validate parameters
740 return STG_E_INVALIDPOINTER;
743 return STG_E_INVALIDNAME;
746 * Validate the STGM flags
748 if ( FAILED( validateSTGM(grfMode) ))
749 return STG_E_INVALIDFLAG;
754 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
755 (grfMode & STGM_DELETEONRELEASE) ||
756 (grfMode & STGM_TRANSACTED) )
757 return STG_E_INVALIDFUNCTION;
760 * Initialize the out parameter
765 * Create a property enumeration to search the properties
767 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
768 This->rootPropertySetIndex);
770 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
774 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
776 if (foundPropertyIndex != PROPERTY_NULL)
779 * An element with this name already exists
781 if (grfMode & STGM_CREATE)
783 IStorage_DestroyElement(iface, pwcsName);
786 return STG_E_FILEALREADYEXISTS;
790 * memset the empty property
792 memset(&newStreamProperty, 0, sizeof(StgProperty));
794 newStreamProperty.sizeOfNameString =
795 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
797 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
798 return STG_E_INVALIDNAME;
800 lstrcpyW(newStreamProperty.name, pwcsName);
802 newStreamProperty.propertyType = PROPTYPE_STREAM;
803 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
804 newStreamProperty.size.s.LowPart = 0;
805 newStreamProperty.size.s.HighPart = 0;
807 newStreamProperty.previousProperty = PROPERTY_NULL;
808 newStreamProperty.nextProperty = PROPERTY_NULL;
809 newStreamProperty.dirProperty = PROPERTY_NULL;
811 /* call CoFileTime to get the current time
812 newStreamProperty.timeStampS1
813 newStreamProperty.timeStampD1
814 newStreamProperty.timeStampS2
815 newStreamProperty.timeStampD2
818 /* newStreamProperty.propertyUniqueID */
821 * Get a free property or create a new one
823 newPropertyIndex = getFreeProperty(This->ancestorStorage);
826 * Save the new property into the new property spot
828 StorageImpl_WriteProperty(
829 This->ancestorStorage,
834 * Find a spot in the property chain for our newly created property.
842 * Open the stream to return it.
844 newStream = StgStreamImpl_Construct(This, newPropertyIndex);
848 *ppstm = (IStream*)newStream;
851 * Since we are returning a pointer to the interface, we have to nail down
854 StgStreamImpl_AddRef(*ppstm);
858 return STG_E_INSUFFICIENTMEMORY;
864 /************************************************************************
865 * Storage32BaseImpl_SetClass (IStorage)
867 * This method will write the specified CLSID in the property of this
870 * See Windows documentation for more details on IStorage methods.
872 HRESULT WINAPI StorageBaseImpl_SetClass(
874 REFCLSID clsid) /* [in] */
876 ICOM_THIS(StorageBaseImpl,iface);
877 HRESULT hRes = E_FAIL;
878 StgProperty curProperty;
881 TRACE("(%p, %p)\n", iface, clsid);
883 success = StorageImpl_ReadProperty(This->ancestorStorage,
884 This->rootPropertySetIndex,
888 curProperty.propertyUniqueID = *clsid;
890 success = StorageImpl_WriteProperty(This->ancestorStorage,
891 This->rootPropertySetIndex,
900 /************************************************************************
901 ** Storage32Impl implementation
904 /************************************************************************
905 * Storage32Impl_CreateStorage (IStorage)
907 * This method will create the storage object within the provided storage.
909 * See Windows documentation for more details on IStorage methods.
911 HRESULT WINAPI StorageImpl_CreateStorage(
913 const OLECHAR *pwcsName, /* [string][in] */
914 DWORD grfMode, /* [in] */
915 DWORD reserved1, /* [in] */
916 DWORD reserved2, /* [in] */
917 IStorage **ppstg) /* [out] */
919 StorageImpl* const This=(StorageImpl*)iface;
921 IEnumSTATSTGImpl *propertyEnumeration;
922 StgProperty currentProperty;
923 StgProperty newProperty;
924 ULONG foundPropertyIndex;
925 ULONG newPropertyIndex;
928 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
929 iface, debugstr_w(pwcsName), grfMode,
930 reserved1, reserved2, ppstg);
933 * Validate parameters
936 return STG_E_INVALIDPOINTER;
939 return STG_E_INVALIDNAME;
942 * Validate the STGM flags
944 if ( FAILED( validateSTGM(grfMode) ) ||
945 (grfMode & STGM_DELETEONRELEASE) )
946 return STG_E_INVALIDFLAG;
949 * Initialize the out parameter
954 * Create a property enumeration and search the properties
956 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
957 This->rootPropertySetIndex);
959 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
962 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
964 if (foundPropertyIndex != PROPERTY_NULL)
967 * An element with this name already exists
969 if (grfMode & STGM_CREATE)
970 IStorage_DestroyElement(iface, pwcsName);
972 return STG_E_FILEALREADYEXISTS;
976 * memset the empty property
978 memset(&newProperty, 0, sizeof(StgProperty));
980 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
982 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
983 return STG_E_INVALIDNAME;
985 lstrcpyW(newProperty.name, pwcsName);
987 newProperty.propertyType = PROPTYPE_STORAGE;
988 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
989 newProperty.size.s.LowPart = 0;
990 newProperty.size.s.HighPart = 0;
992 newProperty.previousProperty = PROPERTY_NULL;
993 newProperty.nextProperty = PROPERTY_NULL;
994 newProperty.dirProperty = PROPERTY_NULL;
996 /* call CoFileTime to get the current time
997 newProperty.timeStampS1
998 newProperty.timeStampD1
999 newProperty.timeStampS2
1000 newProperty.timeStampD2
1003 /* newStorageProperty.propertyUniqueID */
1006 * Obtain a free property in the property chain
1008 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1011 * Save the new property into the new property spot
1013 StorageImpl_WriteProperty(
1014 This->ancestorStorage,
1019 * Find a spot in the property chain for our newly created property.
1021 updatePropertyChain(
1027 * Open it to get a pointer to return.
1029 hr = IStorage_OpenStorage(
1038 if( (hr != S_OK) || (*ppstg == NULL))
1048 /***************************************************************************
1052 * Get a free property or create a new one.
1054 static ULONG getFreeProperty(
1055 StorageImpl *storage)
1057 ULONG currentPropertyIndex = 0;
1058 ULONG newPropertyIndex = PROPERTY_NULL;
1059 BOOL readSuccessful = TRUE;
1060 StgProperty currentProperty;
1065 * Start by reading the root property
1067 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1068 currentPropertyIndex,
1072 if (currentProperty.sizeOfNameString == 0)
1075 * The property existis and is available, we found it.
1077 newPropertyIndex = currentPropertyIndex;
1083 * We exhausted the property list, we will create more space below
1085 newPropertyIndex = currentPropertyIndex;
1087 currentPropertyIndex++;
1089 } while (newPropertyIndex == PROPERTY_NULL);
1092 * grow the property chain
1094 if (! readSuccessful)
1096 StgProperty emptyProperty;
1097 ULARGE_INTEGER newSize;
1098 ULONG propertyIndex;
1099 ULONG lastProperty = 0;
1100 ULONG blockCount = 0;
1103 * obtain the new count of property blocks
1105 blockCount = BlockChainStream_GetCount(
1106 storage->ancestorStorage->rootBlockChain)+1;
1109 * initialize the size used by the property stream
1111 newSize.s.HighPart = 0;
1112 newSize.s.LowPart = storage->bigBlockSize * blockCount;
1115 * add a property block to the property chain
1117 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1120 * memset the empty property in order to initialize the unused newly
1123 memset(&emptyProperty, 0, sizeof(StgProperty));
1128 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1131 propertyIndex = newPropertyIndex;
1132 propertyIndex < lastProperty;
1135 StorageImpl_WriteProperty(
1136 storage->ancestorStorage,
1142 return newPropertyIndex;
1145 /****************************************************************************
1149 * Case insensitive comparaison of StgProperty.name by first considering
1152 * Returns <0 when newPrpoerty < currentProperty
1153 * >0 when newPrpoerty > currentProperty
1154 * 0 when newPrpoerty == currentProperty
1156 static LONG propertyNameCmp(
1157 OLECHAR *newProperty,
1158 OLECHAR *currentProperty)
1160 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1165 * We compare the string themselves only when they are of the same lenght
1167 diff = lstrcmpiW( newProperty, currentProperty);
1173 /****************************************************************************
1177 * Properly link this new element in the property chain.
1179 static void updatePropertyChain(
1180 StorageImpl *storage,
1181 ULONG newPropertyIndex,
1182 StgProperty newProperty)
1184 StgProperty currentProperty;
1187 * Read the root property
1189 StorageImpl_ReadProperty(storage->ancestorStorage,
1190 storage->rootPropertySetIndex,
1193 if (currentProperty.dirProperty != PROPERTY_NULL)
1196 * The root storage contains some element, therefore, start the research
1197 * for the appropriate location.
1200 ULONG current, next, previous, currentPropertyId;
1203 * Keep the StgProperty sequence number of the storage first property
1205 currentPropertyId = currentProperty.dirProperty;
1210 StorageImpl_ReadProperty(storage->ancestorStorage,
1211 currentProperty.dirProperty,
1214 previous = currentProperty.previousProperty;
1215 next = currentProperty.nextProperty;
1216 current = currentPropertyId;
1220 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1224 if (previous != PROPERTY_NULL)
1226 StorageImpl_ReadProperty(storage->ancestorStorage,
1233 currentProperty.previousProperty = newPropertyIndex;
1234 StorageImpl_WriteProperty(storage->ancestorStorage,
1242 if (next != PROPERTY_NULL)
1244 StorageImpl_ReadProperty(storage->ancestorStorage,
1251 currentProperty.nextProperty = newPropertyIndex;
1252 StorageImpl_WriteProperty(storage->ancestorStorage,
1261 * Trying to insert an item with the same name in the
1262 * subtree structure.
1267 previous = currentProperty.previousProperty;
1268 next = currentProperty.nextProperty;
1274 * The root storage is empty, link the new property to it's dir property
1276 currentProperty.dirProperty = newPropertyIndex;
1277 StorageImpl_WriteProperty(storage->ancestorStorage,
1278 storage->rootPropertySetIndex,
1284 /*************************************************************************
1287 HRESULT WINAPI StorageImpl_CopyTo(
1289 DWORD ciidExclude, /* [in] */
1290 const IID* rgiidExclude, /* [size_is][unique][in] */
1291 SNB snbExclude, /* [unique][in] */
1292 IStorage* pstgDest) /* [unique][in] */
1294 IEnumSTATSTG *elements = 0;
1295 STATSTG curElement, strStat;
1297 IStorage *pstgTmp, *pstgChild;
1298 IStream *pstrTmp, *pstrChild;
1300 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1301 FIXME("Exclude option not implemented\n");
1303 TRACE("(%p, %ld, %p, %p, %p)\n",
1304 iface, ciidExclude, rgiidExclude,
1305 snbExclude, pstgDest);
1308 * Perform a sanity check
1310 if ( pstgDest == 0 )
1311 return STG_E_INVALIDPOINTER;
1314 * Enumerate the elements
1316 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1324 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1325 IStorage_SetClass( pstgDest, &curElement.clsid );
1330 * Obtain the next element
1332 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1334 if ( hr == S_FALSE )
1336 hr = S_OK; /* done, every element has been copied */
1340 if (curElement.type == STGTY_STORAGE)
1343 * open child source storage
1345 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1346 STGM_READ|STGM_SHARE_EXCLUSIVE,
1347 NULL, 0, &pstgChild );
1353 * Check if destination storage is not a child of the source
1354 * storage, which will cause an infinite loop
1356 if (pstgChild == pstgDest)
1358 IEnumSTATSTG_Release(elements);
1360 return STG_E_ACCESSDENIED;
1364 * create a new storage in destination storage
1366 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1367 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1371 * if it already exist, don't create a new one use this one
1373 if (hr == STG_E_FILEALREADYEXISTS)
1375 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1376 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1377 NULL, 0, &pstgTmp );
1385 * do the copy recursively
1387 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1388 snbExclude, pstgTmp );
1390 IStorage_Release( pstgTmp );
1391 IStorage_Release( pstgChild );
1393 else if (curElement.type == STGTY_STREAM)
1396 * create a new stream in destination storage. If the stream already
1397 * exist, it will be deleted and a new one will be created.
1399 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1400 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1407 * open child stream storage
1409 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1410 STGM_READ|STGM_SHARE_EXCLUSIVE,
1417 * Get the size of the source stream
1419 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1422 * Set the size of the destination stream.
1424 IStream_SetSize(pstrTmp, strStat.cbSize);
1429 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1432 IStream_Release( pstrTmp );
1433 IStream_Release( pstrChild );
1437 WARN("unknown element type: %ld\n", curElement.type);
1440 } while (hr == S_OK);
1445 IEnumSTATSTG_Release(elements);
1450 /*************************************************************************
1451 * MoveElementTo (IStorage)
1453 HRESULT WINAPI StorageImpl_MoveElementTo(
1455 const OLECHAR *pwcsName, /* [string][in] */
1456 IStorage *pstgDest, /* [unique][in] */
1457 const OLECHAR *pwcsNewName,/* [string][in] */
1458 DWORD grfFlags) /* [in] */
1460 FIXME("not implemented!\n");
1464 /*************************************************************************
1467 HRESULT WINAPI StorageImpl_Commit(
1469 DWORD grfCommitFlags)/* [in] */
1471 FIXME("(%ld): stub!\n", grfCommitFlags);
1475 /*************************************************************************
1478 HRESULT WINAPI StorageImpl_Revert(
1481 FIXME("not implemented!\n");
1485 /*************************************************************************
1486 * DestroyElement (IStorage)
1488 * Stategy: This implementation is build this way for simplicity not for speed.
1489 * I always delete the top most element of the enumeration and adjust
1490 * the deleted element pointer all the time. This takes longer to
1491 * do but allow to reinvoke DestroyElement whenever we encounter a
1492 * storage object. The optimisation reside in the usage of another
1493 * enumeration stategy that would give all the leaves of a storage
1494 * first. (postfix order)
1496 HRESULT WINAPI StorageImpl_DestroyElement(
1498 const OLECHAR *pwcsName)/* [string][in] */
1500 StorageImpl* const This=(StorageImpl*)iface;
1502 IEnumSTATSTGImpl* propertyEnumeration;
1505 StgProperty propertyToDelete;
1506 StgProperty parentProperty;
1507 ULONG foundPropertyIndexToDelete;
1508 ULONG typeOfRelation;
1509 ULONG parentPropertyId;
1512 iface, debugstr_w(pwcsName));
1515 * Perform a sanity check on the parameters.
1518 return STG_E_INVALIDPOINTER;
1521 * Create a property enumeration to search the property with the given name
1523 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1524 This->ancestorStorage,
1525 This->rootPropertySetIndex);
1527 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1528 propertyEnumeration,
1532 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1534 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1536 return STG_E_FILENOTFOUND;
1540 * Find the parent property of the property to delete (the one that
1541 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1542 * the parent is This. Otherwise, the parent is one of it's sibling...
1546 * First, read This's StgProperty..
1548 res = StorageImpl_ReadProperty(
1549 This->ancestorStorage,
1550 This->rootPropertySetIndex,
1556 * Second, check to see if by any chance the actual storage (This) is not
1557 * the parent of the property to delete... We never know...
1559 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1562 * Set data as it would have been done in the else part...
1564 typeOfRelation = PROPERTY_RELATION_DIR;
1565 parentPropertyId = This->rootPropertySetIndex;
1570 * Create a property enumeration to search the parent properties, and
1571 * delete it once done.
1573 IEnumSTATSTGImpl* propertyEnumeration2;
1575 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1576 This->ancestorStorage,
1577 This->rootPropertySetIndex);
1579 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1580 propertyEnumeration2,
1581 foundPropertyIndexToDelete,
1585 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1588 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1590 hr = deleteStorageProperty(
1592 propertyToDelete.name);
1594 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1596 hr = deleteStreamProperty(
1598 foundPropertyIndexToDelete,
1606 * Adjust the property chain
1608 hr = adjustPropertyChain(
1619 /*********************************************************************
1623 * Perform the deletion of a complete storage node
1626 static HRESULT deleteStorageProperty(
1627 StorageImpl *parentStorage,
1628 OLECHAR *propertyToDeleteName)
1630 IEnumSTATSTG *elements = 0;
1631 IStorage *childStorage = 0;
1632 STATSTG currentElement;
1634 HRESULT destroyHr = S_OK;
1637 * Open the storage and enumerate it
1639 hr = StorageBaseImpl_OpenStorage(
1640 (IStorage*)parentStorage,
1641 propertyToDeleteName,
1643 STGM_SHARE_EXCLUSIVE,
1654 * Enumerate the elements
1656 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1661 * Obtain the next element
1663 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1666 destroyHr = StorageImpl_DestroyElement(
1667 (IStorage*)childStorage,
1668 (OLECHAR*)currentElement.pwcsName);
1670 CoTaskMemFree(currentElement.pwcsName);
1674 * We need to Reset the enumeration every time because we delete elements
1675 * and the enumeration could be invalid
1677 IEnumSTATSTG_Reset(elements);
1679 } while ((hr == S_OK) && (destroyHr == S_OK));
1681 IStorage_Release(childStorage);
1682 IEnumSTATSTG_Release(elements);
1687 /*********************************************************************
1691 * Perform the deletion of a stream node
1694 static HRESULT deleteStreamProperty(
1695 StorageImpl *parentStorage,
1696 ULONG indexOfPropertyToDelete,
1697 StgProperty propertyToDelete)
1701 ULARGE_INTEGER size;
1703 size.s.HighPart = 0;
1706 hr = StorageBaseImpl_OpenStream(
1707 (IStorage*)parentStorage,
1708 (OLECHAR*)propertyToDelete.name,
1710 STGM_SHARE_EXCLUSIVE,
1722 hr = IStream_SetSize(pis, size);
1730 * Release the stream object.
1732 IStream_Release(pis);
1735 * Invalidate the property by zeroing it's name member.
1737 propertyToDelete.sizeOfNameString = 0;
1740 * Here we should re-read the property so we get the updated pointer
1741 * but since we are here to zap it, I don't do it...
1743 StorageImpl_WriteProperty(
1744 parentStorage->ancestorStorage,
1745 indexOfPropertyToDelete,
1751 /*********************************************************************
1755 * Finds a placeholder for the StgProperty within the Storage
1758 static HRESULT findPlaceholder(
1759 StorageImpl *storage,
1760 ULONG propertyIndexToStore,
1761 ULONG storePropertyIndex,
1764 StgProperty storeProperty;
1769 * Read the storage property
1771 res = StorageImpl_ReadProperty(
1772 storage->ancestorStorage,
1781 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1783 if (storeProperty.previousProperty != PROPERTY_NULL)
1785 return findPlaceholder(
1787 propertyIndexToStore,
1788 storeProperty.previousProperty,
1793 storeProperty.previousProperty = propertyIndexToStore;
1796 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1798 if (storeProperty.nextProperty != PROPERTY_NULL)
1800 return findPlaceholder(
1802 propertyIndexToStore,
1803 storeProperty.nextProperty,
1808 storeProperty.nextProperty = propertyIndexToStore;
1811 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1813 if (storeProperty.dirProperty != PROPERTY_NULL)
1815 return findPlaceholder(
1817 propertyIndexToStore,
1818 storeProperty.dirProperty,
1823 storeProperty.dirProperty = propertyIndexToStore;
1827 hr = StorageImpl_WriteProperty(
1828 storage->ancestorStorage,
1840 /*************************************************************************
1844 * This method takes the previous and the next property link of a property
1845 * to be deleted and find them a place in the Storage.
1847 static HRESULT adjustPropertyChain(
1849 StgProperty propertyToDelete,
1850 StgProperty parentProperty,
1851 ULONG parentPropertyId,
1854 ULONG newLinkProperty = PROPERTY_NULL;
1855 BOOL needToFindAPlaceholder = FALSE;
1856 ULONG storeNode = PROPERTY_NULL;
1857 ULONG toStoreNode = PROPERTY_NULL;
1858 INT relationType = 0;
1862 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1864 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1867 * Set the parent previous to the property to delete previous
1869 newLinkProperty = propertyToDelete.previousProperty;
1871 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1874 * We also need to find a storage for the other link, setup variables
1875 * to do this at the end...
1877 needToFindAPlaceholder = TRUE;
1878 storeNode = propertyToDelete.previousProperty;
1879 toStoreNode = propertyToDelete.nextProperty;
1880 relationType = PROPERTY_RELATION_NEXT;
1883 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1886 * Set the parent previous to the property to delete next
1888 newLinkProperty = propertyToDelete.nextProperty;
1892 * Link it for real...
1894 parentProperty.previousProperty = newLinkProperty;
1897 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1899 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1902 * Set the parent next to the property to delete next previous
1904 newLinkProperty = propertyToDelete.previousProperty;
1906 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1909 * We also need to find a storage for the other link, setup variables
1910 * to do this at the end...
1912 needToFindAPlaceholder = TRUE;
1913 storeNode = propertyToDelete.previousProperty;
1914 toStoreNode = propertyToDelete.nextProperty;
1915 relationType = PROPERTY_RELATION_NEXT;
1918 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1921 * Set the parent next to the property to delete next
1923 newLinkProperty = propertyToDelete.nextProperty;
1927 * Link it for real...
1929 parentProperty.nextProperty = newLinkProperty;
1931 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
1933 if (propertyToDelete.previousProperty != PROPERTY_NULL)
1936 * Set the parent dir to the property to delete previous
1938 newLinkProperty = propertyToDelete.previousProperty;
1940 if (propertyToDelete.nextProperty != PROPERTY_NULL)
1943 * We also need to find a storage for the other link, setup variables
1944 * to do this at the end...
1946 needToFindAPlaceholder = TRUE;
1947 storeNode = propertyToDelete.previousProperty;
1948 toStoreNode = propertyToDelete.nextProperty;
1949 relationType = PROPERTY_RELATION_NEXT;
1952 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
1955 * Set the parent dir to the property to delete next
1957 newLinkProperty = propertyToDelete.nextProperty;
1961 * Link it for real...
1963 parentProperty.dirProperty = newLinkProperty;
1967 * Write back the parent property
1969 res = StorageImpl_WriteProperty(
1970 This->ancestorStorage,
1979 * If a placeholder is required for the other link, then, find one and
1980 * get out of here...
1982 if (needToFindAPlaceholder)
1984 hr = findPlaceholder(
1995 /******************************************************************************
1996 * SetElementTimes (IStorage)
1998 HRESULT WINAPI StorageImpl_SetElementTimes(
2000 const OLECHAR *pwcsName,/* [string][in] */
2001 const FILETIME *pctime, /* [in] */
2002 const FILETIME *patime, /* [in] */
2003 const FILETIME *pmtime) /* [in] */
2005 FIXME("not implemented!\n");
2009 /******************************************************************************
2010 * SetStateBits (IStorage)
2012 HRESULT WINAPI StorageImpl_SetStateBits(
2014 DWORD grfStateBits,/* [in] */
2015 DWORD grfMask) /* [in] */
2017 FIXME("not implemented!\n");
2021 HRESULT StorageImpl_Construct(
2029 StgProperty currentProperty;
2030 BOOL readSuccessful;
2031 ULONG currentPropertyIndex;
2033 if ( FAILED( validateSTGM(openFlags) ))
2034 return STG_E_INVALIDFLAG;
2036 memset(This, 0, sizeof(StorageImpl));
2039 * Initialize the virtual fgunction table.
2041 ICOM_VTBL(This) = &Storage32Impl_Vtbl;
2042 This->v_destructor = &StorageImpl_Destroy;
2045 * This is the top-level storage so initialize the ancester pointer
2048 This->ancestorStorage = This;
2051 * Initialize the physical support of the storage.
2053 This->hFile = hFile;
2056 * Initialize the big block cache.
2058 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2059 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2060 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2066 if (This->bigBlockFile == 0)
2069 if (openFlags & STGM_CREATE)
2071 ULARGE_INTEGER size;
2072 BYTE* bigBlockBuffer;
2075 * Initialize all header variables:
2076 * - The big block depot consists of one block and it is at block 0
2077 * - The properties start at block 1
2078 * - There is no small block depot
2080 memset( This->bigBlockDepotStart,
2082 sizeof(This->bigBlockDepotStart));
2084 This->bigBlockDepotCount = 1;
2085 This->bigBlockDepotStart[0] = 0;
2086 This->rootStartBlock = 1;
2087 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2088 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2089 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2090 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2091 This->extBigBlockDepotCount = 0;
2093 StorageImpl_SaveFileHeader(This);
2096 * Add one block for the big block depot and one block for the properties
2098 size.s.HighPart = 0;
2099 size.s.LowPart = This->bigBlockSize * 3;
2100 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2103 * Initialize the big block depot
2105 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2106 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2107 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2108 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2109 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2114 * Load the header for the file.
2116 hr = StorageImpl_LoadFileHeader(This);
2120 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2127 * There is no block depot cached yet.
2129 This->indexBlockDepotCached = 0xFFFFFFFF;
2132 * Start searching for free blocks with block 0.
2134 This->prevFreeBlock = 0;
2137 * Create the block chain abstractions.
2139 This->rootBlockChain =
2140 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2142 This->smallBlockDepotChain = BlockChainStream_Construct(
2144 &This->smallBlockDepotStart,
2148 * Write the root property
2150 if (openFlags & STGM_CREATE)
2152 StgProperty rootProp;
2154 * Initialize the property chain
2156 memset(&rootProp, 0, sizeof(rootProp));
2157 lstrcpyAtoW(rootProp.name, rootPropertyName);
2159 rootProp.sizeOfNameString = (lstrlenW(rootProp.name)+1) * sizeof(WCHAR);
2160 rootProp.propertyType = PROPTYPE_ROOT;
2161 rootProp.previousProperty = PROPERTY_NULL;
2162 rootProp.nextProperty = PROPERTY_NULL;
2163 rootProp.dirProperty = PROPERTY_NULL;
2164 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2165 rootProp.size.s.HighPart = 0;
2166 rootProp.size.s.LowPart = 0;
2168 StorageImpl_WriteProperty(This, 0, &rootProp);
2172 * Find the ID of the root int he property sets.
2174 currentPropertyIndex = 0;
2178 readSuccessful = StorageImpl_ReadProperty(
2180 currentPropertyIndex,
2185 if ( (currentProperty.sizeOfNameString != 0 ) &&
2186 (currentProperty.propertyType == PROPTYPE_ROOT) )
2188 This->rootPropertySetIndex = currentPropertyIndex;
2192 currentPropertyIndex++;
2194 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2196 if (!readSuccessful)
2203 * Create the block chain abstraction for the small block root chain.
2205 This->smallBlockRootChain = BlockChainStream_Construct(
2208 This->rootPropertySetIndex);
2213 void StorageImpl_Destroy(
2216 TRACE("(%p)\n", This);
2218 BlockChainStream_Destroy(This->smallBlockRootChain);
2219 BlockChainStream_Destroy(This->rootBlockChain);
2220 BlockChainStream_Destroy(This->smallBlockDepotChain);
2222 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2226 /******************************************************************************
2227 * Storage32Impl_GetNextFreeBigBlock
2229 * Returns the index of the next free big block.
2230 * If the big block depot is filled, this method will enlarge it.
2233 ULONG StorageImpl_GetNextFreeBigBlock(
2236 ULONG depotBlockIndexPos;
2238 ULONG depotBlockOffset;
2239 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2240 ULONG nextBlockIndex = BLOCK_SPECIAL;
2242 ULONG freeBlock = BLOCK_UNUSED;
2244 depotIndex = This->prevFreeBlock / blocksPerDepot;
2245 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2248 * Scan the entire big block depot until we find a block marked free
2250 while (nextBlockIndex != BLOCK_UNUSED)
2252 if (depotIndex < COUNT_BBDEPOTINHEADER)
2254 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2257 * Grow the primary depot.
2259 if (depotBlockIndexPos == BLOCK_UNUSED)
2261 depotBlockIndexPos = depotIndex*blocksPerDepot;
2264 * Add a block depot.
2266 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2267 This->bigBlockDepotCount++;
2268 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2271 * Flag it as a block depot.
2273 StorageImpl_SetNextBlockInChain(This,
2277 /* Save new header information.
2279 StorageImpl_SaveFileHeader(This);
2284 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2286 if (depotBlockIndexPos == BLOCK_UNUSED)
2289 * Grow the extended depot.
2291 ULONG extIndex = BLOCK_UNUSED;
2292 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2293 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2295 if (extBlockOffset == 0)
2297 /* We need an extended block.
2299 extIndex = Storage32Impl_AddExtBlockDepot(This);
2300 This->extBigBlockDepotCount++;
2301 depotBlockIndexPos = extIndex + 1;
2304 depotBlockIndexPos = depotIndex * blocksPerDepot;
2307 * Add a block depot and mark it in the extended block.
2309 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2310 This->bigBlockDepotCount++;
2311 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2313 /* Flag the block depot.
2315 StorageImpl_SetNextBlockInChain(This,
2319 /* If necessary, flag the extended depot block.
2321 if (extIndex != BLOCK_UNUSED)
2322 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2324 /* Save header information.
2326 StorageImpl_SaveFileHeader(This);
2330 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2332 if (depotBuffer != 0)
2334 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2335 ( nextBlockIndex != BLOCK_UNUSED))
2337 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2339 if (nextBlockIndex == BLOCK_UNUSED)
2341 freeBlock = (depotIndex * blocksPerDepot) +
2342 (depotBlockOffset/sizeof(ULONG));
2345 depotBlockOffset += sizeof(ULONG);
2348 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2352 depotBlockOffset = 0;
2355 This->prevFreeBlock = freeBlock;
2360 /******************************************************************************
2361 * Storage32Impl_AddBlockDepot
2363 * This will create a depot block, essentially it is a block initialized
2366 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2370 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2373 * Initialize blocks as free
2375 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2377 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2380 /******************************************************************************
2381 * Storage32Impl_GetExtDepotBlock
2383 * Returns the index of the block that corresponds to the specified depot
2384 * index. This method is only for depot indexes equal or greater than
2385 * COUNT_BBDEPOTINHEADER.
2387 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2389 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2390 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2391 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2392 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2393 ULONG blockIndex = BLOCK_UNUSED;
2394 ULONG extBlockIndex = This->extBigBlockDepotStart;
2396 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2398 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2399 return BLOCK_UNUSED;
2401 while (extBlockCount > 0)
2403 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2407 if (extBlockIndex != BLOCK_UNUSED)
2411 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2413 if (depotBuffer != 0)
2415 StorageUtl_ReadDWord(depotBuffer,
2416 extBlockOffset * sizeof(ULONG),
2419 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2426 /******************************************************************************
2427 * Storage32Impl_SetExtDepotBlock
2429 * Associates the specified block index to the specified depot index.
2430 * This method is only for depot indexes equal or greater than
2431 * COUNT_BBDEPOTINHEADER.
2433 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2437 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2438 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2439 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2440 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2441 ULONG extBlockIndex = This->extBigBlockDepotStart;
2443 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2445 while (extBlockCount > 0)
2447 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2451 if (extBlockIndex != BLOCK_UNUSED)
2455 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2457 if (depotBuffer != 0)
2459 StorageUtl_WriteDWord(depotBuffer,
2460 extBlockOffset * sizeof(ULONG),
2463 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2468 /******************************************************************************
2469 * Storage32Impl_AddExtBlockDepot
2471 * Creates an extended depot block.
2473 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2475 ULONG numExtBlocks = This->extBigBlockDepotCount;
2476 ULONG nextExtBlock = This->extBigBlockDepotStart;
2477 BYTE* depotBuffer = NULL;
2478 ULONG index = BLOCK_UNUSED;
2479 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2480 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2481 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2483 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2484 blocksPerDepotBlock;
2486 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2489 * The first extended block.
2491 This->extBigBlockDepotStart = index;
2497 * Follow the chain to the last one.
2499 for (i = 0; i < (numExtBlocks - 1); i++)
2501 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2505 * Add the new extended block to the chain.
2507 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2508 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2509 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2513 * Initialize this block.
2515 depotBuffer = StorageImpl_GetBigBlock(This, index);
2516 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2517 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2522 /******************************************************************************
2523 * Storage32Impl_FreeBigBlock
2525 * This method will flag the specified block as free in the big block depot.
2527 void StorageImpl_FreeBigBlock(
2531 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2533 if (blockIndex < This->prevFreeBlock)
2534 This->prevFreeBlock = blockIndex;
2537 /************************************************************************
2538 * Storage32Impl_GetNextBlockInChain
2540 * This method will retrieve the block index of the next big block in
2543 * Params: This - Pointer to the Storage object.
2544 * blockIndex - Index of the block to retrieve the chain
2547 * Returns: This method returns the index of the next block in the chain.
2548 * It will return the constants:
2549 * BLOCK_SPECIAL - If the block given was not part of a
2551 * BLOCK_END_OF_CHAIN - If the block given was the last in
2553 * BLOCK_UNUSED - If the block given was not past of a chain
2555 * BLOCK_EXTBBDEPOT - This block is part of the extended
2558 * See Windows documentation for more details on IStorage methods.
2560 ULONG StorageImpl_GetNextBlockInChain(
2564 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2565 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2566 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2567 ULONG nextBlockIndex = BLOCK_SPECIAL;
2569 ULONG depotBlockIndexPos;
2571 assert(depotBlockCount < This->bigBlockDepotCount);
2574 * Cache the currently accessed depot block.
2576 if (depotBlockCount != This->indexBlockDepotCached)
2578 This->indexBlockDepotCached = depotBlockCount;
2580 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2582 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2587 * We have to look in the extended depot.
2589 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2592 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2598 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2600 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2601 This->blockDepotCached[index] = nextBlockIndex;
2604 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2608 nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2610 return nextBlockIndex;
2613 /******************************************************************************
2614 * Storage32Impl_GetNextExtendedBlock
2616 * Given an extended block this method will return the next extended block.
2619 * The last ULONG of an extended block is the block index of the next
2620 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2624 * - The index of the next extended block
2625 * - BLOCK_UNUSED: there is no next extended block.
2626 * - Any other return values denotes failure.
2628 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2630 ULONG nextBlockIndex = BLOCK_SPECIAL;
2631 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2634 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2638 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2640 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2643 return nextBlockIndex;
2646 /******************************************************************************
2647 * Storage32Impl_SetNextBlockInChain
2649 * This method will write the index of the specified block's next block
2650 * in the big block depot.
2652 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2655 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2656 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2657 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2660 void StorageImpl_SetNextBlockInChain(
2665 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2666 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2667 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2668 ULONG depotBlockIndexPos;
2671 assert(depotBlockCount < This->bigBlockDepotCount);
2672 assert(blockIndex != nextBlock);
2674 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2676 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2681 * We have to look in the extended depot.
2683 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2686 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2690 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2691 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2695 * Update the cached block depot, if necessary.
2697 if (depotBlockCount == This->indexBlockDepotCached)
2699 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2703 /******************************************************************************
2704 * Storage32Impl_LoadFileHeader
2706 * This method will read in the file header, i.e. big block index -1.
2708 HRESULT StorageImpl_LoadFileHeader(
2711 HRESULT hr = STG_E_FILENOTFOUND;
2712 void* headerBigBlock = NULL;
2716 * Get a pointer to the big block of data containing the header.
2718 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2721 * Extract the information from the header.
2723 if (headerBigBlock!=0)
2726 * Check for the "magic number" signature and return an error if it is not
2729 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2731 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2732 return STG_E_OLDFORMAT;
2735 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2737 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2738 return STG_E_INVALIDHEADER;
2741 StorageUtl_ReadWord(
2743 OFFSET_BIGBLOCKSIZEBITS,
2744 &This->bigBlockSizeBits);
2746 StorageUtl_ReadWord(
2748 OFFSET_SMALLBLOCKSIZEBITS,
2749 &This->smallBlockSizeBits);
2751 StorageUtl_ReadDWord(
2753 OFFSET_BBDEPOTCOUNT,
2754 &This->bigBlockDepotCount);
2756 StorageUtl_ReadDWord(
2758 OFFSET_ROOTSTARTBLOCK,
2759 &This->rootStartBlock);
2761 StorageUtl_ReadDWord(
2763 OFFSET_SBDEPOTSTART,
2764 &This->smallBlockDepotStart);
2766 StorageUtl_ReadDWord(
2768 OFFSET_EXTBBDEPOTSTART,
2769 &This->extBigBlockDepotStart);
2771 StorageUtl_ReadDWord(
2773 OFFSET_EXTBBDEPOTCOUNT,
2774 &This->extBigBlockDepotCount);
2776 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2778 StorageUtl_ReadDWord(
2780 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2781 &(This->bigBlockDepotStart[index]));
2785 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2789 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2790 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2794 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2795 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2799 * Right now, the code is making some assumptions about the size of the
2800 * blocks, just make sure they are what we're expecting.
2802 assert( (This->bigBlockSize==DEF_BIG_BLOCK_SIZE) &&
2803 (This->smallBlockSize==DEF_SMALL_BLOCK_SIZE));
2806 * Release the block.
2808 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2816 /******************************************************************************
2817 * Storage32Impl_SaveFileHeader
2819 * This method will save to the file the header, i.e. big block -1.
2821 void StorageImpl_SaveFileHeader(
2824 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2829 * Get a pointer to the big block of data containing the header.
2831 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2834 * If the block read failed, the file is probably new.
2839 * Initialize for all unknown fields.
2841 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2844 * Initialize the magic number.
2846 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2849 * And a bunch of things we don't know what they mean
2851 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2852 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2853 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2854 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2855 StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
2859 * Write the information to the header.
2861 if (headerBigBlock!=0)
2863 StorageUtl_WriteWord(
2865 OFFSET_BIGBLOCKSIZEBITS,
2866 This->bigBlockSizeBits);
2868 StorageUtl_WriteWord(
2870 OFFSET_SMALLBLOCKSIZEBITS,
2871 This->smallBlockSizeBits);
2873 StorageUtl_WriteDWord(
2875 OFFSET_BBDEPOTCOUNT,
2876 This->bigBlockDepotCount);
2878 StorageUtl_WriteDWord(
2880 OFFSET_ROOTSTARTBLOCK,
2881 This->rootStartBlock);
2883 StorageUtl_WriteDWord(
2885 OFFSET_SBDEPOTSTART,
2886 This->smallBlockDepotStart);
2888 StorageUtl_WriteDWord(
2890 OFFSET_EXTBBDEPOTSTART,
2891 This->extBigBlockDepotStart);
2893 StorageUtl_WriteDWord(
2895 OFFSET_EXTBBDEPOTCOUNT,
2896 This->extBigBlockDepotCount);
2898 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2900 StorageUtl_WriteDWord(
2902 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2903 (This->bigBlockDepotStart[index]));
2908 * Write the big block back to the file.
2910 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2913 /******************************************************************************
2914 * Storage32Impl_ReadProperty
2916 * This method will read the specified property from the property chain.
2918 BOOL StorageImpl_ReadProperty(
2921 StgProperty* buffer)
2923 BYTE currentProperty[PROPSET_BLOCK_SIZE];
2924 ULARGE_INTEGER offsetInPropSet;
2925 BOOL readSuccessful;
2928 offsetInPropSet.s.HighPart = 0;
2929 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
2931 readSuccessful = BlockChainStream_ReadAt(
2932 This->rootBlockChain,
2940 memset(buffer->name, 0, sizeof(buffer->name));
2943 currentProperty+OFFSET_PS_NAME,
2944 PROPERTY_NAME_BUFFER_LEN );
2946 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
2948 StorageUtl_ReadWord(
2950 OFFSET_PS_NAMELENGTH,
2951 &buffer->sizeOfNameString);
2953 StorageUtl_ReadDWord(
2955 OFFSET_PS_PREVIOUSPROP,
2956 &buffer->previousProperty);
2958 StorageUtl_ReadDWord(
2961 &buffer->nextProperty);
2963 StorageUtl_ReadDWord(
2966 &buffer->dirProperty);
2968 StorageUtl_ReadGUID(
2971 &buffer->propertyUniqueID);
2973 StorageUtl_ReadDWord(
2976 &buffer->timeStampS1);
2978 StorageUtl_ReadDWord(
2981 &buffer->timeStampD1);
2983 StorageUtl_ReadDWord(
2986 &buffer->timeStampS2);
2988 StorageUtl_ReadDWord(
2991 &buffer->timeStampD2);
2993 StorageUtl_ReadDWord(
2995 OFFSET_PS_STARTBLOCK,
2996 &buffer->startingBlock);
2998 StorageUtl_ReadDWord(
3001 &buffer->size.s.LowPart);
3003 buffer->size.s.HighPart = 0;
3006 return readSuccessful;
3009 /*********************************************************************
3010 * Write the specified property into the property chain
3012 BOOL StorageImpl_WriteProperty(
3015 StgProperty* buffer)
3017 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3018 ULARGE_INTEGER offsetInPropSet;
3019 BOOL writeSuccessful;
3022 offsetInPropSet.s.HighPart = 0;
3023 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3025 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3028 currentProperty + OFFSET_PS_NAME,
3030 PROPERTY_NAME_BUFFER_LEN );
3032 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3035 * Reassign the size in case of mistake....
3037 buffer->sizeOfNameString = (lstrlenW(buffer->name)+1) * sizeof(WCHAR);
3039 StorageUtl_WriteWord(
3041 OFFSET_PS_NAMELENGTH,
3042 buffer->sizeOfNameString);
3044 StorageUtl_WriteDWord(
3046 OFFSET_PS_PREVIOUSPROP,
3047 buffer->previousProperty);
3049 StorageUtl_WriteDWord(
3052 buffer->nextProperty);
3054 StorageUtl_WriteDWord(
3057 buffer->dirProperty);
3059 StorageUtl_WriteGUID(
3062 &buffer->propertyUniqueID);
3064 StorageUtl_WriteDWord(
3067 buffer->timeStampS1);
3069 StorageUtl_WriteDWord(
3072 buffer->timeStampD1);
3074 StorageUtl_WriteDWord(
3077 buffer->timeStampS2);
3079 StorageUtl_WriteDWord(
3082 buffer->timeStampD2);
3084 StorageUtl_WriteDWord(
3086 OFFSET_PS_STARTBLOCK,
3087 buffer->startingBlock);
3089 StorageUtl_WriteDWord(
3092 buffer->size.s.LowPart);
3094 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3099 return writeSuccessful;
3102 BOOL StorageImpl_ReadBigBlock(
3107 void* bigBlockBuffer;
3109 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3111 if (bigBlockBuffer!=0)
3113 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3115 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3123 BOOL StorageImpl_WriteBigBlock(
3128 void* bigBlockBuffer;
3130 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3132 if (bigBlockBuffer!=0)
3134 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3136 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3144 void* StorageImpl_GetROBigBlock(
3148 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3151 void* StorageImpl_GetBigBlock(
3155 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3158 void StorageImpl_ReleaseBigBlock(
3162 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3165 /******************************************************************************
3166 * Storage32Impl_SmallBlocksToBigBlocks
3168 * This method will convert a small block chain to a big block chain.
3169 * The small block chain will be destroyed.
3171 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3173 SmallBlockChainStream** ppsbChain)
3175 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3176 ULARGE_INTEGER size, offset;
3177 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3178 ULONG propertyIndex;
3179 BOOL successRead, successWrite;
3180 StgProperty chainProperty;
3182 BlockChainStream *bbTempChain = NULL;
3183 BlockChainStream *bigBlockChain = NULL;
3186 * Create a temporary big block chain that doesn't have
3187 * an associated property. This temporary chain will be
3188 * used to copy data from small blocks to big blocks.
3190 bbTempChain = BlockChainStream_Construct(This,
3195 * Grow the big block chain.
3197 size = SmallBlockChainStream_GetSize(*ppsbChain);
3198 BlockChainStream_SetSize(bbTempChain, size);
3201 * Copy the contents of the small block chain to the big block chain
3202 * by small block size increments.
3204 offset.s.LowPart = 0;
3205 offset.s.HighPart = 0;
3209 buffer = (BYTE *) malloc(DEF_SMALL_BLOCK_SIZE);
3212 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3214 DEF_SMALL_BLOCK_SIZE,
3217 cbTotalRead += cbRead;
3219 successWrite = BlockChainStream_WriteAt(bbTempChain,
3224 cbTotalWritten += cbWritten;
3226 offset.s.LowPart += This->smallBlockSize;
3228 } while (successRead && successWrite);
3231 assert(cbTotalRead == cbTotalWritten);
3234 * Destroy the small block chain.
3236 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3237 size.s.HighPart = 0;
3239 SmallBlockChainStream_SetSize(*ppsbChain, size);
3240 SmallBlockChainStream_Destroy(*ppsbChain);
3244 * Change the property information. This chain is now a big block chain
3245 * and it doesn't reside in the small blocks chain anymore.
3247 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3249 chainProperty.startingBlock = bbHeadOfChain;
3251 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3254 * Destroy the temporary propertyless big block chain.
3255 * Create a new big block chain associated with this property.
3257 BlockChainStream_Destroy(bbTempChain);
3258 bigBlockChain = BlockChainStream_Construct(This,
3262 return bigBlockChain;
3265 /******************************************************************************
3266 ** Storage32InternalImpl implementation
3269 StorageInternalImpl* StorageInternalImpl_Construct(
3270 StorageImpl* ancestorStorage,
3271 ULONG rootPropertyIndex)
3273 StorageInternalImpl* newStorage;
3276 * Allocate space for the new storage object
3278 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3282 memset(newStorage, 0, sizeof(StorageInternalImpl));
3285 * Initialize the virtual function table.
3287 ICOM_VTBL(newStorage) = &Storage32InternalImpl_Vtbl;
3288 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3291 * Keep the ancestor storage pointer and nail a reference to it.
3293 newStorage->ancestorStorage = ancestorStorage;
3294 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3297 * Keep the index of the root property set for this storage,
3299 newStorage->rootPropertySetIndex = rootPropertyIndex;
3307 void StorageInternalImpl_Destroy(
3308 StorageInternalImpl* This)
3310 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3311 HeapFree(GetProcessHeap(), 0, This);
3314 /******************************************************************************
3316 ** Storage32InternalImpl_Commit
3318 ** The non-root storages cannot be opened in transacted mode thus this function
3321 HRESULT WINAPI StorageInternalImpl_Commit(
3323 DWORD grfCommitFlags) /* [in] */
3328 /******************************************************************************
3330 ** Storage32InternalImpl_Revert
3332 ** The non-root storages cannot be opened in transacted mode thus this function
3335 HRESULT WINAPI StorageInternalImpl_Revert(
3341 /******************************************************************************
3342 ** IEnumSTATSTGImpl implementation
3345 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3346 StorageImpl* parentStorage,
3347 ULONG firstPropertyNode)
3349 IEnumSTATSTGImpl* newEnumeration;
3351 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3353 if (newEnumeration!=0)
3356 * Set-up the virtual function table and reference count.
3358 ICOM_VTBL(newEnumeration) = &IEnumSTATSTGImpl_Vtbl;
3359 newEnumeration->ref = 0;
3362 * We want to nail-down the reference to the storage in case the
3363 * enumeration out-lives the storage in the client application.
3365 newEnumeration->parentStorage = parentStorage;
3366 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3368 newEnumeration->firstPropertyNode = firstPropertyNode;
3371 * Initialize the search stack
3373 newEnumeration->stackSize = 0;
3374 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3375 newEnumeration->stackToVisit =
3376 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3379 * Make sure the current node of the iterator is the first one.
3381 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3384 return newEnumeration;
3387 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3389 IStorage_Release((IStorage*)This->parentStorage);
3390 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3391 HeapFree(GetProcessHeap(), 0, This);
3394 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3395 IEnumSTATSTG* iface,
3399 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3402 * Perform a sanity check on the parameters.
3405 return E_INVALIDARG;
3408 * Initialize the return parameter.
3413 * Compare the riid with the interface IDs implemented by this object.
3415 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3417 *ppvObject = (IEnumSTATSTG*)This;
3419 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3421 *ppvObject = (IEnumSTATSTG*)This;
3425 * Check that we obtained an interface.
3427 if ((*ppvObject)==0)
3428 return E_NOINTERFACE;
3431 * Query Interface always increases the reference count by one when it is
3434 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3439 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3440 IEnumSTATSTG* iface)
3442 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3448 ULONG WINAPI IEnumSTATSTGImpl_Release(
3449 IEnumSTATSTG* iface)
3451 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3459 * If the reference count goes down to 0, perform suicide.
3463 IEnumSTATSTGImpl_Destroy(This);
3469 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3470 IEnumSTATSTG* iface,
3473 ULONG* pceltFetched)
3475 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3477 StgProperty currentProperty;
3478 STATSTG* currentReturnStruct = rgelt;
3479 ULONG objectFetched = 0;
3480 ULONG currentSearchNode;
3483 * Perform a sanity check on the parameters.
3485 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3486 return E_INVALIDARG;
3489 * To avoid the special case, get another pointer to a ULONG value if
3490 * the caller didn't supply one.
3492 if (pceltFetched==0)
3493 pceltFetched = &objectFetched;
3496 * Start the iteration, we will iterate until we hit the end of the
3497 * linked list or until we hit the number of items to iterate through
3502 * Start with the node at the top of the stack.
3504 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3506 while ( ( *pceltFetched < celt) &&
3507 ( currentSearchNode!=PROPERTY_NULL) )
3510 * Remove the top node from the stack
3512 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3515 * Read the property from the storage.
3517 StorageImpl_ReadProperty(This->parentStorage,
3522 * Copy the information to the return buffer.
3524 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3529 * Step to the next item in the iteration
3532 currentReturnStruct++;
3535 * Push the next search node in the search stack.
3537 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3540 * continue the iteration.
3542 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3545 if (*pceltFetched == celt)
3552 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3553 IEnumSTATSTG* iface,
3556 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3558 StgProperty currentProperty;
3559 ULONG objectFetched = 0;
3560 ULONG currentSearchNode;
3563 * Start with the node at the top of the stack.
3565 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3567 while ( (objectFetched < celt) &&
3568 (currentSearchNode!=PROPERTY_NULL) )
3571 * Remove the top node from the stack
3573 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3576 * Read the property from the storage.
3578 StorageImpl_ReadProperty(This->parentStorage,
3583 * Step to the next item in the iteration
3588 * Push the next search node in the search stack.
3590 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3593 * continue the iteration.
3595 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3598 if (objectFetched == celt)
3604 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3605 IEnumSTATSTG* iface)
3607 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3609 StgProperty rootProperty;
3610 BOOL readSuccessful;
3613 * Re-initialize the search stack to an empty stack
3615 This->stackSize = 0;
3618 * Read the root property from the storage.
3620 readSuccessful = StorageImpl_ReadProperty(
3621 This->parentStorage,
3622 This->firstPropertyNode,
3627 assert(rootProperty.sizeOfNameString!=0);
3630 * Push the search node in the search stack.
3632 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3638 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3639 IEnumSTATSTG* iface,
3640 IEnumSTATSTG** ppenum)
3642 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3644 IEnumSTATSTGImpl* newClone;
3647 * Perform a sanity check on the parameters.
3650 return E_INVALIDARG;
3652 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3653 This->firstPropertyNode);
3657 * The new clone enumeration must point to the same current node as
3660 newClone->stackSize = This->stackSize ;
3661 newClone->stackMaxSize = This->stackMaxSize ;
3662 newClone->stackToVisit =
3663 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3666 newClone->stackToVisit,
3668 sizeof(ULONG) * newClone->stackSize);
3670 *ppenum = (IEnumSTATSTG*)newClone;
3673 * Don't forget to nail down a reference to the clone before
3676 IEnumSTATSTGImpl_AddRef(*ppenum);
3681 INT IEnumSTATSTGImpl_FindParentProperty(
3682 IEnumSTATSTGImpl *This,
3683 ULONG childProperty,
3684 StgProperty *currentProperty,
3687 ULONG currentSearchNode;
3691 * To avoid the special case, get another pointer to a ULONG value if
3692 * the caller didn't supply one.
3695 thisNodeId = &foundNode;
3698 * Start with the node at the top of the stack.
3700 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3703 while (currentSearchNode!=PROPERTY_NULL)
3706 * Store the current node in the returned parameters
3708 *thisNodeId = currentSearchNode;
3711 * Remove the top node from the stack
3713 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3716 * Read the property from the storage.
3718 StorageImpl_ReadProperty(
3719 This->parentStorage,
3723 if (currentProperty->previousProperty == childProperty)
3724 return PROPERTY_RELATION_PREVIOUS;
3726 else if (currentProperty->nextProperty == childProperty)
3727 return PROPERTY_RELATION_NEXT;
3729 else if (currentProperty->dirProperty == childProperty)
3730 return PROPERTY_RELATION_DIR;
3733 * Push the next search node in the search stack.
3735 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3738 * continue the iteration.
3740 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3743 return PROPERTY_NULL;
3746 ULONG IEnumSTATSTGImpl_FindProperty(
3747 IEnumSTATSTGImpl* This,
3748 const OLECHAR* lpszPropName,
3749 StgProperty* currentProperty)
3751 ULONG currentSearchNode;
3754 * Start with the node at the top of the stack.
3756 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3758 while (currentSearchNode!=PROPERTY_NULL)
3761 * Remove the top node from the stack
3763 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3766 * Read the property from the storage.
3768 StorageImpl_ReadProperty(This->parentStorage,
3772 if ( propertyNameCmp(
3773 (OLECHAR*)currentProperty->name,
3774 (OLECHAR*)lpszPropName) == 0)
3775 return currentSearchNode;
3778 * Push the next search node in the search stack.
3780 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3783 * continue the iteration.
3785 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3788 return PROPERTY_NULL;
3791 void IEnumSTATSTGImpl_PushSearchNode(
3792 IEnumSTATSTGImpl* This,
3795 StgProperty rootProperty;
3796 BOOL readSuccessful;
3799 * First, make sure we're not trying to push an unexisting node.
3801 if (nodeToPush==PROPERTY_NULL)
3805 * First push the node to the stack
3807 if (This->stackSize == This->stackMaxSize)
3809 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3811 This->stackToVisit = HeapReAlloc(
3815 sizeof(ULONG) * This->stackMaxSize);
3818 This->stackToVisit[This->stackSize] = nodeToPush;
3822 * Read the root property from the storage.
3824 readSuccessful = StorageImpl_ReadProperty(
3825 This->parentStorage,
3831 assert(rootProperty.sizeOfNameString!=0);
3834 * Push the previous search node in the search stack.
3836 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3840 ULONG IEnumSTATSTGImpl_PopSearchNode(
3841 IEnumSTATSTGImpl* This,
3846 if (This->stackSize == 0)
3847 return PROPERTY_NULL;
3849 topNode = This->stackToVisit[This->stackSize-1];
3857 /******************************************************************************
3858 ** StorageUtl implementation
3861 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
3863 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
3866 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
3868 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
3871 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
3873 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
3876 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
3878 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
3881 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
3883 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
3884 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
3885 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
3887 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
3890 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
3892 StorageUtl_WriteDWord(buffer, offset, value->Data1);
3893 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
3894 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
3896 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
3899 void StorageUtl_CopyPropertyToSTATSTG(
3900 STATSTG* destination,
3901 StgProperty* source,
3905 * The copy of the string occurs only when the flag is not set
3907 if ((statFlags & STATFLAG_NONAME) != 0)
3909 destination->pwcsName = 0;
3913 destination->pwcsName =
3914 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
3916 lstrcpyW((LPWSTR)destination->pwcsName, source->name);
3919 switch (source->propertyType)
3921 case PROPTYPE_STORAGE:
3923 destination->type = STGTY_STORAGE;
3925 case PROPTYPE_STREAM:
3926 destination->type = STGTY_STREAM;
3929 destination->type = STGTY_STREAM;
3933 destination->cbSize = source->size;
3935 currentReturnStruct->mtime = {0}; TODO
3936 currentReturnStruct->ctime = {0};
3937 currentReturnStruct->atime = {0};
3939 destination->grfMode = 0;
3940 destination->grfLocksSupported = 0;
3941 destination->clsid = source->propertyUniqueID;
3942 destination->grfStateBits = 0;
3943 destination->reserved = 0;
3946 /******************************************************************************
3947 ** BlockChainStream implementation
3950 BlockChainStream* BlockChainStream_Construct(
3951 StorageImpl* parentStorage,
3952 ULONG* headOfStreamPlaceHolder,
3953 ULONG propertyIndex)
3955 BlockChainStream* newStream;
3958 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
3960 newStream->parentStorage = parentStorage;
3961 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
3962 newStream->ownerPropertyIndex = propertyIndex;
3963 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
3964 newStream->tailIndex = BLOCK_END_OF_CHAIN;
3965 newStream->numBlocks = 0;
3967 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
3969 while (blockIndex != BLOCK_END_OF_CHAIN)
3971 newStream->numBlocks++;
3972 newStream->tailIndex = blockIndex;
3974 blockIndex = StorageImpl_GetNextBlockInChain(
3982 void BlockChainStream_Destroy(BlockChainStream* This)
3984 HeapFree(GetProcessHeap(), 0, This);
3987 /******************************************************************************
3988 * BlockChainStream_GetHeadOfChain
3990 * Returns the head of this stream chain.
3991 * Some special chains don't have properties, their heads are kept in
3992 * This->headOfStreamPlaceHolder.
3995 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
3997 StgProperty chainProperty;
3998 BOOL readSuccessful;
4000 if (This->headOfStreamPlaceHolder != 0)
4001 return *(This->headOfStreamPlaceHolder);
4003 if (This->ownerPropertyIndex != PROPERTY_NULL)
4005 readSuccessful = StorageImpl_ReadProperty(
4006 This->parentStorage,
4007 This->ownerPropertyIndex,
4012 return chainProperty.startingBlock;
4016 return BLOCK_END_OF_CHAIN;
4019 /******************************************************************************
4020 * BlockChainStream_GetCount
4022 * Returns the number of blocks that comprises this chain.
4023 * This is not the size of the stream as the last block may not be full!
4026 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4031 blockIndex = BlockChainStream_GetHeadOfChain(This);
4033 while (blockIndex != BLOCK_END_OF_CHAIN)
4037 blockIndex = StorageImpl_GetNextBlockInChain(
4038 This->parentStorage,
4045 /******************************************************************************
4046 * BlockChainStream_ReadAt
4048 * Reads a specified number of bytes from this chain at the specified offset.
4049 * bytesRead may be NULL.
4050 * Failure will be returned if the specified number of bytes has not been read.
4052 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4053 ULARGE_INTEGER offset,
4058 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4059 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4060 ULONG bytesToReadInBuffer;
4063 BYTE* bigBlockBuffer;
4066 * Find the first block in the stream that contains part of the buffer.
4068 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4069 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4070 (blockNoInSequence < This->lastBlockNoInSequence) )
4072 blockIndex = BlockChainStream_GetHeadOfChain(This);
4073 This->lastBlockNoInSequence = blockNoInSequence;
4077 ULONG temp = blockNoInSequence;
4079 blockIndex = This->lastBlockNoInSequenceIndex;
4080 blockNoInSequence -= This->lastBlockNoInSequence;
4081 This->lastBlockNoInSequence = temp;
4084 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4087 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4089 blockNoInSequence--;
4092 This->lastBlockNoInSequenceIndex = blockIndex;
4095 * Start reading the buffer.
4098 bufferWalker = buffer;
4100 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4103 * Calculate how many bytes we can copy from this big block.
4105 bytesToReadInBuffer =
4106 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4109 * Copy those bytes to the buffer
4112 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4114 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4116 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4119 * Step to the next big block.
4122 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4124 bufferWalker += bytesToReadInBuffer;
4125 size -= bytesToReadInBuffer;
4126 *bytesRead += bytesToReadInBuffer;
4127 offsetInBlock = 0; /* There is no offset on the next block */
4134 /******************************************************************************
4135 * BlockChainStream_WriteAt
4137 * Writes the specified number of bytes to this chain at the specified offset.
4138 * bytesWritten may be NULL.
4139 * Will fail if not all specified number of bytes have been written.
4141 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4142 ULARGE_INTEGER offset,
4145 ULONG* bytesWritten)
4147 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4148 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4152 BYTE* bigBlockBuffer;
4155 * Find the first block in the stream that contains part of the buffer.
4157 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4158 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4159 (blockNoInSequence < This->lastBlockNoInSequence) )
4161 blockIndex = BlockChainStream_GetHeadOfChain(This);
4162 This->lastBlockNoInSequence = blockNoInSequence;
4166 ULONG temp = blockNoInSequence;
4168 blockIndex = This->lastBlockNoInSequenceIndex;
4169 blockNoInSequence -= This->lastBlockNoInSequence;
4170 This->lastBlockNoInSequence = temp;
4173 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4176 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4178 blockNoInSequence--;
4181 This->lastBlockNoInSequenceIndex = blockIndex;
4184 * Here, I'm casting away the constness on the buffer variable
4185 * This is OK since we don't intend to modify that buffer.
4188 bufferWalker = (BYTE*)buffer;
4190 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4193 * Calculate how many bytes we can copy from this big block.
4196 MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4199 * Copy those bytes to the buffer
4201 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4203 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4205 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4208 * Step to the next big block.
4211 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4213 bufferWalker += bytesToWrite;
4214 size -= bytesToWrite;
4215 *bytesWritten += bytesToWrite;
4216 offsetInBlock = 0; /* There is no offset on the next block */
4222 /******************************************************************************
4223 * BlockChainStream_Shrink
4225 * Shrinks this chain in the big block depot.
4227 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4228 ULARGE_INTEGER newSize)
4230 ULONG blockIndex, extraBlock;
4235 * Reset the last accessed block cache.
4237 This->lastBlockNoInSequence = 0xFFFFFFFF;
4238 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4241 * Figure out how many blocks are needed to contain the new size
4243 numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4245 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4248 blockIndex = BlockChainStream_GetHeadOfChain(This);
4251 * Go to the new end of chain
4253 while (count < numBlocks)
4256 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4261 /* Get the next block before marking the new end */
4263 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4265 /* Mark the new end of chain */
4266 StorageImpl_SetNextBlockInChain(
4267 This->parentStorage,
4269 BLOCK_END_OF_CHAIN);
4271 This->tailIndex = blockIndex;
4272 This->numBlocks = numBlocks;
4275 * Mark the extra blocks as free
4277 while (extraBlock != BLOCK_END_OF_CHAIN)
4280 StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4282 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4283 extraBlock = blockIndex;
4289 /******************************************************************************
4290 * BlockChainStream_Enlarge
4292 * Grows this chain in the big block depot.
4294 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4295 ULARGE_INTEGER newSize)
4297 ULONG blockIndex, currentBlock;
4299 ULONG oldNumBlocks = 0;
4301 blockIndex = BlockChainStream_GetHeadOfChain(This);
4304 * Empty chain. Create the head.
4306 if (blockIndex == BLOCK_END_OF_CHAIN)
4308 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4309 StorageImpl_SetNextBlockInChain(This->parentStorage,
4311 BLOCK_END_OF_CHAIN);
4313 if (This->headOfStreamPlaceHolder != 0)
4315 *(This->headOfStreamPlaceHolder) = blockIndex;
4319 StgProperty chainProp;
4320 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4322 StorageImpl_ReadProperty(
4323 This->parentStorage,
4324 This->ownerPropertyIndex,
4327 chainProp.startingBlock = blockIndex;
4329 StorageImpl_WriteProperty(
4330 This->parentStorage,
4331 This->ownerPropertyIndex,
4335 This->tailIndex = blockIndex;
4336 This->numBlocks = 1;
4340 * Figure out how many blocks are needed to contain this stream
4342 newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4344 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4348 * Go to the current end of chain
4350 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4352 currentBlock = blockIndex;
4354 while (blockIndex != BLOCK_END_OF_CHAIN)
4357 currentBlock = blockIndex;
4360 StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4363 This->tailIndex = currentBlock;
4366 currentBlock = This->tailIndex;
4367 oldNumBlocks = This->numBlocks;
4370 * Add new blocks to the chain
4372 if (oldNumBlocks < newNumBlocks)
4374 while (oldNumBlocks < newNumBlocks)
4376 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4378 StorageImpl_SetNextBlockInChain(
4379 This->parentStorage,
4383 StorageImpl_SetNextBlockInChain(
4384 This->parentStorage,
4386 BLOCK_END_OF_CHAIN);
4388 currentBlock = blockIndex;
4392 This->tailIndex = blockIndex;
4393 This->numBlocks = newNumBlocks;
4399 /******************************************************************************
4400 * BlockChainStream_SetSize
4402 * Sets the size of this stream. The big block depot will be updated.
4403 * The file will grow if we grow the chain.
4405 * TODO: Free the actual blocks in the file when we shrink the chain.
4406 * Currently, the blocks are still in the file. So the file size
4407 * doesn't shrink even if we shrink streams.
4409 BOOL BlockChainStream_SetSize(
4410 BlockChainStream* This,
4411 ULARGE_INTEGER newSize)
4413 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4415 if (newSize.s.LowPart == size.s.LowPart)
4418 if (newSize.s.LowPart < size.s.LowPart)
4420 BlockChainStream_Shrink(This, newSize);
4424 ULARGE_INTEGER fileSize =
4425 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4427 ULONG diff = newSize.s.LowPart - size.s.LowPart;
4430 * Make sure the file stays a multiple of blocksize
4432 if ((diff % This->parentStorage->bigBlockSize) != 0)
4433 diff += (This->parentStorage->bigBlockSize -
4434 (diff % This->parentStorage->bigBlockSize) );
4436 fileSize.s.LowPart += diff;
4437 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4439 BlockChainStream_Enlarge(This, newSize);
4445 /******************************************************************************
4446 * BlockChainStream_GetSize
4448 * Returns the size of this chain.
4449 * Will return the block count if this chain doesn't have a property.
4451 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4453 StgProperty chainProperty;
4455 if(This->headOfStreamPlaceHolder == NULL)
4458 * This chain is a data stream read the property and return
4459 * the appropriate size
4461 StorageImpl_ReadProperty(
4462 This->parentStorage,
4463 This->ownerPropertyIndex,
4466 return chainProperty.size;
4471 * this chain is a chain that does not have a property, figure out the
4472 * size by making the product number of used blocks times the
4475 ULARGE_INTEGER result;
4476 result.s.HighPart = 0;
4479 BlockChainStream_GetCount(This) *
4480 This->parentStorage->bigBlockSize;
4486 /******************************************************************************
4487 ** SmallBlockChainStream implementation
4490 SmallBlockChainStream* SmallBlockChainStream_Construct(
4491 StorageImpl* parentStorage,
4492 ULONG propertyIndex)
4494 SmallBlockChainStream* newStream;
4496 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4498 newStream->parentStorage = parentStorage;
4499 newStream->ownerPropertyIndex = propertyIndex;
4504 void SmallBlockChainStream_Destroy(
4505 SmallBlockChainStream* This)
4507 HeapFree(GetProcessHeap(), 0, This);
4510 /******************************************************************************
4511 * SmallBlockChainStream_GetHeadOfChain
4513 * Returns the head of this chain of small blocks.
4515 ULONG SmallBlockChainStream_GetHeadOfChain(
4516 SmallBlockChainStream* This)
4518 StgProperty chainProperty;
4519 BOOL readSuccessful;
4521 if (This->ownerPropertyIndex)
4523 readSuccessful = StorageImpl_ReadProperty(
4524 This->parentStorage,
4525 This->ownerPropertyIndex,
4530 return chainProperty.startingBlock;
4535 return BLOCK_END_OF_CHAIN;
4538 /******************************************************************************
4539 * SmallBlockChainStream_GetNextBlockInChain
4541 * Returns the index of the next small block in this chain.
4544 * - BLOCK_END_OF_CHAIN: end of this chain
4545 * - BLOCK_UNUSED: small block 'blockIndex' is free
4547 ULONG SmallBlockChainStream_GetNextBlockInChain(
4548 SmallBlockChainStream* This,
4551 ULARGE_INTEGER offsetOfBlockInDepot;
4553 ULONG nextBlockInChain = BLOCK_END_OF_CHAIN;
4557 offsetOfBlockInDepot.s.HighPart = 0;
4558 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4561 * Read those bytes in the buffer from the small block file.
4563 success = BlockChainStream_ReadAt(
4564 This->parentStorage->smallBlockDepotChain,
4565 offsetOfBlockInDepot,
4572 StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4575 return nextBlockInChain;
4578 /******************************************************************************
4579 * SmallBlockChainStream_SetNextBlockInChain
4581 * Writes the index of the next block of the specified block in the small
4583 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4584 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4586 void SmallBlockChainStream_SetNextBlockInChain(
4587 SmallBlockChainStream* This,
4591 ULARGE_INTEGER offsetOfBlockInDepot;
4595 offsetOfBlockInDepot.s.HighPart = 0;
4596 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4598 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4601 * Read those bytes in the buffer from the small block file.
4603 BlockChainStream_WriteAt(
4604 This->parentStorage->smallBlockDepotChain,
4605 offsetOfBlockInDepot,
4611 /******************************************************************************
4612 * SmallBlockChainStream_FreeBlock
4614 * Flag small block 'blockIndex' as free in the small block depot.
4616 void SmallBlockChainStream_FreeBlock(
4617 SmallBlockChainStream* This,
4620 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4623 /******************************************************************************
4624 * SmallBlockChainStream_GetNextFreeBlock
4626 * Returns the index of a free small block. The small block depot will be
4627 * enlarged if necessary. The small block chain will also be enlarged if
4630 ULONG SmallBlockChainStream_GetNextFreeBlock(
4631 SmallBlockChainStream* This)
4633 ULARGE_INTEGER offsetOfBlockInDepot;
4636 ULONG blockIndex = 0;
4637 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4638 BOOL success = TRUE;
4639 ULONG smallBlocksPerBigBlock;
4641 offsetOfBlockInDepot.s.HighPart = 0;
4644 * Scan the small block depot for a free block
4646 while (nextBlockIndex != BLOCK_UNUSED)
4648 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4650 success = BlockChainStream_ReadAt(
4651 This->parentStorage->smallBlockDepotChain,
4652 offsetOfBlockInDepot,
4658 * If we run out of space for the small block depot, enlarge it
4662 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4664 if (nextBlockIndex != BLOCK_UNUSED)
4670 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4672 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4673 ULONG nextBlock, newsbdIndex;
4674 BYTE* smallBlockDepot;
4676 nextBlock = sbdIndex;
4677 while (nextBlock != BLOCK_END_OF_CHAIN)
4679 sbdIndex = nextBlock;
4681 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4684 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4685 if (sbdIndex != BLOCK_END_OF_CHAIN)
4686 StorageImpl_SetNextBlockInChain(
4687 This->parentStorage,
4691 StorageImpl_SetNextBlockInChain(
4692 This->parentStorage,
4694 BLOCK_END_OF_CHAIN);
4697 * Initialize all the small blocks to free
4700 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4702 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4703 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4708 * We have just created the small block depot.
4710 StgProperty rootProp;
4714 * Save it in the header
4716 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4717 StorageImpl_SaveFileHeader(This->parentStorage);
4720 * And allocate the first big block that will contain small blocks
4723 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4725 StorageImpl_SetNextBlockInChain(
4726 This->parentStorage,
4728 BLOCK_END_OF_CHAIN);
4730 StorageImpl_ReadProperty(
4731 This->parentStorage,
4732 This->parentStorage->rootPropertySetIndex,
4735 rootProp.startingBlock = sbStartIndex;
4736 rootProp.size.s.HighPart = 0;
4737 rootProp.size.s.LowPart = This->parentStorage->bigBlockSize;
4739 StorageImpl_WriteProperty(
4740 This->parentStorage,
4741 This->parentStorage->rootPropertySetIndex,
4747 smallBlocksPerBigBlock =
4748 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4751 * Verify if we have to allocate big blocks to contain small blocks
4753 if (blockIndex % smallBlocksPerBigBlock == 0)
4755 StgProperty rootProp;
4756 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4758 StorageImpl_ReadProperty(
4759 This->parentStorage,
4760 This->parentStorage->rootPropertySetIndex,
4763 if (rootProp.size.s.LowPart <
4764 (blocksRequired * This->parentStorage->bigBlockSize))
4766 rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4768 BlockChainStream_SetSize(
4769 This->parentStorage->smallBlockRootChain,
4772 StorageImpl_WriteProperty(
4773 This->parentStorage,
4774 This->parentStorage->rootPropertySetIndex,
4782 /******************************************************************************
4783 * SmallBlockChainStream_ReadAt
4785 * Reads a specified number of bytes from this chain at the specified offset.
4786 * bytesRead may be NULL.
4787 * Failure will be returned if the specified number of bytes has not been read.
4789 BOOL SmallBlockChainStream_ReadAt(
4790 SmallBlockChainStream* This,
4791 ULARGE_INTEGER offset,
4796 ULARGE_INTEGER offsetInBigBlockFile;
4797 ULONG blockNoInSequence =
4798 offset.s.LowPart / This->parentStorage->smallBlockSize;
4800 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4801 ULONG bytesToReadInBuffer;
4803 ULONG bytesReadFromBigBlockFile;
4807 * This should never happen on a small block file.
4809 assert(offset.s.HighPart==0);
4812 * Find the first block in the stream that contains part of the buffer.
4814 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4816 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4818 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4820 blockNoInSequence--;
4824 * Start reading the buffer.
4827 bufferWalker = buffer;
4829 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4832 * Calculate how many bytes we can copy from this small block.
4834 bytesToReadInBuffer =
4835 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4838 * Calculate the offset of the small block in the small block file.
4840 offsetInBigBlockFile.s.HighPart = 0;
4841 offsetInBigBlockFile.s.LowPart =
4842 blockIndex * This->parentStorage->smallBlockSize;
4844 offsetInBigBlockFile.s.LowPart += offsetInBlock;
4847 * Read those bytes in the buffer from the small block file.
4849 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
4850 offsetInBigBlockFile,
4851 bytesToReadInBuffer,
4853 &bytesReadFromBigBlockFile);
4855 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
4858 * Step to the next big block.
4860 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4861 bufferWalker += bytesToReadInBuffer;
4862 size -= bytesToReadInBuffer;
4863 *bytesRead += bytesToReadInBuffer;
4864 offsetInBlock = 0; /* There is no offset on the next block */
4870 /******************************************************************************
4871 * SmallBlockChainStream_WriteAt
4873 * Writes the specified number of bytes to this chain at the specified offset.
4874 * bytesWritten may be NULL.
4875 * Will fail if not all specified number of bytes have been written.
4877 BOOL SmallBlockChainStream_WriteAt(
4878 SmallBlockChainStream* This,
4879 ULARGE_INTEGER offset,
4882 ULONG* bytesWritten)
4884 ULARGE_INTEGER offsetInBigBlockFile;
4885 ULONG blockNoInSequence =
4886 offset.s.LowPart / This->parentStorage->smallBlockSize;
4888 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4889 ULONG bytesToWriteInBuffer;
4891 ULONG bytesWrittenFromBigBlockFile;
4895 * This should never happen on a small block file.
4897 assert(offset.s.HighPart==0);
4900 * Find the first block in the stream that contains part of the buffer.
4902 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4904 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4906 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4908 blockNoInSequence--;
4912 * Start writing the buffer.
4914 * Here, I'm casting away the constness on the buffer variable
4915 * This is OK since we don't intend to modify that buffer.
4918 bufferWalker = (BYTE*)buffer;
4919 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4922 * Calculate how many bytes we can copy to this small block.
4924 bytesToWriteInBuffer =
4925 MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4928 * Calculate the offset of the small block in the small block file.
4930 offsetInBigBlockFile.s.HighPart = 0;
4931 offsetInBigBlockFile.s.LowPart =
4932 blockIndex * This->parentStorage->smallBlockSize;
4934 offsetInBigBlockFile.s.LowPart += offsetInBlock;
4937 * Write those bytes in the buffer to the small block file.
4939 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
4940 offsetInBigBlockFile,
4941 bytesToWriteInBuffer,
4943 &bytesWrittenFromBigBlockFile);
4945 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
4948 * Step to the next big block.
4950 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4951 bufferWalker += bytesToWriteInBuffer;
4952 size -= bytesToWriteInBuffer;
4953 *bytesWritten += bytesToWriteInBuffer;
4954 offsetInBlock = 0; /* There is no offset on the next block */
4960 /******************************************************************************
4961 * SmallBlockChainStream_Shrink
4963 * Shrinks this chain in the small block depot.
4965 BOOL SmallBlockChainStream_Shrink(
4966 SmallBlockChainStream* This,
4967 ULARGE_INTEGER newSize)
4969 ULONG blockIndex, extraBlock;
4973 numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
4975 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
4978 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4981 * Go to the new end of chain
4983 while (count < numBlocks)
4985 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4990 * If the count is 0, we have a special case, the head of the chain was
4995 StgProperty chainProp;
4997 StorageImpl_ReadProperty(This->parentStorage,
4998 This->ownerPropertyIndex,
5001 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5003 StorageImpl_WriteProperty(This->parentStorage,
5004 This->ownerPropertyIndex,
5008 * We start freeing the chain at the head block.
5010 extraBlock = blockIndex;
5014 /* Get the next block before marking the new end */
5015 extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5017 /* Mark the new end of chain */
5018 SmallBlockChainStream_SetNextBlockInChain(
5021 BLOCK_END_OF_CHAIN);
5025 * Mark the extra blocks as free
5027 while (extraBlock != BLOCK_END_OF_CHAIN)
5029 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
5030 SmallBlockChainStream_FreeBlock(This, extraBlock);
5031 extraBlock = blockIndex;
5037 /******************************************************************************
5038 * SmallBlockChainStream_Enlarge
5040 * Grows this chain in the small block depot.
5042 BOOL SmallBlockChainStream_Enlarge(
5043 SmallBlockChainStream* This,
5044 ULARGE_INTEGER newSize)
5046 ULONG blockIndex, currentBlock;
5048 ULONG oldNumBlocks = 0;
5050 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5055 if (blockIndex == BLOCK_END_OF_CHAIN)
5058 StgProperty chainProp;
5060 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5063 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5065 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5068 blockIndex = chainProp.startingBlock;
5069 SmallBlockChainStream_SetNextBlockInChain(
5072 BLOCK_END_OF_CHAIN);
5075 currentBlock = blockIndex;
5078 * Figure out how many blocks are needed to contain this stream
5080 newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5082 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5086 * Go to the current end of chain
5088 while (blockIndex != BLOCK_END_OF_CHAIN)
5091 currentBlock = blockIndex;
5092 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5096 * Add new blocks to the chain
5098 while (oldNumBlocks < newNumBlocks)
5100 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5101 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5103 SmallBlockChainStream_SetNextBlockInChain(
5106 BLOCK_END_OF_CHAIN);
5108 currentBlock = blockIndex;
5115 /******************************************************************************
5116 * SmallBlockChainStream_GetCount
5118 * Returns the number of blocks that comprises this chain.
5119 * This is not the size of this chain as the last block may not be full!
5121 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5126 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5128 while (blockIndex != BLOCK_END_OF_CHAIN)
5132 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5138 /******************************************************************************
5139 * SmallBlockChainStream_SetSize
5141 * Sets the size of this stream.
5142 * The file will grow if we grow the chain.
5144 * TODO: Free the actual blocks in the file when we shrink the chain.
5145 * Currently, the blocks are still in the file. So the file size
5146 * doesn't shrink even if we shrink streams.
5148 BOOL SmallBlockChainStream_SetSize(
5149 SmallBlockChainStream* This,
5150 ULARGE_INTEGER newSize)
5152 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5154 if (newSize.s.LowPart == size.s.LowPart)
5157 if (newSize.s.LowPart < size.s.LowPart)
5159 SmallBlockChainStream_Shrink(This, newSize);
5163 SmallBlockChainStream_Enlarge(This, newSize);
5169 /******************************************************************************
5170 * SmallBlockChainStream_GetSize
5172 * Returns the size of this chain.
5174 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5176 StgProperty chainProperty;
5178 StorageImpl_ReadProperty(
5179 This->parentStorage,
5180 This->ownerPropertyIndex,
5183 return chainProperty.size;
5186 /******************************************************************************
5187 * StgCreateDocfile32 [OLE32.144]
5189 HRESULT WINAPI StgCreateDocfile(
5193 IStorage **ppstgOpen)
5195 StorageImpl* newStorage = 0;
5196 HANDLE hFile = INVALID_HANDLE_VALUE;
5201 DWORD fileAttributes;
5202 WCHAR tempFileName[MAX_PATH];
5204 TRACE("(%s, %lx, %ld, %p)\n",
5205 debugstr_w(pwcsName), grfMode,
5206 reserved, ppstgOpen);
5209 * Validate the parameters
5212 return STG_E_INVALIDPOINTER;
5215 * Validate the STGM flags
5217 if ( FAILED( validateSTGM(grfMode) ))
5218 return STG_E_INVALIDFLAG;
5221 * Generate a unique name.
5225 WCHAR tempPath[MAX_PATH];
5226 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5228 memset(tempPath, 0, sizeof(tempPath));
5229 memset(tempFileName, 0, sizeof(tempFileName));
5231 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5234 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5235 pwcsName = tempFileName;
5237 return STG_E_INSUFFICIENTMEMORY;
5241 * Interpret the STGM value grfMode
5243 shareMode = GetShareModeFromSTGM(grfMode);
5244 accessMode = GetAccessModeFromSTGM(grfMode);
5245 creationMode = GetCreationModeFromSTGM(grfMode);
5247 if (grfMode & STGM_DELETEONRELEASE)
5248 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5250 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5252 if (grfMode & STGM_TRANSACTED)
5253 FIXME("Transacted mode not implemented.\n");
5256 * Initialize the "out" parameter.
5260 hFile = CreateFileW(pwcsName,
5268 if (hFile == INVALID_HANDLE_VALUE)
5274 * Allocate and initialize the new IStorage32object.
5276 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5278 if (newStorage == 0)
5279 return STG_E_INSUFFICIENTMEMORY;
5281 hr = StorageImpl_Construct(
5290 HeapFree(GetProcessHeap(), 0, newStorage);
5295 * Get an "out" pointer for the caller.
5297 hr = StorageBaseImpl_QueryInterface(
5298 (IStorage*)newStorage,
5299 (REFIID)&IID_IStorage,
5305 /******************************************************************************
5306 * StgOpenStorage32 [OLE32.148]
5308 HRESULT WINAPI StgOpenStorage(
5309 const OLECHAR *pwcsName,
5310 IStorage *pstgPriority,
5314 IStorage **ppstgOpen)
5316 StorageImpl* newStorage = 0;
5322 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5323 debugstr_w(pwcsName), pstgPriority, grfMode,
5324 snbExclude, reserved, ppstgOpen);
5327 * Perform a sanity check
5329 if (( pwcsName == 0) || (ppstgOpen == 0) )
5330 return STG_E_INVALIDPOINTER;
5333 * Validate the STGM flags
5335 if ( FAILED( validateSTGM(grfMode) ))
5336 return STG_E_INVALIDFLAG;
5339 * Interpret the STGM value grfMode
5341 shareMode = GetShareModeFromSTGM(grfMode);
5342 accessMode = GetAccessModeFromSTGM(grfMode);
5345 * Initialize the "out" parameter.
5349 hFile = CreateFileW( pwcsName,
5354 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5358 if (hFile==INVALID_HANDLE_VALUE)
5364 * Allocate and initialize the new IStorage32object.
5366 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5368 if (newStorage == 0)
5369 return STG_E_INSUFFICIENTMEMORY;
5371 hr = StorageImpl_Construct(
5380 HeapFree(GetProcessHeap(), 0, newStorage);
5385 * Get an "out" pointer for the caller.
5387 hr = StorageBaseImpl_QueryInterface(
5388 (IStorage*)newStorage,
5389 (REFIID)&IID_IStorage,
5395 /******************************************************************************
5396 * StgCreateDocfileOnILockBytes [OLE32.145]
5398 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5402 IStorage** ppstgOpen)
5404 StorageImpl* newStorage = 0;
5408 * Validate the parameters
5410 if ((ppstgOpen == 0) || (plkbyt == 0))
5411 return STG_E_INVALIDPOINTER;
5414 * Allocate and initialize the new IStorage object.
5416 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5418 if (newStorage == 0)
5419 return STG_E_INSUFFICIENTMEMORY;
5421 hr = StorageImpl_Construct(
5430 HeapFree(GetProcessHeap(), 0, newStorage);
5435 * Get an "out" pointer for the caller.
5437 hr = StorageBaseImpl_QueryInterface(
5438 (IStorage*)newStorage,
5439 (REFIID)&IID_IStorage,
5445 /******************************************************************************
5446 * StgOpenStorageOnILockBytes [OLE32.149]
5448 HRESULT WINAPI StgOpenStorageOnILockBytes(
5450 IStorage *pstgPriority,
5454 IStorage **ppstgOpen)
5456 StorageImpl* newStorage = 0;
5460 * Perform a sanity check
5462 if ((plkbyt == 0) || (ppstgOpen == 0))
5463 return STG_E_INVALIDPOINTER;
5466 * Validate the STGM flags
5468 if ( FAILED( validateSTGM(grfMode) ))
5469 return STG_E_INVALIDFLAG;
5472 * Initialize the "out" parameter.
5477 * Allocate and initialize the new IStorage object.
5479 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5481 if (newStorage == 0)
5482 return STG_E_INSUFFICIENTMEMORY;
5484 hr = StorageImpl_Construct(
5493 HeapFree(GetProcessHeap(), 0, newStorage);
5498 * Get an "out" pointer for the caller.
5500 hr = StorageBaseImpl_QueryInterface(
5501 (IStorage*)newStorage,
5502 (REFIID)&IID_IStorage,
5508 /******************************************************************************
5509 * StgSetTimes [ole32.150]
5513 HRESULT WINAPI StgSetTimes(WCHAR * str, FILETIME * a, FILETIME * b, FILETIME *c )
5516 FIXME("(%p, %p, %p, %p),stub!\n", str, a, b, c);
5520 /******************************************************************************
5521 * StgIsStorageILockBytes [OLE32.147]
5523 * Determines if the ILockBytes contains a storage object.
5525 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5528 ULARGE_INTEGER offset;
5530 offset.s.HighPart = 0;
5531 offset.s.LowPart = 0;
5533 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5535 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5541 /******************************************************************************
5542 * WriteClassStg32 [OLE32.158]
5544 * This method will store the specified CLSID in the specified storage object
5546 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5552 hRes = IStorage_SetClass(pStg, rclsid);
5557 /*******************************************************************************************
5560 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5562 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5572 * read a STATSTG structure (contains the clsid) from the storage
5574 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5577 *pclsid=pstatstg.clsid;
5582 /*************************************************************************************
5585 * This function loads an object from stream
5587 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5592 FIXME("(),stub!\n");
5594 res=ReadClassStm(pStm,&clsid);
5596 if (SUCCEEDED(res)){
5598 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5602 res=IPersistStream_Load((IPersistStream*)ppvObj,pStm);
5608 /************************************************************************************************
5611 * This function saves an object with the IPersistStream interface on it to the specified stream
5613 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5619 TRACE("(%p,%p)\n",pPStm,pStm);
5621 res=IPersistStream_GetClassID(pPStm,&clsid);
5623 if (SUCCEEDED(res)){
5625 res=WriteClassStm(pStm,&clsid);
5629 res=IPersistStream_Save(pPStm,pStm,FALSE);
5635 /****************************************************************************
5636 * This method validate a STGM parameter that can contain the values below
5638 * STGM_DIRECT 0x00000000
5639 * STGM_TRANSACTED 0x00010000
5640 * STGM_SIMPLE 0x08000000
5642 * STGM_READ 0x00000000
5643 * STGM_WRITE 0x00000001
5644 * STGM_READWRITE 0x00000002
5646 * STGM_SHARE_DENY_NONE 0x00000040
5647 * STGM_SHARE_DENY_READ 0x00000030
5648 * STGM_SHARE_DENY_WRITE 0x00000020
5649 * STGM_SHARE_EXCLUSIVE 0x00000010
5651 * STGM_PRIORITY 0x00040000
5652 * STGM_DELETEONRELEASE 0x04000000
5654 * STGM_CREATE 0x00001000
5655 * STGM_CONVERT 0x00020000
5656 * STGM_FAILIFTHERE 0x00000000
5658 * STGM_NOSCRATCH 0x00100000
5659 * STGM_NOSNAPSHOT 0x00200000
5661 static HRESULT validateSTGM(DWORD stgm)
5663 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5664 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5665 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5667 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5668 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5669 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5671 BOOL bSTGM_SHARE_DENY_NONE =
5672 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5674 BOOL bSTGM_SHARE_DENY_READ =
5675 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5677 BOOL bSTGM_SHARE_DENY_WRITE =
5678 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5680 BOOL bSTGM_SHARE_EXCLUSIVE =
5681 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5683 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5684 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5686 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5687 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5690 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5692 if ( ! bSTGM_DIRECT )
5693 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5697 * STGM_WRITE | STGM_READWRITE | STGM_READ
5700 if( bSTGM_WRITE && bSTGM_READWRITE )
5704 * STGM_SHARE_DENY_NONE | others
5705 * (I assume here that DENY_READ implies DENY_WRITE)
5707 if ( bSTGM_SHARE_DENY_NONE )
5708 if ( bSTGM_SHARE_DENY_READ ||
5709 bSTGM_SHARE_DENY_WRITE ||
5710 bSTGM_SHARE_EXCLUSIVE)
5714 * STGM_CREATE | STGM_CONVERT
5715 * if both are false, STGM_FAILIFTHERE is set to TRUE
5717 if ( bSTGM_CREATE && bSTGM_CONVERT )
5721 * STGM_NOSCRATCH requires STGM_TRANSACTED
5723 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5727 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5728 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5730 if (bSTGM_NOSNAPSHOT)
5732 if ( ! ( bSTGM_TRANSACTED &&
5733 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5740 /****************************************************************************
5741 * GetShareModeFromSTGM
5743 * This method will return a share mode flag from a STGM value.
5744 * The STGM value is assumed valid.
5746 static DWORD GetShareModeFromSTGM(DWORD stgm)
5748 DWORD dwShareMode = 0;
5749 BOOL bSTGM_SHARE_DENY_NONE =
5750 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5752 BOOL bSTGM_SHARE_DENY_READ =
5753 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5755 BOOL bSTGM_SHARE_DENY_WRITE =
5756 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5758 BOOL bSTGM_SHARE_EXCLUSIVE =
5759 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5761 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5764 if (bSTGM_SHARE_DENY_NONE)
5765 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5767 if (bSTGM_SHARE_DENY_WRITE)
5768 dwShareMode = FILE_SHARE_READ;
5773 /****************************************************************************
5774 * GetAccessModeFromSTGM
5776 * This method will return an access mode flag from a STGM value.
5777 * The STGM value is assumed valid.
5779 static DWORD GetAccessModeFromSTGM(DWORD stgm)
5781 DWORD dwDesiredAccess = GENERIC_READ;
5782 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5783 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5784 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5787 dwDesiredAccess = GENERIC_READ;
5790 dwDesiredAccess |= GENERIC_WRITE;
5792 if (bSTGM_READWRITE)
5793 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5795 return dwDesiredAccess;
5798 /****************************************************************************
5799 * GetCreationModeFromSTGM
5801 * This method will return a creation mode flag from a STGM value.
5802 * The STGM value is assumed valid.
5804 static DWORD GetCreationModeFromSTGM(DWORD stgm)
5806 if ( stgm & STGM_CREATE)
5807 return CREATE_ALWAYS;
5808 if (stgm & STGM_CONVERT) {
5809 FIXME("STGM_CONVERT not implemented!\n");
5812 /* All other cases */
5813 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5814 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));