Added the DFCS_{HOT,TRANSPARENT} definitions.
[wine/gsoc_dplay.git] / dlls / ole32 / storage32.c
blobc73e407165bac672ff29b795755b57a45746e4a1
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <assert.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
32 #include "winbase.h" /* for lstrlenW() and the likes */
33 #include "winnls.h"
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
37 #include "storage32.h"
38 #include "ole2.h" /* For Write/ReadClassStm */
40 #include "winreg.h"
41 #include "wine/wingdi16.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(storage);
45 #define FILE_BEGIN 0
48 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
49 #define OLESTREAM_ID 0x501
50 #define OLESTREAM_MAX_STR_LEN 255
52 static const char rootPropertyName[] = "Root Entry";
55 /* OLESTREAM memory structure to use for Get and Put Routines */
56 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
57 typedef struct
59 DWORD dwOleID;
60 DWORD dwTypeID;
61 DWORD dwOleTypeNameLength;
62 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
63 CHAR *pstrOleObjFileName;
64 DWORD dwOleObjFileNameLength;
65 DWORD dwMetaFileWidth;
66 DWORD dwMetaFileHeight;
67 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
68 DWORD dwDataLength;
69 BYTE *pData;
70 }OLECONVERT_OLESTREAM_DATA;
72 /* CompObj Stream structure */
73 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
74 typedef struct
76 BYTE byUnknown1[12];
77 CLSID clsid;
78 DWORD dwCLSIDNameLength;
79 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
80 DWORD dwOleTypeNameLength;
81 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
82 DWORD dwProgIDNameLength;
83 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
84 BYTE byUnknown2[16];
85 }OLECONVERT_ISTORAGE_COMPOBJ;
88 /* Ole Presention Stream structure */
89 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
90 typedef struct
92 BYTE byUnknown1[28];
93 DWORD dwExtentX;
94 DWORD dwExtentY;
95 DWORD dwSize;
96 BYTE *pData;
97 }OLECONVERT_ISTORAGE_OLEPRES;
101 /***********************************************************************
102 * Forward declaration of internal functions used by the method DestroyElement
104 static HRESULT deleteStorageProperty(
105 StorageImpl *parentStorage,
106 ULONG foundPropertyIndexToDelete,
107 StgProperty propertyToDelete);
109 static HRESULT deleteStreamProperty(
110 StorageImpl *parentStorage,
111 ULONG foundPropertyIndexToDelete,
112 StgProperty propertyToDelete);
114 static HRESULT findPlaceholder(
115 StorageImpl *storage,
116 ULONG propertyIndexToStore,
117 ULONG storagePropertyIndex,
118 INT typeOfRelation);
120 static HRESULT adjustPropertyChain(
121 StorageImpl *This,
122 StgProperty propertyToDelete,
123 StgProperty parentProperty,
124 ULONG parentPropertyId,
125 INT typeOfRelation);
127 /***********************************************************************
128 * Declaration of the functions used to manipulate StgProperty
131 static ULONG getFreeProperty(
132 StorageImpl *storage);
134 static void updatePropertyChain(
135 StorageImpl *storage,
136 ULONG newPropertyIndex,
137 StgProperty newProperty);
139 static LONG propertyNameCmp(
140 OLECHAR *newProperty,
141 OLECHAR *currentProperty);
144 /***********************************************************************
145 * Declaration of miscellaneous functions...
147 static HRESULT validateSTGM(DWORD stgmValue);
149 static DWORD GetShareModeFromSTGM(DWORD stgm);
150 static DWORD GetAccessModeFromSTGM(DWORD stgm);
151 static DWORD GetCreationModeFromSTGM(DWORD stgm);
154 * Virtual function table for the IStorage32Impl class.
156 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
158 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
159 StorageBaseImpl_QueryInterface,
160 StorageBaseImpl_AddRef,
161 StorageBaseImpl_Release,
162 StorageBaseImpl_CreateStream,
163 StorageBaseImpl_OpenStream,
164 StorageImpl_CreateStorage,
165 StorageBaseImpl_OpenStorage,
166 StorageImpl_CopyTo,
167 StorageImpl_MoveElementTo,
168 StorageImpl_Commit,
169 StorageImpl_Revert,
170 StorageBaseImpl_EnumElements,
171 StorageImpl_DestroyElement,
172 StorageBaseImpl_RenameElement,
173 StorageImpl_SetElementTimes,
174 StorageBaseImpl_SetClass,
175 StorageImpl_SetStateBits,
176 StorageImpl_Stat
180 * Virtual function table for the Storage32InternalImpl class.
182 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
184 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
185 StorageBaseImpl_QueryInterface,
186 StorageBaseImpl_AddRef,
187 StorageBaseImpl_Release,
188 StorageBaseImpl_CreateStream,
189 StorageBaseImpl_OpenStream,
190 StorageImpl_CreateStorage,
191 StorageBaseImpl_OpenStorage,
192 StorageImpl_CopyTo,
193 StorageImpl_MoveElementTo,
194 StorageInternalImpl_Commit,
195 StorageInternalImpl_Revert,
196 StorageBaseImpl_EnumElements,
197 StorageImpl_DestroyElement,
198 StorageBaseImpl_RenameElement,
199 StorageImpl_SetElementTimes,
200 StorageBaseImpl_SetClass,
201 StorageImpl_SetStateBits,
202 StorageBaseImpl_Stat
206 * Virtual function table for the IEnumSTATSTGImpl class.
208 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
210 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
211 IEnumSTATSTGImpl_QueryInterface,
212 IEnumSTATSTGImpl_AddRef,
213 IEnumSTATSTGImpl_Release,
214 IEnumSTATSTGImpl_Next,
215 IEnumSTATSTGImpl_Skip,
216 IEnumSTATSTGImpl_Reset,
217 IEnumSTATSTGImpl_Clone
224 /************************************************************************
225 ** Storage32BaseImpl implementatiion
228 /************************************************************************
229 * Storage32BaseImpl_QueryInterface (IUnknown)
231 * This method implements the common QueryInterface for all IStorage32
232 * implementations contained in this file.
234 * See Windows documentation for more details on IUnknown methods.
236 HRESULT WINAPI StorageBaseImpl_QueryInterface(
237 IStorage* iface,
238 REFIID riid,
239 void** ppvObject)
241 ICOM_THIS(StorageBaseImpl,iface);
243 * Perform a sanity check on the parameters.
245 if ( (This==0) || (ppvObject==0) )
246 return E_INVALIDARG;
249 * Initialize the return parameter.
251 *ppvObject = 0;
254 * Compare the riid with the interface IDs implemented by this object.
256 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
258 *ppvObject = (IStorage*)This;
260 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
262 *ppvObject = (IStorage*)This;
266 * Check that we obtained an interface.
268 if ((*ppvObject)==0)
269 return E_NOINTERFACE;
272 * Query Interface always increases the reference count by one when it is
273 * successful
275 StorageBaseImpl_AddRef(iface);
277 return S_OK;
280 /************************************************************************
281 * Storage32BaseImpl_AddRef (IUnknown)
283 * This method implements the common AddRef for all IStorage32
284 * implementations contained in this file.
286 * See Windows documentation for more details on IUnknown methods.
288 ULONG WINAPI StorageBaseImpl_AddRef(
289 IStorage* iface)
291 ICOM_THIS(StorageBaseImpl,iface);
292 This->ref++;
294 return This->ref;
297 /************************************************************************
298 * Storage32BaseImpl_Release (IUnknown)
300 * This method implements the common Release for all IStorage32
301 * implementations contained in this file.
303 * See Windows documentation for more details on IUnknown methods.
305 ULONG WINAPI StorageBaseImpl_Release(
306 IStorage* iface)
308 ICOM_THIS(StorageBaseImpl,iface);
310 * Decrease the reference count on this object.
312 This->ref--;
315 * If the reference count goes down to 0, perform suicide.
317 if (This->ref==0)
320 * Since we are using a system of base-classes, we want to call the
321 * destructor of the appropriate derived class. To do this, we are
322 * using virtual functions to implement the destructor.
324 This->v_destructor(This);
326 return 0;
329 return This->ref;
332 /************************************************************************
333 * Storage32BaseImpl_OpenStream (IStorage)
335 * This method will open the specified stream object from the current storage.
337 * See Windows documentation for more details on IStorage methods.
339 HRESULT WINAPI StorageBaseImpl_OpenStream(
340 IStorage* iface,
341 const OLECHAR* pwcsName, /* [string][in] */
342 void* reserved1, /* [unique][in] */
343 DWORD grfMode, /* [in] */
344 DWORD reserved2, /* [in] */
345 IStream** ppstm) /* [out] */
347 ICOM_THIS(StorageBaseImpl,iface);
348 IEnumSTATSTGImpl* propertyEnumeration;
349 StgStreamImpl* newStream;
350 StgProperty currentProperty;
351 ULONG foundPropertyIndex;
352 HRESULT res = STG_E_UNKNOWN;
354 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
355 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
358 * Perform a sanity check on the parameters.
360 if ( (pwcsName==NULL) || (ppstm==0) )
362 res = E_INVALIDARG;
363 goto end;
367 * Initialize the out parameter
369 *ppstm = NULL;
372 * Validate the STGM flags
374 if ( FAILED( validateSTGM(grfMode) ))
376 res = STG_E_INVALIDFLAG;
377 goto end;
381 * As documented.
383 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
384 (grfMode & STGM_DELETEONRELEASE) ||
385 (grfMode & STGM_TRANSACTED) )
387 res = STG_E_INVALIDFUNCTION;
388 goto end;
392 * Create a property enumeration to search the properties
394 propertyEnumeration = IEnumSTATSTGImpl_Construct(
395 This->ancestorStorage,
396 This->rootPropertySetIndex);
399 * Search the enumeration for the property with the given name
401 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
402 propertyEnumeration,
403 pwcsName,
404 &currentProperty);
407 * Delete the property enumeration since we don't need it anymore
409 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
412 * If it was found, construct the stream object and return a pointer to it.
414 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
415 (currentProperty.propertyType==PROPTYPE_STREAM) )
417 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
419 if (newStream!=0)
421 newStream->grfMode = grfMode;
422 *ppstm = (IStream*)newStream;
425 * Since we are returning a pointer to the interface, we have to
426 * nail down the reference.
428 StgStreamImpl_AddRef(*ppstm);
430 res = S_OK;
431 goto end;
434 res = E_OUTOFMEMORY;
435 goto end;
438 res = STG_E_FILENOTFOUND;
440 end:
441 if (res == S_OK)
442 TRACE("<-- IStream %p\n", *ppstm);
443 TRACE("<-- %08lx\n", res);
444 return res;
447 /************************************************************************
448 * Storage32BaseImpl_OpenStorage (IStorage)
450 * This method will open a new storage object from the current storage.
452 * See Windows documentation for more details on IStorage methods.
454 HRESULT WINAPI StorageBaseImpl_OpenStorage(
455 IStorage* iface,
456 const OLECHAR* pwcsName, /* [string][unique][in] */
457 IStorage* pstgPriority, /* [unique][in] */
458 DWORD grfMode, /* [in] */
459 SNB snbExclude, /* [unique][in] */
460 DWORD reserved, /* [in] */
461 IStorage** ppstg) /* [out] */
463 ICOM_THIS(StorageBaseImpl,iface);
464 StorageInternalImpl* newStorage;
465 IEnumSTATSTGImpl* propertyEnumeration;
466 StgProperty currentProperty;
467 ULONG foundPropertyIndex;
468 HRESULT res = STG_E_UNKNOWN;
470 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
471 iface, debugstr_w(pwcsName), pstgPriority,
472 grfMode, snbExclude, reserved, ppstg);
475 * Perform a sanity check on the parameters.
477 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
479 res = E_INVALIDARG;
480 goto end;
483 /* as documented */
484 if (snbExclude != NULL)
486 res = STG_E_INVALIDPARAMETER;
487 goto end;
491 * Validate the STGM flags
493 if ( FAILED( validateSTGM(grfMode) ))
495 res = STG_E_INVALIDFLAG;
496 goto end;
500 * As documented.
502 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
503 (grfMode & STGM_DELETEONRELEASE) ||
504 (grfMode & STGM_PRIORITY) )
506 res = STG_E_INVALIDFUNCTION;
507 goto end;
511 * Initialize the out parameter
513 *ppstg = NULL;
516 * Create a property enumeration to search the properties
518 propertyEnumeration = IEnumSTATSTGImpl_Construct(
519 This->ancestorStorage,
520 This->rootPropertySetIndex);
523 * Search the enumeration for the property with the given name
525 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
526 propertyEnumeration,
527 pwcsName,
528 &currentProperty);
531 * Delete the property enumeration since we don't need it anymore
533 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
536 * If it was found, construct the stream object and return a pointer to it.
538 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
539 (currentProperty.propertyType==PROPTYPE_STORAGE) )
542 * Construct a new Storage object
544 newStorage = StorageInternalImpl_Construct(
545 This->ancestorStorage,
546 foundPropertyIndex);
548 if (newStorage != 0)
550 *ppstg = (IStorage*)newStorage;
553 * Since we are returning a pointer to the interface,
554 * we have to nail down the reference.
556 StorageBaseImpl_AddRef(*ppstg);
558 res = S_OK;
559 goto end;
562 res = STG_E_INSUFFICIENTMEMORY;
563 goto end;
566 res = STG_E_FILENOTFOUND;
568 end:
569 TRACE("<-- %08lx\n", res);
570 return res;
573 /************************************************************************
574 * Storage32BaseImpl_EnumElements (IStorage)
576 * This method will create an enumerator object that can be used to
577 * retrieve informatino about all the properties in the storage object.
579 * See Windows documentation for more details on IStorage methods.
581 HRESULT WINAPI StorageBaseImpl_EnumElements(
582 IStorage* iface,
583 DWORD reserved1, /* [in] */
584 void* reserved2, /* [size_is][unique][in] */
585 DWORD reserved3, /* [in] */
586 IEnumSTATSTG** ppenum) /* [out] */
588 ICOM_THIS(StorageBaseImpl,iface);
589 IEnumSTATSTGImpl* newEnum;
591 TRACE("(%p, %ld, %p, %ld, %p)\n",
592 iface, reserved1, reserved2, reserved3, ppenum);
595 * Perform a sanity check on the parameters.
597 if ( (This==0) || (ppenum==0))
598 return E_INVALIDARG;
601 * Construct the enumerator.
603 newEnum = IEnumSTATSTGImpl_Construct(
604 This->ancestorStorage,
605 This->rootPropertySetIndex);
607 if (newEnum!=0)
609 *ppenum = (IEnumSTATSTG*)newEnum;
612 * Don't forget to nail down a reference to the new object before
613 * returning it.
615 IEnumSTATSTGImpl_AddRef(*ppenum);
617 return S_OK;
620 return E_OUTOFMEMORY;
623 /************************************************************************
624 * Storage32BaseImpl_Stat (IStorage)
626 * This method will retrieve information about this storage object.
628 * See Windows documentation for more details on IStorage methods.
630 HRESULT WINAPI StorageBaseImpl_Stat(
631 IStorage* iface,
632 STATSTG* pstatstg, /* [out] */
633 DWORD grfStatFlag) /* [in] */
635 ICOM_THIS(StorageBaseImpl,iface);
636 StgProperty curProperty;
637 BOOL readSuccessful;
638 HRESULT res = STG_E_UNKNOWN;
640 TRACE("(%p, %p, %lx)\n",
641 iface, pstatstg, grfStatFlag);
644 * Perform a sanity check on the parameters.
646 if ( (This==0) || (pstatstg==0))
648 res = E_INVALIDARG;
649 goto end;
653 * Read the information from the property.
655 readSuccessful = StorageImpl_ReadProperty(
656 This->ancestorStorage,
657 This->rootPropertySetIndex,
658 &curProperty);
660 if (readSuccessful)
662 StorageUtl_CopyPropertyToSTATSTG(
663 pstatstg,
664 &curProperty,
665 grfStatFlag);
667 res = S_OK;
668 goto end;
671 res = E_FAIL;
673 end:
674 if (res == S_OK)
676 TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %08lx, grfLocksSupported: %ld, grfStateBits: %08lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.s.LowPart, pstatstg->cbSize.s.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
678 TRACE("<-- %08lx\n", res);
679 return res;
682 /************************************************************************
683 * Storage32BaseImpl_RenameElement (IStorage)
685 * This method will rename the specified element.
687 * See Windows documentation for more details on IStorage methods.
689 * Implementation notes: The method used to rename consists of creating a clone
690 * of the deleted StgProperty object setting it with the new name and to
691 * perform a DestroyElement of the old StgProperty.
693 HRESULT WINAPI StorageBaseImpl_RenameElement(
694 IStorage* iface,
695 const OLECHAR* pwcsOldName, /* [in] */
696 const OLECHAR* pwcsNewName) /* [in] */
698 ICOM_THIS(StorageBaseImpl,iface);
699 IEnumSTATSTGImpl* propertyEnumeration;
700 StgProperty currentProperty;
701 ULONG foundPropertyIndex;
703 TRACE("(%p, %s, %s)\n",
704 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
707 * Create a property enumeration to search the properties
709 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
710 This->rootPropertySetIndex);
713 * Search the enumeration for the new property name
715 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
716 pwcsNewName,
717 &currentProperty);
719 if (foundPropertyIndex != PROPERTY_NULL)
722 * There is already a property with the new name
724 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
725 return STG_E_FILEALREADYEXISTS;
728 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
731 * Search the enumeration for the old property name
733 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
734 pwcsOldName,
735 &currentProperty);
738 * Delete the property enumeration since we don't need it anymore
740 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
742 if (foundPropertyIndex != PROPERTY_NULL)
744 StgProperty renamedProperty;
745 ULONG renamedPropertyIndex;
748 * Setup a new property for the renamed property
750 renamedProperty.sizeOfNameString =
751 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
753 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
754 return STG_E_INVALIDNAME;
756 strcpyW(renamedProperty.name, pwcsNewName);
758 renamedProperty.propertyType = currentProperty.propertyType;
759 renamedProperty.startingBlock = currentProperty.startingBlock;
760 renamedProperty.size.s.LowPart = currentProperty.size.s.LowPart;
761 renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
763 renamedProperty.previousProperty = PROPERTY_NULL;
764 renamedProperty.nextProperty = PROPERTY_NULL;
767 * Bring the dirProperty link in case it is a storage and in which
768 * case the renamed storage elements don't require to be reorganized.
770 renamedProperty.dirProperty = currentProperty.dirProperty;
772 /* call CoFileTime to get the current time
773 renamedProperty.timeStampS1
774 renamedProperty.timeStampD1
775 renamedProperty.timeStampS2
776 renamedProperty.timeStampD2
777 renamedProperty.propertyUniqueID
781 * Obtain a free property in the property chain
783 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
786 * Save the new property into the new property spot
788 StorageImpl_WriteProperty(
789 This->ancestorStorage,
790 renamedPropertyIndex,
791 &renamedProperty);
794 * Find a spot in the property chain for our newly created property.
796 updatePropertyChain(
797 (StorageImpl*)This,
798 renamedPropertyIndex,
799 renamedProperty);
802 * At this point the renamed property has been inserted in the tree,
803 * now, before to Destroy the old property we must zeroed it's dirProperty
804 * otherwise the DestroyProperty below will zap it all and we do not want
805 * this to happen.
806 * Also, we fake that the old property is a storage so the DestroyProperty
807 * will not do a SetSize(0) on the stream data.
809 * This means that we need to tweek the StgProperty if it is a stream or a
810 * non empty storage.
812 StorageImpl_ReadProperty(This->ancestorStorage,
813 foundPropertyIndex,
814 &currentProperty);
816 currentProperty.dirProperty = PROPERTY_NULL;
817 currentProperty.propertyType = PROPTYPE_STORAGE;
818 StorageImpl_WriteProperty(
819 This->ancestorStorage,
820 foundPropertyIndex,
821 &currentProperty);
824 * Invoke Destroy to get rid of the ole property and automatically redo
825 * the linking of it's previous and next members...
827 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
830 else
833 * There is no property with the old name
835 return STG_E_FILENOTFOUND;
838 return S_OK;
841 /************************************************************************
842 * Storage32BaseImpl_CreateStream (IStorage)
844 * This method will create a stream object within this storage
846 * See Windows documentation for more details on IStorage methods.
848 HRESULT WINAPI StorageBaseImpl_CreateStream(
849 IStorage* iface,
850 const OLECHAR* pwcsName, /* [string][in] */
851 DWORD grfMode, /* [in] */
852 DWORD reserved1, /* [in] */
853 DWORD reserved2, /* [in] */
854 IStream** ppstm) /* [out] */
856 ICOM_THIS(StorageBaseImpl,iface);
857 IEnumSTATSTGImpl* propertyEnumeration;
858 StgStreamImpl* newStream;
859 StgProperty currentProperty, newStreamProperty;
860 ULONG foundPropertyIndex, newPropertyIndex;
862 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
863 iface, debugstr_w(pwcsName), grfMode,
864 reserved1, reserved2, ppstm);
867 * Validate parameters
869 if (ppstm == 0)
870 return STG_E_INVALIDPOINTER;
872 if (pwcsName == 0)
873 return STG_E_INVALIDNAME;
876 * Validate the STGM flags
878 if ( FAILED( validateSTGM(grfMode) ))
879 return STG_E_INVALIDFLAG;
882 * As documented.
884 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
885 (grfMode & STGM_DELETEONRELEASE) ||
886 (grfMode & STGM_TRANSACTED) )
887 return STG_E_INVALIDFUNCTION;
890 * Initialize the out parameter
892 *ppstm = 0;
895 * Create a property enumeration to search the properties
897 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
898 This->rootPropertySetIndex);
900 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
901 pwcsName,
902 &currentProperty);
904 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
906 if (foundPropertyIndex != PROPERTY_NULL)
909 * An element with this name already exists
911 if (grfMode & STGM_CREATE)
913 IStorage_DestroyElement(iface, pwcsName);
915 else
916 return STG_E_FILEALREADYEXISTS;
920 * memset the empty property
922 memset(&newStreamProperty, 0, sizeof(StgProperty));
924 newStreamProperty.sizeOfNameString =
925 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
927 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
928 return STG_E_INVALIDNAME;
930 strcpyW(newStreamProperty.name, pwcsName);
932 newStreamProperty.propertyType = PROPTYPE_STREAM;
933 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
934 newStreamProperty.size.s.LowPart = 0;
935 newStreamProperty.size.s.HighPart = 0;
937 newStreamProperty.previousProperty = PROPERTY_NULL;
938 newStreamProperty.nextProperty = PROPERTY_NULL;
939 newStreamProperty.dirProperty = PROPERTY_NULL;
941 /* call CoFileTime to get the current time
942 newStreamProperty.timeStampS1
943 newStreamProperty.timeStampD1
944 newStreamProperty.timeStampS2
945 newStreamProperty.timeStampD2
948 /* newStreamProperty.propertyUniqueID */
951 * Get a free property or create a new one
953 newPropertyIndex = getFreeProperty(This->ancestorStorage);
956 * Save the new property into the new property spot
958 StorageImpl_WriteProperty(
959 This->ancestorStorage,
960 newPropertyIndex,
961 &newStreamProperty);
964 * Find a spot in the property chain for our newly created property.
966 updatePropertyChain(
967 (StorageImpl*)This,
968 newPropertyIndex,
969 newStreamProperty);
972 * Open the stream to return it.
974 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
976 if (newStream != 0)
978 *ppstm = (IStream*)newStream;
981 * Since we are returning a pointer to the interface, we have to nail down
982 * the reference.
984 StgStreamImpl_AddRef(*ppstm);
986 else
988 return STG_E_INSUFFICIENTMEMORY;
991 return S_OK;
994 /************************************************************************
995 * Storage32BaseImpl_SetClass (IStorage)
997 * This method will write the specified CLSID in the property of this
998 * storage.
1000 * See Windows documentation for more details on IStorage methods.
1002 HRESULT WINAPI StorageBaseImpl_SetClass(
1003 IStorage* iface,
1004 REFCLSID clsid) /* [in] */
1006 ICOM_THIS(StorageBaseImpl,iface);
1007 HRESULT hRes = E_FAIL;
1008 StgProperty curProperty;
1009 BOOL success;
1011 TRACE("(%p, %p)\n", iface, clsid);
1013 success = StorageImpl_ReadProperty(This->ancestorStorage,
1014 This->rootPropertySetIndex,
1015 &curProperty);
1016 if (success)
1018 curProperty.propertyUniqueID = *clsid;
1020 success = StorageImpl_WriteProperty(This->ancestorStorage,
1021 This->rootPropertySetIndex,
1022 &curProperty);
1023 if (success)
1024 hRes = S_OK;
1027 return hRes;
1030 /************************************************************************
1031 ** Storage32Impl implementation
1034 /************************************************************************
1035 * Storage32Impl_CreateStorage (IStorage)
1037 * This method will create the storage object within the provided storage.
1039 * See Windows documentation for more details on IStorage methods.
1041 HRESULT WINAPI StorageImpl_CreateStorage(
1042 IStorage* iface,
1043 const OLECHAR *pwcsName, /* [string][in] */
1044 DWORD grfMode, /* [in] */
1045 DWORD reserved1, /* [in] */
1046 DWORD reserved2, /* [in] */
1047 IStorage **ppstg) /* [out] */
1049 StorageImpl* const This=(StorageImpl*)iface;
1051 IEnumSTATSTGImpl *propertyEnumeration;
1052 StgProperty currentProperty;
1053 StgProperty newProperty;
1054 ULONG foundPropertyIndex;
1055 ULONG newPropertyIndex;
1056 HRESULT hr;
1058 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1059 iface, debugstr_w(pwcsName), grfMode,
1060 reserved1, reserved2, ppstg);
1063 * Validate parameters
1065 if (ppstg == 0)
1066 return STG_E_INVALIDPOINTER;
1068 if (pwcsName == 0)
1069 return STG_E_INVALIDNAME;
1072 * Validate the STGM flags
1074 if ( FAILED( validateSTGM(grfMode) ) ||
1075 (grfMode & STGM_DELETEONRELEASE) )
1076 return STG_E_INVALIDFLAG;
1079 * Initialize the out parameter
1081 *ppstg = 0;
1084 * Create a property enumeration and search the properties
1086 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1087 This->rootPropertySetIndex);
1089 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1090 pwcsName,
1091 &currentProperty);
1092 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1094 if (foundPropertyIndex != PROPERTY_NULL)
1097 * An element with this name already exists
1099 if (grfMode & STGM_CREATE)
1100 IStorage_DestroyElement(iface, pwcsName);
1101 else
1102 return STG_E_FILEALREADYEXISTS;
1106 * memset the empty property
1108 memset(&newProperty, 0, sizeof(StgProperty));
1110 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1112 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1113 return STG_E_INVALIDNAME;
1115 strcpyW(newProperty.name, pwcsName);
1117 newProperty.propertyType = PROPTYPE_STORAGE;
1118 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1119 newProperty.size.s.LowPart = 0;
1120 newProperty.size.s.HighPart = 0;
1122 newProperty.previousProperty = PROPERTY_NULL;
1123 newProperty.nextProperty = PROPERTY_NULL;
1124 newProperty.dirProperty = PROPERTY_NULL;
1126 /* call CoFileTime to get the current time
1127 newProperty.timeStampS1
1128 newProperty.timeStampD1
1129 newProperty.timeStampS2
1130 newProperty.timeStampD2
1133 /* newStorageProperty.propertyUniqueID */
1136 * Obtain a free property in the property chain
1138 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1141 * Save the new property into the new property spot
1143 StorageImpl_WriteProperty(
1144 This->ancestorStorage,
1145 newPropertyIndex,
1146 &newProperty);
1149 * Find a spot in the property chain for our newly created property.
1151 updatePropertyChain(
1152 This,
1153 newPropertyIndex,
1154 newProperty);
1157 * Open it to get a pointer to return.
1159 hr = IStorage_OpenStorage(
1160 iface,
1161 (OLECHAR*)pwcsName,
1163 grfMode,
1166 ppstg);
1168 if( (hr != S_OK) || (*ppstg == NULL))
1170 return hr;
1174 return S_OK;
1178 /***************************************************************************
1180 * Internal Method
1182 * Get a free property or create a new one.
1184 static ULONG getFreeProperty(
1185 StorageImpl *storage)
1187 ULONG currentPropertyIndex = 0;
1188 ULONG newPropertyIndex = PROPERTY_NULL;
1189 BOOL readSuccessful = TRUE;
1190 StgProperty currentProperty;
1195 * Start by reading the root property
1197 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1198 currentPropertyIndex,
1199 &currentProperty);
1200 if (readSuccessful)
1202 if (currentProperty.sizeOfNameString == 0)
1205 * The property existis and is available, we found it.
1207 newPropertyIndex = currentPropertyIndex;
1210 else
1213 * We exhausted the property list, we will create more space below
1215 newPropertyIndex = currentPropertyIndex;
1217 currentPropertyIndex++;
1219 } while (newPropertyIndex == PROPERTY_NULL);
1222 * grow the property chain
1224 if (! readSuccessful)
1226 StgProperty emptyProperty;
1227 ULARGE_INTEGER newSize;
1228 ULONG propertyIndex;
1229 ULONG lastProperty = 0;
1230 ULONG blockCount = 0;
1233 * obtain the new count of property blocks
1235 blockCount = BlockChainStream_GetCount(
1236 storage->ancestorStorage->rootBlockChain)+1;
1239 * initialize the size used by the property stream
1241 newSize.s.HighPart = 0;
1242 newSize.s.LowPart = storage->bigBlockSize * blockCount;
1245 * add a property block to the property chain
1247 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1250 * memset the empty property in order to initialize the unused newly
1251 * created property
1253 memset(&emptyProperty, 0, sizeof(StgProperty));
1256 * initialize them
1258 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1260 for(
1261 propertyIndex = newPropertyIndex;
1262 propertyIndex < lastProperty;
1263 propertyIndex++)
1265 StorageImpl_WriteProperty(
1266 storage->ancestorStorage,
1267 propertyIndex,
1268 &emptyProperty);
1272 return newPropertyIndex;
1275 /****************************************************************************
1277 * Internal Method
1279 * Case insensitive comparaison of StgProperty.name by first considering
1280 * their size.
1282 * Returns <0 when newPrpoerty < currentProperty
1283 * >0 when newPrpoerty > currentProperty
1284 * 0 when newPrpoerty == currentProperty
1286 static LONG propertyNameCmp(
1287 OLECHAR *newProperty,
1288 OLECHAR *currentProperty)
1290 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1292 if (diff == 0)
1295 * We compare the string themselves only when they are of the same lenght
1297 diff = lstrcmpiW( newProperty, currentProperty);
1300 return diff;
1303 /****************************************************************************
1305 * Internal Method
1307 * Properly link this new element in the property chain.
1309 static void updatePropertyChain(
1310 StorageImpl *storage,
1311 ULONG newPropertyIndex,
1312 StgProperty newProperty)
1314 StgProperty currentProperty;
1317 * Read the root property
1319 StorageImpl_ReadProperty(storage->ancestorStorage,
1320 storage->rootPropertySetIndex,
1321 &currentProperty);
1323 if (currentProperty.dirProperty != PROPERTY_NULL)
1326 * The root storage contains some element, therefore, start the research
1327 * for the appropriate location.
1329 BOOL found = 0;
1330 ULONG current, next, previous, currentPropertyId;
1333 * Keep the StgProperty sequence number of the storage first property
1335 currentPropertyId = currentProperty.dirProperty;
1338 * Read
1340 StorageImpl_ReadProperty(storage->ancestorStorage,
1341 currentProperty.dirProperty,
1342 &currentProperty);
1344 previous = currentProperty.previousProperty;
1345 next = currentProperty.nextProperty;
1346 current = currentPropertyId;
1348 while (found == 0)
1350 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1352 if (diff < 0)
1354 if (previous != PROPERTY_NULL)
1356 StorageImpl_ReadProperty(storage->ancestorStorage,
1357 previous,
1358 &currentProperty);
1359 current = previous;
1361 else
1363 currentProperty.previousProperty = newPropertyIndex;
1364 StorageImpl_WriteProperty(storage->ancestorStorage,
1365 current,
1366 &currentProperty);
1367 found = 1;
1370 else if (diff > 0)
1372 if (next != PROPERTY_NULL)
1374 StorageImpl_ReadProperty(storage->ancestorStorage,
1375 next,
1376 &currentProperty);
1377 current = next;
1379 else
1381 currentProperty.nextProperty = newPropertyIndex;
1382 StorageImpl_WriteProperty(storage->ancestorStorage,
1383 current,
1384 &currentProperty);
1385 found = 1;
1388 else
1391 * Trying to insert an item with the same name in the
1392 * subtree structure.
1394 assert(FALSE);
1397 previous = currentProperty.previousProperty;
1398 next = currentProperty.nextProperty;
1401 else
1404 * The root storage is empty, link the new property to it's dir property
1406 currentProperty.dirProperty = newPropertyIndex;
1407 StorageImpl_WriteProperty(storage->ancestorStorage,
1408 storage->rootPropertySetIndex,
1409 &currentProperty);
1414 /*************************************************************************
1415 * CopyTo (IStorage)
1417 HRESULT WINAPI StorageImpl_CopyTo(
1418 IStorage* iface,
1419 DWORD ciidExclude, /* [in] */
1420 const IID* rgiidExclude, /* [size_is][unique][in] */
1421 SNB snbExclude, /* [unique][in] */
1422 IStorage* pstgDest) /* [unique][in] */
1424 IEnumSTATSTG *elements = 0;
1425 STATSTG curElement, strStat;
1426 HRESULT hr;
1427 IStorage *pstgTmp, *pstgChild;
1428 IStream *pstrTmp, *pstrChild;
1430 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1431 FIXME("Exclude option not implemented\n");
1433 TRACE("(%p, %ld, %p, %p, %p)\n",
1434 iface, ciidExclude, rgiidExclude,
1435 snbExclude, pstgDest);
1438 * Perform a sanity check
1440 if ( pstgDest == 0 )
1441 return STG_E_INVALIDPOINTER;
1444 * Enumerate the elements
1446 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1448 if ( hr != S_OK )
1449 return hr;
1452 * set the class ID
1454 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1455 IStorage_SetClass( pstgDest, &curElement.clsid );
1460 * Obtain the next element
1462 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1464 if ( hr == S_FALSE )
1466 hr = S_OK; /* done, every element has been copied */
1467 break;
1470 if (curElement.type == STGTY_STORAGE)
1473 * open child source storage
1475 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1476 STGM_READ|STGM_SHARE_EXCLUSIVE,
1477 NULL, 0, &pstgChild );
1479 if (hr != S_OK)
1480 break;
1483 * Check if destination storage is not a child of the source
1484 * storage, which will cause an infinite loop
1486 if (pstgChild == pstgDest)
1488 IEnumSTATSTG_Release(elements);
1490 return STG_E_ACCESSDENIED;
1494 * create a new storage in destination storage
1496 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1497 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1498 0, 0,
1499 &pstgTmp );
1501 * if it already exist, don't create a new one use this one
1503 if (hr == STG_E_FILEALREADYEXISTS)
1505 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1506 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1507 NULL, 0, &pstgTmp );
1510 if (hr != S_OK)
1511 break;
1515 * do the copy recursively
1517 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1518 snbExclude, pstgTmp );
1520 IStorage_Release( pstgTmp );
1521 IStorage_Release( pstgChild );
1523 else if (curElement.type == STGTY_STREAM)
1526 * create a new stream in destination storage. If the stream already
1527 * exist, it will be deleted and a new one will be created.
1529 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1530 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1531 0, 0, &pstrTmp );
1533 if (hr != S_OK)
1534 break;
1537 * open child stream storage
1539 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1540 STGM_READ|STGM_SHARE_EXCLUSIVE,
1541 0, &pstrChild );
1543 if (hr != S_OK)
1544 break;
1547 * Get the size of the source stream
1549 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1552 * Set the size of the destination stream.
1554 IStream_SetSize(pstrTmp, strStat.cbSize);
1557 * do the copy
1559 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1560 NULL, NULL );
1562 IStream_Release( pstrTmp );
1563 IStream_Release( pstrChild );
1565 else
1567 WARN("unknown element type: %ld\n", curElement.type);
1570 } while (hr == S_OK);
1573 * Clean-up
1575 IEnumSTATSTG_Release(elements);
1577 return hr;
1580 /*************************************************************************
1581 * MoveElementTo (IStorage)
1583 HRESULT WINAPI StorageImpl_MoveElementTo(
1584 IStorage* iface,
1585 const OLECHAR *pwcsName, /* [string][in] */
1586 IStorage *pstgDest, /* [unique][in] */
1587 const OLECHAR *pwcsNewName,/* [string][in] */
1588 DWORD grfFlags) /* [in] */
1590 FIXME("not implemented!\n");
1591 return E_NOTIMPL;
1594 /*************************************************************************
1595 * Commit (IStorage)
1597 HRESULT WINAPI StorageImpl_Commit(
1598 IStorage* iface,
1599 DWORD grfCommitFlags)/* [in] */
1601 FIXME("(%ld): stub!\n", grfCommitFlags);
1602 return S_OK;
1605 /*************************************************************************
1606 * Revert (IStorage)
1608 HRESULT WINAPI StorageImpl_Revert(
1609 IStorage* iface)
1611 FIXME("not implemented!\n");
1612 return E_NOTIMPL;
1615 /*************************************************************************
1616 * DestroyElement (IStorage)
1618 * Stategy: This implementation is build this way for simplicity not for speed.
1619 * I always delete the top most element of the enumeration and adjust
1620 * the deleted element pointer all the time. This takes longer to
1621 * do but allow to reinvoke DestroyElement whenever we encounter a
1622 * storage object. The optimisation reside in the usage of another
1623 * enumeration stategy that would give all the leaves of a storage
1624 * first. (postfix order)
1626 HRESULT WINAPI StorageImpl_DestroyElement(
1627 IStorage* iface,
1628 const OLECHAR *pwcsName)/* [string][in] */
1630 StorageImpl* const This=(StorageImpl*)iface;
1632 IEnumSTATSTGImpl* propertyEnumeration;
1633 HRESULT hr = S_OK;
1634 BOOL res;
1635 StgProperty propertyToDelete;
1636 StgProperty parentProperty;
1637 ULONG foundPropertyIndexToDelete;
1638 ULONG typeOfRelation;
1639 ULONG parentPropertyId;
1641 TRACE("(%p, %s)\n",
1642 iface, debugstr_w(pwcsName));
1645 * Perform a sanity check on the parameters.
1647 if (pwcsName==NULL)
1648 return STG_E_INVALIDPOINTER;
1651 * Create a property enumeration to search the property with the given name
1653 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1654 This->ancestorStorage,
1655 This->rootPropertySetIndex);
1657 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1658 propertyEnumeration,
1659 pwcsName,
1660 &propertyToDelete);
1662 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1664 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1666 return STG_E_FILENOTFOUND;
1670 * Find the parent property of the property to delete (the one that
1671 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1672 * the parent is This. Otherwise, the parent is one of it's sibling...
1676 * First, read This's StgProperty..
1678 res = StorageImpl_ReadProperty(
1679 This->ancestorStorage,
1680 This->rootPropertySetIndex,
1681 &parentProperty);
1683 assert(res==TRUE);
1686 * Second, check to see if by any chance the actual storage (This) is not
1687 * the parent of the property to delete... We never know...
1689 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1692 * Set data as it would have been done in the else part...
1694 typeOfRelation = PROPERTY_RELATION_DIR;
1695 parentPropertyId = This->rootPropertySetIndex;
1697 else
1700 * Create a property enumeration to search the parent properties, and
1701 * delete it once done.
1703 IEnumSTATSTGImpl* propertyEnumeration2;
1705 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1706 This->ancestorStorage,
1707 This->rootPropertySetIndex);
1709 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1710 propertyEnumeration2,
1711 foundPropertyIndexToDelete,
1712 &parentProperty,
1713 &parentPropertyId);
1715 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1718 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1720 hr = deleteStorageProperty(
1721 This,
1722 foundPropertyIndexToDelete,
1723 propertyToDelete);
1725 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1727 hr = deleteStreamProperty(
1728 This,
1729 foundPropertyIndexToDelete,
1730 propertyToDelete);
1733 if (hr!=S_OK)
1734 return hr;
1737 * Adjust the property chain
1739 hr = adjustPropertyChain(
1740 This,
1741 propertyToDelete,
1742 parentProperty,
1743 parentPropertyId,
1744 typeOfRelation);
1746 return hr;
1750 /************************************************************************
1751 * StorageImpl_Stat (IStorage)
1753 * This method will retrieve information about this storage object.
1755 * See Windows documentation for more details on IStorage methods.
1757 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1758 STATSTG* pstatstg, /* [out] */
1759 DWORD grfStatFlag) /* [in] */
1761 StorageImpl* const This = (StorageImpl*)iface;
1762 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1764 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1766 CoTaskMemFree(pstatstg->pwcsName);
1767 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1768 strcpyW(pstatstg->pwcsName, This->pwcsName);
1771 return result;
1776 /*********************************************************************
1778 * Internal Method
1780 * Perform the deletion of a complete storage node
1783 static HRESULT deleteStorageProperty(
1784 StorageImpl *parentStorage,
1785 ULONG indexOfPropertyToDelete,
1786 StgProperty propertyToDelete)
1788 IEnumSTATSTG *elements = 0;
1789 IStorage *childStorage = 0;
1790 STATSTG currentElement;
1791 HRESULT hr;
1792 HRESULT destroyHr = S_OK;
1795 * Open the storage and enumerate it
1797 hr = StorageBaseImpl_OpenStorage(
1798 (IStorage*)parentStorage,
1799 propertyToDelete.name,
1801 STGM_SHARE_EXCLUSIVE,
1804 &childStorage);
1806 if (hr != S_OK)
1808 return hr;
1812 * Enumerate the elements
1814 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1819 * Obtain the next element
1821 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1822 if (hr==S_OK)
1824 destroyHr = StorageImpl_DestroyElement(
1825 (IStorage*)childStorage,
1826 (OLECHAR*)currentElement.pwcsName);
1828 CoTaskMemFree(currentElement.pwcsName);
1832 * We need to Reset the enumeration every time because we delete elements
1833 * and the enumeration could be invalid
1835 IEnumSTATSTG_Reset(elements);
1837 } while ((hr == S_OK) && (destroyHr == S_OK));
1840 * Invalidate the property by zeroing it's name member.
1842 propertyToDelete.sizeOfNameString = 0;
1844 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1845 indexOfPropertyToDelete,
1846 &propertyToDelete);
1848 IStorage_Release(childStorage);
1849 IEnumSTATSTG_Release(elements);
1851 return destroyHr;
1854 /*********************************************************************
1856 * Internal Method
1858 * Perform the deletion of a stream node
1861 static HRESULT deleteStreamProperty(
1862 StorageImpl *parentStorage,
1863 ULONG indexOfPropertyToDelete,
1864 StgProperty propertyToDelete)
1866 IStream *pis;
1867 HRESULT hr;
1868 ULARGE_INTEGER size;
1870 size.s.HighPart = 0;
1871 size.s.LowPart = 0;
1873 hr = StorageBaseImpl_OpenStream(
1874 (IStorage*)parentStorage,
1875 (OLECHAR*)propertyToDelete.name,
1876 NULL,
1877 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1879 &pis);
1881 if (hr!=S_OK)
1883 return(hr);
1887 * Zap the stream
1889 hr = IStream_SetSize(pis, size);
1891 if(hr != S_OK)
1893 return hr;
1897 * Release the stream object.
1899 IStream_Release(pis);
1902 * Invalidate the property by zeroing it's name member.
1904 propertyToDelete.sizeOfNameString = 0;
1907 * Here we should re-read the property so we get the updated pointer
1908 * but since we are here to zap it, I don't do it...
1910 StorageImpl_WriteProperty(
1911 parentStorage->ancestorStorage,
1912 indexOfPropertyToDelete,
1913 &propertyToDelete);
1915 return S_OK;
1918 /*********************************************************************
1920 * Internal Method
1922 * Finds a placeholder for the StgProperty within the Storage
1925 static HRESULT findPlaceholder(
1926 StorageImpl *storage,
1927 ULONG propertyIndexToStore,
1928 ULONG storePropertyIndex,
1929 INT typeOfRelation)
1931 StgProperty storeProperty;
1932 HRESULT hr = S_OK;
1933 BOOL res = TRUE;
1936 * Read the storage property
1938 res = StorageImpl_ReadProperty(
1939 storage->ancestorStorage,
1940 storePropertyIndex,
1941 &storeProperty);
1943 if(! res)
1945 return E_FAIL;
1948 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1950 if (storeProperty.previousProperty != PROPERTY_NULL)
1952 return findPlaceholder(
1953 storage,
1954 propertyIndexToStore,
1955 storeProperty.previousProperty,
1956 typeOfRelation);
1958 else
1960 storeProperty.previousProperty = propertyIndexToStore;
1963 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1965 if (storeProperty.nextProperty != PROPERTY_NULL)
1967 return findPlaceholder(
1968 storage,
1969 propertyIndexToStore,
1970 storeProperty.nextProperty,
1971 typeOfRelation);
1973 else
1975 storeProperty.nextProperty = propertyIndexToStore;
1978 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1980 if (storeProperty.dirProperty != PROPERTY_NULL)
1982 return findPlaceholder(
1983 storage,
1984 propertyIndexToStore,
1985 storeProperty.dirProperty,
1986 typeOfRelation);
1988 else
1990 storeProperty.dirProperty = propertyIndexToStore;
1994 hr = StorageImpl_WriteProperty(
1995 storage->ancestorStorage,
1996 storePropertyIndex,
1997 &storeProperty);
1999 if(! hr)
2001 return E_FAIL;
2004 return S_OK;
2007 /*************************************************************************
2009 * Internal Method
2011 * This method takes the previous and the next property link of a property
2012 * to be deleted and find them a place in the Storage.
2014 static HRESULT adjustPropertyChain(
2015 StorageImpl *This,
2016 StgProperty propertyToDelete,
2017 StgProperty parentProperty,
2018 ULONG parentPropertyId,
2019 INT typeOfRelation)
2021 ULONG newLinkProperty = PROPERTY_NULL;
2022 BOOL needToFindAPlaceholder = FALSE;
2023 ULONG storeNode = PROPERTY_NULL;
2024 ULONG toStoreNode = PROPERTY_NULL;
2025 INT relationType = 0;
2026 HRESULT hr = S_OK;
2027 BOOL res = TRUE;
2029 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2031 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2034 * Set the parent previous to the property to delete previous
2036 newLinkProperty = propertyToDelete.previousProperty;
2038 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2041 * We also need to find a storage for the other link, setup variables
2042 * to do this at the end...
2044 needToFindAPlaceholder = TRUE;
2045 storeNode = propertyToDelete.previousProperty;
2046 toStoreNode = propertyToDelete.nextProperty;
2047 relationType = PROPERTY_RELATION_NEXT;
2050 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2053 * Set the parent previous to the property to delete next
2055 newLinkProperty = propertyToDelete.nextProperty;
2059 * Link it for real...
2061 parentProperty.previousProperty = newLinkProperty;
2064 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2066 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2069 * Set the parent next to the property to delete next previous
2071 newLinkProperty = propertyToDelete.previousProperty;
2073 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2076 * We also need to find a storage for the other link, setup variables
2077 * to do this at the end...
2079 needToFindAPlaceholder = TRUE;
2080 storeNode = propertyToDelete.previousProperty;
2081 toStoreNode = propertyToDelete.nextProperty;
2082 relationType = PROPERTY_RELATION_NEXT;
2085 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2088 * Set the parent next to the property to delete next
2090 newLinkProperty = propertyToDelete.nextProperty;
2094 * Link it for real...
2096 parentProperty.nextProperty = newLinkProperty;
2098 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2100 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2103 * Set the parent dir to the property to delete previous
2105 newLinkProperty = propertyToDelete.previousProperty;
2107 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2110 * We also need to find a storage for the other link, setup variables
2111 * to do this at the end...
2113 needToFindAPlaceholder = TRUE;
2114 storeNode = propertyToDelete.previousProperty;
2115 toStoreNode = propertyToDelete.nextProperty;
2116 relationType = PROPERTY_RELATION_NEXT;
2119 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2122 * Set the parent dir to the property to delete next
2124 newLinkProperty = propertyToDelete.nextProperty;
2128 * Link it for real...
2130 parentProperty.dirProperty = newLinkProperty;
2134 * Write back the parent property
2136 res = StorageImpl_WriteProperty(
2137 This->ancestorStorage,
2138 parentPropertyId,
2139 &parentProperty);
2140 if(! res)
2142 return E_FAIL;
2146 * If a placeholder is required for the other link, then, find one and
2147 * get out of here...
2149 if (needToFindAPlaceholder)
2151 hr = findPlaceholder(
2152 This,
2153 toStoreNode,
2154 storeNode,
2155 relationType);
2158 return hr;
2162 /******************************************************************************
2163 * SetElementTimes (IStorage)
2165 HRESULT WINAPI StorageImpl_SetElementTimes(
2166 IStorage* iface,
2167 const OLECHAR *pwcsName,/* [string][in] */
2168 const FILETIME *pctime, /* [in] */
2169 const FILETIME *patime, /* [in] */
2170 const FILETIME *pmtime) /* [in] */
2172 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2173 return S_OK;
2176 /******************************************************************************
2177 * SetStateBits (IStorage)
2179 HRESULT WINAPI StorageImpl_SetStateBits(
2180 IStorage* iface,
2181 DWORD grfStateBits,/* [in] */
2182 DWORD grfMask) /* [in] */
2184 FIXME("not implemented!\n");
2185 return E_NOTIMPL;
2188 HRESULT StorageImpl_Construct(
2189 StorageImpl* This,
2190 HANDLE hFile,
2191 LPCOLESTR pwcsName,
2192 ILockBytes* pLkbyt,
2193 DWORD openFlags,
2194 BOOL fileBased,
2195 BOOL fileCreate)
2197 HRESULT hr = S_OK;
2198 StgProperty currentProperty;
2199 BOOL readSuccessful;
2200 ULONG currentPropertyIndex;
2202 if ( FAILED( validateSTGM(openFlags) ))
2203 return STG_E_INVALIDFLAG;
2205 memset(This, 0, sizeof(StorageImpl));
2208 * Initialize the virtual function table.
2210 ICOM_VTBL(This) = &Storage32Impl_Vtbl;
2211 This->v_destructor = &StorageImpl_Destroy;
2214 * This is the top-level storage so initialize the ancestor pointer
2215 * to this.
2217 This->ancestorStorage = This;
2220 * Initialize the physical support of the storage.
2222 This->hFile = hFile;
2225 * Store copy of file path.
2227 if(pwcsName) {
2228 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2229 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2230 if (!This->pwcsName)
2231 return STG_E_INSUFFICIENTMEMORY;
2232 strcpyW(This->pwcsName, pwcsName);
2236 * Initialize the big block cache.
2238 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2239 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2240 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2241 pLkbyt,
2242 openFlags,
2243 This->bigBlockSize,
2244 fileBased);
2246 if (This->bigBlockFile == 0)
2247 return E_FAIL;
2249 if (fileCreate)
2251 ULARGE_INTEGER size;
2252 BYTE* bigBlockBuffer;
2255 * Initialize all header variables:
2256 * - The big block depot consists of one block and it is at block 0
2257 * - The properties start at block 1
2258 * - There is no small block depot
2260 memset( This->bigBlockDepotStart,
2261 BLOCK_UNUSED,
2262 sizeof(This->bigBlockDepotStart));
2264 This->bigBlockDepotCount = 1;
2265 This->bigBlockDepotStart[0] = 0;
2266 This->rootStartBlock = 1;
2267 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2268 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2269 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2270 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2271 This->extBigBlockDepotCount = 0;
2273 StorageImpl_SaveFileHeader(This);
2276 * Add one block for the big block depot and one block for the properties
2278 size.s.HighPart = 0;
2279 size.s.LowPart = This->bigBlockSize * 3;
2280 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2283 * Initialize the big block depot
2285 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2286 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2287 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2288 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2289 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2291 else
2294 * Load the header for the file.
2296 hr = StorageImpl_LoadFileHeader(This);
2298 if (FAILED(hr))
2300 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2302 return hr;
2307 * There is no block depot cached yet.
2309 This->indexBlockDepotCached = 0xFFFFFFFF;
2312 * Start searching for free blocks with block 0.
2314 This->prevFreeBlock = 0;
2317 * Create the block chain abstractions.
2319 This->rootBlockChain =
2320 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2322 This->smallBlockDepotChain = BlockChainStream_Construct(
2323 This,
2324 &This->smallBlockDepotStart,
2325 PROPERTY_NULL);
2328 * Write the root property
2330 if (fileCreate)
2332 StgProperty rootProp;
2334 * Initialize the property chain
2336 memset(&rootProp, 0, sizeof(rootProp));
2337 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2338 sizeof(rootProp.name)/sizeof(WCHAR) );
2339 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2340 rootProp.propertyType = PROPTYPE_ROOT;
2341 rootProp.previousProperty = PROPERTY_NULL;
2342 rootProp.nextProperty = PROPERTY_NULL;
2343 rootProp.dirProperty = PROPERTY_NULL;
2344 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2345 rootProp.size.s.HighPart = 0;
2346 rootProp.size.s.LowPart = 0;
2348 StorageImpl_WriteProperty(This, 0, &rootProp);
2352 * Find the ID of the root in the property sets.
2354 currentPropertyIndex = 0;
2358 readSuccessful = StorageImpl_ReadProperty(
2359 This,
2360 currentPropertyIndex,
2361 &currentProperty);
2363 if (readSuccessful)
2365 if ( (currentProperty.sizeOfNameString != 0 ) &&
2366 (currentProperty.propertyType == PROPTYPE_ROOT) )
2368 This->rootPropertySetIndex = currentPropertyIndex;
2372 currentPropertyIndex++;
2374 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2376 if (!readSuccessful)
2378 /* TODO CLEANUP */
2379 return E_FAIL;
2383 * Create the block chain abstraction for the small block root chain.
2385 This->smallBlockRootChain = BlockChainStream_Construct(
2386 This,
2387 NULL,
2388 This->rootPropertySetIndex);
2390 return hr;
2393 void StorageImpl_Destroy(
2394 StorageImpl* This)
2396 TRACE("(%p)\n", This);
2398 if(This->pwcsName)
2399 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2401 BlockChainStream_Destroy(This->smallBlockRootChain);
2402 BlockChainStream_Destroy(This->rootBlockChain);
2403 BlockChainStream_Destroy(This->smallBlockDepotChain);
2405 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2406 return;
2409 /******************************************************************************
2410 * Storage32Impl_GetNextFreeBigBlock
2412 * Returns the index of the next free big block.
2413 * If the big block depot is filled, this method will enlarge it.
2416 ULONG StorageImpl_GetNextFreeBigBlock(
2417 StorageImpl* This)
2419 ULONG depotBlockIndexPos;
2420 void *depotBuffer;
2421 ULONG depotBlockOffset;
2422 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2423 ULONG nextBlockIndex = BLOCK_SPECIAL;
2424 int depotIndex = 0;
2425 ULONG freeBlock = BLOCK_UNUSED;
2427 depotIndex = This->prevFreeBlock / blocksPerDepot;
2428 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2431 * Scan the entire big block depot until we find a block marked free
2433 while (nextBlockIndex != BLOCK_UNUSED)
2435 if (depotIndex < COUNT_BBDEPOTINHEADER)
2437 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2440 * Grow the primary depot.
2442 if (depotBlockIndexPos == BLOCK_UNUSED)
2444 depotBlockIndexPos = depotIndex*blocksPerDepot;
2447 * Add a block depot.
2449 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2450 This->bigBlockDepotCount++;
2451 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2454 * Flag it as a block depot.
2456 StorageImpl_SetNextBlockInChain(This,
2457 depotBlockIndexPos,
2458 BLOCK_SPECIAL);
2460 /* Save new header information.
2462 StorageImpl_SaveFileHeader(This);
2465 else
2467 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2469 if (depotBlockIndexPos == BLOCK_UNUSED)
2472 * Grow the extended depot.
2474 ULONG extIndex = BLOCK_UNUSED;
2475 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2476 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2478 if (extBlockOffset == 0)
2480 /* We need an extended block.
2482 extIndex = Storage32Impl_AddExtBlockDepot(This);
2483 This->extBigBlockDepotCount++;
2484 depotBlockIndexPos = extIndex + 1;
2486 else
2487 depotBlockIndexPos = depotIndex * blocksPerDepot;
2490 * Add a block depot and mark it in the extended block.
2492 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2493 This->bigBlockDepotCount++;
2494 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2496 /* Flag the block depot.
2498 StorageImpl_SetNextBlockInChain(This,
2499 depotBlockIndexPos,
2500 BLOCK_SPECIAL);
2502 /* If necessary, flag the extended depot block.
2504 if (extIndex != BLOCK_UNUSED)
2505 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2507 /* Save header information.
2509 StorageImpl_SaveFileHeader(This);
2513 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2515 if (depotBuffer != 0)
2517 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2518 ( nextBlockIndex != BLOCK_UNUSED))
2520 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2522 if (nextBlockIndex == BLOCK_UNUSED)
2524 freeBlock = (depotIndex * blocksPerDepot) +
2525 (depotBlockOffset/sizeof(ULONG));
2528 depotBlockOffset += sizeof(ULONG);
2531 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2534 depotIndex++;
2535 depotBlockOffset = 0;
2538 This->prevFreeBlock = freeBlock;
2540 return freeBlock;
2543 /******************************************************************************
2544 * Storage32Impl_AddBlockDepot
2546 * This will create a depot block, essentially it is a block initialized
2547 * to BLOCK_UNUSEDs.
2549 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2551 BYTE* blockBuffer;
2553 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2556 * Initialize blocks as free
2558 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2560 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2563 /******************************************************************************
2564 * Storage32Impl_GetExtDepotBlock
2566 * Returns the index of the block that corresponds to the specified depot
2567 * index. This method is only for depot indexes equal or greater than
2568 * COUNT_BBDEPOTINHEADER.
2570 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2572 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2573 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2574 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2575 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2576 ULONG blockIndex = BLOCK_UNUSED;
2577 ULONG extBlockIndex = This->extBigBlockDepotStart;
2579 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2581 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2582 return BLOCK_UNUSED;
2584 while (extBlockCount > 0)
2586 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2587 extBlockCount--;
2590 if (extBlockIndex != BLOCK_UNUSED)
2592 BYTE* depotBuffer;
2594 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2596 if (depotBuffer != 0)
2598 StorageUtl_ReadDWord(depotBuffer,
2599 extBlockOffset * sizeof(ULONG),
2600 &blockIndex);
2602 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2606 return blockIndex;
2609 /******************************************************************************
2610 * Storage32Impl_SetExtDepotBlock
2612 * Associates the specified block index to the specified depot index.
2613 * This method is only for depot indexes equal or greater than
2614 * COUNT_BBDEPOTINHEADER.
2616 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2617 ULONG depotIndex,
2618 ULONG blockIndex)
2620 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2621 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2622 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2623 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2624 ULONG extBlockIndex = This->extBigBlockDepotStart;
2626 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2628 while (extBlockCount > 0)
2630 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2631 extBlockCount--;
2634 if (extBlockIndex != BLOCK_UNUSED)
2636 BYTE* depotBuffer;
2638 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2640 if (depotBuffer != 0)
2642 StorageUtl_WriteDWord(depotBuffer,
2643 extBlockOffset * sizeof(ULONG),
2644 blockIndex);
2646 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2651 /******************************************************************************
2652 * Storage32Impl_AddExtBlockDepot
2654 * Creates an extended depot block.
2656 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2658 ULONG numExtBlocks = This->extBigBlockDepotCount;
2659 ULONG nextExtBlock = This->extBigBlockDepotStart;
2660 BYTE* depotBuffer = NULL;
2661 ULONG index = BLOCK_UNUSED;
2662 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2663 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2664 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2666 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2667 blocksPerDepotBlock;
2669 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2672 * The first extended block.
2674 This->extBigBlockDepotStart = index;
2676 else
2678 int i;
2680 * Follow the chain to the last one.
2682 for (i = 0; i < (numExtBlocks - 1); i++)
2684 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2688 * Add the new extended block to the chain.
2690 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2691 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2692 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2696 * Initialize this block.
2698 depotBuffer = StorageImpl_GetBigBlock(This, index);
2699 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2700 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2702 return index;
2705 /******************************************************************************
2706 * Storage32Impl_FreeBigBlock
2708 * This method will flag the specified block as free in the big block depot.
2710 void StorageImpl_FreeBigBlock(
2711 StorageImpl* This,
2712 ULONG blockIndex)
2714 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2716 if (blockIndex < This->prevFreeBlock)
2717 This->prevFreeBlock = blockIndex;
2720 /************************************************************************
2721 * Storage32Impl_GetNextBlockInChain
2723 * This method will retrieve the block index of the next big block in
2724 * in the chain.
2726 * Params: This - Pointer to the Storage object.
2727 * blockIndex - Index of the block to retrieve the chain
2728 * for.
2730 * Returns: This method returns the index of the next block in the chain.
2731 * It will return the constants:
2732 * BLOCK_SPECIAL - If the block given was not part of a
2733 * chain.
2734 * BLOCK_END_OF_CHAIN - If the block given was the last in
2735 * a chain.
2736 * BLOCK_UNUSED - If the block given was not past of a chain
2737 * and is available.
2738 * BLOCK_EXTBBDEPOT - This block is part of the extended
2739 * big block depot.
2741 * See Windows documentation for more details on IStorage methods.
2743 ULONG StorageImpl_GetNextBlockInChain(
2744 StorageImpl* This,
2745 ULONG blockIndex)
2747 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2748 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2749 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2750 ULONG nextBlockIndex = BLOCK_SPECIAL;
2751 void* depotBuffer;
2752 ULONG depotBlockIndexPos;
2754 assert(depotBlockCount < This->bigBlockDepotCount);
2757 * Cache the currently accessed depot block.
2759 if (depotBlockCount != This->indexBlockDepotCached)
2761 This->indexBlockDepotCached = depotBlockCount;
2763 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2765 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2767 else
2770 * We have to look in the extended depot.
2772 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2775 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2777 if (depotBuffer!=0)
2779 int index;
2781 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2783 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2784 This->blockDepotCached[index] = nextBlockIndex;
2787 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2791 nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2793 return nextBlockIndex;
2796 /******************************************************************************
2797 * Storage32Impl_GetNextExtendedBlock
2799 * Given an extended block this method will return the next extended block.
2801 * NOTES:
2802 * The last ULONG of an extended block is the block index of the next
2803 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2804 * depot.
2806 * Return values:
2807 * - The index of the next extended block
2808 * - BLOCK_UNUSED: there is no next extended block.
2809 * - Any other return values denotes failure.
2811 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2813 ULONG nextBlockIndex = BLOCK_SPECIAL;
2814 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2815 void* depotBuffer;
2817 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2819 if (depotBuffer!=0)
2821 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2823 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2826 return nextBlockIndex;
2829 /******************************************************************************
2830 * Storage32Impl_SetNextBlockInChain
2832 * This method will write the index of the specified block's next block
2833 * in the big block depot.
2835 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2836 * do the following
2838 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2839 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2840 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2843 void StorageImpl_SetNextBlockInChain(
2844 StorageImpl* This,
2845 ULONG blockIndex,
2846 ULONG nextBlock)
2848 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2849 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2850 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2851 ULONG depotBlockIndexPos;
2852 void* depotBuffer;
2854 assert(depotBlockCount < This->bigBlockDepotCount);
2855 assert(blockIndex != nextBlock);
2857 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2859 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2861 else
2864 * We have to look in the extended depot.
2866 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2869 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2871 if (depotBuffer!=0)
2873 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2874 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2878 * Update the cached block depot, if necessary.
2880 if (depotBlockCount == This->indexBlockDepotCached)
2882 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2886 /******************************************************************************
2887 * Storage32Impl_LoadFileHeader
2889 * This method will read in the file header, i.e. big block index -1.
2891 HRESULT StorageImpl_LoadFileHeader(
2892 StorageImpl* This)
2894 HRESULT hr = STG_E_FILENOTFOUND;
2895 void* headerBigBlock = NULL;
2896 int index;
2899 * Get a pointer to the big block of data containing the header.
2901 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2904 * Extract the information from the header.
2906 if (headerBigBlock!=0)
2909 * Check for the "magic number" signature and return an error if it is not
2910 * found.
2912 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2914 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2915 return STG_E_OLDFORMAT;
2918 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2920 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2921 return STG_E_INVALIDHEADER;
2924 StorageUtl_ReadWord(
2925 headerBigBlock,
2926 OFFSET_BIGBLOCKSIZEBITS,
2927 &This->bigBlockSizeBits);
2929 StorageUtl_ReadWord(
2930 headerBigBlock,
2931 OFFSET_SMALLBLOCKSIZEBITS,
2932 &This->smallBlockSizeBits);
2934 StorageUtl_ReadDWord(
2935 headerBigBlock,
2936 OFFSET_BBDEPOTCOUNT,
2937 &This->bigBlockDepotCount);
2939 StorageUtl_ReadDWord(
2940 headerBigBlock,
2941 OFFSET_ROOTSTARTBLOCK,
2942 &This->rootStartBlock);
2944 StorageUtl_ReadDWord(
2945 headerBigBlock,
2946 OFFSET_SBDEPOTSTART,
2947 &This->smallBlockDepotStart);
2949 StorageUtl_ReadDWord(
2950 headerBigBlock,
2951 OFFSET_EXTBBDEPOTSTART,
2952 &This->extBigBlockDepotStart);
2954 StorageUtl_ReadDWord(
2955 headerBigBlock,
2956 OFFSET_EXTBBDEPOTCOUNT,
2957 &This->extBigBlockDepotCount);
2959 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2961 StorageUtl_ReadDWord(
2962 headerBigBlock,
2963 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2964 &(This->bigBlockDepotStart[index]));
2968 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2970 if ((1 << 2) == 4)
2972 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2973 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2975 else
2977 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2978 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2982 * Right now, the code is making some assumptions about the size of the
2983 * blocks, just make sure they are what we're expecting.
2985 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2986 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2988 WARN("Broken OLE storage file\n");
2989 hr = STG_E_INVALIDHEADER;
2991 else
2992 hr = S_OK;
2995 * Release the block.
2997 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3000 return hr;
3003 /******************************************************************************
3004 * Storage32Impl_SaveFileHeader
3006 * This method will save to the file the header, i.e. big block -1.
3008 void StorageImpl_SaveFileHeader(
3009 StorageImpl* This)
3011 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3012 int index;
3013 BOOL success;
3016 * Get a pointer to the big block of data containing the header.
3018 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3021 * If the block read failed, the file is probably new.
3023 if (!success)
3026 * Initialize for all unknown fields.
3028 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3031 * Initialize the magic number.
3033 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3036 * And a bunch of things we don't know what they mean
3038 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3039 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3040 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3041 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3042 StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
3046 * Write the information to the header.
3048 if (headerBigBlock!=0)
3050 StorageUtl_WriteWord(
3051 headerBigBlock,
3052 OFFSET_BIGBLOCKSIZEBITS,
3053 This->bigBlockSizeBits);
3055 StorageUtl_WriteWord(
3056 headerBigBlock,
3057 OFFSET_SMALLBLOCKSIZEBITS,
3058 This->smallBlockSizeBits);
3060 StorageUtl_WriteDWord(
3061 headerBigBlock,
3062 OFFSET_BBDEPOTCOUNT,
3063 This->bigBlockDepotCount);
3065 StorageUtl_WriteDWord(
3066 headerBigBlock,
3067 OFFSET_ROOTSTARTBLOCK,
3068 This->rootStartBlock);
3070 StorageUtl_WriteDWord(
3071 headerBigBlock,
3072 OFFSET_SBDEPOTSTART,
3073 This->smallBlockDepotStart);
3075 StorageUtl_WriteDWord(
3076 headerBigBlock,
3077 OFFSET_EXTBBDEPOTSTART,
3078 This->extBigBlockDepotStart);
3080 StorageUtl_WriteDWord(
3081 headerBigBlock,
3082 OFFSET_EXTBBDEPOTCOUNT,
3083 This->extBigBlockDepotCount);
3085 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3087 StorageUtl_WriteDWord(
3088 headerBigBlock,
3089 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3090 (This->bigBlockDepotStart[index]));
3095 * Write the big block back to the file.
3097 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3100 /******************************************************************************
3101 * Storage32Impl_ReadProperty
3103 * This method will read the specified property from the property chain.
3105 BOOL StorageImpl_ReadProperty(
3106 StorageImpl* This,
3107 ULONG index,
3108 StgProperty* buffer)
3110 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3111 ULARGE_INTEGER offsetInPropSet;
3112 BOOL readSuccessful;
3113 ULONG bytesRead;
3115 offsetInPropSet.s.HighPart = 0;
3116 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3118 readSuccessful = BlockChainStream_ReadAt(
3119 This->rootBlockChain,
3120 offsetInPropSet,
3121 PROPSET_BLOCK_SIZE,
3122 currentProperty,
3123 &bytesRead);
3125 if (readSuccessful)
3127 /* replace the name of root entry (often "Root Entry") by the file name */
3128 WCHAR *propName = (index == This->rootPropertySetIndex) ?
3129 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3131 memset(buffer->name, 0, sizeof(buffer->name));
3132 memcpy(
3133 buffer->name,
3134 propName,
3135 PROPERTY_NAME_BUFFER_LEN );
3136 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3138 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3140 StorageUtl_ReadWord(
3141 currentProperty,
3142 OFFSET_PS_NAMELENGTH,
3143 &buffer->sizeOfNameString);
3145 StorageUtl_ReadDWord(
3146 currentProperty,
3147 OFFSET_PS_PREVIOUSPROP,
3148 &buffer->previousProperty);
3150 StorageUtl_ReadDWord(
3151 currentProperty,
3152 OFFSET_PS_NEXTPROP,
3153 &buffer->nextProperty);
3155 StorageUtl_ReadDWord(
3156 currentProperty,
3157 OFFSET_PS_DIRPROP,
3158 &buffer->dirProperty);
3160 StorageUtl_ReadGUID(
3161 currentProperty,
3162 OFFSET_PS_GUID,
3163 &buffer->propertyUniqueID);
3165 StorageUtl_ReadDWord(
3166 currentProperty,
3167 OFFSET_PS_TSS1,
3168 &buffer->timeStampS1);
3170 StorageUtl_ReadDWord(
3171 currentProperty,
3172 OFFSET_PS_TSD1,
3173 &buffer->timeStampD1);
3175 StorageUtl_ReadDWord(
3176 currentProperty,
3177 OFFSET_PS_TSS2,
3178 &buffer->timeStampS2);
3180 StorageUtl_ReadDWord(
3181 currentProperty,
3182 OFFSET_PS_TSD2,
3183 &buffer->timeStampD2);
3185 StorageUtl_ReadDWord(
3186 currentProperty,
3187 OFFSET_PS_STARTBLOCK,
3188 &buffer->startingBlock);
3190 StorageUtl_ReadDWord(
3191 currentProperty,
3192 OFFSET_PS_SIZE,
3193 &buffer->size.s.LowPart);
3195 buffer->size.s.HighPart = 0;
3198 return readSuccessful;
3201 /*********************************************************************
3202 * Write the specified property into the property chain
3204 BOOL StorageImpl_WriteProperty(
3205 StorageImpl* This,
3206 ULONG index,
3207 StgProperty* buffer)
3209 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3210 ULARGE_INTEGER offsetInPropSet;
3211 BOOL writeSuccessful;
3212 ULONG bytesWritten;
3214 offsetInPropSet.s.HighPart = 0;
3215 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3217 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3219 memcpy(
3220 currentProperty + OFFSET_PS_NAME,
3221 buffer->name,
3222 PROPERTY_NAME_BUFFER_LEN );
3224 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3226 StorageUtl_WriteWord(
3227 currentProperty,
3228 OFFSET_PS_NAMELENGTH,
3229 buffer->sizeOfNameString);
3231 StorageUtl_WriteDWord(
3232 currentProperty,
3233 OFFSET_PS_PREVIOUSPROP,
3234 buffer->previousProperty);
3236 StorageUtl_WriteDWord(
3237 currentProperty,
3238 OFFSET_PS_NEXTPROP,
3239 buffer->nextProperty);
3241 StorageUtl_WriteDWord(
3242 currentProperty,
3243 OFFSET_PS_DIRPROP,
3244 buffer->dirProperty);
3246 StorageUtl_WriteGUID(
3247 currentProperty,
3248 OFFSET_PS_GUID,
3249 &buffer->propertyUniqueID);
3251 StorageUtl_WriteDWord(
3252 currentProperty,
3253 OFFSET_PS_TSS1,
3254 buffer->timeStampS1);
3256 StorageUtl_WriteDWord(
3257 currentProperty,
3258 OFFSET_PS_TSD1,
3259 buffer->timeStampD1);
3261 StorageUtl_WriteDWord(
3262 currentProperty,
3263 OFFSET_PS_TSS2,
3264 buffer->timeStampS2);
3266 StorageUtl_WriteDWord(
3267 currentProperty,
3268 OFFSET_PS_TSD2,
3269 buffer->timeStampD2);
3271 StorageUtl_WriteDWord(
3272 currentProperty,
3273 OFFSET_PS_STARTBLOCK,
3274 buffer->startingBlock);
3276 StorageUtl_WriteDWord(
3277 currentProperty,
3278 OFFSET_PS_SIZE,
3279 buffer->size.s.LowPart);
3281 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3282 offsetInPropSet,
3283 PROPSET_BLOCK_SIZE,
3284 currentProperty,
3285 &bytesWritten);
3286 return writeSuccessful;
3289 BOOL StorageImpl_ReadBigBlock(
3290 StorageImpl* This,
3291 ULONG blockIndex,
3292 void* buffer)
3294 void* bigBlockBuffer;
3296 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3298 if (bigBlockBuffer!=0)
3300 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3302 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3304 return TRUE;
3307 return FALSE;
3310 BOOL StorageImpl_WriteBigBlock(
3311 StorageImpl* This,
3312 ULONG blockIndex,
3313 void* buffer)
3315 void* bigBlockBuffer;
3317 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3319 if (bigBlockBuffer!=0)
3321 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3323 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3325 return TRUE;
3328 return FALSE;
3331 void* StorageImpl_GetROBigBlock(
3332 StorageImpl* This,
3333 ULONG blockIndex)
3335 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3338 void* StorageImpl_GetBigBlock(
3339 StorageImpl* This,
3340 ULONG blockIndex)
3342 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3345 void StorageImpl_ReleaseBigBlock(
3346 StorageImpl* This,
3347 void* pBigBlock)
3349 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3352 /******************************************************************************
3353 * Storage32Impl_SmallBlocksToBigBlocks
3355 * This method will convert a small block chain to a big block chain.
3356 * The small block chain will be destroyed.
3358 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3359 StorageImpl* This,
3360 SmallBlockChainStream** ppsbChain)
3362 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3363 ULARGE_INTEGER size, offset;
3364 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3365 ULONG propertyIndex;
3366 BOOL successRead, successWrite;
3367 StgProperty chainProperty;
3368 BYTE *buffer;
3369 BlockChainStream *bbTempChain = NULL;
3370 BlockChainStream *bigBlockChain = NULL;
3373 * Create a temporary big block chain that doesn't have
3374 * an associated property. This temporary chain will be
3375 * used to copy data from small blocks to big blocks.
3377 bbTempChain = BlockChainStream_Construct(This,
3378 &bbHeadOfChain,
3379 PROPERTY_NULL);
3382 * Grow the big block chain.
3384 size = SmallBlockChainStream_GetSize(*ppsbChain);
3385 BlockChainStream_SetSize(bbTempChain, size);
3388 * Copy the contents of the small block chain to the big block chain
3389 * by small block size increments.
3391 offset.s.LowPart = 0;
3392 offset.s.HighPart = 0;
3393 cbTotalRead = 0;
3394 cbTotalWritten = 0;
3396 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3399 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3400 offset,
3401 DEF_SMALL_BLOCK_SIZE,
3402 buffer,
3403 &cbRead);
3404 cbTotalRead += cbRead;
3406 successWrite = BlockChainStream_WriteAt(bbTempChain,
3407 offset,
3408 cbRead,
3409 buffer,
3410 &cbWritten);
3411 cbTotalWritten += cbWritten;
3413 offset.s.LowPart += This->smallBlockSize;
3415 } while (successRead && successWrite);
3416 HeapFree(GetProcessHeap(),0,buffer);
3418 assert(cbTotalRead == cbTotalWritten);
3421 * Destroy the small block chain.
3423 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3424 size.s.HighPart = 0;
3425 size.s.LowPart = 0;
3426 SmallBlockChainStream_SetSize(*ppsbChain, size);
3427 SmallBlockChainStream_Destroy(*ppsbChain);
3428 *ppsbChain = 0;
3431 * Change the property information. This chain is now a big block chain
3432 * and it doesn't reside in the small blocks chain anymore.
3434 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3436 chainProperty.startingBlock = bbHeadOfChain;
3438 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3441 * Destroy the temporary propertyless big block chain.
3442 * Create a new big block chain associated with this property.
3444 BlockChainStream_Destroy(bbTempChain);
3445 bigBlockChain = BlockChainStream_Construct(This,
3446 NULL,
3447 propertyIndex);
3449 return bigBlockChain;
3452 /******************************************************************************
3453 ** Storage32InternalImpl implementation
3456 StorageInternalImpl* StorageInternalImpl_Construct(
3457 StorageImpl* ancestorStorage,
3458 ULONG rootPropertyIndex)
3460 StorageInternalImpl* newStorage;
3463 * Allocate space for the new storage object
3465 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3467 if (newStorage!=0)
3469 memset(newStorage, 0, sizeof(StorageInternalImpl));
3472 * Initialize the virtual function table.
3474 ICOM_VTBL(newStorage) = &Storage32InternalImpl_Vtbl;
3475 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3478 * Keep the ancestor storage pointer and nail a reference to it.
3480 newStorage->ancestorStorage = ancestorStorage;
3481 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3484 * Keep the index of the root property set for this storage,
3486 newStorage->rootPropertySetIndex = rootPropertyIndex;
3488 return newStorage;
3491 return 0;
3494 void StorageInternalImpl_Destroy(
3495 StorageInternalImpl* This)
3497 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3498 HeapFree(GetProcessHeap(), 0, This);
3501 /******************************************************************************
3503 ** Storage32InternalImpl_Commit
3505 ** The non-root storages cannot be opened in transacted mode thus this function
3506 ** does nothing.
3508 HRESULT WINAPI StorageInternalImpl_Commit(
3509 IStorage* iface,
3510 DWORD grfCommitFlags) /* [in] */
3512 return S_OK;
3515 /******************************************************************************
3517 ** Storage32InternalImpl_Revert
3519 ** The non-root storages cannot be opened in transacted mode thus this function
3520 ** does nothing.
3522 HRESULT WINAPI StorageInternalImpl_Revert(
3523 IStorage* iface)
3525 return S_OK;
3528 /******************************************************************************
3529 ** IEnumSTATSTGImpl implementation
3532 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3533 StorageImpl* parentStorage,
3534 ULONG firstPropertyNode)
3536 IEnumSTATSTGImpl* newEnumeration;
3538 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3540 if (newEnumeration!=0)
3543 * Set-up the virtual function table and reference count.
3545 ICOM_VTBL(newEnumeration) = &IEnumSTATSTGImpl_Vtbl;
3546 newEnumeration->ref = 0;
3549 * We want to nail-down the reference to the storage in case the
3550 * enumeration out-lives the storage in the client application.
3552 newEnumeration->parentStorage = parentStorage;
3553 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3555 newEnumeration->firstPropertyNode = firstPropertyNode;
3558 * Initialize the search stack
3560 newEnumeration->stackSize = 0;
3561 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3562 newEnumeration->stackToVisit =
3563 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3566 * Make sure the current node of the iterator is the first one.
3568 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3571 return newEnumeration;
3574 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3576 IStorage_Release((IStorage*)This->parentStorage);
3577 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3578 HeapFree(GetProcessHeap(), 0, This);
3581 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3582 IEnumSTATSTG* iface,
3583 REFIID riid,
3584 void** ppvObject)
3586 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3589 * Perform a sanity check on the parameters.
3591 if (ppvObject==0)
3592 return E_INVALIDARG;
3595 * Initialize the return parameter.
3597 *ppvObject = 0;
3600 * Compare the riid with the interface IDs implemented by this object.
3602 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3604 *ppvObject = (IEnumSTATSTG*)This;
3606 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3608 *ppvObject = (IEnumSTATSTG*)This;
3612 * Check that we obtained an interface.
3614 if ((*ppvObject)==0)
3615 return E_NOINTERFACE;
3618 * Query Interface always increases the reference count by one when it is
3619 * successful
3621 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3623 return S_OK;
3626 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3627 IEnumSTATSTG* iface)
3629 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3631 This->ref++;
3632 return This->ref;
3635 ULONG WINAPI IEnumSTATSTGImpl_Release(
3636 IEnumSTATSTG* iface)
3638 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3640 ULONG newRef;
3642 This->ref--;
3643 newRef = This->ref;
3646 * If the reference count goes down to 0, perform suicide.
3648 if (newRef==0)
3650 IEnumSTATSTGImpl_Destroy(This);
3653 return newRef;;
3656 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3657 IEnumSTATSTG* iface,
3658 ULONG celt,
3659 STATSTG* rgelt,
3660 ULONG* pceltFetched)
3662 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3664 StgProperty currentProperty;
3665 STATSTG* currentReturnStruct = rgelt;
3666 ULONG objectFetched = 0;
3667 ULONG currentSearchNode;
3670 * Perform a sanity check on the parameters.
3672 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3673 return E_INVALIDARG;
3676 * To avoid the special case, get another pointer to a ULONG value if
3677 * the caller didn't supply one.
3679 if (pceltFetched==0)
3680 pceltFetched = &objectFetched;
3683 * Start the iteration, we will iterate until we hit the end of the
3684 * linked list or until we hit the number of items to iterate through
3686 *pceltFetched = 0;
3689 * Start with the node at the top of the stack.
3691 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3693 while ( ( *pceltFetched < celt) &&
3694 ( currentSearchNode!=PROPERTY_NULL) )
3697 * Remove the top node from the stack
3699 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3702 * Read the property from the storage.
3704 StorageImpl_ReadProperty(This->parentStorage,
3705 currentSearchNode,
3706 &currentProperty);
3709 * Copy the information to the return buffer.
3711 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3712 &currentProperty,
3713 STATFLAG_DEFAULT);
3716 * Step to the next item in the iteration
3718 (*pceltFetched)++;
3719 currentReturnStruct++;
3722 * Push the next search node in the search stack.
3724 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3727 * continue the iteration.
3729 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3732 if (*pceltFetched == celt)
3733 return S_OK;
3735 return S_FALSE;
3739 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3740 IEnumSTATSTG* iface,
3741 ULONG celt)
3743 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3745 StgProperty currentProperty;
3746 ULONG objectFetched = 0;
3747 ULONG currentSearchNode;
3750 * Start with the node at the top of the stack.
3752 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3754 while ( (objectFetched < celt) &&
3755 (currentSearchNode!=PROPERTY_NULL) )
3758 * Remove the top node from the stack
3760 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3763 * Read the property from the storage.
3765 StorageImpl_ReadProperty(This->parentStorage,
3766 currentSearchNode,
3767 &currentProperty);
3770 * Step to the next item in the iteration
3772 objectFetched++;
3775 * Push the next search node in the search stack.
3777 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3780 * continue the iteration.
3782 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3785 if (objectFetched == celt)
3786 return S_OK;
3788 return S_FALSE;
3791 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3792 IEnumSTATSTG* iface)
3794 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3796 StgProperty rootProperty;
3797 BOOL readSuccessful;
3800 * Re-initialize the search stack to an empty stack
3802 This->stackSize = 0;
3805 * Read the root property from the storage.
3807 readSuccessful = StorageImpl_ReadProperty(
3808 This->parentStorage,
3809 This->firstPropertyNode,
3810 &rootProperty);
3812 if (readSuccessful)
3814 assert(rootProperty.sizeOfNameString!=0);
3817 * Push the search node in the search stack.
3819 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3822 return S_OK;
3825 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3826 IEnumSTATSTG* iface,
3827 IEnumSTATSTG** ppenum)
3829 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3831 IEnumSTATSTGImpl* newClone;
3834 * Perform a sanity check on the parameters.
3836 if (ppenum==0)
3837 return E_INVALIDARG;
3839 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3840 This->firstPropertyNode);
3844 * The new clone enumeration must point to the same current node as
3845 * the ole one.
3847 newClone->stackSize = This->stackSize ;
3848 newClone->stackMaxSize = This->stackMaxSize ;
3849 newClone->stackToVisit =
3850 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3852 memcpy(
3853 newClone->stackToVisit,
3854 This->stackToVisit,
3855 sizeof(ULONG) * newClone->stackSize);
3857 *ppenum = (IEnumSTATSTG*)newClone;
3860 * Don't forget to nail down a reference to the clone before
3861 * returning it.
3863 IEnumSTATSTGImpl_AddRef(*ppenum);
3865 return S_OK;
3868 INT IEnumSTATSTGImpl_FindParentProperty(
3869 IEnumSTATSTGImpl *This,
3870 ULONG childProperty,
3871 StgProperty *currentProperty,
3872 ULONG *thisNodeId)
3874 ULONG currentSearchNode;
3875 ULONG foundNode;
3878 * To avoid the special case, get another pointer to a ULONG value if
3879 * the caller didn't supply one.
3881 if (thisNodeId==0)
3882 thisNodeId = &foundNode;
3885 * Start with the node at the top of the stack.
3887 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3890 while (currentSearchNode!=PROPERTY_NULL)
3893 * Store the current node in the returned parameters
3895 *thisNodeId = currentSearchNode;
3898 * Remove the top node from the stack
3900 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3903 * Read the property from the storage.
3905 StorageImpl_ReadProperty(
3906 This->parentStorage,
3907 currentSearchNode,
3908 currentProperty);
3910 if (currentProperty->previousProperty == childProperty)
3911 return PROPERTY_RELATION_PREVIOUS;
3913 else if (currentProperty->nextProperty == childProperty)
3914 return PROPERTY_RELATION_NEXT;
3916 else if (currentProperty->dirProperty == childProperty)
3917 return PROPERTY_RELATION_DIR;
3920 * Push the next search node in the search stack.
3922 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3925 * continue the iteration.
3927 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3930 return PROPERTY_NULL;
3933 ULONG IEnumSTATSTGImpl_FindProperty(
3934 IEnumSTATSTGImpl* This,
3935 const OLECHAR* lpszPropName,
3936 StgProperty* currentProperty)
3938 ULONG currentSearchNode;
3941 * Start with the node at the top of the stack.
3943 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3945 while (currentSearchNode!=PROPERTY_NULL)
3948 * Remove the top node from the stack
3950 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3953 * Read the property from the storage.
3955 StorageImpl_ReadProperty(This->parentStorage,
3956 currentSearchNode,
3957 currentProperty);
3959 if ( propertyNameCmp(
3960 (OLECHAR*)currentProperty->name,
3961 (OLECHAR*)lpszPropName) == 0)
3962 return currentSearchNode;
3965 * Push the next search node in the search stack.
3967 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3970 * continue the iteration.
3972 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3975 return PROPERTY_NULL;
3978 void IEnumSTATSTGImpl_PushSearchNode(
3979 IEnumSTATSTGImpl* This,
3980 ULONG nodeToPush)
3982 StgProperty rootProperty;
3983 BOOL readSuccessful;
3986 * First, make sure we're not trying to push an unexisting node.
3988 if (nodeToPush==PROPERTY_NULL)
3989 return;
3992 * First push the node to the stack
3994 if (This->stackSize == This->stackMaxSize)
3996 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3998 This->stackToVisit = HeapReAlloc(
3999 GetProcessHeap(),
4001 This->stackToVisit,
4002 sizeof(ULONG) * This->stackMaxSize);
4005 This->stackToVisit[This->stackSize] = nodeToPush;
4006 This->stackSize++;
4009 * Read the root property from the storage.
4011 readSuccessful = StorageImpl_ReadProperty(
4012 This->parentStorage,
4013 nodeToPush,
4014 &rootProperty);
4016 if (readSuccessful)
4018 assert(rootProperty.sizeOfNameString!=0);
4021 * Push the previous search node in the search stack.
4023 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4027 ULONG IEnumSTATSTGImpl_PopSearchNode(
4028 IEnumSTATSTGImpl* This,
4029 BOOL remove)
4031 ULONG topNode;
4033 if (This->stackSize == 0)
4034 return PROPERTY_NULL;
4036 topNode = This->stackToVisit[This->stackSize-1];
4038 if (remove)
4039 This->stackSize--;
4041 return topNode;
4044 /******************************************************************************
4045 ** StorageUtl implementation
4048 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4050 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4053 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4055 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4058 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4060 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4063 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4065 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4068 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4070 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4071 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4072 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4074 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4077 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4079 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4080 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4081 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4083 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4086 void StorageUtl_CopyPropertyToSTATSTG(
4087 STATSTG* destination,
4088 StgProperty* source,
4089 int statFlags)
4092 * The copy of the string occurs only when the flag is not set
4094 if ((statFlags & STATFLAG_NONAME) != 0)
4096 destination->pwcsName = 0;
4098 else
4100 destination->pwcsName =
4101 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4103 strcpyW((LPWSTR)destination->pwcsName, source->name);
4106 switch (source->propertyType)
4108 case PROPTYPE_STORAGE:
4109 case PROPTYPE_ROOT:
4110 destination->type = STGTY_STORAGE;
4111 break;
4112 case PROPTYPE_STREAM:
4113 destination->type = STGTY_STREAM;
4114 break;
4115 default:
4116 destination->type = STGTY_STREAM;
4117 break;
4120 destination->cbSize = source->size;
4122 currentReturnStruct->mtime = {0}; TODO
4123 currentReturnStruct->ctime = {0};
4124 currentReturnStruct->atime = {0};
4126 destination->grfMode = 0;
4127 destination->grfLocksSupported = 0;
4128 destination->clsid = source->propertyUniqueID;
4129 destination->grfStateBits = 0;
4130 destination->reserved = 0;
4133 /******************************************************************************
4134 ** BlockChainStream implementation
4137 BlockChainStream* BlockChainStream_Construct(
4138 StorageImpl* parentStorage,
4139 ULONG* headOfStreamPlaceHolder,
4140 ULONG propertyIndex)
4142 BlockChainStream* newStream;
4143 ULONG blockIndex;
4145 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4147 newStream->parentStorage = parentStorage;
4148 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4149 newStream->ownerPropertyIndex = propertyIndex;
4150 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4151 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4152 newStream->numBlocks = 0;
4154 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4156 while (blockIndex != BLOCK_END_OF_CHAIN)
4158 newStream->numBlocks++;
4159 newStream->tailIndex = blockIndex;
4161 blockIndex = StorageImpl_GetNextBlockInChain(
4162 parentStorage,
4163 blockIndex);
4166 return newStream;
4169 void BlockChainStream_Destroy(BlockChainStream* This)
4171 HeapFree(GetProcessHeap(), 0, This);
4174 /******************************************************************************
4175 * BlockChainStream_GetHeadOfChain
4177 * Returns the head of this stream chain.
4178 * Some special chains don't have properties, their heads are kept in
4179 * This->headOfStreamPlaceHolder.
4182 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4184 StgProperty chainProperty;
4185 BOOL readSuccessful;
4187 if (This->headOfStreamPlaceHolder != 0)
4188 return *(This->headOfStreamPlaceHolder);
4190 if (This->ownerPropertyIndex != PROPERTY_NULL)
4192 readSuccessful = StorageImpl_ReadProperty(
4193 This->parentStorage,
4194 This->ownerPropertyIndex,
4195 &chainProperty);
4197 if (readSuccessful)
4199 return chainProperty.startingBlock;
4203 return BLOCK_END_OF_CHAIN;
4206 /******************************************************************************
4207 * BlockChainStream_GetCount
4209 * Returns the number of blocks that comprises this chain.
4210 * This is not the size of the stream as the last block may not be full!
4213 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4215 ULONG blockIndex;
4216 ULONG count = 0;
4218 blockIndex = BlockChainStream_GetHeadOfChain(This);
4220 while (blockIndex != BLOCK_END_OF_CHAIN)
4222 count++;
4224 blockIndex = StorageImpl_GetNextBlockInChain(
4225 This->parentStorage,
4226 blockIndex);
4229 return count;
4232 /******************************************************************************
4233 * BlockChainStream_ReadAt
4235 * Reads a specified number of bytes from this chain at the specified offset.
4236 * bytesRead may be NULL.
4237 * Failure will be returned if the specified number of bytes has not been read.
4239 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4240 ULARGE_INTEGER offset,
4241 ULONG size,
4242 void* buffer,
4243 ULONG* bytesRead)
4245 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4246 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4247 ULONG bytesToReadInBuffer;
4248 ULONG blockIndex;
4249 BYTE* bufferWalker;
4250 BYTE* bigBlockBuffer;
4253 * Find the first block in the stream that contains part of the buffer.
4255 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4256 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4257 (blockNoInSequence < This->lastBlockNoInSequence) )
4259 blockIndex = BlockChainStream_GetHeadOfChain(This);
4260 This->lastBlockNoInSequence = blockNoInSequence;
4262 else
4264 ULONG temp = blockNoInSequence;
4266 blockIndex = This->lastBlockNoInSequenceIndex;
4267 blockNoInSequence -= This->lastBlockNoInSequence;
4268 This->lastBlockNoInSequence = temp;
4271 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4273 blockIndex =
4274 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4276 blockNoInSequence--;
4279 This->lastBlockNoInSequenceIndex = blockIndex;
4282 * Start reading the buffer.
4284 *bytesRead = 0;
4285 bufferWalker = buffer;
4287 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4290 * Calculate how many bytes we can copy from this big block.
4292 bytesToReadInBuffer =
4293 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4296 * Copy those bytes to the buffer
4298 bigBlockBuffer =
4299 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4301 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4303 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4306 * Step to the next big block.
4308 blockIndex =
4309 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4311 bufferWalker += bytesToReadInBuffer;
4312 size -= bytesToReadInBuffer;
4313 *bytesRead += bytesToReadInBuffer;
4314 offsetInBlock = 0; /* There is no offset on the next block */
4318 return (size == 0);
4321 /******************************************************************************
4322 * BlockChainStream_WriteAt
4324 * Writes the specified number of bytes to this chain at the specified offset.
4325 * bytesWritten may be NULL.
4326 * Will fail if not all specified number of bytes have been written.
4328 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4329 ULARGE_INTEGER offset,
4330 ULONG size,
4331 const void* buffer,
4332 ULONG* bytesWritten)
4334 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4335 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4336 ULONG bytesToWrite;
4337 ULONG blockIndex;
4338 BYTE* bufferWalker;
4339 BYTE* bigBlockBuffer;
4342 * Find the first block in the stream that contains part of the buffer.
4344 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4345 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4346 (blockNoInSequence < This->lastBlockNoInSequence) )
4348 blockIndex = BlockChainStream_GetHeadOfChain(This);
4349 This->lastBlockNoInSequence = blockNoInSequence;
4351 else
4353 ULONG temp = blockNoInSequence;
4355 blockIndex = This->lastBlockNoInSequenceIndex;
4356 blockNoInSequence -= This->lastBlockNoInSequence;
4357 This->lastBlockNoInSequence = temp;
4360 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4362 blockIndex =
4363 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4365 blockNoInSequence--;
4368 This->lastBlockNoInSequenceIndex = blockIndex;
4371 * Here, I'm casting away the constness on the buffer variable
4372 * This is OK since we don't intend to modify that buffer.
4374 *bytesWritten = 0;
4375 bufferWalker = (BYTE*)buffer;
4377 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4380 * Calculate how many bytes we can copy from this big block.
4382 bytesToWrite =
4383 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4386 * Copy those bytes to the buffer
4388 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4390 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4392 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4395 * Step to the next big block.
4397 blockIndex =
4398 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4400 bufferWalker += bytesToWrite;
4401 size -= bytesToWrite;
4402 *bytesWritten += bytesToWrite;
4403 offsetInBlock = 0; /* There is no offset on the next block */
4406 return (size == 0);
4409 /******************************************************************************
4410 * BlockChainStream_Shrink
4412 * Shrinks this chain in the big block depot.
4414 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4415 ULARGE_INTEGER newSize)
4417 ULONG blockIndex, extraBlock;
4418 ULONG numBlocks;
4419 ULONG count = 1;
4422 * Reset the last accessed block cache.
4424 This->lastBlockNoInSequence = 0xFFFFFFFF;
4425 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4428 * Figure out how many blocks are needed to contain the new size
4430 numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4432 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4433 numBlocks++;
4435 blockIndex = BlockChainStream_GetHeadOfChain(This);
4438 * Go to the new end of chain
4440 while (count < numBlocks)
4442 blockIndex =
4443 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4445 count++;
4448 /* Get the next block before marking the new end */
4449 extraBlock =
4450 StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4452 /* Mark the new end of chain */
4453 StorageImpl_SetNextBlockInChain(
4454 This->parentStorage,
4455 blockIndex,
4456 BLOCK_END_OF_CHAIN);
4458 This->tailIndex = blockIndex;
4459 This->numBlocks = numBlocks;
4462 * Mark the extra blocks as free
4464 while (extraBlock != BLOCK_END_OF_CHAIN)
4466 blockIndex =
4467 StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4469 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4470 extraBlock = blockIndex;
4473 return TRUE;
4476 /******************************************************************************
4477 * BlockChainStream_Enlarge
4479 * Grows this chain in the big block depot.
4481 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4482 ULARGE_INTEGER newSize)
4484 ULONG blockIndex, currentBlock;
4485 ULONG newNumBlocks;
4486 ULONG oldNumBlocks = 0;
4488 blockIndex = BlockChainStream_GetHeadOfChain(This);
4491 * Empty chain. Create the head.
4493 if (blockIndex == BLOCK_END_OF_CHAIN)
4495 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4496 StorageImpl_SetNextBlockInChain(This->parentStorage,
4497 blockIndex,
4498 BLOCK_END_OF_CHAIN);
4500 if (This->headOfStreamPlaceHolder != 0)
4502 *(This->headOfStreamPlaceHolder) = blockIndex;
4504 else
4506 StgProperty chainProp;
4507 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4509 StorageImpl_ReadProperty(
4510 This->parentStorage,
4511 This->ownerPropertyIndex,
4512 &chainProp);
4514 chainProp.startingBlock = blockIndex;
4516 StorageImpl_WriteProperty(
4517 This->parentStorage,
4518 This->ownerPropertyIndex,
4519 &chainProp);
4522 This->tailIndex = blockIndex;
4523 This->numBlocks = 1;
4527 * Figure out how many blocks are needed to contain this stream
4529 newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4531 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4532 newNumBlocks++;
4535 * Go to the current end of chain
4537 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4539 currentBlock = blockIndex;
4541 while (blockIndex != BLOCK_END_OF_CHAIN)
4543 This->numBlocks++;
4544 currentBlock = blockIndex;
4546 blockIndex =
4547 StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4550 This->tailIndex = currentBlock;
4553 currentBlock = This->tailIndex;
4554 oldNumBlocks = This->numBlocks;
4557 * Add new blocks to the chain
4559 if (oldNumBlocks < newNumBlocks)
4561 while (oldNumBlocks < newNumBlocks)
4563 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4565 StorageImpl_SetNextBlockInChain(
4566 This->parentStorage,
4567 currentBlock,
4568 blockIndex);
4570 StorageImpl_SetNextBlockInChain(
4571 This->parentStorage,
4572 blockIndex,
4573 BLOCK_END_OF_CHAIN);
4575 currentBlock = blockIndex;
4576 oldNumBlocks++;
4579 This->tailIndex = blockIndex;
4580 This->numBlocks = newNumBlocks;
4583 return TRUE;
4586 /******************************************************************************
4587 * BlockChainStream_SetSize
4589 * Sets the size of this stream. The big block depot will be updated.
4590 * The file will grow if we grow the chain.
4592 * TODO: Free the actual blocks in the file when we shrink the chain.
4593 * Currently, the blocks are still in the file. So the file size
4594 * doesn't shrink even if we shrink streams.
4596 BOOL BlockChainStream_SetSize(
4597 BlockChainStream* This,
4598 ULARGE_INTEGER newSize)
4600 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4602 if (newSize.s.LowPart == size.s.LowPart)
4603 return TRUE;
4605 if (newSize.s.LowPart < size.s.LowPart)
4607 BlockChainStream_Shrink(This, newSize);
4609 else
4611 ULARGE_INTEGER fileSize =
4612 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4614 ULONG diff = newSize.s.LowPart - size.s.LowPart;
4617 * Make sure the file stays a multiple of blocksize
4619 if ((diff % This->parentStorage->bigBlockSize) != 0)
4620 diff += (This->parentStorage->bigBlockSize -
4621 (diff % This->parentStorage->bigBlockSize) );
4623 fileSize.s.LowPart += diff;
4624 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4626 BlockChainStream_Enlarge(This, newSize);
4629 return TRUE;
4632 /******************************************************************************
4633 * BlockChainStream_GetSize
4635 * Returns the size of this chain.
4636 * Will return the block count if this chain doesn't have a property.
4638 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4640 StgProperty chainProperty;
4642 if(This->headOfStreamPlaceHolder == NULL)
4645 * This chain is a data stream read the property and return
4646 * the appropriate size
4648 StorageImpl_ReadProperty(
4649 This->parentStorage,
4650 This->ownerPropertyIndex,
4651 &chainProperty);
4653 return chainProperty.size;
4655 else
4658 * this chain is a chain that does not have a property, figure out the
4659 * size by making the product number of used blocks times the
4660 * size of them
4662 ULARGE_INTEGER result;
4663 result.s.HighPart = 0;
4665 result.s.LowPart =
4666 BlockChainStream_GetCount(This) *
4667 This->parentStorage->bigBlockSize;
4669 return result;
4673 /******************************************************************************
4674 ** SmallBlockChainStream implementation
4677 SmallBlockChainStream* SmallBlockChainStream_Construct(
4678 StorageImpl* parentStorage,
4679 ULONG propertyIndex)
4681 SmallBlockChainStream* newStream;
4683 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4685 newStream->parentStorage = parentStorage;
4686 newStream->ownerPropertyIndex = propertyIndex;
4688 return newStream;
4691 void SmallBlockChainStream_Destroy(
4692 SmallBlockChainStream* This)
4694 HeapFree(GetProcessHeap(), 0, This);
4697 /******************************************************************************
4698 * SmallBlockChainStream_GetHeadOfChain
4700 * Returns the head of this chain of small blocks.
4702 ULONG SmallBlockChainStream_GetHeadOfChain(
4703 SmallBlockChainStream* This)
4705 StgProperty chainProperty;
4706 BOOL readSuccessful;
4708 if (This->ownerPropertyIndex)
4710 readSuccessful = StorageImpl_ReadProperty(
4711 This->parentStorage,
4712 This->ownerPropertyIndex,
4713 &chainProperty);
4715 if (readSuccessful)
4717 return chainProperty.startingBlock;
4722 return BLOCK_END_OF_CHAIN;
4725 /******************************************************************************
4726 * SmallBlockChainStream_GetNextBlockInChain
4728 * Returns the index of the next small block in this chain.
4730 * Return Values:
4731 * - BLOCK_END_OF_CHAIN: end of this chain
4732 * - BLOCK_UNUSED: small block 'blockIndex' is free
4734 ULONG SmallBlockChainStream_GetNextBlockInChain(
4735 SmallBlockChainStream* This,
4736 ULONG blockIndex)
4738 ULARGE_INTEGER offsetOfBlockInDepot;
4739 DWORD buffer;
4740 ULONG nextBlockInChain = BLOCK_END_OF_CHAIN;
4741 ULONG bytesRead;
4742 BOOL success;
4744 offsetOfBlockInDepot.s.HighPart = 0;
4745 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4748 * Read those bytes in the buffer from the small block file.
4750 success = BlockChainStream_ReadAt(
4751 This->parentStorage->smallBlockDepotChain,
4752 offsetOfBlockInDepot,
4753 sizeof(DWORD),
4754 &buffer,
4755 &bytesRead);
4757 if (success)
4759 StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4762 return nextBlockInChain;
4765 /******************************************************************************
4766 * SmallBlockChainStream_SetNextBlockInChain
4768 * Writes the index of the next block of the specified block in the small
4769 * block depot.
4770 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4771 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4773 void SmallBlockChainStream_SetNextBlockInChain(
4774 SmallBlockChainStream* This,
4775 ULONG blockIndex,
4776 ULONG nextBlock)
4778 ULARGE_INTEGER offsetOfBlockInDepot;
4779 DWORD buffer;
4780 ULONG bytesWritten;
4782 offsetOfBlockInDepot.s.HighPart = 0;
4783 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4785 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4788 * Read those bytes in the buffer from the small block file.
4790 BlockChainStream_WriteAt(
4791 This->parentStorage->smallBlockDepotChain,
4792 offsetOfBlockInDepot,
4793 sizeof(DWORD),
4794 &buffer,
4795 &bytesWritten);
4798 /******************************************************************************
4799 * SmallBlockChainStream_FreeBlock
4801 * Flag small block 'blockIndex' as free in the small block depot.
4803 void SmallBlockChainStream_FreeBlock(
4804 SmallBlockChainStream* This,
4805 ULONG blockIndex)
4807 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4810 /******************************************************************************
4811 * SmallBlockChainStream_GetNextFreeBlock
4813 * Returns the index of a free small block. The small block depot will be
4814 * enlarged if necessary. The small block chain will also be enlarged if
4815 * necessary.
4817 ULONG SmallBlockChainStream_GetNextFreeBlock(
4818 SmallBlockChainStream* This)
4820 ULARGE_INTEGER offsetOfBlockInDepot;
4821 DWORD buffer;
4822 ULONG bytesRead;
4823 ULONG blockIndex = 0;
4824 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4825 BOOL success = TRUE;
4826 ULONG smallBlocksPerBigBlock;
4828 offsetOfBlockInDepot.s.HighPart = 0;
4831 * Scan the small block depot for a free block
4833 while (nextBlockIndex != BLOCK_UNUSED)
4835 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4837 success = BlockChainStream_ReadAt(
4838 This->parentStorage->smallBlockDepotChain,
4839 offsetOfBlockInDepot,
4840 sizeof(DWORD),
4841 &buffer,
4842 &bytesRead);
4845 * If we run out of space for the small block depot, enlarge it
4847 if (success)
4849 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4851 if (nextBlockIndex != BLOCK_UNUSED)
4852 blockIndex++;
4854 else
4856 ULONG count =
4857 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4859 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4860 ULONG nextBlock, newsbdIndex;
4861 BYTE* smallBlockDepot;
4863 nextBlock = sbdIndex;
4864 while (nextBlock != BLOCK_END_OF_CHAIN)
4866 sbdIndex = nextBlock;
4867 nextBlock =
4868 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4871 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4872 if (sbdIndex != BLOCK_END_OF_CHAIN)
4873 StorageImpl_SetNextBlockInChain(
4874 This->parentStorage,
4875 sbdIndex,
4876 newsbdIndex);
4878 StorageImpl_SetNextBlockInChain(
4879 This->parentStorage,
4880 newsbdIndex,
4881 BLOCK_END_OF_CHAIN);
4884 * Initialize all the small blocks to free
4886 smallBlockDepot =
4887 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4889 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4890 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4892 if (count == 0)
4895 * We have just created the small block depot.
4897 StgProperty rootProp;
4898 ULONG sbStartIndex;
4901 * Save it in the header
4903 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4904 StorageImpl_SaveFileHeader(This->parentStorage);
4907 * And allocate the first big block that will contain small blocks
4909 sbStartIndex =
4910 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4912 StorageImpl_SetNextBlockInChain(
4913 This->parentStorage,
4914 sbStartIndex,
4915 BLOCK_END_OF_CHAIN);
4917 StorageImpl_ReadProperty(
4918 This->parentStorage,
4919 This->parentStorage->rootPropertySetIndex,
4920 &rootProp);
4922 rootProp.startingBlock = sbStartIndex;
4923 rootProp.size.s.HighPart = 0;
4924 rootProp.size.s.LowPart = This->parentStorage->bigBlockSize;
4926 StorageImpl_WriteProperty(
4927 This->parentStorage,
4928 This->parentStorage->rootPropertySetIndex,
4929 &rootProp);
4934 smallBlocksPerBigBlock =
4935 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4938 * Verify if we have to allocate big blocks to contain small blocks
4940 if (blockIndex % smallBlocksPerBigBlock == 0)
4942 StgProperty rootProp;
4943 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4945 StorageImpl_ReadProperty(
4946 This->parentStorage,
4947 This->parentStorage->rootPropertySetIndex,
4948 &rootProp);
4950 if (rootProp.size.s.LowPart <
4951 (blocksRequired * This->parentStorage->bigBlockSize))
4953 rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4955 BlockChainStream_SetSize(
4956 This->parentStorage->smallBlockRootChain,
4957 rootProp.size);
4959 StorageImpl_WriteProperty(
4960 This->parentStorage,
4961 This->parentStorage->rootPropertySetIndex,
4962 &rootProp);
4966 return blockIndex;
4969 /******************************************************************************
4970 * SmallBlockChainStream_ReadAt
4972 * Reads a specified number of bytes from this chain at the specified offset.
4973 * bytesRead may be NULL.
4974 * Failure will be returned if the specified number of bytes has not been read.
4976 BOOL SmallBlockChainStream_ReadAt(
4977 SmallBlockChainStream* This,
4978 ULARGE_INTEGER offset,
4979 ULONG size,
4980 void* buffer,
4981 ULONG* bytesRead)
4983 ULARGE_INTEGER offsetInBigBlockFile;
4984 ULONG blockNoInSequence =
4985 offset.s.LowPart / This->parentStorage->smallBlockSize;
4987 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4988 ULONG bytesToReadInBuffer;
4989 ULONG blockIndex;
4990 ULONG bytesReadFromBigBlockFile;
4991 BYTE* bufferWalker;
4994 * This should never happen on a small block file.
4996 assert(offset.s.HighPart==0);
4999 * Find the first block in the stream that contains part of the buffer.
5001 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5003 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5005 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5007 blockNoInSequence--;
5011 * Start reading the buffer.
5013 *bytesRead = 0;
5014 bufferWalker = buffer;
5016 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5019 * Calculate how many bytes we can copy from this small block.
5021 bytesToReadInBuffer =
5022 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5025 * Calculate the offset of the small block in the small block file.
5027 offsetInBigBlockFile.s.HighPart = 0;
5028 offsetInBigBlockFile.s.LowPart =
5029 blockIndex * This->parentStorage->smallBlockSize;
5031 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5034 * Read those bytes in the buffer from the small block file.
5036 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5037 offsetInBigBlockFile,
5038 bytesToReadInBuffer,
5039 bufferWalker,
5040 &bytesReadFromBigBlockFile);
5042 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5045 * Step to the next big block.
5047 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5048 bufferWalker += bytesToReadInBuffer;
5049 size -= bytesToReadInBuffer;
5050 *bytesRead += bytesToReadInBuffer;
5051 offsetInBlock = 0; /* There is no offset on the next block */
5054 return (size == 0);
5057 /******************************************************************************
5058 * SmallBlockChainStream_WriteAt
5060 * Writes the specified number of bytes to this chain at the specified offset.
5061 * bytesWritten may be NULL.
5062 * Will fail if not all specified number of bytes have been written.
5064 BOOL SmallBlockChainStream_WriteAt(
5065 SmallBlockChainStream* This,
5066 ULARGE_INTEGER offset,
5067 ULONG size,
5068 const void* buffer,
5069 ULONG* bytesWritten)
5071 ULARGE_INTEGER offsetInBigBlockFile;
5072 ULONG blockNoInSequence =
5073 offset.s.LowPart / This->parentStorage->smallBlockSize;
5075 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
5076 ULONG bytesToWriteInBuffer;
5077 ULONG blockIndex;
5078 ULONG bytesWrittenFromBigBlockFile;
5079 BYTE* bufferWalker;
5082 * This should never happen on a small block file.
5084 assert(offset.s.HighPart==0);
5087 * Find the first block in the stream that contains part of the buffer.
5089 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5091 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5093 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5095 blockNoInSequence--;
5099 * Start writing the buffer.
5101 * Here, I'm casting away the constness on the buffer variable
5102 * This is OK since we don't intend to modify that buffer.
5104 *bytesWritten = 0;
5105 bufferWalker = (BYTE*)buffer;
5106 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5109 * Calculate how many bytes we can copy to this small block.
5111 bytesToWriteInBuffer =
5112 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5115 * Calculate the offset of the small block in the small block file.
5117 offsetInBigBlockFile.s.HighPart = 0;
5118 offsetInBigBlockFile.s.LowPart =
5119 blockIndex * This->parentStorage->smallBlockSize;
5121 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5124 * Write those bytes in the buffer to the small block file.
5126 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5127 offsetInBigBlockFile,
5128 bytesToWriteInBuffer,
5129 bufferWalker,
5130 &bytesWrittenFromBigBlockFile);
5132 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5135 * Step to the next big block.
5137 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5138 bufferWalker += bytesToWriteInBuffer;
5139 size -= bytesToWriteInBuffer;
5140 *bytesWritten += bytesToWriteInBuffer;
5141 offsetInBlock = 0; /* There is no offset on the next block */
5144 return (size == 0);
5147 /******************************************************************************
5148 * SmallBlockChainStream_Shrink
5150 * Shrinks this chain in the small block depot.
5152 BOOL SmallBlockChainStream_Shrink(
5153 SmallBlockChainStream* This,
5154 ULARGE_INTEGER newSize)
5156 ULONG blockIndex, extraBlock;
5157 ULONG numBlocks;
5158 ULONG count = 0;
5160 numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5162 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5163 numBlocks++;
5165 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5168 * Go to the new end of chain
5170 while (count < numBlocks)
5172 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5173 count++;
5177 * If the count is 0, we have a special case, the head of the chain was
5178 * just freed.
5180 if (count == 0)
5182 StgProperty chainProp;
5184 StorageImpl_ReadProperty(This->parentStorage,
5185 This->ownerPropertyIndex,
5186 &chainProp);
5188 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5190 StorageImpl_WriteProperty(This->parentStorage,
5191 This->ownerPropertyIndex,
5192 &chainProp);
5195 * We start freeing the chain at the head block.
5197 extraBlock = blockIndex;
5199 else
5201 /* Get the next block before marking the new end */
5202 extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5204 /* Mark the new end of chain */
5205 SmallBlockChainStream_SetNextBlockInChain(
5206 This,
5207 blockIndex,
5208 BLOCK_END_OF_CHAIN);
5212 * Mark the extra blocks as free
5214 while (extraBlock != BLOCK_END_OF_CHAIN)
5216 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
5217 SmallBlockChainStream_FreeBlock(This, extraBlock);
5218 extraBlock = blockIndex;
5221 return TRUE;
5224 /******************************************************************************
5225 * SmallBlockChainStream_Enlarge
5227 * Grows this chain in the small block depot.
5229 BOOL SmallBlockChainStream_Enlarge(
5230 SmallBlockChainStream* This,
5231 ULARGE_INTEGER newSize)
5233 ULONG blockIndex, currentBlock;
5234 ULONG newNumBlocks;
5235 ULONG oldNumBlocks = 0;
5237 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5240 * Empty chain
5242 if (blockIndex == BLOCK_END_OF_CHAIN)
5245 StgProperty chainProp;
5247 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5248 &chainProp);
5250 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5252 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5253 &chainProp);
5255 blockIndex = chainProp.startingBlock;
5256 SmallBlockChainStream_SetNextBlockInChain(
5257 This,
5258 blockIndex,
5259 BLOCK_END_OF_CHAIN);
5262 currentBlock = blockIndex;
5265 * Figure out how many blocks are needed to contain this stream
5267 newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5269 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5270 newNumBlocks++;
5273 * Go to the current end of chain
5275 while (blockIndex != BLOCK_END_OF_CHAIN)
5277 oldNumBlocks++;
5278 currentBlock = blockIndex;
5279 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5283 * Add new blocks to the chain
5285 while (oldNumBlocks < newNumBlocks)
5287 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5288 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5290 SmallBlockChainStream_SetNextBlockInChain(
5291 This,
5292 blockIndex,
5293 BLOCK_END_OF_CHAIN);
5295 currentBlock = blockIndex;
5296 oldNumBlocks++;
5299 return TRUE;
5302 /******************************************************************************
5303 * SmallBlockChainStream_GetCount
5305 * Returns the number of blocks that comprises this chain.
5306 * This is not the size of this chain as the last block may not be full!
5308 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5310 ULONG blockIndex;
5311 ULONG count = 0;
5313 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5315 while (blockIndex != BLOCK_END_OF_CHAIN)
5317 count++;
5319 blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5322 return count;
5325 /******************************************************************************
5326 * SmallBlockChainStream_SetSize
5328 * Sets the size of this stream.
5329 * The file will grow if we grow the chain.
5331 * TODO: Free the actual blocks in the file when we shrink the chain.
5332 * Currently, the blocks are still in the file. So the file size
5333 * doesn't shrink even if we shrink streams.
5335 BOOL SmallBlockChainStream_SetSize(
5336 SmallBlockChainStream* This,
5337 ULARGE_INTEGER newSize)
5339 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5341 if (newSize.s.LowPart == size.s.LowPart)
5342 return TRUE;
5344 if (newSize.s.LowPart < size.s.LowPart)
5346 SmallBlockChainStream_Shrink(This, newSize);
5348 else
5350 SmallBlockChainStream_Enlarge(This, newSize);
5353 return TRUE;
5356 /******************************************************************************
5357 * SmallBlockChainStream_GetSize
5359 * Returns the size of this chain.
5361 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5363 StgProperty chainProperty;
5365 StorageImpl_ReadProperty(
5366 This->parentStorage,
5367 This->ownerPropertyIndex,
5368 &chainProperty);
5370 return chainProperty.size;
5373 /******************************************************************************
5374 * StgCreateDocfile [OLE32.144]
5376 HRESULT WINAPI StgCreateDocfile(
5377 LPCOLESTR pwcsName,
5378 DWORD grfMode,
5379 DWORD reserved,
5380 IStorage **ppstgOpen)
5382 StorageImpl* newStorage = 0;
5383 HANDLE hFile = INVALID_HANDLE_VALUE;
5384 HRESULT hr = S_OK;
5385 DWORD shareMode;
5386 DWORD accessMode;
5387 DWORD creationMode;
5388 DWORD fileAttributes;
5389 WCHAR tempFileName[MAX_PATH];
5391 TRACE("(%s, %lx, %ld, %p)\n",
5392 debugstr_w(pwcsName), grfMode,
5393 reserved, ppstgOpen);
5396 * Validate the parameters
5398 if (ppstgOpen == 0)
5399 return STG_E_INVALIDPOINTER;
5402 * Validate the STGM flags
5404 if ( FAILED( validateSTGM(grfMode) ))
5405 return STG_E_INVALIDFLAG;
5408 * Generate a unique name.
5410 if (pwcsName == 0)
5412 WCHAR tempPath[MAX_PATH];
5413 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5415 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5416 return STG_E_INVALIDFLAG;
5417 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5418 return STG_E_INVALIDFLAG;
5420 memset(tempPath, 0, sizeof(tempPath));
5421 memset(tempFileName, 0, sizeof(tempFileName));
5423 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5424 tempPath[0] = '.';
5426 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5427 pwcsName = tempFileName;
5428 else
5429 return STG_E_INSUFFICIENTMEMORY;
5431 creationMode = TRUNCATE_EXISTING;
5433 else
5435 creationMode = GetCreationModeFromSTGM(grfMode);
5439 * Interpret the STGM value grfMode
5441 shareMode = GetShareModeFromSTGM(grfMode);
5442 accessMode = GetAccessModeFromSTGM(grfMode);
5444 if (grfMode & STGM_DELETEONRELEASE)
5445 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5446 else
5447 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5449 if (grfMode & STGM_TRANSACTED)
5450 FIXME("Transacted mode not implemented.\n");
5453 * Initialize the "out" parameter.
5455 *ppstgOpen = 0;
5457 hFile = CreateFileW(pwcsName,
5458 accessMode,
5459 shareMode,
5460 NULL,
5461 creationMode,
5462 fileAttributes,
5465 if (hFile == INVALID_HANDLE_VALUE)
5467 return E_FAIL;
5471 * Allocate and initialize the new IStorage32object.
5473 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5475 if (newStorage == 0)
5476 return STG_E_INSUFFICIENTMEMORY;
5478 hr = StorageImpl_Construct(
5479 newStorage,
5480 hFile,
5481 pwcsName,
5482 NULL,
5483 grfMode,
5484 TRUE,
5485 TRUE);
5487 if (FAILED(hr))
5489 HeapFree(GetProcessHeap(), 0, newStorage);
5490 return hr;
5494 * Get an "out" pointer for the caller.
5496 hr = StorageBaseImpl_QueryInterface(
5497 (IStorage*)newStorage,
5498 (REFIID)&IID_IStorage,
5499 (void**)ppstgOpen);
5501 return hr;
5504 /******************************************************************************
5505 * StgOpenStorage [OLE32.148]
5507 HRESULT WINAPI StgOpenStorage(
5508 const OLECHAR *pwcsName,
5509 IStorage *pstgPriority,
5510 DWORD grfMode,
5511 SNB snbExclude,
5512 DWORD reserved,
5513 IStorage **ppstgOpen)
5515 StorageImpl* newStorage = 0;
5516 HRESULT hr = S_OK;
5517 HANDLE hFile = 0;
5518 DWORD shareMode;
5519 DWORD accessMode;
5520 WCHAR fullname[MAX_PATH];
5521 DWORD length;
5523 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5524 debugstr_w(pwcsName), pstgPriority, grfMode,
5525 snbExclude, reserved, ppstgOpen);
5528 * Perform a sanity check
5530 if (( pwcsName == 0) || (ppstgOpen == 0) )
5532 hr = STG_E_INVALIDPOINTER;
5533 goto end;
5537 * Validate the STGM flags
5539 if ( FAILED( validateSTGM(grfMode) ))
5541 hr = STG_E_INVALIDFLAG;
5542 goto end;
5546 * Interpret the STGM value grfMode
5548 shareMode = GetShareModeFromSTGM(grfMode);
5549 accessMode = GetAccessModeFromSTGM(grfMode);
5552 * Initialize the "out" parameter.
5554 *ppstgOpen = 0;
5556 hFile = CreateFileW( pwcsName,
5557 accessMode,
5558 shareMode,
5559 NULL,
5560 OPEN_EXISTING,
5561 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5564 length = GetFileSize(hFile, NULL);
5566 if (hFile==INVALID_HANDLE_VALUE)
5568 DWORD last_error = GetLastError();
5570 hr = E_FAIL;
5572 switch (last_error)
5574 case ERROR_FILE_NOT_FOUND:
5575 hr = STG_E_FILENOTFOUND;
5576 break;
5578 case ERROR_PATH_NOT_FOUND:
5579 hr = STG_E_PATHNOTFOUND;
5580 break;
5582 case ERROR_ACCESS_DENIED:
5583 case ERROR_WRITE_PROTECT:
5584 hr = STG_E_ACCESSDENIED;
5585 break;
5587 case ERROR_SHARING_VIOLATION:
5588 hr = STG_E_SHAREVIOLATION;
5589 break;
5591 default:
5592 hr = E_FAIL;
5595 goto end;
5599 * Allocate and initialize the new IStorage32object.
5601 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5603 if (newStorage == 0)
5605 hr = STG_E_INSUFFICIENTMEMORY;
5606 goto end;
5609 /* if the file's length was zero, initialize the storage */
5610 hr = StorageImpl_Construct(
5611 newStorage,
5612 hFile,
5613 pwcsName,
5614 NULL,
5615 grfMode,
5616 TRUE,
5617 !length );
5619 if (FAILED(hr))
5621 HeapFree(GetProcessHeap(), 0, newStorage);
5623 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5625 if(hr == STG_E_INVALIDHEADER)
5626 hr = STG_E_FILEALREADYEXISTS;
5627 goto end;
5630 /* prepare the file name string given in lieu of the root property name */
5631 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5632 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5633 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5636 * Get an "out" pointer for the caller.
5638 hr = StorageBaseImpl_QueryInterface(
5639 (IStorage*)newStorage,
5640 (REFIID)&IID_IStorage,
5641 (void**)ppstgOpen);
5643 end:
5644 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5645 return hr;
5648 /******************************************************************************
5649 * StgCreateDocfileOnILockBytes [OLE32.145]
5651 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5652 ILockBytes *plkbyt,
5653 DWORD grfMode,
5654 DWORD reserved,
5655 IStorage** ppstgOpen)
5657 StorageImpl* newStorage = 0;
5658 HRESULT hr = S_OK;
5661 * Validate the parameters
5663 if ((ppstgOpen == 0) || (plkbyt == 0))
5664 return STG_E_INVALIDPOINTER;
5667 * Allocate and initialize the new IStorage object.
5669 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5671 if (newStorage == 0)
5672 return STG_E_INSUFFICIENTMEMORY;
5674 hr = StorageImpl_Construct(
5675 newStorage,
5678 plkbyt,
5679 grfMode,
5680 FALSE,
5681 TRUE);
5683 if (FAILED(hr))
5685 HeapFree(GetProcessHeap(), 0, newStorage);
5686 return hr;
5690 * Get an "out" pointer for the caller.
5692 hr = StorageBaseImpl_QueryInterface(
5693 (IStorage*)newStorage,
5694 (REFIID)&IID_IStorage,
5695 (void**)ppstgOpen);
5697 return hr;
5700 /******************************************************************************
5701 * StgOpenStorageOnILockBytes [OLE32.149]
5703 HRESULT WINAPI StgOpenStorageOnILockBytes(
5704 ILockBytes *plkbyt,
5705 IStorage *pstgPriority,
5706 DWORD grfMode,
5707 SNB snbExclude,
5708 DWORD reserved,
5709 IStorage **ppstgOpen)
5711 StorageImpl* newStorage = 0;
5712 HRESULT hr = S_OK;
5715 * Perform a sanity check
5717 if ((plkbyt == 0) || (ppstgOpen == 0))
5718 return STG_E_INVALIDPOINTER;
5721 * Validate the STGM flags
5723 if ( FAILED( validateSTGM(grfMode) ))
5724 return STG_E_INVALIDFLAG;
5727 * Initialize the "out" parameter.
5729 *ppstgOpen = 0;
5732 * Allocate and initialize the new IStorage object.
5734 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5736 if (newStorage == 0)
5737 return STG_E_INSUFFICIENTMEMORY;
5739 hr = StorageImpl_Construct(
5740 newStorage,
5743 plkbyt,
5744 grfMode,
5745 FALSE,
5746 FALSE);
5748 if (FAILED(hr))
5750 HeapFree(GetProcessHeap(), 0, newStorage);
5751 return hr;
5755 * Get an "out" pointer for the caller.
5757 hr = StorageBaseImpl_QueryInterface(
5758 (IStorage*)newStorage,
5759 (REFIID)&IID_IStorage,
5760 (void**)ppstgOpen);
5762 return hr;
5765 /******************************************************************************
5766 * StgSetTimes [ole32.150]
5767 * StgSetTimes [OLE32.150]
5771 HRESULT WINAPI StgSetTimes(OLECHAR *str, FILETIME *a, FILETIME *b, FILETIME *c )
5773 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5774 return S_OK;
5777 /******************************************************************************
5778 * StgIsStorageILockBytes [OLE32.147]
5780 * Determines if the ILockBytes contains a storage object.
5782 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5784 BYTE sig[8];
5785 ULARGE_INTEGER offset;
5787 offset.s.HighPart = 0;
5788 offset.s.LowPart = 0;
5790 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5792 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5793 return S_OK;
5795 return S_FALSE;
5798 /******************************************************************************
5799 * WriteClassStg [OLE32.158]
5801 * This method will store the specified CLSID in the specified storage object
5803 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5805 HRESULT hRes;
5807 assert(pStg != 0);
5809 hRes = IStorage_SetClass(pStg, rclsid);
5811 return hRes;
5814 /***********************************************************************
5815 * ReadClassStg (OLE32.134)
5817 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5819 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5821 STATSTG pstatstg;
5822 HRESULT hRes;
5824 TRACE("()\n");
5826 if(pclsid==NULL)
5827 return E_POINTER;
5829 * read a STATSTG structure (contains the clsid) from the storage
5831 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5833 if(SUCCEEDED(hRes))
5834 *pclsid=pstatstg.clsid;
5836 return hRes;
5839 /***********************************************************************
5840 * OleLoadFromStream (OLE32.113)
5842 * This function loads an object from stream
5844 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5846 CLSID clsid;
5847 HRESULT res;
5848 LPPERSISTSTREAM xstm;
5850 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5852 res=ReadClassStm(pStm,&clsid);
5853 if (!SUCCEEDED(res))
5854 return res;
5855 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5856 if (!SUCCEEDED(res))
5857 return res;
5858 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5859 if (!SUCCEEDED(res)) {
5860 IUnknown_Release((IUnknown*)*ppvObj);
5861 return res;
5863 res=IPersistStream_Load(xstm,pStm);
5864 IPersistStream_Release(xstm);
5865 /* FIXME: all refcounts ok at this point? I think they should be:
5866 * pStm : unchanged
5867 * ppvObj : 1
5868 * xstm : 0 (released)
5870 return res;
5873 /***********************************************************************
5874 * OleSaveToStream (OLE32.125)
5876 * This function saves an object with the IPersistStream interface on it
5877 * to the specified stream.
5879 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5882 CLSID clsid;
5883 HRESULT res;
5885 TRACE("(%p,%p)\n",pPStm,pStm);
5887 res=IPersistStream_GetClassID(pPStm,&clsid);
5889 if (SUCCEEDED(res)){
5891 res=WriteClassStm(pStm,&clsid);
5893 if (SUCCEEDED(res))
5895 res=IPersistStream_Save(pPStm,pStm,TRUE);
5898 TRACE("Finished Save\n");
5899 return res;
5902 /****************************************************************************
5903 * This method validate a STGM parameter that can contain the values below
5905 * STGM_DIRECT 0x00000000
5906 * STGM_TRANSACTED 0x00010000
5907 * STGM_SIMPLE 0x08000000
5909 * STGM_READ 0x00000000
5910 * STGM_WRITE 0x00000001
5911 * STGM_READWRITE 0x00000002
5913 * STGM_SHARE_DENY_NONE 0x00000040
5914 * STGM_SHARE_DENY_READ 0x00000030
5915 * STGM_SHARE_DENY_WRITE 0x00000020
5916 * STGM_SHARE_EXCLUSIVE 0x00000010
5918 * STGM_PRIORITY 0x00040000
5919 * STGM_DELETEONRELEASE 0x04000000
5921 * STGM_CREATE 0x00001000
5922 * STGM_CONVERT 0x00020000
5923 * STGM_FAILIFTHERE 0x00000000
5925 * STGM_NOSCRATCH 0x00100000
5926 * STGM_NOSNAPSHOT 0x00200000
5928 static HRESULT validateSTGM(DWORD stgm)
5930 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5931 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5932 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5934 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5935 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5936 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5938 BOOL bSTGM_SHARE_DENY_NONE =
5939 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5941 BOOL bSTGM_SHARE_DENY_READ =
5942 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5944 BOOL bSTGM_SHARE_DENY_WRITE =
5945 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5947 BOOL bSTGM_SHARE_EXCLUSIVE =
5948 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5950 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5951 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5953 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5954 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5957 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5959 if ( ! bSTGM_DIRECT )
5960 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5961 return E_FAIL;
5964 * STGM_WRITE | STGM_READWRITE | STGM_READ
5966 if ( ! bSTGM_READ )
5967 if( bSTGM_WRITE && bSTGM_READWRITE )
5968 return E_FAIL;
5971 * STGM_SHARE_DENY_NONE | others
5972 * (I assume here that DENY_READ implies DENY_WRITE)
5974 if ( bSTGM_SHARE_DENY_NONE )
5975 if ( bSTGM_SHARE_DENY_READ ||
5976 bSTGM_SHARE_DENY_WRITE ||
5977 bSTGM_SHARE_EXCLUSIVE)
5978 return E_FAIL;
5981 * STGM_CREATE | STGM_CONVERT
5982 * if both are false, STGM_FAILIFTHERE is set to TRUE
5984 if ( bSTGM_CREATE && bSTGM_CONVERT )
5985 return E_FAIL;
5988 * STGM_NOSCRATCH requires STGM_TRANSACTED
5990 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5991 return E_FAIL;
5994 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
5995 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5997 if (bSTGM_NOSNAPSHOT)
5999 if ( ! ( bSTGM_TRANSACTED &&
6000 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6001 return E_FAIL;
6004 return S_OK;
6007 /****************************************************************************
6008 * GetShareModeFromSTGM
6010 * This method will return a share mode flag from a STGM value.
6011 * The STGM value is assumed valid.
6013 static DWORD GetShareModeFromSTGM(DWORD stgm)
6015 DWORD dwShareMode = 0;
6016 BOOL bSTGM_SHARE_DENY_NONE =
6017 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6019 BOOL bSTGM_SHARE_DENY_READ =
6020 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6022 BOOL bSTGM_SHARE_DENY_WRITE =
6023 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6025 BOOL bSTGM_SHARE_EXCLUSIVE =
6026 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6028 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6029 dwShareMode = 0;
6031 if (bSTGM_SHARE_DENY_NONE)
6032 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6034 if (bSTGM_SHARE_DENY_WRITE)
6035 dwShareMode = FILE_SHARE_READ;
6037 return dwShareMode;
6040 /****************************************************************************
6041 * GetAccessModeFromSTGM
6043 * This method will return an access mode flag from a STGM value.
6044 * The STGM value is assumed valid.
6046 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6048 DWORD dwDesiredAccess = GENERIC_READ;
6049 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6050 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6051 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6053 if (bSTGM_READ)
6054 dwDesiredAccess = GENERIC_READ;
6056 if (bSTGM_WRITE)
6057 dwDesiredAccess |= GENERIC_WRITE;
6059 if (bSTGM_READWRITE)
6060 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6062 return dwDesiredAccess;
6065 /****************************************************************************
6066 * GetCreationModeFromSTGM
6068 * This method will return a creation mode flag from a STGM value.
6069 * The STGM value is assumed valid.
6071 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6073 if ( stgm & STGM_CREATE)
6074 return CREATE_ALWAYS;
6075 if (stgm & STGM_CONVERT) {
6076 FIXME("STGM_CONVERT not implemented!\n");
6077 return CREATE_NEW;
6079 /* All other cases */
6080 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6081 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6082 return CREATE_NEW;
6086 /*************************************************************************
6087 * OLECONVERT_LoadOLE10 [Internal]
6089 * Loads the OLE10 STREAM to memory
6091 * PARAMS
6092 * pOleStream [I] The OLESTREAM
6093 * pData [I] Data Structure for the OLESTREAM Data
6095 * RETURNS
6096 * Success: S_OK
6097 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6098 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6100 * NOTES
6101 * This function is used by OleConvertOLESTREAMToIStorage only.
6103 * Memory allocated for pData must be freed by the caller
6105 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6107 DWORD dwSize;
6108 HRESULT hRes = S_OK;
6109 int nTryCnt=0;
6110 int max_try = 6;
6112 pData->pData = NULL;
6113 pData->pstrOleObjFileName = (CHAR *) NULL;
6115 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6117 /* Get the OleID */
6118 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6119 if(dwSize != sizeof(pData->dwOleID))
6121 hRes = CONVERT10_E_OLESTREAM_GET;
6123 else if(pData->dwOleID != OLESTREAM_ID)
6125 hRes = CONVERT10_E_OLESTREAM_FMT;
6127 else
6129 hRes = S_OK;
6130 break;
6134 if(hRes == S_OK)
6136 /* Get the TypeID...more info needed for this field */
6137 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6138 if(dwSize != sizeof(pData->dwTypeID))
6140 hRes = CONVERT10_E_OLESTREAM_GET;
6143 if(hRes == S_OK)
6145 if(pData->dwTypeID != 0)
6147 /* Get the lenght of the OleTypeName */
6148 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6149 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6151 hRes = CONVERT10_E_OLESTREAM_GET;
6154 if(hRes == S_OK)
6156 if(pData->dwOleTypeNameLength > 0)
6158 /* Get the OleTypeName */
6159 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6160 if(dwSize != pData->dwOleTypeNameLength)
6162 hRes = CONVERT10_E_OLESTREAM_GET;
6166 if(bStrem1)
6168 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6169 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6171 hRes = CONVERT10_E_OLESTREAM_GET;
6173 if(hRes == S_OK)
6175 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6176 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6177 pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6178 if(pData->pstrOleObjFileName)
6180 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6181 if(dwSize != pData->dwOleObjFileNameLength)
6183 hRes = CONVERT10_E_OLESTREAM_GET;
6186 else
6187 hRes = CONVERT10_E_OLESTREAM_GET;
6190 else
6192 /* Get the Width of the Metafile */
6193 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6194 if(dwSize != sizeof(pData->dwMetaFileWidth))
6196 hRes = CONVERT10_E_OLESTREAM_GET;
6198 if(hRes == S_OK)
6200 /* Get the Height of the Metafile */
6201 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6202 if(dwSize != sizeof(pData->dwMetaFileHeight))
6204 hRes = CONVERT10_E_OLESTREAM_GET;
6208 if(hRes == S_OK)
6210 /* Get the Lenght of the Data */
6211 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6212 if(dwSize != sizeof(pData->dwDataLength))
6214 hRes = CONVERT10_E_OLESTREAM_GET;
6218 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6220 if(!bStrem1) /* if it is a second OLE stream data */
6222 pData->dwDataLength -= 8;
6223 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6224 if(dwSize != sizeof(pData->strUnknown))
6226 hRes = CONVERT10_E_OLESTREAM_GET;
6230 if(hRes == S_OK)
6232 if(pData->dwDataLength > 0)
6234 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6236 /* Get Data (ex. IStorage, Metafile, or BMP) */
6237 if(pData->pData)
6239 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6240 if(dwSize != pData->dwDataLength)
6242 hRes = CONVERT10_E_OLESTREAM_GET;
6245 else
6247 hRes = CONVERT10_E_OLESTREAM_GET;
6253 return hRes;
6256 /*************************************************************************
6257 * OLECONVERT_SaveOLE10 [Internal]
6259 * Saves the OLE10 STREAM From memory
6261 * PARAMS
6262 * pData [I] Data Structure for the OLESTREAM Data
6263 * pOleStream [I] The OLESTREAM to save
6265 * RETURNS
6266 * Success: S_OK
6267 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6269 * NOTES
6270 * This function is used by OleConvertIStorageToOLESTREAM only.
6273 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6275 DWORD dwSize;
6276 HRESULT hRes = S_OK;
6279 /* Set the OleID */
6280 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6281 if(dwSize != sizeof(pData->dwOleID))
6283 hRes = CONVERT10_E_OLESTREAM_PUT;
6286 if(hRes == S_OK)
6288 /* Set the TypeID */
6289 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6290 if(dwSize != sizeof(pData->dwTypeID))
6292 hRes = CONVERT10_E_OLESTREAM_PUT;
6296 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6298 /* Set the Lenght of the OleTypeName */
6299 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6300 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6302 hRes = CONVERT10_E_OLESTREAM_PUT;
6305 if(hRes == S_OK)
6307 if(pData->dwOleTypeNameLength > 0)
6309 /* Set the OleTypeName */
6310 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6311 if(dwSize != pData->dwOleTypeNameLength)
6313 hRes = CONVERT10_E_OLESTREAM_PUT;
6318 if(hRes == S_OK)
6320 /* Set the width of the Metafile */
6321 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6322 if(dwSize != sizeof(pData->dwMetaFileWidth))
6324 hRes = CONVERT10_E_OLESTREAM_PUT;
6328 if(hRes == S_OK)
6330 /* Set the height of the Metafile */
6331 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6332 if(dwSize != sizeof(pData->dwMetaFileHeight))
6334 hRes = CONVERT10_E_OLESTREAM_PUT;
6338 if(hRes == S_OK)
6340 /* Set the lenght of the Data */
6341 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6342 if(dwSize != sizeof(pData->dwDataLength))
6344 hRes = CONVERT10_E_OLESTREAM_PUT;
6348 if(hRes == S_OK)
6350 if(pData->dwDataLength > 0)
6352 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6353 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6354 if(dwSize != pData->dwDataLength)
6356 hRes = CONVERT10_E_OLESTREAM_PUT;
6361 return hRes;
6364 /*************************************************************************
6365 * OLECONVERT_GetOLE20FromOLE10[Internal]
6367 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6368 * opens it, and copies the content to the dest IStorage for
6369 * OleConvertOLESTREAMToIStorage
6372 * PARAMS
6373 * pDestStorage [I] The IStorage to copy the data to
6374 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6375 * nBufferLength [I] The size of the buffer
6377 * RETURNS
6378 * Nothing
6380 * NOTES
6384 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6386 HRESULT hRes;
6387 HANDLE hFile;
6388 IStorage *pTempStorage;
6389 DWORD dwNumOfBytesWritten;
6390 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6391 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6393 /* Create a temp File */
6394 GetTempPathW(MAX_PATH, wstrTempDir);
6395 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6396 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6398 if(hFile != INVALID_HANDLE_VALUE)
6400 /* Write IStorage Data to File */
6401 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6402 CloseHandle(hFile);
6404 /* Open and copy temp storage to the Dest Storage */
6405 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6406 if(hRes == S_OK)
6408 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6409 StorageBaseImpl_Release(pTempStorage);
6411 DeleteFileW(wstrTempFile);
6416 /*************************************************************************
6417 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6419 * Saves the OLE10 STREAM From memory
6421 * PARAMS
6422 * pStorage [I] The Src IStorage to copy
6423 * pData [I] The Dest Memory to write to.
6425 * RETURNS
6426 * The size in bytes allocated for pData
6428 * NOTES
6429 * Memory allocated for pData must be freed by the caller
6431 * Used by OleConvertIStorageToOLESTREAM only.
6434 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6436 HANDLE hFile;
6437 HRESULT hRes;
6438 DWORD nDataLength = 0;
6439 IStorage *pTempStorage;
6440 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6441 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6443 *pData = NULL;
6445 /* Create temp Storage */
6446 GetTempPathW(MAX_PATH, wstrTempDir);
6447 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6448 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6450 if(hRes == S_OK)
6452 /* Copy Src Storage to the Temp Storage */
6453 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6454 StorageBaseImpl_Release(pTempStorage);
6456 /* Open Temp Storage as a file and copy to memory */
6457 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6458 if(hFile != INVALID_HANDLE_VALUE)
6460 nDataLength = GetFileSize(hFile, NULL);
6461 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6462 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6463 CloseHandle(hFile);
6465 DeleteFileW(wstrTempFile);
6467 return nDataLength;
6470 /*************************************************************************
6471 * OLECONVERT_CreateOleStream [Internal]
6473 * Creates the "\001OLE" stream in the IStorage if neccessary.
6475 * PARAMS
6476 * pStorage [I] Dest storage to create the stream in
6478 * RETURNS
6479 * Nothing
6481 * NOTES
6482 * This function is used by OleConvertOLESTREAMToIStorage only.
6484 * This stream is still unknown, MS Word seems to have extra data
6485 * but since the data is stored in the OLESTREAM there should be
6486 * no need to recreate the stream. If the stream is manually
6487 * deleted it will create it with this default data.
6490 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6492 HRESULT hRes;
6493 IStream *pStream;
6494 WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6495 BYTE pOleStreamHeader [] =
6497 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6498 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6499 0x00, 0x00, 0x00, 0x00
6502 /* Create stream if not present */
6503 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6504 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6506 if(hRes == S_OK)
6508 /* Write default Data */
6509 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6510 IStream_Release(pStream);
6515 /*************************************************************************
6516 * OLECONVERT_CreateCompObjStream [Internal]
6518 * Creates a "\001CompObj" is the destination IStorage if necessary.
6520 * PARAMS
6521 * pStorage [I] The dest IStorage to create the CompObj Stream
6522 * if necessary.
6523 * strOleTypeName [I] The ProgID
6525 * RETURNS
6526 * Success: S_OK
6527 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6529 * NOTES
6530 * This function is used by OleConvertOLESTREAMToIStorage only.
6532 * The stream data is stored in the OLESTREAM and there should be
6533 * no need to recreate the stream. If the stream is manually
6534 * deleted it will attempt to create it by querying the registry.
6538 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6540 IStream *pStream;
6541 HRESULT hStorageRes, hRes = S_OK;
6542 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6543 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6545 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6546 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6548 /* Initialize the CompObj structure */
6549 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6550 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6551 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6554 /* Create a CompObj stream if it doesn't exist */
6555 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6556 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6557 if(hStorageRes == S_OK)
6559 /* copy the OleTypeName to the compobj struct */
6560 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6561 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6563 /* copy the OleTypeName to the compobj struct */
6564 /* Note: in the test made, these were Identical */
6565 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6566 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6568 /* Get the CLSID */
6569 hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6571 if(hRes == S_OK)
6573 HKEY hKey;
6574 LONG hErr;
6575 /* Get the CLSID Default Name from the Registry */
6576 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6577 if(hErr == ERROR_SUCCESS)
6579 char strTemp[OLESTREAM_MAX_STR_LEN];
6580 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6581 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6582 if(hErr == ERROR_SUCCESS)
6584 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6586 RegCloseKey(hKey);
6590 /* Write CompObj Structure to stream */
6591 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6593 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6595 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6596 if(IStorageCompObj.dwCLSIDNameLength > 0)
6598 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6600 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6601 if(IStorageCompObj.dwOleTypeNameLength > 0)
6603 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6605 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6606 if(IStorageCompObj.dwProgIDNameLength > 0)
6608 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6610 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6611 IStream_Release(pStream);
6613 return hRes;
6617 /*************************************************************************
6618 * OLECONVERT_CreateOlePresStream[Internal]
6620 * Creates the "\002OlePres000" Stream with the Metafile data
6622 * PARAMS
6623 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6624 * dwExtentX [I] Width of the Metafile
6625 * dwExtentY [I] Height of the Metafile
6626 * pData [I] Metafile data
6627 * dwDataLength [I] Size of the Metafile data
6629 * RETURNS
6630 * Success: S_OK
6631 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6633 * NOTES
6634 * This function is used by OleConvertOLESTREAMToIStorage only.
6637 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6639 HRESULT hRes;
6640 IStream *pStream;
6641 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6642 BYTE pOlePresStreamHeader [] =
6644 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6645 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6646 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6647 0x00, 0x00, 0x00, 0x00
6650 BYTE pOlePresStreamHeaderEmpty [] =
6652 0x00, 0x00, 0x00, 0x00,
6653 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6654 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6655 0x00, 0x00, 0x00, 0x00
6658 /* Create the OlePres000 Stream */
6659 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6660 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6662 if(hRes == S_OK)
6664 DWORD nHeaderSize;
6665 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6667 memset(&OlePres, 0, sizeof(OlePres));
6668 /* Do we have any metafile data to save */
6669 if(dwDataLength > 0)
6671 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6672 nHeaderSize = sizeof(pOlePresStreamHeader);
6674 else
6676 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6677 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6679 /* Set width and height of the metafile */
6680 OlePres.dwExtentX = dwExtentX;
6681 OlePres.dwExtentY = -dwExtentY;
6683 /* Set Data and Lenght */
6684 if(dwDataLength > sizeof(METAFILEPICT16))
6686 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6687 OlePres.pData = &(pData[8]);
6689 /* Save OlePres000 Data to Stream */
6690 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
6691 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
6692 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
6693 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
6694 if(OlePres.dwSize > 0)
6696 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
6698 IStream_Release(pStream);
6702 /*************************************************************************
6703 * OLECONVERT_CreateOle10NativeStream [Internal]
6705 * Creates the "\001Ole10Native" Stream (should contain a BMP)
6707 * PARAMS
6708 * pStorage [I] Dest storage to create the stream in
6709 * pData [I] Ole10 Native Data (ex. bmp)
6710 * dwDataLength [I] Size of the Ole10 Native Data
6712 * RETURNS
6713 * Nothing
6715 * NOTES
6716 * This function is used by OleConvertOLESTREAMToIStorage only.
6718 * Might need to verify the data and return appropriate error message
6721 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
6723 HRESULT hRes;
6724 IStream *pStream;
6725 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6727 /* Create the Ole10Native Stream */
6728 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6729 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6731 if(hRes == S_OK)
6733 /* Write info to stream */
6734 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
6735 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
6736 IStream_Release(pStream);
6741 /*************************************************************************
6742 * OLECONVERT_GetOLE10ProgID [Internal]
6744 * Finds the ProgID (or OleTypeID) from the IStorage
6746 * PARAMS
6747 * pStorage [I] The Src IStorage to get the ProgID
6748 * strProgID [I] the ProgID string to get
6749 * dwSize [I] the size of the string
6751 * RETURNS
6752 * Success: S_OK
6753 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6755 * NOTES
6756 * This function is used by OleConvertIStorageToOLESTREAM only.
6760 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
6762 HRESULT hRes;
6763 IStream *pStream;
6764 LARGE_INTEGER iSeekPos;
6765 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
6766 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6768 /* Open the CompObj Stream */
6769 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6770 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6771 if(hRes == S_OK)
6774 /*Get the OleType from the CompObj Stream */
6775 iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
6776 iSeekPos.s.HighPart = 0;
6778 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6779 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
6780 iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
6781 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6782 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
6783 iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
6784 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6786 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
6787 if(*dwSize > 0)
6789 IStream_Read(pStream, strProgID, *dwSize, NULL);
6791 IStream_Release(pStream);
6793 else
6795 STATSTG stat;
6796 LPOLESTR wstrProgID;
6798 /* Get the OleType from the registry */
6799 REFCLSID clsid = &(stat.clsid);
6800 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
6801 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
6802 if(hRes == S_OK)
6804 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
6808 return hRes;
6811 /*************************************************************************
6812 * OLECONVERT_GetOle10PresData [Internal]
6814 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
6816 * PARAMS
6817 * pStorage [I] Src IStroage
6818 * pOleStream [I] Dest OleStream Mem Struct
6820 * RETURNS
6821 * Nothing
6823 * NOTES
6824 * This function is used by OleConvertIStorageToOLESTREAM only.
6826 * Memory allocated for pData must be freed by the caller
6830 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6833 HRESULT hRes;
6834 IStream *pStream;
6835 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6837 /* Initialize Default data for OLESTREAM */
6838 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6839 pOleStreamData[0].dwTypeID = 2;
6840 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6841 pOleStreamData[1].dwTypeID = 0;
6842 pOleStreamData[0].dwMetaFileWidth = 0;
6843 pOleStreamData[0].dwMetaFileHeight = 0;
6844 pOleStreamData[0].pData = NULL;
6845 pOleStreamData[1].pData = NULL;
6847 /* Open Ole10Native Stream */
6848 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6849 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6850 if(hRes == S_OK)
6853 /* Read Size and Data */
6854 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
6855 if(pOleStreamData->dwDataLength > 0)
6857 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
6858 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
6860 IStream_Release(pStream);
6866 /*************************************************************************
6867 * OLECONVERT_GetOle20PresData[Internal]
6869 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
6871 * PARAMS
6872 * pStorage [I] Src IStroage
6873 * pOleStreamData [I] Dest OleStream Mem Struct
6875 * RETURNS
6876 * Nothing
6878 * NOTES
6879 * This function is used by OleConvertIStorageToOLESTREAM only.
6881 * Memory allocated for pData must be freed by the caller
6883 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6885 HRESULT hRes;
6886 IStream *pStream;
6887 OLECONVERT_ISTORAGE_OLEPRES olePress;
6888 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6890 /* Initialize Default data for OLESTREAM */
6891 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6892 pOleStreamData[0].dwTypeID = 2;
6893 pOleStreamData[0].dwMetaFileWidth = 0;
6894 pOleStreamData[0].dwMetaFileHeight = 0;
6895 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
6896 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6897 pOleStreamData[1].dwTypeID = 0;
6898 pOleStreamData[1].dwOleTypeNameLength = 0;
6899 pOleStreamData[1].strOleTypeName[0] = 0;
6900 pOleStreamData[1].dwMetaFileWidth = 0;
6901 pOleStreamData[1].dwMetaFileHeight = 0;
6902 pOleStreamData[1].pData = NULL;
6903 pOleStreamData[1].dwDataLength = 0;
6906 /* Open OlePress000 stream */
6907 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6908 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6909 if(hRes == S_OK)
6911 LARGE_INTEGER iSeekPos;
6912 METAFILEPICT16 MetaFilePict;
6913 char strMetafilePictName[] = "METAFILEPICT";
6915 /* Set the TypeID for a Metafile */
6916 pOleStreamData[1].dwTypeID = 5;
6918 /* Set the OleTypeName to Metafile */
6919 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
6920 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
6922 iSeekPos.s.HighPart = 0;
6923 iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
6925 /* Get Presentation Data */
6926 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6927 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
6928 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
6929 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
6931 /*Set width and Height */
6932 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
6933 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
6934 if(olePress.dwSize > 0)
6936 /* Set Length */
6937 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
6939 /* Set MetaFilePict struct */
6940 MetaFilePict.mm = 8;
6941 MetaFilePict.xExt = olePress.dwExtentX;
6942 MetaFilePict.yExt = olePress.dwExtentY;
6943 MetaFilePict.hMF = 0;
6945 /* Get Metafile Data */
6946 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
6947 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
6948 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
6950 IStream_Release(pStream);
6954 /*************************************************************************
6955 * OleConvertOLESTREAMToIStorage [OLE32.87]
6957 * Read info on MSDN
6959 * TODO
6960 * DVTARGETDEVICE paramenter is not handled
6961 * Still unsure of some mem fields for OLE 10 Stream
6962 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6963 * and "\001OLE" streams
6966 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
6967 LPOLESTREAM pOleStream,
6968 LPSTORAGE pstg,
6969 const DVTARGETDEVICE* ptd)
6971 int i;
6972 HRESULT hRes=S_OK;
6973 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6975 memset(pOleStreamData, 0, sizeof(pOleStreamData));
6977 if(ptd != NULL)
6979 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
6982 if(pstg == NULL || pOleStream == NULL)
6984 hRes = E_INVALIDARG;
6987 if(hRes == S_OK)
6989 /* Load the OLESTREAM to Memory */
6990 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
6993 if(hRes == S_OK)
6995 /* Load the OLESTREAM to Memory (part 2)*/
6996 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
6999 if(hRes == S_OK)
7002 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7004 /* Do we have the IStorage Data in the OLESTREAM */
7005 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7007 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7008 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7010 else
7012 /* It must be an original OLE 1.0 source */
7013 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7016 else
7018 /* It must be an original OLE 1.0 source */
7019 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7022 /* Create CompObj Stream if necessary */
7023 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7024 if(hRes == S_OK)
7026 /*Create the Ole Stream if necessary */
7027 OLECONVERT_CreateOleStream(pstg);
7032 /* Free allocated memory */
7033 for(i=0; i < 2; i++)
7035 if(pOleStreamData[i].pData != NULL)
7037 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7039 if(pOleStreamData[i].pstrOleObjFileName != NULL)
7041 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7042 pOleStreamData[i].pstrOleObjFileName = NULL;
7045 return hRes;
7048 /*************************************************************************
7049 * OleConvertIStorageToOLESTREAM [OLE32.85]
7051 * Read info on MSDN
7053 * Read info on MSDN
7055 * TODO
7056 * Still unsure of some mem fields for OLE 10 Stream
7057 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7058 * and "\001OLE" streams.
7061 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7062 LPSTORAGE pstg,
7063 LPOLESTREAM pOleStream)
7065 int i;
7066 HRESULT hRes = S_OK;
7067 IStream *pStream;
7068 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7069 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7072 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7074 if(pstg == NULL || pOleStream == NULL)
7076 hRes = E_INVALIDARG;
7078 if(hRes == S_OK)
7080 /* Get the ProgID */
7081 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7082 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7084 if(hRes == S_OK)
7086 /* Was it originally Ole10 */
7087 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7088 if(hRes == S_OK)
7090 IStream_Release(pStream);
7091 /* Get Presentation Data for Ole10Native */
7092 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7094 else
7096 /* Get Presentation Data (OLE20) */
7097 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7100 /* Save OLESTREAM */
7101 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7102 if(hRes == S_OK)
7104 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7109 /* Free allocated memory */
7110 for(i=0; i < 2; i++)
7112 if(pOleStreamData[i].pData != NULL)
7114 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7118 return hRes;
7121 /***********************************************************************
7122 * GetConvertStg (OLE32.68)
7124 HRESULT WINAPI GetConvertStg(LPGUID guid) {
7125 FIXME("(%s), unimplemented stub!\n",debugstr_guid(guid));
7126 return E_FAIL;