Release 20050930.
[wine/gsoc-2012-control.git] / dlls / ole32 / storage32.c
blobd1360d95bdf45340cec608a2348039102aeafe73
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
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
32 * MSDN
33 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
36 #include <assert.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
42 #define COBJMACROS
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
46 #include "windef.h"
47 #include "winbase.h"
48 #include "winnls.h"
49 #include "winuser.h"
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 #include "storage32.h"
54 #include "ole2.h" /* For Write/ReadClassStm */
56 #include "winreg.h"
57 #include "wine/wingdi16.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(storage);
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 #define OLESTREAM_ID 0x501
63 #define OLESTREAM_MAX_STR_LEN 255
65 static const char rootPropertyName[] = "Root Entry";
68 /* OLESTREAM memory structure to use for Get and Put Routines */
69 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
70 typedef struct
72 DWORD dwOleID;
73 DWORD dwTypeID;
74 DWORD dwOleTypeNameLength;
75 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
76 CHAR *pstrOleObjFileName;
77 DWORD dwOleObjFileNameLength;
78 DWORD dwMetaFileWidth;
79 DWORD dwMetaFileHeight;
80 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
81 DWORD dwDataLength;
82 BYTE *pData;
83 }OLECONVERT_OLESTREAM_DATA;
85 /* CompObj Stream structure */
86 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
87 typedef struct
89 BYTE byUnknown1[12];
90 CLSID clsid;
91 DWORD dwCLSIDNameLength;
92 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
93 DWORD dwOleTypeNameLength;
94 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
95 DWORD dwProgIDNameLength;
96 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
97 BYTE byUnknown2[16];
98 }OLECONVERT_ISTORAGE_COMPOBJ;
101 /* Ole Presention Stream structure */
102 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
103 typedef struct
105 BYTE byUnknown1[28];
106 DWORD dwExtentX;
107 DWORD dwExtentY;
108 DWORD dwSize;
109 BYTE *pData;
110 }OLECONVERT_ISTORAGE_OLEPRES;
114 /***********************************************************************
115 * Forward declaration of internal functions used by the method DestroyElement
117 static HRESULT deleteStorageProperty(
118 StorageImpl *parentStorage,
119 ULONG foundPropertyIndexToDelete,
120 StgProperty propertyToDelete);
122 static HRESULT deleteStreamProperty(
123 StorageImpl *parentStorage,
124 ULONG foundPropertyIndexToDelete,
125 StgProperty propertyToDelete);
127 static HRESULT findPlaceholder(
128 StorageImpl *storage,
129 ULONG propertyIndexToStore,
130 ULONG storagePropertyIndex,
131 INT typeOfRelation);
133 static HRESULT adjustPropertyChain(
134 StorageImpl *This,
135 StgProperty propertyToDelete,
136 StgProperty parentProperty,
137 ULONG parentPropertyId,
138 INT typeOfRelation);
140 /***********************************************************************
141 * Declaration of the functions used to manipulate StgProperty
144 static ULONG getFreeProperty(
145 StorageImpl *storage);
147 static void updatePropertyChain(
148 StorageImpl *storage,
149 ULONG newPropertyIndex,
150 StgProperty newProperty);
152 static LONG propertyNameCmp(
153 const OLECHAR *newProperty,
154 const OLECHAR *currentProperty);
157 /***********************************************************************
158 * Declaration of miscellaneous functions...
160 static HRESULT validateSTGM(DWORD stgmValue);
162 static DWORD GetShareModeFromSTGM(DWORD stgm);
163 static DWORD GetAccessModeFromSTGM(DWORD stgm);
164 static DWORD GetCreationModeFromSTGM(DWORD stgm);
166 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
170 /************************************************************************
171 ** Storage32BaseImpl implementatiion
174 /************************************************************************
175 * Storage32BaseImpl_QueryInterface (IUnknown)
177 * This method implements the common QueryInterface for all IStorage32
178 * implementations contained in this file.
180 * See Windows documentation for more details on IUnknown methods.
182 HRESULT WINAPI StorageBaseImpl_QueryInterface(
183 IStorage* iface,
184 REFIID riid,
185 void** ppvObject)
187 StorageBaseImpl *This = (StorageBaseImpl *)iface;
189 * Perform a sanity check on the parameters.
191 if ( (This==0) || (ppvObject==0) )
192 return E_INVALIDARG;
195 * Initialize the return parameter.
197 *ppvObject = 0;
200 * Compare the riid with the interface IDs implemented by this object.
202 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
204 *ppvObject = (IStorage*)This;
206 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
208 *ppvObject = (IStorage*)This;
210 else if (memcmp(&IID_IPropertySetStorage, riid, sizeof(IID_IPropertySetStorage)) == 0)
212 *ppvObject = (IStorage*)&This->pssVtbl;
216 * Check that we obtained an interface.
218 if ((*ppvObject)==0)
219 return E_NOINTERFACE;
222 * Query Interface always increases the reference count by one when it is
223 * successful
225 IStorage_AddRef(iface);
227 return S_OK;
230 /************************************************************************
231 * Storage32BaseImpl_AddRef (IUnknown)
233 * This method implements the common AddRef for all IStorage32
234 * implementations contained in this file.
236 * See Windows documentation for more details on IUnknown methods.
238 ULONG WINAPI StorageBaseImpl_AddRef(
239 IStorage* iface)
241 StorageBaseImpl *This = (StorageBaseImpl *)iface;
242 ULONG ref = InterlockedIncrement(&This->ref);
244 TRACE("(%p) AddRef to %ld\n", This, ref);
246 return ref;
249 /************************************************************************
250 * Storage32BaseImpl_Release (IUnknown)
252 * This method implements the common Release for all IStorage32
253 * implementations contained in this file.
255 * See Windows documentation for more details on IUnknown methods.
257 ULONG WINAPI StorageBaseImpl_Release(
258 IStorage* iface)
260 StorageBaseImpl *This = (StorageBaseImpl *)iface;
262 * Decrease the reference count on this object.
264 ULONG ref = InterlockedDecrement(&This->ref);
266 TRACE("(%p) ReleaseRef to %ld\n", This, ref);
269 * If the reference count goes down to 0, perform suicide.
271 if (ref == 0)
274 * Since we are using a system of base-classes, we want to call the
275 * destructor of the appropriate derived class. To do this, we are
276 * using virtual functions to implement the destructor.
278 This->v_destructor(This);
281 return ref;
284 /************************************************************************
285 * Storage32BaseImpl_OpenStream (IStorage)
287 * This method will open the specified stream object from the current storage.
289 * See Windows documentation for more details on IStorage methods.
291 HRESULT WINAPI StorageBaseImpl_OpenStream(
292 IStorage* iface,
293 const OLECHAR* pwcsName, /* [string][in] */
294 void* reserved1, /* [unique][in] */
295 DWORD grfMode, /* [in] */
296 DWORD reserved2, /* [in] */
297 IStream** ppstm) /* [out] */
299 StorageBaseImpl *This = (StorageBaseImpl *)iface;
300 IEnumSTATSTGImpl* propertyEnumeration;
301 StgStreamImpl* newStream;
302 StgProperty currentProperty;
303 ULONG foundPropertyIndex;
304 HRESULT res = STG_E_UNKNOWN;
305 DWORD parent_grfMode;
307 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
308 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
311 * Perform a sanity check on the parameters.
313 if ( (pwcsName==NULL) || (ppstm==0) )
315 res = E_INVALIDARG;
316 goto end;
320 * Initialize the out parameter
322 *ppstm = NULL;
325 * Validate the STGM flags
327 if ( FAILED( validateSTGM(grfMode) ))
329 res = STG_E_INVALIDFLAG;
330 goto end;
334 * As documented.
336 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
337 (grfMode & STGM_DELETEONRELEASE) ||
338 (grfMode & STGM_TRANSACTED) )
340 res = STG_E_INVALIDFUNCTION;
341 goto end;
345 * Check that we're compatible with the parent's storage mode
347 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
348 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
350 res = STG_E_ACCESSDENIED;
351 goto end;
355 * Create a property enumeration to search the properties
357 propertyEnumeration = IEnumSTATSTGImpl_Construct(
358 This->ancestorStorage,
359 This->rootPropertySetIndex);
362 * Search the enumeration for the property with the given name
364 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
365 propertyEnumeration,
366 pwcsName,
367 &currentProperty);
370 * Delete the property enumeration since we don't need it anymore
372 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
375 * If it was found, construct the stream object and return a pointer to it.
377 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
378 (currentProperty.propertyType==PROPTYPE_STREAM) )
380 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
382 if (newStream!=0)
384 newStream->grfMode = grfMode;
385 *ppstm = (IStream*)newStream;
388 * Since we are returning a pointer to the interface, we have to
389 * nail down the reference.
391 IStream_AddRef(*ppstm);
393 res = S_OK;
394 goto end;
397 res = E_OUTOFMEMORY;
398 goto end;
401 res = STG_E_FILENOTFOUND;
403 end:
404 if (res == S_OK)
405 TRACE("<-- IStream %p\n", *ppstm);
406 TRACE("<-- %08lx\n", res);
407 return res;
410 /************************************************************************
411 * Storage32BaseImpl_OpenStorage (IStorage)
413 * This method will open a new storage object from the current storage.
415 * See Windows documentation for more details on IStorage methods.
417 HRESULT WINAPI StorageBaseImpl_OpenStorage(
418 IStorage* iface,
419 const OLECHAR* pwcsName, /* [string][unique][in] */
420 IStorage* pstgPriority, /* [unique][in] */
421 DWORD grfMode, /* [in] */
422 SNB snbExclude, /* [unique][in] */
423 DWORD reserved, /* [in] */
424 IStorage** ppstg) /* [out] */
426 StorageBaseImpl *This = (StorageBaseImpl *)iface;
427 StorageInternalImpl* newStorage;
428 IEnumSTATSTGImpl* propertyEnumeration;
429 StgProperty currentProperty;
430 ULONG foundPropertyIndex;
431 HRESULT res = STG_E_UNKNOWN;
432 DWORD parent_grfMode;
434 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
435 iface, debugstr_w(pwcsName), pstgPriority,
436 grfMode, snbExclude, reserved, ppstg);
439 * Perform a sanity check on the parameters.
441 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
443 res = E_INVALIDARG;
444 goto end;
447 /* as documented */
448 if (snbExclude != NULL)
450 res = STG_E_INVALIDPARAMETER;
451 goto end;
455 * Validate the STGM flags
457 if ( FAILED( validateSTGM(grfMode) ))
459 res = STG_E_INVALIDFLAG;
460 goto end;
464 * As documented.
466 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
467 (grfMode & STGM_DELETEONRELEASE) ||
468 (grfMode & STGM_PRIORITY) )
470 res = STG_E_INVALIDFUNCTION;
471 goto end;
475 * Check that we're compatible with the parent's storage mode
477 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
478 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
480 res = STG_E_ACCESSDENIED;
481 goto end;
485 * Initialize the out parameter
487 *ppstg = NULL;
490 * Create a property enumeration to search the properties
492 propertyEnumeration = IEnumSTATSTGImpl_Construct(
493 This->ancestorStorage,
494 This->rootPropertySetIndex);
497 * Search the enumeration for the property with the given name
499 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
500 propertyEnumeration,
501 pwcsName,
502 &currentProperty);
505 * Delete the property enumeration since we don't need it anymore
507 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
510 * If it was found, construct the stream object and return a pointer to it.
512 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
513 (currentProperty.propertyType==PROPTYPE_STORAGE) )
516 * Construct a new Storage object
518 newStorage = StorageInternalImpl_Construct(
519 This->ancestorStorage,
520 grfMode,
521 foundPropertyIndex);
523 if (newStorage != 0)
525 *ppstg = (IStorage*)newStorage;
528 * Since we are returning a pointer to the interface,
529 * we have to nail down the reference.
531 StorageBaseImpl_AddRef(*ppstg);
533 res = S_OK;
534 goto end;
537 res = STG_E_INSUFFICIENTMEMORY;
538 goto end;
541 res = STG_E_FILENOTFOUND;
543 end:
544 TRACE("<-- %08lx\n", res);
545 return res;
548 /************************************************************************
549 * Storage32BaseImpl_EnumElements (IStorage)
551 * This method will create an enumerator object that can be used to
552 * retrieve informatino about all the properties in the storage object.
554 * See Windows documentation for more details on IStorage methods.
556 HRESULT WINAPI StorageBaseImpl_EnumElements(
557 IStorage* iface,
558 DWORD reserved1, /* [in] */
559 void* reserved2, /* [size_is][unique][in] */
560 DWORD reserved3, /* [in] */
561 IEnumSTATSTG** ppenum) /* [out] */
563 StorageBaseImpl *This = (StorageBaseImpl *)iface;
564 IEnumSTATSTGImpl* newEnum;
566 TRACE("(%p, %ld, %p, %ld, %p)\n",
567 iface, reserved1, reserved2, reserved3, ppenum);
570 * Perform a sanity check on the parameters.
572 if ( (This==0) || (ppenum==0))
573 return E_INVALIDARG;
576 * Construct the enumerator.
578 newEnum = IEnumSTATSTGImpl_Construct(
579 This->ancestorStorage,
580 This->rootPropertySetIndex);
582 if (newEnum!=0)
584 *ppenum = (IEnumSTATSTG*)newEnum;
587 * Don't forget to nail down a reference to the new object before
588 * returning it.
590 IEnumSTATSTG_AddRef(*ppenum);
592 return S_OK;
595 return E_OUTOFMEMORY;
598 /************************************************************************
599 * Storage32BaseImpl_Stat (IStorage)
601 * This method will retrieve information about this storage object.
603 * See Windows documentation for more details on IStorage methods.
605 HRESULT WINAPI StorageBaseImpl_Stat(
606 IStorage* iface,
607 STATSTG* pstatstg, /* [out] */
608 DWORD grfStatFlag) /* [in] */
610 StorageBaseImpl *This = (StorageBaseImpl *)iface;
611 StgProperty curProperty;
612 BOOL readSuccessful;
613 HRESULT res = STG_E_UNKNOWN;
615 TRACE("(%p, %p, %lx)\n",
616 iface, pstatstg, grfStatFlag);
619 * Perform a sanity check on the parameters.
621 if ( (This==0) || (pstatstg==0))
623 res = E_INVALIDARG;
624 goto end;
628 * Read the information from the property.
630 readSuccessful = StorageImpl_ReadProperty(
631 This->ancestorStorage,
632 This->rootPropertySetIndex,
633 &curProperty);
635 if (readSuccessful)
637 StorageUtl_CopyPropertyToSTATSTG(
638 pstatstg,
639 &curProperty,
640 grfStatFlag);
642 res = S_OK;
643 goto end;
646 res = E_FAIL;
648 end:
649 if (res == S_OK)
651 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.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
653 TRACE("<-- %08lx\n", res);
654 return res;
657 /************************************************************************
658 * Storage32BaseImpl_RenameElement (IStorage)
660 * This method will rename the specified element.
662 * See Windows documentation for more details on IStorage methods.
664 * Implementation notes: The method used to rename consists of creating a clone
665 * of the deleted StgProperty object setting it with the new name and to
666 * perform a DestroyElement of the old StgProperty.
668 HRESULT WINAPI StorageBaseImpl_RenameElement(
669 IStorage* iface,
670 const OLECHAR* pwcsOldName, /* [in] */
671 const OLECHAR* pwcsNewName) /* [in] */
673 StorageBaseImpl *This = (StorageBaseImpl *)iface;
674 IEnumSTATSTGImpl* propertyEnumeration;
675 StgProperty currentProperty;
676 ULONG foundPropertyIndex;
678 TRACE("(%p, %s, %s)\n",
679 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
682 * Create a property enumeration to search the properties
684 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
685 This->rootPropertySetIndex);
688 * Search the enumeration for the new property name
690 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
691 pwcsNewName,
692 &currentProperty);
694 if (foundPropertyIndex != PROPERTY_NULL)
697 * There is already a property with the new name
699 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
700 return STG_E_FILEALREADYEXISTS;
703 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
706 * Search the enumeration for the old property name
708 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
709 pwcsOldName,
710 &currentProperty);
713 * Delete the property enumeration since we don't need it anymore
715 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
717 if (foundPropertyIndex != PROPERTY_NULL)
719 StgProperty renamedProperty;
720 ULONG renamedPropertyIndex;
723 * Setup a new property for the renamed property
725 renamedProperty.sizeOfNameString =
726 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
728 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
729 return STG_E_INVALIDNAME;
731 strcpyW(renamedProperty.name, pwcsNewName);
733 renamedProperty.propertyType = currentProperty.propertyType;
734 renamedProperty.startingBlock = currentProperty.startingBlock;
735 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
736 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
738 renamedProperty.previousProperty = PROPERTY_NULL;
739 renamedProperty.nextProperty = PROPERTY_NULL;
742 * Bring the dirProperty link in case it is a storage and in which
743 * case the renamed storage elements don't require to be reorganized.
745 renamedProperty.dirProperty = currentProperty.dirProperty;
747 /* call CoFileTime to get the current time
748 renamedProperty.timeStampS1
749 renamedProperty.timeStampD1
750 renamedProperty.timeStampS2
751 renamedProperty.timeStampD2
752 renamedProperty.propertyUniqueID
756 * Obtain a free property in the property chain
758 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
761 * Save the new property into the new property spot
763 StorageImpl_WriteProperty(
764 This->ancestorStorage,
765 renamedPropertyIndex,
766 &renamedProperty);
769 * Find a spot in the property chain for our newly created property.
771 updatePropertyChain(
772 (StorageImpl*)This,
773 renamedPropertyIndex,
774 renamedProperty);
777 * At this point the renamed property has been inserted in the tree,
778 * now, before to Destroy the old property we must zeroed it's dirProperty
779 * otherwise the DestroyProperty below will zap it all and we do not want
780 * this to happen.
781 * Also, we fake that the old property is a storage so the DestroyProperty
782 * will not do a SetSize(0) on the stream data.
784 * This means that we need to tweek the StgProperty if it is a stream or a
785 * non empty storage.
787 StorageImpl_ReadProperty(This->ancestorStorage,
788 foundPropertyIndex,
789 &currentProperty);
791 currentProperty.dirProperty = PROPERTY_NULL;
792 currentProperty.propertyType = PROPTYPE_STORAGE;
793 StorageImpl_WriteProperty(
794 This->ancestorStorage,
795 foundPropertyIndex,
796 &currentProperty);
799 * Invoke Destroy to get rid of the ole property and automatically redo
800 * the linking of it's previous and next members...
802 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
805 else
808 * There is no property with the old name
810 return STG_E_FILENOTFOUND;
813 return S_OK;
816 /************************************************************************
817 * Storage32BaseImpl_CreateStream (IStorage)
819 * This method will create a stream object within this storage
821 * See Windows documentation for more details on IStorage methods.
823 HRESULT WINAPI StorageBaseImpl_CreateStream(
824 IStorage* iface,
825 const OLECHAR* pwcsName, /* [string][in] */
826 DWORD grfMode, /* [in] */
827 DWORD reserved1, /* [in] */
828 DWORD reserved2, /* [in] */
829 IStream** ppstm) /* [out] */
831 StorageBaseImpl *This = (StorageBaseImpl *)iface;
832 IEnumSTATSTGImpl* propertyEnumeration;
833 StgStreamImpl* newStream;
834 StgProperty currentProperty, newStreamProperty;
835 ULONG foundPropertyIndex, newPropertyIndex;
836 DWORD parent_grfMode;
838 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
839 iface, debugstr_w(pwcsName), grfMode,
840 reserved1, reserved2, ppstm);
843 * Validate parameters
845 if (ppstm == 0)
846 return STG_E_INVALIDPOINTER;
848 if (pwcsName == 0)
849 return STG_E_INVALIDNAME;
851 if (reserved1 || reserved2)
852 return STG_E_INVALIDPARAMETER;
855 * Validate the STGM flags
857 if ( FAILED( validateSTGM(grfMode) ))
858 return STG_E_INVALIDFLAG;
860 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
861 return STG_E_INVALIDFLAG;
864 * As documented.
866 if ((grfMode & STGM_DELETEONRELEASE) ||
867 (grfMode & STGM_TRANSACTED))
868 return STG_E_INVALIDFUNCTION;
871 * Check that we're compatible with the parent's storage mode
873 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
874 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
875 return STG_E_ACCESSDENIED;
878 * Initialize the out parameter
880 *ppstm = 0;
883 * Create a property enumeration to search the properties
885 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
886 This->rootPropertySetIndex);
888 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
889 pwcsName,
890 &currentProperty);
892 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
894 if (foundPropertyIndex != PROPERTY_NULL)
897 * An element with this name already exists
899 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
901 IStorage_DestroyElement(iface, pwcsName);
903 else
904 return STG_E_FILEALREADYEXISTS;
908 * memset the empty property
910 memset(&newStreamProperty, 0, sizeof(StgProperty));
912 newStreamProperty.sizeOfNameString =
913 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
915 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
916 return STG_E_INVALIDNAME;
918 strcpyW(newStreamProperty.name, pwcsName);
920 newStreamProperty.propertyType = PROPTYPE_STREAM;
921 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
922 newStreamProperty.size.u.LowPart = 0;
923 newStreamProperty.size.u.HighPart = 0;
925 newStreamProperty.previousProperty = PROPERTY_NULL;
926 newStreamProperty.nextProperty = PROPERTY_NULL;
927 newStreamProperty.dirProperty = PROPERTY_NULL;
929 /* call CoFileTime to get the current time
930 newStreamProperty.timeStampS1
931 newStreamProperty.timeStampD1
932 newStreamProperty.timeStampS2
933 newStreamProperty.timeStampD2
936 /* newStreamProperty.propertyUniqueID */
939 * Get a free property or create a new one
941 newPropertyIndex = getFreeProperty(This->ancestorStorage);
944 * Save the new property into the new property spot
946 StorageImpl_WriteProperty(
947 This->ancestorStorage,
948 newPropertyIndex,
949 &newStreamProperty);
952 * Find a spot in the property chain for our newly created property.
954 updatePropertyChain(
955 (StorageImpl*)This,
956 newPropertyIndex,
957 newStreamProperty);
960 * Open the stream to return it.
962 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
964 if (newStream != 0)
966 *ppstm = (IStream*)newStream;
969 * Since we are returning a pointer to the interface, we have to nail down
970 * the reference.
972 IStream_AddRef(*ppstm);
974 else
976 return STG_E_INSUFFICIENTMEMORY;
979 return S_OK;
982 /************************************************************************
983 * Storage32BaseImpl_SetClass (IStorage)
985 * This method will write the specified CLSID in the property of this
986 * storage.
988 * See Windows documentation for more details on IStorage methods.
990 HRESULT WINAPI StorageBaseImpl_SetClass(
991 IStorage* iface,
992 REFCLSID clsid) /* [in] */
994 StorageBaseImpl *This = (StorageBaseImpl *)iface;
995 HRESULT hRes = E_FAIL;
996 StgProperty curProperty;
997 BOOL success;
999 TRACE("(%p, %p)\n", iface, clsid);
1001 success = StorageImpl_ReadProperty(This->ancestorStorage,
1002 This->rootPropertySetIndex,
1003 &curProperty);
1004 if (success)
1006 curProperty.propertyUniqueID = *clsid;
1008 success = StorageImpl_WriteProperty(This->ancestorStorage,
1009 This->rootPropertySetIndex,
1010 &curProperty);
1011 if (success)
1012 hRes = S_OK;
1015 return hRes;
1018 /************************************************************************
1019 ** Storage32Impl implementation
1022 /************************************************************************
1023 * Storage32Impl_CreateStorage (IStorage)
1025 * This method will create the storage object within the provided storage.
1027 * See Windows documentation for more details on IStorage methods.
1029 HRESULT WINAPI StorageImpl_CreateStorage(
1030 IStorage* iface,
1031 const OLECHAR *pwcsName, /* [string][in] */
1032 DWORD grfMode, /* [in] */
1033 DWORD reserved1, /* [in] */
1034 DWORD reserved2, /* [in] */
1035 IStorage **ppstg) /* [out] */
1037 StorageImpl* const This=(StorageImpl*)iface;
1039 IEnumSTATSTGImpl *propertyEnumeration;
1040 StgProperty currentProperty;
1041 StgProperty newProperty;
1042 ULONG foundPropertyIndex;
1043 ULONG newPropertyIndex;
1044 HRESULT hr;
1045 DWORD parent_grfMode;
1047 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1048 iface, debugstr_w(pwcsName), grfMode,
1049 reserved1, reserved2, ppstg);
1052 * Validate parameters
1054 if (ppstg == 0)
1055 return STG_E_INVALIDPOINTER;
1057 if (pwcsName == 0)
1058 return STG_E_INVALIDNAME;
1061 * Initialize the out parameter
1063 *ppstg = NULL;
1066 * Validate the STGM flags
1068 if ( FAILED( validateSTGM(grfMode) ) ||
1069 (grfMode & STGM_DELETEONRELEASE) )
1071 WARN("bad grfMode: 0x%lx\n", grfMode);
1072 return STG_E_INVALIDFLAG;
1076 * Check that we're compatible with the parent's storage mode
1078 parent_grfMode = STGM_ACCESS_MODE( This->base.ancestorStorage->base.openFlags );
1079 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
1081 WARN("access denied\n");
1082 return STG_E_ACCESSDENIED;
1086 * Create a property enumeration and search the properties
1088 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1089 This->base.rootPropertySetIndex);
1091 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1092 pwcsName,
1093 &currentProperty);
1094 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1096 if (foundPropertyIndex != PROPERTY_NULL)
1099 * An element with this name already exists
1101 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1102 IStorage_DestroyElement(iface, pwcsName);
1103 else
1105 WARN("file already exists\n");
1106 return STG_E_FILEALREADYEXISTS;
1111 * memset the empty property
1113 memset(&newProperty, 0, sizeof(StgProperty));
1115 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1117 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1119 FIXME("name too long\n");
1120 return STG_E_INVALIDNAME;
1123 strcpyW(newProperty.name, pwcsName);
1125 newProperty.propertyType = PROPTYPE_STORAGE;
1126 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1127 newProperty.size.u.LowPart = 0;
1128 newProperty.size.u.HighPart = 0;
1130 newProperty.previousProperty = PROPERTY_NULL;
1131 newProperty.nextProperty = PROPERTY_NULL;
1132 newProperty.dirProperty = PROPERTY_NULL;
1134 /* call CoFileTime to get the current time
1135 newProperty.timeStampS1
1136 newProperty.timeStampD1
1137 newProperty.timeStampS2
1138 newProperty.timeStampD2
1141 /* newStorageProperty.propertyUniqueID */
1144 * Obtain a free property in the property chain
1146 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1149 * Save the new property into the new property spot
1151 StorageImpl_WriteProperty(
1152 This->base.ancestorStorage,
1153 newPropertyIndex,
1154 &newProperty);
1157 * Find a spot in the property chain for our newly created property.
1159 updatePropertyChain(
1160 This,
1161 newPropertyIndex,
1162 newProperty);
1165 * Open it to get a pointer to return.
1167 hr = IStorage_OpenStorage(
1168 iface,
1169 (const OLECHAR*)pwcsName,
1171 grfMode,
1174 ppstg);
1176 if( (hr != S_OK) || (*ppstg == NULL))
1178 return hr;
1182 return S_OK;
1186 /***************************************************************************
1188 * Internal Method
1190 * Get a free property or create a new one.
1192 static ULONG getFreeProperty(
1193 StorageImpl *storage)
1195 ULONG currentPropertyIndex = 0;
1196 ULONG newPropertyIndex = PROPERTY_NULL;
1197 BOOL readSuccessful = TRUE;
1198 StgProperty currentProperty;
1203 * Start by reading the root property
1205 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1206 currentPropertyIndex,
1207 &currentProperty);
1208 if (readSuccessful)
1210 if (currentProperty.sizeOfNameString == 0)
1213 * The property existis and is available, we found it.
1215 newPropertyIndex = currentPropertyIndex;
1218 else
1221 * We exhausted the property list, we will create more space below
1223 newPropertyIndex = currentPropertyIndex;
1225 currentPropertyIndex++;
1227 } while (newPropertyIndex == PROPERTY_NULL);
1230 * grow the property chain
1232 if (! readSuccessful)
1234 StgProperty emptyProperty;
1235 ULARGE_INTEGER newSize;
1236 ULONG propertyIndex;
1237 ULONG lastProperty = 0;
1238 ULONG blockCount = 0;
1241 * obtain the new count of property blocks
1243 blockCount = BlockChainStream_GetCount(
1244 storage->base.ancestorStorage->rootBlockChain)+1;
1247 * initialize the size used by the property stream
1249 newSize.u.HighPart = 0;
1250 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1253 * add a property block to the property chain
1255 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1258 * memset the empty property in order to initialize the unused newly
1259 * created property
1261 memset(&emptyProperty, 0, sizeof(StgProperty));
1264 * initialize them
1266 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1268 for(
1269 propertyIndex = newPropertyIndex;
1270 propertyIndex < lastProperty;
1271 propertyIndex++)
1273 StorageImpl_WriteProperty(
1274 storage->base.ancestorStorage,
1275 propertyIndex,
1276 &emptyProperty);
1280 return newPropertyIndex;
1283 /****************************************************************************
1285 * Internal Method
1287 * Case insensitive comparaison of StgProperty.name by first considering
1288 * their size.
1290 * Returns <0 when newPrpoerty < currentProperty
1291 * >0 when newPrpoerty > currentProperty
1292 * 0 when newPrpoerty == currentProperty
1294 static LONG propertyNameCmp(
1295 const OLECHAR *newProperty,
1296 const OLECHAR *currentProperty)
1298 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1300 if (diff == 0)
1303 * We compare the string themselves only when they are of the same length
1305 diff = lstrcmpiW( newProperty, currentProperty);
1308 return diff;
1311 /****************************************************************************
1313 * Internal Method
1315 * Properly link this new element in the property chain.
1317 static void updatePropertyChain(
1318 StorageImpl *storage,
1319 ULONG newPropertyIndex,
1320 StgProperty newProperty)
1322 StgProperty currentProperty;
1325 * Read the root property
1327 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1328 storage->base.rootPropertySetIndex,
1329 &currentProperty);
1331 if (currentProperty.dirProperty != PROPERTY_NULL)
1334 * The root storage contains some element, therefore, start the research
1335 * for the appropriate location.
1337 BOOL found = 0;
1338 ULONG current, next, previous, currentPropertyId;
1341 * Keep the StgProperty sequence number of the storage first property
1343 currentPropertyId = currentProperty.dirProperty;
1346 * Read
1348 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1349 currentProperty.dirProperty,
1350 &currentProperty);
1352 previous = currentProperty.previousProperty;
1353 next = currentProperty.nextProperty;
1354 current = currentPropertyId;
1356 while (found == 0)
1358 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1360 if (diff < 0)
1362 if (previous != PROPERTY_NULL)
1364 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1365 previous,
1366 &currentProperty);
1367 current = previous;
1369 else
1371 currentProperty.previousProperty = newPropertyIndex;
1372 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1373 current,
1374 &currentProperty);
1375 found = 1;
1378 else if (diff > 0)
1380 if (next != PROPERTY_NULL)
1382 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1383 next,
1384 &currentProperty);
1385 current = next;
1387 else
1389 currentProperty.nextProperty = newPropertyIndex;
1390 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1391 current,
1392 &currentProperty);
1393 found = 1;
1396 else
1399 * Trying to insert an item with the same name in the
1400 * subtree structure.
1402 assert(FALSE);
1405 previous = currentProperty.previousProperty;
1406 next = currentProperty.nextProperty;
1409 else
1412 * The root storage is empty, link the new property to it's dir property
1414 currentProperty.dirProperty = newPropertyIndex;
1415 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1416 storage->base.rootPropertySetIndex,
1417 &currentProperty);
1422 /*************************************************************************
1423 * CopyTo (IStorage)
1425 HRESULT WINAPI StorageImpl_CopyTo(
1426 IStorage* iface,
1427 DWORD ciidExclude, /* [in] */
1428 const IID* rgiidExclude, /* [size_is][unique][in] */
1429 SNB snbExclude, /* [unique][in] */
1430 IStorage* pstgDest) /* [unique][in] */
1432 IEnumSTATSTG *elements = 0;
1433 STATSTG curElement, strStat;
1434 HRESULT hr;
1435 IStorage *pstgTmp, *pstgChild;
1436 IStream *pstrTmp, *pstrChild;
1438 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1439 FIXME("Exclude option not implemented\n");
1441 TRACE("(%p, %ld, %p, %p, %p)\n",
1442 iface, ciidExclude, rgiidExclude,
1443 snbExclude, pstgDest);
1446 * Perform a sanity check
1448 if ( pstgDest == 0 )
1449 return STG_E_INVALIDPOINTER;
1452 * Enumerate the elements
1454 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1456 if ( hr != S_OK )
1457 return hr;
1460 * set the class ID
1462 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1463 IStorage_SetClass( pstgDest, &curElement.clsid );
1468 * Obtain the next element
1470 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1472 if ( hr == S_FALSE )
1474 hr = S_OK; /* done, every element has been copied */
1475 break;
1478 if (curElement.type == STGTY_STORAGE)
1481 * open child source storage
1483 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1484 STGM_READ|STGM_SHARE_EXCLUSIVE,
1485 NULL, 0, &pstgChild );
1487 if (hr != S_OK)
1488 break;
1491 * Check if destination storage is not a child of the source
1492 * storage, which will cause an infinite loop
1494 if (pstgChild == pstgDest)
1496 IEnumSTATSTG_Release(elements);
1498 return STG_E_ACCESSDENIED;
1502 * create a new storage in destination storage
1504 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1505 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1506 0, 0,
1507 &pstgTmp );
1509 * if it already exist, don't create a new one use this one
1511 if (hr == STG_E_FILEALREADYEXISTS)
1513 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1514 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1515 NULL, 0, &pstgTmp );
1518 if (hr != S_OK)
1519 break;
1523 * do the copy recursively
1525 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1526 snbExclude, pstgTmp );
1528 IStorage_Release( pstgTmp );
1529 IStorage_Release( pstgChild );
1531 else if (curElement.type == STGTY_STREAM)
1534 * create a new stream in destination storage. If the stream already
1535 * exist, it will be deleted and a new one will be created.
1537 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1538 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1539 0, 0, &pstrTmp );
1541 if (hr != S_OK)
1542 break;
1545 * open child stream storage
1547 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1548 STGM_READ|STGM_SHARE_EXCLUSIVE,
1549 0, &pstrChild );
1551 if (hr != S_OK)
1552 break;
1555 * Get the size of the source stream
1557 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1560 * Set the size of the destination stream.
1562 IStream_SetSize(pstrTmp, strStat.cbSize);
1565 * do the copy
1567 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1568 NULL, NULL );
1570 IStream_Release( pstrTmp );
1571 IStream_Release( pstrChild );
1573 else
1575 WARN("unknown element type: %ld\n", curElement.type);
1578 } while (hr == S_OK);
1581 * Clean-up
1583 IEnumSTATSTG_Release(elements);
1585 return hr;
1588 /*************************************************************************
1589 * MoveElementTo (IStorage)
1591 HRESULT WINAPI StorageImpl_MoveElementTo(
1592 IStorage* iface,
1593 const OLECHAR *pwcsName, /* [string][in] */
1594 IStorage *pstgDest, /* [unique][in] */
1595 const OLECHAR *pwcsNewName,/* [string][in] */
1596 DWORD grfFlags) /* [in] */
1598 FIXME("not implemented!\n");
1599 return E_NOTIMPL;
1602 /*************************************************************************
1603 * Commit (IStorage)
1605 * Ensures that any changes made to a storage object open in transacted mode
1606 * are reflected in the parent storage
1608 * NOTES
1609 * Wine doesn't implement transacted mode, which seems to be a basic
1610 * optimization, so we can ignore this stub for now.
1612 HRESULT WINAPI StorageImpl_Commit(
1613 IStorage* iface,
1614 DWORD grfCommitFlags)/* [in] */
1616 FIXME("(%ld): stub!\n", grfCommitFlags);
1617 return S_OK;
1620 /*************************************************************************
1621 * Revert (IStorage)
1623 * Discard all changes that have been made since the last commit operation
1625 HRESULT WINAPI StorageImpl_Revert(
1626 IStorage* iface)
1628 FIXME("not implemented!\n");
1629 return E_NOTIMPL;
1632 /*************************************************************************
1633 * DestroyElement (IStorage)
1635 * Stategy: This implementation is build this way for simplicity not for speed.
1636 * I always delete the top most element of the enumeration and adjust
1637 * the deleted element pointer all the time. This takes longer to
1638 * do but allow to reinvoke DestroyElement whenever we encounter a
1639 * storage object. The optimisation reside in the usage of another
1640 * enumeration stategy that would give all the leaves of a storage
1641 * first. (postfix order)
1643 HRESULT WINAPI StorageImpl_DestroyElement(
1644 IStorage* iface,
1645 const OLECHAR *pwcsName)/* [string][in] */
1647 StorageImpl* const This=(StorageImpl*)iface;
1649 IEnumSTATSTGImpl* propertyEnumeration;
1650 HRESULT hr = S_OK;
1651 BOOL res;
1652 StgProperty propertyToDelete;
1653 StgProperty parentProperty;
1654 ULONG foundPropertyIndexToDelete;
1655 ULONG typeOfRelation;
1656 ULONG parentPropertyId;
1658 TRACE("(%p, %s)\n",
1659 iface, debugstr_w(pwcsName));
1662 * Perform a sanity check on the parameters.
1664 if (pwcsName==NULL)
1665 return STG_E_INVALIDPOINTER;
1668 * Create a property enumeration to search the property with the given name
1670 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1671 This->base.ancestorStorage,
1672 This->base.rootPropertySetIndex);
1674 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1675 propertyEnumeration,
1676 pwcsName,
1677 &propertyToDelete);
1679 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1681 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1683 return STG_E_FILENOTFOUND;
1687 * Find the parent property of the property to delete (the one that
1688 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1689 * the parent is This. Otherwise, the parent is one of it's sibling...
1693 * First, read This's StgProperty..
1695 res = StorageImpl_ReadProperty(
1696 This->base.ancestorStorage,
1697 This->base.rootPropertySetIndex,
1698 &parentProperty);
1700 assert(res);
1703 * Second, check to see if by any chance the actual storage (This) is not
1704 * the parent of the property to delete... We never know...
1706 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1709 * Set data as it would have been done in the else part...
1711 typeOfRelation = PROPERTY_RELATION_DIR;
1712 parentPropertyId = This->base.rootPropertySetIndex;
1714 else
1717 * Create a property enumeration to search the parent properties, and
1718 * delete it once done.
1720 IEnumSTATSTGImpl* propertyEnumeration2;
1722 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1723 This->base.ancestorStorage,
1724 This->base.rootPropertySetIndex);
1726 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1727 propertyEnumeration2,
1728 foundPropertyIndexToDelete,
1729 &parentProperty,
1730 &parentPropertyId);
1732 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1735 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1737 hr = deleteStorageProperty(
1738 This,
1739 foundPropertyIndexToDelete,
1740 propertyToDelete);
1742 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1744 hr = deleteStreamProperty(
1745 This,
1746 foundPropertyIndexToDelete,
1747 propertyToDelete);
1750 if (hr!=S_OK)
1751 return hr;
1754 * Adjust the property chain
1756 hr = adjustPropertyChain(
1757 This,
1758 propertyToDelete,
1759 parentProperty,
1760 parentPropertyId,
1761 typeOfRelation);
1763 return hr;
1767 /************************************************************************
1768 * StorageImpl_Stat (IStorage)
1770 * This method will retrieve information about this storage object.
1772 * See Windows documentation for more details on IStorage methods.
1774 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1775 STATSTG* pstatstg, /* [out] */
1776 DWORD grfStatFlag) /* [in] */
1778 StorageImpl* const This = (StorageImpl*)iface;
1779 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1781 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1783 CoTaskMemFree(pstatstg->pwcsName);
1784 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1785 strcpyW(pstatstg->pwcsName, This->pwcsName);
1788 return result;
1793 /*********************************************************************
1795 * Internal Method
1797 * Perform the deletion of a complete storage node
1800 static HRESULT deleteStorageProperty(
1801 StorageImpl *parentStorage,
1802 ULONG indexOfPropertyToDelete,
1803 StgProperty propertyToDelete)
1805 IEnumSTATSTG *elements = 0;
1806 IStorage *childStorage = 0;
1807 STATSTG currentElement;
1808 HRESULT hr;
1809 HRESULT destroyHr = S_OK;
1812 * Open the storage and enumerate it
1814 hr = StorageBaseImpl_OpenStorage(
1815 (IStorage*)parentStorage,
1816 propertyToDelete.name,
1818 STGM_SHARE_EXCLUSIVE,
1821 &childStorage);
1823 if (hr != S_OK)
1825 return hr;
1829 * Enumerate the elements
1831 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1836 * Obtain the next element
1838 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1839 if (hr==S_OK)
1841 destroyHr = StorageImpl_DestroyElement(
1842 (IStorage*)childStorage,
1843 (OLECHAR*)currentElement.pwcsName);
1845 CoTaskMemFree(currentElement.pwcsName);
1849 * We need to Reset the enumeration every time because we delete elements
1850 * and the enumeration could be invalid
1852 IEnumSTATSTG_Reset(elements);
1854 } while ((hr == S_OK) && (destroyHr == S_OK));
1857 * Invalidate the property by zeroing it's name member.
1859 propertyToDelete.sizeOfNameString = 0;
1861 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1862 indexOfPropertyToDelete,
1863 &propertyToDelete);
1865 IStorage_Release(childStorage);
1866 IEnumSTATSTG_Release(elements);
1868 return destroyHr;
1871 /*********************************************************************
1873 * Internal Method
1875 * Perform the deletion of a stream node
1878 static HRESULT deleteStreamProperty(
1879 StorageImpl *parentStorage,
1880 ULONG indexOfPropertyToDelete,
1881 StgProperty propertyToDelete)
1883 IStream *pis;
1884 HRESULT hr;
1885 ULARGE_INTEGER size;
1887 size.u.HighPart = 0;
1888 size.u.LowPart = 0;
1890 hr = StorageBaseImpl_OpenStream(
1891 (IStorage*)parentStorage,
1892 (OLECHAR*)propertyToDelete.name,
1893 NULL,
1894 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1896 &pis);
1898 if (hr!=S_OK)
1900 return(hr);
1904 * Zap the stream
1906 hr = IStream_SetSize(pis, size);
1908 if(hr != S_OK)
1910 return hr;
1914 * Release the stream object.
1916 IStream_Release(pis);
1919 * Invalidate the property by zeroing it's name member.
1921 propertyToDelete.sizeOfNameString = 0;
1924 * Here we should re-read the property so we get the updated pointer
1925 * but since we are here to zap it, I don't do it...
1927 StorageImpl_WriteProperty(
1928 parentStorage->base.ancestorStorage,
1929 indexOfPropertyToDelete,
1930 &propertyToDelete);
1932 return S_OK;
1935 /*********************************************************************
1937 * Internal Method
1939 * Finds a placeholder for the StgProperty within the Storage
1942 static HRESULT findPlaceholder(
1943 StorageImpl *storage,
1944 ULONG propertyIndexToStore,
1945 ULONG storePropertyIndex,
1946 INT typeOfRelation)
1948 StgProperty storeProperty;
1949 HRESULT hr = S_OK;
1950 BOOL res = TRUE;
1953 * Read the storage property
1955 res = StorageImpl_ReadProperty(
1956 storage->base.ancestorStorage,
1957 storePropertyIndex,
1958 &storeProperty);
1960 if(! res)
1962 return E_FAIL;
1965 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1967 if (storeProperty.previousProperty != PROPERTY_NULL)
1969 return findPlaceholder(
1970 storage,
1971 propertyIndexToStore,
1972 storeProperty.previousProperty,
1973 typeOfRelation);
1975 else
1977 storeProperty.previousProperty = propertyIndexToStore;
1980 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1982 if (storeProperty.nextProperty != PROPERTY_NULL)
1984 return findPlaceholder(
1985 storage,
1986 propertyIndexToStore,
1987 storeProperty.nextProperty,
1988 typeOfRelation);
1990 else
1992 storeProperty.nextProperty = propertyIndexToStore;
1995 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1997 if (storeProperty.dirProperty != PROPERTY_NULL)
1999 return findPlaceholder(
2000 storage,
2001 propertyIndexToStore,
2002 storeProperty.dirProperty,
2003 typeOfRelation);
2005 else
2007 storeProperty.dirProperty = propertyIndexToStore;
2011 hr = StorageImpl_WriteProperty(
2012 storage->base.ancestorStorage,
2013 storePropertyIndex,
2014 &storeProperty);
2016 if(! hr)
2018 return E_FAIL;
2021 return S_OK;
2024 /*************************************************************************
2026 * Internal Method
2028 * This method takes the previous and the next property link of a property
2029 * to be deleted and find them a place in the Storage.
2031 static HRESULT adjustPropertyChain(
2032 StorageImpl *This,
2033 StgProperty propertyToDelete,
2034 StgProperty parentProperty,
2035 ULONG parentPropertyId,
2036 INT typeOfRelation)
2038 ULONG newLinkProperty = PROPERTY_NULL;
2039 BOOL needToFindAPlaceholder = FALSE;
2040 ULONG storeNode = PROPERTY_NULL;
2041 ULONG toStoreNode = PROPERTY_NULL;
2042 INT relationType = 0;
2043 HRESULT hr = S_OK;
2044 BOOL res = TRUE;
2046 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2048 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2051 * Set the parent previous to the property to delete previous
2053 newLinkProperty = propertyToDelete.previousProperty;
2055 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2058 * We also need to find a storage for the other link, setup variables
2059 * to do this at the end...
2061 needToFindAPlaceholder = TRUE;
2062 storeNode = propertyToDelete.previousProperty;
2063 toStoreNode = propertyToDelete.nextProperty;
2064 relationType = PROPERTY_RELATION_NEXT;
2067 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2070 * Set the parent previous to the property to delete next
2072 newLinkProperty = propertyToDelete.nextProperty;
2076 * Link it for real...
2078 parentProperty.previousProperty = newLinkProperty;
2081 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2083 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2086 * Set the parent next to the property to delete next previous
2088 newLinkProperty = propertyToDelete.previousProperty;
2090 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2093 * We also need to find a storage for the other link, setup variables
2094 * to do this at the end...
2096 needToFindAPlaceholder = TRUE;
2097 storeNode = propertyToDelete.previousProperty;
2098 toStoreNode = propertyToDelete.nextProperty;
2099 relationType = PROPERTY_RELATION_NEXT;
2102 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2105 * Set the parent next to the property to delete next
2107 newLinkProperty = propertyToDelete.nextProperty;
2111 * Link it for real...
2113 parentProperty.nextProperty = newLinkProperty;
2115 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2117 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2120 * Set the parent dir to the property to delete previous
2122 newLinkProperty = propertyToDelete.previousProperty;
2124 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2127 * We also need to find a storage for the other link, setup variables
2128 * to do this at the end...
2130 needToFindAPlaceholder = TRUE;
2131 storeNode = propertyToDelete.previousProperty;
2132 toStoreNode = propertyToDelete.nextProperty;
2133 relationType = PROPERTY_RELATION_NEXT;
2136 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2139 * Set the parent dir to the property to delete next
2141 newLinkProperty = propertyToDelete.nextProperty;
2145 * Link it for real...
2147 parentProperty.dirProperty = newLinkProperty;
2151 * Write back the parent property
2153 res = StorageImpl_WriteProperty(
2154 This->base.ancestorStorage,
2155 parentPropertyId,
2156 &parentProperty);
2157 if(! res)
2159 return E_FAIL;
2163 * If a placeholder is required for the other link, then, find one and
2164 * get out of here...
2166 if (needToFindAPlaceholder)
2168 hr = findPlaceholder(
2169 This,
2170 toStoreNode,
2171 storeNode,
2172 relationType);
2175 return hr;
2179 /******************************************************************************
2180 * SetElementTimes (IStorage)
2182 HRESULT WINAPI StorageImpl_SetElementTimes(
2183 IStorage* iface,
2184 const OLECHAR *pwcsName,/* [string][in] */
2185 const FILETIME *pctime, /* [in] */
2186 const FILETIME *patime, /* [in] */
2187 const FILETIME *pmtime) /* [in] */
2189 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2190 return S_OK;
2193 /******************************************************************************
2194 * SetStateBits (IStorage)
2196 HRESULT WINAPI StorageImpl_SetStateBits(
2197 IStorage* iface,
2198 DWORD grfStateBits,/* [in] */
2199 DWORD grfMask) /* [in] */
2201 FIXME("not implemented!\n");
2202 return E_NOTIMPL;
2206 * Virtual function table for the IStorage32Impl class.
2208 static const IStorageVtbl Storage32Impl_Vtbl =
2210 StorageBaseImpl_QueryInterface,
2211 StorageBaseImpl_AddRef,
2212 StorageBaseImpl_Release,
2213 StorageBaseImpl_CreateStream,
2214 StorageBaseImpl_OpenStream,
2215 StorageImpl_CreateStorage,
2216 StorageBaseImpl_OpenStorage,
2217 StorageImpl_CopyTo,
2218 StorageImpl_MoveElementTo,
2219 StorageImpl_Commit,
2220 StorageImpl_Revert,
2221 StorageBaseImpl_EnumElements,
2222 StorageImpl_DestroyElement,
2223 StorageBaseImpl_RenameElement,
2224 StorageImpl_SetElementTimes,
2225 StorageBaseImpl_SetClass,
2226 StorageImpl_SetStateBits,
2227 StorageImpl_Stat
2230 HRESULT StorageImpl_Construct(
2231 StorageImpl* This,
2232 HANDLE hFile,
2233 LPCOLESTR pwcsName,
2234 ILockBytes* pLkbyt,
2235 DWORD openFlags,
2236 BOOL fileBased,
2237 BOOL fileCreate)
2239 HRESULT hr = S_OK;
2240 StgProperty currentProperty;
2241 BOOL readSuccessful;
2242 ULONG currentPropertyIndex;
2244 if ( FAILED( validateSTGM(openFlags) ))
2245 return STG_E_INVALIDFLAG;
2247 memset(This, 0, sizeof(StorageImpl));
2250 * Initialize the virtual function table.
2252 This->base.lpVtbl = &Storage32Impl_Vtbl;
2253 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2254 This->base.v_destructor = &StorageImpl_Destroy;
2255 This->base.openFlags = openFlags;
2258 * This is the top-level storage so initialize the ancestor pointer
2259 * to this.
2261 This->base.ancestorStorage = This;
2264 * Initialize the physical support of the storage.
2266 This->hFile = hFile;
2269 * Store copy of file path.
2271 if(pwcsName) {
2272 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2273 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2274 if (!This->pwcsName)
2275 return STG_E_INSUFFICIENTMEMORY;
2276 strcpyW(This->pwcsName, pwcsName);
2280 * Initialize the big block cache.
2282 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2283 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2284 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2285 pLkbyt,
2286 openFlags,
2287 This->bigBlockSize,
2288 fileBased);
2290 if (This->bigBlockFile == 0)
2291 return E_FAIL;
2293 if (fileCreate)
2295 ULARGE_INTEGER size;
2296 BYTE* bigBlockBuffer;
2299 * Initialize all header variables:
2300 * - The big block depot consists of one block and it is at block 0
2301 * - The properties start at block 1
2302 * - There is no small block depot
2304 memset( This->bigBlockDepotStart,
2305 BLOCK_UNUSED,
2306 sizeof(This->bigBlockDepotStart));
2308 This->bigBlockDepotCount = 1;
2309 This->bigBlockDepotStart[0] = 0;
2310 This->rootStartBlock = 1;
2311 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2312 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2313 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2314 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2315 This->extBigBlockDepotCount = 0;
2317 StorageImpl_SaveFileHeader(This);
2320 * Add one block for the big block depot and one block for the properties
2322 size.u.HighPart = 0;
2323 size.u.LowPart = This->bigBlockSize * 3;
2324 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2327 * Initialize the big block depot
2329 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2330 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2331 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2332 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2333 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2335 else
2338 * Load the header for the file.
2340 hr = StorageImpl_LoadFileHeader(This);
2342 if (FAILED(hr))
2344 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2346 return hr;
2351 * There is no block depot cached yet.
2353 This->indexBlockDepotCached = 0xFFFFFFFF;
2356 * Start searching for free blocks with block 0.
2358 This->prevFreeBlock = 0;
2361 * Create the block chain abstractions.
2363 if(!(This->rootBlockChain =
2364 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2365 return STG_E_READFAULT;
2367 if(!(This->smallBlockDepotChain =
2368 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2369 PROPERTY_NULL)))
2370 return STG_E_READFAULT;
2373 * Write the root property
2375 if (fileCreate)
2377 StgProperty rootProp;
2379 * Initialize the property chain
2381 memset(&rootProp, 0, sizeof(rootProp));
2382 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2383 sizeof(rootProp.name)/sizeof(WCHAR) );
2384 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2385 rootProp.propertyType = PROPTYPE_ROOT;
2386 rootProp.previousProperty = PROPERTY_NULL;
2387 rootProp.nextProperty = PROPERTY_NULL;
2388 rootProp.dirProperty = PROPERTY_NULL;
2389 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2390 rootProp.size.u.HighPart = 0;
2391 rootProp.size.u.LowPart = 0;
2393 StorageImpl_WriteProperty(This, 0, &rootProp);
2397 * Find the ID of the root in the property sets.
2399 currentPropertyIndex = 0;
2403 readSuccessful = StorageImpl_ReadProperty(
2404 This,
2405 currentPropertyIndex,
2406 &currentProperty);
2408 if (readSuccessful)
2410 if ( (currentProperty.sizeOfNameString != 0 ) &&
2411 (currentProperty.propertyType == PROPTYPE_ROOT) )
2413 This->base.rootPropertySetIndex = currentPropertyIndex;
2417 currentPropertyIndex++;
2419 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2421 if (!readSuccessful)
2423 /* TODO CLEANUP */
2424 return STG_E_READFAULT;
2428 * Create the block chain abstraction for the small block root chain.
2430 if(!(This->smallBlockRootChain =
2431 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2432 return STG_E_READFAULT;
2434 return hr;
2437 void StorageImpl_Destroy(StorageBaseImpl* iface)
2439 StorageImpl *This = (StorageImpl*) iface;
2440 TRACE("(%p)\n", This);
2442 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2444 BlockChainStream_Destroy(This->smallBlockRootChain);
2445 BlockChainStream_Destroy(This->rootBlockChain);
2446 BlockChainStream_Destroy(This->smallBlockDepotChain);
2448 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2449 HeapFree(GetProcessHeap(), 0, This);
2452 /******************************************************************************
2453 * Storage32Impl_GetNextFreeBigBlock
2455 * Returns the index of the next free big block.
2456 * If the big block depot is filled, this method will enlarge it.
2459 ULONG StorageImpl_GetNextFreeBigBlock(
2460 StorageImpl* This)
2462 ULONG depotBlockIndexPos;
2463 void *depotBuffer;
2464 ULONG depotBlockOffset;
2465 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2466 ULONG nextBlockIndex = BLOCK_SPECIAL;
2467 int depotIndex = 0;
2468 ULONG freeBlock = BLOCK_UNUSED;
2470 depotIndex = This->prevFreeBlock / blocksPerDepot;
2471 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2474 * Scan the entire big block depot until we find a block marked free
2476 while (nextBlockIndex != BLOCK_UNUSED)
2478 if (depotIndex < COUNT_BBDEPOTINHEADER)
2480 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2483 * Grow the primary depot.
2485 if (depotBlockIndexPos == BLOCK_UNUSED)
2487 depotBlockIndexPos = depotIndex*blocksPerDepot;
2490 * Add a block depot.
2492 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2493 This->bigBlockDepotCount++;
2494 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2497 * Flag it as a block depot.
2499 StorageImpl_SetNextBlockInChain(This,
2500 depotBlockIndexPos,
2501 BLOCK_SPECIAL);
2503 /* Save new header information.
2505 StorageImpl_SaveFileHeader(This);
2508 else
2510 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2512 if (depotBlockIndexPos == BLOCK_UNUSED)
2515 * Grow the extended depot.
2517 ULONG extIndex = BLOCK_UNUSED;
2518 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2519 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2521 if (extBlockOffset == 0)
2523 /* We need an extended block.
2525 extIndex = Storage32Impl_AddExtBlockDepot(This);
2526 This->extBigBlockDepotCount++;
2527 depotBlockIndexPos = extIndex + 1;
2529 else
2530 depotBlockIndexPos = depotIndex * blocksPerDepot;
2533 * Add a block depot and mark it in the extended block.
2535 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2536 This->bigBlockDepotCount++;
2537 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2539 /* Flag the block depot.
2541 StorageImpl_SetNextBlockInChain(This,
2542 depotBlockIndexPos,
2543 BLOCK_SPECIAL);
2545 /* If necessary, flag the extended depot block.
2547 if (extIndex != BLOCK_UNUSED)
2548 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2550 /* Save header information.
2552 StorageImpl_SaveFileHeader(This);
2556 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2558 if (depotBuffer != 0)
2560 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2561 ( nextBlockIndex != BLOCK_UNUSED))
2563 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2565 if (nextBlockIndex == BLOCK_UNUSED)
2567 freeBlock = (depotIndex * blocksPerDepot) +
2568 (depotBlockOffset/sizeof(ULONG));
2571 depotBlockOffset += sizeof(ULONG);
2574 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2577 depotIndex++;
2578 depotBlockOffset = 0;
2581 This->prevFreeBlock = freeBlock;
2583 return freeBlock;
2586 /******************************************************************************
2587 * Storage32Impl_AddBlockDepot
2589 * This will create a depot block, essentially it is a block initialized
2590 * to BLOCK_UNUSEDs.
2592 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2594 BYTE* blockBuffer;
2596 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2599 * Initialize blocks as free
2601 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2603 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2606 /******************************************************************************
2607 * Storage32Impl_GetExtDepotBlock
2609 * Returns the index of the block that corresponds to the specified depot
2610 * index. This method is only for depot indexes equal or greater than
2611 * COUNT_BBDEPOTINHEADER.
2613 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2615 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2616 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2617 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2618 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2619 ULONG blockIndex = BLOCK_UNUSED;
2620 ULONG extBlockIndex = This->extBigBlockDepotStart;
2622 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2624 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2625 return BLOCK_UNUSED;
2627 while (extBlockCount > 0)
2629 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2630 extBlockCount--;
2633 if (extBlockIndex != BLOCK_UNUSED)
2635 BYTE* depotBuffer;
2637 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2639 if (depotBuffer != 0)
2641 StorageUtl_ReadDWord(depotBuffer,
2642 extBlockOffset * sizeof(ULONG),
2643 &blockIndex);
2645 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2649 return blockIndex;
2652 /******************************************************************************
2653 * Storage32Impl_SetExtDepotBlock
2655 * Associates the specified block index to the specified depot index.
2656 * This method is only for depot indexes equal or greater than
2657 * COUNT_BBDEPOTINHEADER.
2659 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2660 ULONG depotIndex,
2661 ULONG blockIndex)
2663 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2664 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2665 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2666 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2667 ULONG extBlockIndex = This->extBigBlockDepotStart;
2669 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2671 while (extBlockCount > 0)
2673 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2674 extBlockCount--;
2677 if (extBlockIndex != BLOCK_UNUSED)
2679 BYTE* depotBuffer;
2681 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2683 if (depotBuffer != 0)
2685 StorageUtl_WriteDWord(depotBuffer,
2686 extBlockOffset * sizeof(ULONG),
2687 blockIndex);
2689 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2694 /******************************************************************************
2695 * Storage32Impl_AddExtBlockDepot
2697 * Creates an extended depot block.
2699 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2701 ULONG numExtBlocks = This->extBigBlockDepotCount;
2702 ULONG nextExtBlock = This->extBigBlockDepotStart;
2703 BYTE* depotBuffer = NULL;
2704 ULONG index = BLOCK_UNUSED;
2705 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2706 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2707 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2709 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2710 blocksPerDepotBlock;
2712 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2715 * The first extended block.
2717 This->extBigBlockDepotStart = index;
2719 else
2721 unsigned int i;
2723 * Follow the chain to the last one.
2725 for (i = 0; i < (numExtBlocks - 1); i++)
2727 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2731 * Add the new extended block to the chain.
2733 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2734 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2735 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2739 * Initialize this block.
2741 depotBuffer = StorageImpl_GetBigBlock(This, index);
2742 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2743 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2745 return index;
2748 /******************************************************************************
2749 * Storage32Impl_FreeBigBlock
2751 * This method will flag the specified block as free in the big block depot.
2753 void StorageImpl_FreeBigBlock(
2754 StorageImpl* This,
2755 ULONG blockIndex)
2757 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2759 if (blockIndex < This->prevFreeBlock)
2760 This->prevFreeBlock = blockIndex;
2763 /************************************************************************
2764 * Storage32Impl_GetNextBlockInChain
2766 * This method will retrieve the block index of the next big block in
2767 * in the chain.
2769 * Params: This - Pointer to the Storage object.
2770 * blockIndex - Index of the block to retrieve the chain
2771 * for.
2772 * nextBlockIndex - receives the return value.
2774 * Returns: This method returns the index of the next block in the chain.
2775 * It will return the constants:
2776 * BLOCK_SPECIAL - If the block given was not part of a
2777 * chain.
2778 * BLOCK_END_OF_CHAIN - If the block given was the last in
2779 * a chain.
2780 * BLOCK_UNUSED - If the block given was not past of a chain
2781 * and is available.
2782 * BLOCK_EXTBBDEPOT - This block is part of the extended
2783 * big block depot.
2785 * See Windows documentation for more details on IStorage methods.
2787 HRESULT StorageImpl_GetNextBlockInChain(
2788 StorageImpl* This,
2789 ULONG blockIndex,
2790 ULONG* nextBlockIndex)
2792 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2793 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2794 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2795 void* depotBuffer;
2796 ULONG depotBlockIndexPos;
2797 int index;
2799 *nextBlockIndex = BLOCK_SPECIAL;
2801 if(depotBlockCount >= This->bigBlockDepotCount)
2803 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2804 This->bigBlockDepotCount);
2805 return STG_E_READFAULT;
2809 * Cache the currently accessed depot block.
2811 if (depotBlockCount != This->indexBlockDepotCached)
2813 This->indexBlockDepotCached = depotBlockCount;
2815 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2817 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2819 else
2822 * We have to look in the extended depot.
2824 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2827 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2829 if (!depotBuffer)
2830 return STG_E_READFAULT;
2832 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2834 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2835 This->blockDepotCached[index] = *nextBlockIndex;
2837 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2840 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2842 return S_OK;
2845 /******************************************************************************
2846 * Storage32Impl_GetNextExtendedBlock
2848 * Given an extended block this method will return the next extended block.
2850 * NOTES:
2851 * The last ULONG of an extended block is the block index of the next
2852 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2853 * depot.
2855 * Return values:
2856 * - The index of the next extended block
2857 * - BLOCK_UNUSED: there is no next extended block.
2858 * - Any other return values denotes failure.
2860 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2862 ULONG nextBlockIndex = BLOCK_SPECIAL;
2863 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2864 void* depotBuffer;
2866 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2868 if (depotBuffer!=0)
2870 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2872 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2875 return nextBlockIndex;
2878 /******************************************************************************
2879 * Storage32Impl_SetNextBlockInChain
2881 * This method will write the index of the specified block's next block
2882 * in the big block depot.
2884 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2885 * do the following
2887 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2888 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2889 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2892 void StorageImpl_SetNextBlockInChain(
2893 StorageImpl* This,
2894 ULONG blockIndex,
2895 ULONG nextBlock)
2897 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2898 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2899 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2900 ULONG depotBlockIndexPos;
2901 void* depotBuffer;
2903 assert(depotBlockCount < This->bigBlockDepotCount);
2904 assert(blockIndex != nextBlock);
2906 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2908 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2910 else
2913 * We have to look in the extended depot.
2915 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2918 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2920 if (depotBuffer!=0)
2922 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2923 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2927 * Update the cached block depot, if necessary.
2929 if (depotBlockCount == This->indexBlockDepotCached)
2931 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2935 /******************************************************************************
2936 * Storage32Impl_LoadFileHeader
2938 * This method will read in the file header, i.e. big block index -1.
2940 HRESULT StorageImpl_LoadFileHeader(
2941 StorageImpl* This)
2943 HRESULT hr = STG_E_FILENOTFOUND;
2944 void* headerBigBlock = NULL;
2945 int index;
2948 * Get a pointer to the big block of data containing the header.
2950 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2953 * Extract the information from the header.
2955 if (headerBigBlock!=0)
2958 * Check for the "magic number" signature and return an error if it is not
2959 * found.
2961 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2963 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2964 return STG_E_OLDFORMAT;
2967 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2969 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2970 return STG_E_INVALIDHEADER;
2973 StorageUtl_ReadWord(
2974 headerBigBlock,
2975 OFFSET_BIGBLOCKSIZEBITS,
2976 &This->bigBlockSizeBits);
2978 StorageUtl_ReadWord(
2979 headerBigBlock,
2980 OFFSET_SMALLBLOCKSIZEBITS,
2981 &This->smallBlockSizeBits);
2983 StorageUtl_ReadDWord(
2984 headerBigBlock,
2985 OFFSET_BBDEPOTCOUNT,
2986 &This->bigBlockDepotCount);
2988 StorageUtl_ReadDWord(
2989 headerBigBlock,
2990 OFFSET_ROOTSTARTBLOCK,
2991 &This->rootStartBlock);
2993 StorageUtl_ReadDWord(
2994 headerBigBlock,
2995 OFFSET_SBDEPOTSTART,
2996 &This->smallBlockDepotStart);
2998 StorageUtl_ReadDWord(
2999 headerBigBlock,
3000 OFFSET_EXTBBDEPOTSTART,
3001 &This->extBigBlockDepotStart);
3003 StorageUtl_ReadDWord(
3004 headerBigBlock,
3005 OFFSET_EXTBBDEPOTCOUNT,
3006 &This->extBigBlockDepotCount);
3008 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3010 StorageUtl_ReadDWord(
3011 headerBigBlock,
3012 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3013 &(This->bigBlockDepotStart[index]));
3017 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3019 if ((1 << 2) == 4)
3021 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3022 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3024 else
3026 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3027 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3031 * Right now, the code is making some assumptions about the size of the
3032 * blocks, just make sure they are what we're expecting.
3034 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3035 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3037 WARN("Broken OLE storage file\n");
3038 hr = STG_E_INVALIDHEADER;
3040 else
3041 hr = S_OK;
3044 * Release the block.
3046 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3049 return hr;
3052 /******************************************************************************
3053 * Storage32Impl_SaveFileHeader
3055 * This method will save to the file the header, i.e. big block -1.
3057 void StorageImpl_SaveFileHeader(
3058 StorageImpl* This)
3060 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3061 int index;
3062 BOOL success;
3065 * Get a pointer to the big block of data containing the header.
3067 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3070 * If the block read failed, the file is probably new.
3072 if (!success)
3075 * Initialize for all unknown fields.
3077 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3080 * Initialize the magic number.
3082 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3085 * And a bunch of things we don't know what they mean
3087 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3088 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3089 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3090 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3094 * Write the information to the header.
3096 StorageUtl_WriteWord(
3097 headerBigBlock,
3098 OFFSET_BIGBLOCKSIZEBITS,
3099 This->bigBlockSizeBits);
3101 StorageUtl_WriteWord(
3102 headerBigBlock,
3103 OFFSET_SMALLBLOCKSIZEBITS,
3104 This->smallBlockSizeBits);
3106 StorageUtl_WriteDWord(
3107 headerBigBlock,
3108 OFFSET_BBDEPOTCOUNT,
3109 This->bigBlockDepotCount);
3111 StorageUtl_WriteDWord(
3112 headerBigBlock,
3113 OFFSET_ROOTSTARTBLOCK,
3114 This->rootStartBlock);
3116 StorageUtl_WriteDWord(
3117 headerBigBlock,
3118 OFFSET_SBDEPOTSTART,
3119 This->smallBlockDepotStart);
3121 StorageUtl_WriteDWord(
3122 headerBigBlock,
3123 OFFSET_SBDEPOTCOUNT,
3124 This->smallBlockDepotChain ?
3125 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3127 StorageUtl_WriteDWord(
3128 headerBigBlock,
3129 OFFSET_EXTBBDEPOTSTART,
3130 This->extBigBlockDepotStart);
3132 StorageUtl_WriteDWord(
3133 headerBigBlock,
3134 OFFSET_EXTBBDEPOTCOUNT,
3135 This->extBigBlockDepotCount);
3137 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3139 StorageUtl_WriteDWord(
3140 headerBigBlock,
3141 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3142 (This->bigBlockDepotStart[index]));
3146 * Write the big block back to the file.
3148 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3151 /******************************************************************************
3152 * Storage32Impl_ReadProperty
3154 * This method will read the specified property from the property chain.
3156 BOOL StorageImpl_ReadProperty(
3157 StorageImpl* This,
3158 ULONG index,
3159 StgProperty* buffer)
3161 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3162 ULARGE_INTEGER offsetInPropSet;
3163 BOOL readSuccessful;
3164 ULONG bytesRead;
3166 offsetInPropSet.u.HighPart = 0;
3167 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3169 readSuccessful = BlockChainStream_ReadAt(
3170 This->rootBlockChain,
3171 offsetInPropSet,
3172 PROPSET_BLOCK_SIZE,
3173 currentProperty,
3174 &bytesRead);
3176 if (readSuccessful)
3178 /* replace the name of root entry (often "Root Entry") by the file name */
3179 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3180 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3182 memset(buffer->name, 0, sizeof(buffer->name));
3183 memcpy(
3184 buffer->name,
3185 propName,
3186 PROPERTY_NAME_BUFFER_LEN );
3187 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3189 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3191 StorageUtl_ReadWord(
3192 currentProperty,
3193 OFFSET_PS_NAMELENGTH,
3194 &buffer->sizeOfNameString);
3196 StorageUtl_ReadDWord(
3197 currentProperty,
3198 OFFSET_PS_PREVIOUSPROP,
3199 &buffer->previousProperty);
3201 StorageUtl_ReadDWord(
3202 currentProperty,
3203 OFFSET_PS_NEXTPROP,
3204 &buffer->nextProperty);
3206 StorageUtl_ReadDWord(
3207 currentProperty,
3208 OFFSET_PS_DIRPROP,
3209 &buffer->dirProperty);
3211 StorageUtl_ReadGUID(
3212 currentProperty,
3213 OFFSET_PS_GUID,
3214 &buffer->propertyUniqueID);
3216 StorageUtl_ReadDWord(
3217 currentProperty,
3218 OFFSET_PS_TSS1,
3219 &buffer->timeStampS1);
3221 StorageUtl_ReadDWord(
3222 currentProperty,
3223 OFFSET_PS_TSD1,
3224 &buffer->timeStampD1);
3226 StorageUtl_ReadDWord(
3227 currentProperty,
3228 OFFSET_PS_TSS2,
3229 &buffer->timeStampS2);
3231 StorageUtl_ReadDWord(
3232 currentProperty,
3233 OFFSET_PS_TSD2,
3234 &buffer->timeStampD2);
3236 StorageUtl_ReadDWord(
3237 currentProperty,
3238 OFFSET_PS_STARTBLOCK,
3239 &buffer->startingBlock);
3241 StorageUtl_ReadDWord(
3242 currentProperty,
3243 OFFSET_PS_SIZE,
3244 &buffer->size.u.LowPart);
3246 buffer->size.u.HighPart = 0;
3249 return readSuccessful;
3252 /*********************************************************************
3253 * Write the specified property into the property chain
3255 BOOL StorageImpl_WriteProperty(
3256 StorageImpl* This,
3257 ULONG index,
3258 StgProperty* buffer)
3260 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3261 ULARGE_INTEGER offsetInPropSet;
3262 BOOL writeSuccessful;
3263 ULONG bytesWritten;
3265 offsetInPropSet.u.HighPart = 0;
3266 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3268 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3270 memcpy(
3271 currentProperty + OFFSET_PS_NAME,
3272 buffer->name,
3273 PROPERTY_NAME_BUFFER_LEN );
3275 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3277 StorageUtl_WriteWord(
3278 currentProperty,
3279 OFFSET_PS_NAMELENGTH,
3280 buffer->sizeOfNameString);
3282 StorageUtl_WriteDWord(
3283 currentProperty,
3284 OFFSET_PS_PREVIOUSPROP,
3285 buffer->previousProperty);
3287 StorageUtl_WriteDWord(
3288 currentProperty,
3289 OFFSET_PS_NEXTPROP,
3290 buffer->nextProperty);
3292 StorageUtl_WriteDWord(
3293 currentProperty,
3294 OFFSET_PS_DIRPROP,
3295 buffer->dirProperty);
3297 StorageUtl_WriteGUID(
3298 currentProperty,
3299 OFFSET_PS_GUID,
3300 &buffer->propertyUniqueID);
3302 StorageUtl_WriteDWord(
3303 currentProperty,
3304 OFFSET_PS_TSS1,
3305 buffer->timeStampS1);
3307 StorageUtl_WriteDWord(
3308 currentProperty,
3309 OFFSET_PS_TSD1,
3310 buffer->timeStampD1);
3312 StorageUtl_WriteDWord(
3313 currentProperty,
3314 OFFSET_PS_TSS2,
3315 buffer->timeStampS2);
3317 StorageUtl_WriteDWord(
3318 currentProperty,
3319 OFFSET_PS_TSD2,
3320 buffer->timeStampD2);
3322 StorageUtl_WriteDWord(
3323 currentProperty,
3324 OFFSET_PS_STARTBLOCK,
3325 buffer->startingBlock);
3327 StorageUtl_WriteDWord(
3328 currentProperty,
3329 OFFSET_PS_SIZE,
3330 buffer->size.u.LowPart);
3332 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3333 offsetInPropSet,
3334 PROPSET_BLOCK_SIZE,
3335 currentProperty,
3336 &bytesWritten);
3337 return writeSuccessful;
3340 BOOL StorageImpl_ReadBigBlock(
3341 StorageImpl* This,
3342 ULONG blockIndex,
3343 void* buffer)
3345 void* bigBlockBuffer;
3347 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3349 if (bigBlockBuffer!=0)
3351 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3353 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3355 return TRUE;
3358 return FALSE;
3361 BOOL StorageImpl_WriteBigBlock(
3362 StorageImpl* This,
3363 ULONG blockIndex,
3364 void* buffer)
3366 void* bigBlockBuffer;
3368 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3370 if (bigBlockBuffer!=0)
3372 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3374 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3376 return TRUE;
3379 return FALSE;
3382 void* StorageImpl_GetROBigBlock(
3383 StorageImpl* This,
3384 ULONG blockIndex)
3386 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3389 void* StorageImpl_GetBigBlock(
3390 StorageImpl* This,
3391 ULONG blockIndex)
3393 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3396 void StorageImpl_ReleaseBigBlock(
3397 StorageImpl* This,
3398 void* pBigBlock)
3400 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3403 /******************************************************************************
3404 * Storage32Impl_SmallBlocksToBigBlocks
3406 * This method will convert a small block chain to a big block chain.
3407 * The small block chain will be destroyed.
3409 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3410 StorageImpl* This,
3411 SmallBlockChainStream** ppsbChain)
3413 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3414 ULARGE_INTEGER size, offset;
3415 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3416 ULONG propertyIndex;
3417 BOOL successRead, successWrite;
3418 StgProperty chainProperty;
3419 BYTE *buffer;
3420 BlockChainStream *bbTempChain = NULL;
3421 BlockChainStream *bigBlockChain = NULL;
3424 * Create a temporary big block chain that doesn't have
3425 * an associated property. This temporary chain will be
3426 * used to copy data from small blocks to big blocks.
3428 bbTempChain = BlockChainStream_Construct(This,
3429 &bbHeadOfChain,
3430 PROPERTY_NULL);
3431 if(!bbTempChain) return NULL;
3433 * Grow the big block chain.
3435 size = SmallBlockChainStream_GetSize(*ppsbChain);
3436 BlockChainStream_SetSize(bbTempChain, size);
3439 * Copy the contents of the small block chain to the big block chain
3440 * by small block size increments.
3442 offset.u.LowPart = 0;
3443 offset.u.HighPart = 0;
3444 cbTotalRead = 0;
3445 cbTotalWritten = 0;
3447 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3450 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3451 offset,
3452 DEF_SMALL_BLOCK_SIZE,
3453 buffer,
3454 &cbRead);
3455 cbTotalRead += cbRead;
3457 successWrite = BlockChainStream_WriteAt(bbTempChain,
3458 offset,
3459 cbRead,
3460 buffer,
3461 &cbWritten);
3462 cbTotalWritten += cbWritten;
3464 offset.u.LowPart += This->smallBlockSize;
3466 } while (successRead && successWrite);
3467 HeapFree(GetProcessHeap(),0,buffer);
3469 assert(cbTotalRead == cbTotalWritten);
3472 * Destroy the small block chain.
3474 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3475 size.u.HighPart = 0;
3476 size.u.LowPart = 0;
3477 SmallBlockChainStream_SetSize(*ppsbChain, size);
3478 SmallBlockChainStream_Destroy(*ppsbChain);
3479 *ppsbChain = 0;
3482 * Change the property information. This chain is now a big block chain
3483 * and it doesn't reside in the small blocks chain anymore.
3485 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3487 chainProperty.startingBlock = bbHeadOfChain;
3489 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3492 * Destroy the temporary propertyless big block chain.
3493 * Create a new big block chain associated with this property.
3495 BlockChainStream_Destroy(bbTempChain);
3496 bigBlockChain = BlockChainStream_Construct(This,
3497 NULL,
3498 propertyIndex);
3500 return bigBlockChain;
3503 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3505 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3507 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3508 HeapFree(GetProcessHeap(), 0, This);
3511 /******************************************************************************
3513 ** Storage32InternalImpl_Commit
3515 ** The non-root storages cannot be opened in transacted mode thus this function
3516 ** does nothing.
3518 HRESULT WINAPI StorageInternalImpl_Commit(
3519 IStorage* iface,
3520 DWORD grfCommitFlags) /* [in] */
3522 return S_OK;
3525 /******************************************************************************
3527 ** Storage32InternalImpl_Revert
3529 ** The non-root storages cannot be opened in transacted mode thus this function
3530 ** does nothing.
3532 HRESULT WINAPI StorageInternalImpl_Revert(
3533 IStorage* iface)
3535 return S_OK;
3538 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3540 IStorage_Release((IStorage*)This->parentStorage);
3541 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3542 HeapFree(GetProcessHeap(), 0, This);
3545 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3546 IEnumSTATSTG* iface,
3547 REFIID riid,
3548 void** ppvObject)
3550 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3553 * Perform a sanity check on the parameters.
3555 if (ppvObject==0)
3556 return E_INVALIDARG;
3559 * Initialize the return parameter.
3561 *ppvObject = 0;
3564 * Compare the riid with the interface IDs implemented by this object.
3566 if (IsEqualGUID(&IID_IUnknown, riid) ||
3567 IsEqualGUID(&IID_IStorage, riid))
3569 *ppvObject = (IEnumSTATSTG*)This;
3570 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3571 return S_OK;
3574 return E_NOINTERFACE;
3577 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3578 IEnumSTATSTG* iface)
3580 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3581 return InterlockedIncrement(&This->ref);
3584 ULONG WINAPI IEnumSTATSTGImpl_Release(
3585 IEnumSTATSTG* iface)
3587 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3589 ULONG newRef;
3591 newRef = InterlockedDecrement(&This->ref);
3594 * If the reference count goes down to 0, perform suicide.
3596 if (newRef==0)
3598 IEnumSTATSTGImpl_Destroy(This);
3601 return newRef;
3604 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3605 IEnumSTATSTG* iface,
3606 ULONG celt,
3607 STATSTG* rgelt,
3608 ULONG* pceltFetched)
3610 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3612 StgProperty currentProperty;
3613 STATSTG* currentReturnStruct = rgelt;
3614 ULONG objectFetched = 0;
3615 ULONG currentSearchNode;
3618 * Perform a sanity check on the parameters.
3620 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3621 return E_INVALIDARG;
3624 * To avoid the special case, get another pointer to a ULONG value if
3625 * the caller didn't supply one.
3627 if (pceltFetched==0)
3628 pceltFetched = &objectFetched;
3631 * Start the iteration, we will iterate until we hit the end of the
3632 * linked list or until we hit the number of items to iterate through
3634 *pceltFetched = 0;
3637 * Start with the node at the top of the stack.
3639 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3641 while ( ( *pceltFetched < celt) &&
3642 ( currentSearchNode!=PROPERTY_NULL) )
3645 * Remove the top node from the stack
3647 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3650 * Read the property from the storage.
3652 StorageImpl_ReadProperty(This->parentStorage,
3653 currentSearchNode,
3654 &currentProperty);
3657 * Copy the information to the return buffer.
3659 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3660 &currentProperty,
3661 STATFLAG_DEFAULT);
3664 * Step to the next item in the iteration
3666 (*pceltFetched)++;
3667 currentReturnStruct++;
3670 * Push the next search node in the search stack.
3672 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3675 * continue the iteration.
3677 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3680 if (*pceltFetched == celt)
3681 return S_OK;
3683 return S_FALSE;
3687 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3688 IEnumSTATSTG* iface,
3689 ULONG celt)
3691 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3693 StgProperty currentProperty;
3694 ULONG objectFetched = 0;
3695 ULONG currentSearchNode;
3698 * Start with the node at the top of the stack.
3700 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3702 while ( (objectFetched < celt) &&
3703 (currentSearchNode!=PROPERTY_NULL) )
3706 * Remove the top node from the stack
3708 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3711 * Read the property from the storage.
3713 StorageImpl_ReadProperty(This->parentStorage,
3714 currentSearchNode,
3715 &currentProperty);
3718 * Step to the next item in the iteration
3720 objectFetched++;
3723 * Push the next search node in the search stack.
3725 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3728 * continue the iteration.
3730 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3733 if (objectFetched == celt)
3734 return S_OK;
3736 return S_FALSE;
3739 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3740 IEnumSTATSTG* iface)
3742 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3744 StgProperty rootProperty;
3745 BOOL readSuccessful;
3748 * Re-initialize the search stack to an empty stack
3750 This->stackSize = 0;
3753 * Read the root property from the storage.
3755 readSuccessful = StorageImpl_ReadProperty(
3756 This->parentStorage,
3757 This->firstPropertyNode,
3758 &rootProperty);
3760 if (readSuccessful)
3762 assert(rootProperty.sizeOfNameString!=0);
3765 * Push the search node in the search stack.
3767 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3770 return S_OK;
3773 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3774 IEnumSTATSTG* iface,
3775 IEnumSTATSTG** ppenum)
3777 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3779 IEnumSTATSTGImpl* newClone;
3782 * Perform a sanity check on the parameters.
3784 if (ppenum==0)
3785 return E_INVALIDARG;
3787 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3788 This->firstPropertyNode);
3792 * The new clone enumeration must point to the same current node as
3793 * the ole one.
3795 newClone->stackSize = This->stackSize ;
3796 newClone->stackMaxSize = This->stackMaxSize ;
3797 newClone->stackToVisit =
3798 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3800 memcpy(
3801 newClone->stackToVisit,
3802 This->stackToVisit,
3803 sizeof(ULONG) * newClone->stackSize);
3805 *ppenum = (IEnumSTATSTG*)newClone;
3808 * Don't forget to nail down a reference to the clone before
3809 * returning it.
3811 IEnumSTATSTGImpl_AddRef(*ppenum);
3813 return S_OK;
3816 INT IEnumSTATSTGImpl_FindParentProperty(
3817 IEnumSTATSTGImpl *This,
3818 ULONG childProperty,
3819 StgProperty *currentProperty,
3820 ULONG *thisNodeId)
3822 ULONG currentSearchNode;
3823 ULONG foundNode;
3826 * To avoid the special case, get another pointer to a ULONG value if
3827 * the caller didn't supply one.
3829 if (thisNodeId==0)
3830 thisNodeId = &foundNode;
3833 * Start with the node at the top of the stack.
3835 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3838 while (currentSearchNode!=PROPERTY_NULL)
3841 * Store the current node in the returned parameters
3843 *thisNodeId = currentSearchNode;
3846 * Remove the top node from the stack
3848 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3851 * Read the property from the storage.
3853 StorageImpl_ReadProperty(
3854 This->parentStorage,
3855 currentSearchNode,
3856 currentProperty);
3858 if (currentProperty->previousProperty == childProperty)
3859 return PROPERTY_RELATION_PREVIOUS;
3861 else if (currentProperty->nextProperty == childProperty)
3862 return PROPERTY_RELATION_NEXT;
3864 else if (currentProperty->dirProperty == childProperty)
3865 return PROPERTY_RELATION_DIR;
3868 * Push the next search node in the search stack.
3870 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3873 * continue the iteration.
3875 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3878 return PROPERTY_NULL;
3881 ULONG IEnumSTATSTGImpl_FindProperty(
3882 IEnumSTATSTGImpl* This,
3883 const OLECHAR* lpszPropName,
3884 StgProperty* currentProperty)
3886 ULONG currentSearchNode;
3889 * Start with the node at the top of the stack.
3891 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3893 while (currentSearchNode!=PROPERTY_NULL)
3896 * Remove the top node from the stack
3898 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3901 * Read the property from the storage.
3903 StorageImpl_ReadProperty(This->parentStorage,
3904 currentSearchNode,
3905 currentProperty);
3907 if ( propertyNameCmp(
3908 (const OLECHAR*)currentProperty->name,
3909 (const OLECHAR*)lpszPropName) == 0)
3910 return currentSearchNode;
3913 * Push the next search node in the search stack.
3915 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3918 * continue the iteration.
3920 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3923 return PROPERTY_NULL;
3926 void IEnumSTATSTGImpl_PushSearchNode(
3927 IEnumSTATSTGImpl* This,
3928 ULONG nodeToPush)
3930 StgProperty rootProperty;
3931 BOOL readSuccessful;
3934 * First, make sure we're not trying to push an unexisting node.
3936 if (nodeToPush==PROPERTY_NULL)
3937 return;
3940 * First push the node to the stack
3942 if (This->stackSize == This->stackMaxSize)
3944 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3946 This->stackToVisit = HeapReAlloc(
3947 GetProcessHeap(),
3949 This->stackToVisit,
3950 sizeof(ULONG) * This->stackMaxSize);
3953 This->stackToVisit[This->stackSize] = nodeToPush;
3954 This->stackSize++;
3957 * Read the root property from the storage.
3959 readSuccessful = StorageImpl_ReadProperty(
3960 This->parentStorage,
3961 nodeToPush,
3962 &rootProperty);
3964 if (readSuccessful)
3966 assert(rootProperty.sizeOfNameString!=0);
3969 * Push the previous search node in the search stack.
3971 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3975 ULONG IEnumSTATSTGImpl_PopSearchNode(
3976 IEnumSTATSTGImpl* This,
3977 BOOL remove)
3979 ULONG topNode;
3981 if (This->stackSize == 0)
3982 return PROPERTY_NULL;
3984 topNode = This->stackToVisit[This->stackSize-1];
3986 if (remove)
3987 This->stackSize--;
3989 return topNode;
3993 * Virtual function table for the IEnumSTATSTGImpl class.
3995 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
3997 IEnumSTATSTGImpl_QueryInterface,
3998 IEnumSTATSTGImpl_AddRef,
3999 IEnumSTATSTGImpl_Release,
4000 IEnumSTATSTGImpl_Next,
4001 IEnumSTATSTGImpl_Skip,
4002 IEnumSTATSTGImpl_Reset,
4003 IEnumSTATSTGImpl_Clone
4006 /******************************************************************************
4007 ** IEnumSTATSTGImpl implementation
4010 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4011 StorageImpl* parentStorage,
4012 ULONG firstPropertyNode)
4014 IEnumSTATSTGImpl* newEnumeration;
4016 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4018 if (newEnumeration!=0)
4021 * Set-up the virtual function table and reference count.
4023 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4024 newEnumeration->ref = 0;
4027 * We want to nail-down the reference to the storage in case the
4028 * enumeration out-lives the storage in the client application.
4030 newEnumeration->parentStorage = parentStorage;
4031 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4033 newEnumeration->firstPropertyNode = firstPropertyNode;
4036 * Initialize the search stack
4038 newEnumeration->stackSize = 0;
4039 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4040 newEnumeration->stackToVisit =
4041 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4044 * Make sure the current node of the iterator is the first one.
4046 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4049 return newEnumeration;
4053 * Virtual function table for the Storage32InternalImpl class.
4055 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4057 StorageBaseImpl_QueryInterface,
4058 StorageBaseImpl_AddRef,
4059 StorageBaseImpl_Release,
4060 StorageBaseImpl_CreateStream,
4061 StorageBaseImpl_OpenStream,
4062 StorageImpl_CreateStorage,
4063 StorageBaseImpl_OpenStorage,
4064 StorageImpl_CopyTo,
4065 StorageImpl_MoveElementTo,
4066 StorageInternalImpl_Commit,
4067 StorageInternalImpl_Revert,
4068 StorageBaseImpl_EnumElements,
4069 StorageImpl_DestroyElement,
4070 StorageBaseImpl_RenameElement,
4071 StorageImpl_SetElementTimes,
4072 StorageBaseImpl_SetClass,
4073 StorageImpl_SetStateBits,
4074 StorageBaseImpl_Stat
4077 /******************************************************************************
4078 ** Storage32InternalImpl implementation
4081 StorageInternalImpl* StorageInternalImpl_Construct(
4082 StorageImpl* ancestorStorage,
4083 DWORD openFlags,
4084 ULONG rootPropertyIndex)
4086 StorageInternalImpl* newStorage;
4089 * Allocate space for the new storage object
4091 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4093 if (newStorage!=0)
4095 memset(newStorage, 0, sizeof(StorageInternalImpl));
4098 * Initialize the virtual function table.
4100 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4101 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4102 newStorage->base.openFlags = openFlags;
4105 * Keep the ancestor storage pointer and nail a reference to it.
4107 newStorage->base.ancestorStorage = ancestorStorage;
4108 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4111 * Keep the index of the root property set for this storage,
4113 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4115 return newStorage;
4118 return 0;
4121 /******************************************************************************
4122 ** StorageUtl implementation
4125 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4127 WORD tmp;
4129 memcpy(&tmp, buffer+offset, sizeof(WORD));
4130 *value = le16toh(tmp);
4133 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4135 value = htole16(value);
4136 memcpy(buffer+offset, &value, sizeof(WORD));
4139 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4141 DWORD tmp;
4143 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4144 *value = le32toh(tmp);
4147 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4149 value = htole32(value);
4150 memcpy(buffer+offset, &value, sizeof(DWORD));
4153 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4154 ULARGE_INTEGER* value)
4156 #ifdef WORDS_BIGENDIAN
4157 ULARGE_INTEGER tmp;
4159 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4160 value->u.LowPart = htole32(tmp.u.HighPart);
4161 value->u.HighPart = htole32(tmp.u.LowPart);
4162 #else
4163 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4164 #endif
4167 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4168 const ULARGE_INTEGER *value)
4170 #ifdef WORDS_BIGENDIAN
4171 ULARGE_INTEGER tmp;
4173 tmp.u.LowPart = htole32(value->u.HighPart);
4174 tmp.u.HighPart = htole32(value->u.LowPart);
4175 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4176 #else
4177 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4178 #endif
4181 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4183 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4184 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4185 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4187 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4190 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4192 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4193 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4194 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4196 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4199 void StorageUtl_CopyPropertyToSTATSTG(
4200 STATSTG* destination,
4201 StgProperty* source,
4202 int statFlags)
4205 * The copy of the string occurs only when the flag is not set
4207 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4208 (source->name == NULL) ||
4209 (source->name[0] == 0) )
4211 destination->pwcsName = 0;
4213 else
4215 destination->pwcsName =
4216 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4218 strcpyW((LPWSTR)destination->pwcsName, source->name);
4221 switch (source->propertyType)
4223 case PROPTYPE_STORAGE:
4224 case PROPTYPE_ROOT:
4225 destination->type = STGTY_STORAGE;
4226 break;
4227 case PROPTYPE_STREAM:
4228 destination->type = STGTY_STREAM;
4229 break;
4230 default:
4231 destination->type = STGTY_STREAM;
4232 break;
4235 destination->cbSize = source->size;
4237 currentReturnStruct->mtime = {0}; TODO
4238 currentReturnStruct->ctime = {0};
4239 currentReturnStruct->atime = {0};
4241 destination->grfMode = 0;
4242 destination->grfLocksSupported = 0;
4243 destination->clsid = source->propertyUniqueID;
4244 destination->grfStateBits = 0;
4245 destination->reserved = 0;
4248 /******************************************************************************
4249 ** BlockChainStream implementation
4252 BlockChainStream* BlockChainStream_Construct(
4253 StorageImpl* parentStorage,
4254 ULONG* headOfStreamPlaceHolder,
4255 ULONG propertyIndex)
4257 BlockChainStream* newStream;
4258 ULONG blockIndex;
4260 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4262 newStream->parentStorage = parentStorage;
4263 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4264 newStream->ownerPropertyIndex = propertyIndex;
4265 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4266 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4267 newStream->numBlocks = 0;
4269 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4271 while (blockIndex != BLOCK_END_OF_CHAIN)
4273 newStream->numBlocks++;
4274 newStream->tailIndex = blockIndex;
4276 if(FAILED(StorageImpl_GetNextBlockInChain(
4277 parentStorage,
4278 blockIndex,
4279 &blockIndex)))
4281 HeapFree(GetProcessHeap(), 0, newStream);
4282 return NULL;
4286 return newStream;
4289 void BlockChainStream_Destroy(BlockChainStream* This)
4291 HeapFree(GetProcessHeap(), 0, This);
4294 /******************************************************************************
4295 * BlockChainStream_GetHeadOfChain
4297 * Returns the head of this stream chain.
4298 * Some special chains don't have properties, their heads are kept in
4299 * This->headOfStreamPlaceHolder.
4302 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4304 StgProperty chainProperty;
4305 BOOL readSuccessful;
4307 if (This->headOfStreamPlaceHolder != 0)
4308 return *(This->headOfStreamPlaceHolder);
4310 if (This->ownerPropertyIndex != PROPERTY_NULL)
4312 readSuccessful = StorageImpl_ReadProperty(
4313 This->parentStorage,
4314 This->ownerPropertyIndex,
4315 &chainProperty);
4317 if (readSuccessful)
4319 return chainProperty.startingBlock;
4323 return BLOCK_END_OF_CHAIN;
4326 /******************************************************************************
4327 * BlockChainStream_GetCount
4329 * Returns the number of blocks that comprises this chain.
4330 * This is not the size of the stream as the last block may not be full!
4333 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4335 ULONG blockIndex;
4336 ULONG count = 0;
4338 blockIndex = BlockChainStream_GetHeadOfChain(This);
4340 while (blockIndex != BLOCK_END_OF_CHAIN)
4342 count++;
4344 if(FAILED(StorageImpl_GetNextBlockInChain(
4345 This->parentStorage,
4346 blockIndex,
4347 &blockIndex)))
4348 return 0;
4351 return count;
4354 /******************************************************************************
4355 * BlockChainStream_ReadAt
4357 * Reads a specified number of bytes from this chain at the specified offset.
4358 * bytesRead may be NULL.
4359 * Failure will be returned if the specified number of bytes has not been read.
4361 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4362 ULARGE_INTEGER offset,
4363 ULONG size,
4364 void* buffer,
4365 ULONG* bytesRead)
4367 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4368 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4369 ULONG bytesToReadInBuffer;
4370 ULONG blockIndex;
4371 BYTE* bufferWalker;
4372 BYTE* bigBlockBuffer;
4375 * Find the first block in the stream that contains part of the buffer.
4377 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4378 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4379 (blockNoInSequence < This->lastBlockNoInSequence) )
4381 blockIndex = BlockChainStream_GetHeadOfChain(This);
4382 This->lastBlockNoInSequence = blockNoInSequence;
4384 else
4386 ULONG temp = blockNoInSequence;
4388 blockIndex = This->lastBlockNoInSequenceIndex;
4389 blockNoInSequence -= This->lastBlockNoInSequence;
4390 This->lastBlockNoInSequence = temp;
4393 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4395 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4396 return FALSE;
4397 blockNoInSequence--;
4400 This->lastBlockNoInSequenceIndex = blockIndex;
4403 * Start reading the buffer.
4405 *bytesRead = 0;
4406 bufferWalker = buffer;
4408 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4411 * Calculate how many bytes we can copy from this big block.
4413 bytesToReadInBuffer =
4414 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4417 * Copy those bytes to the buffer
4419 bigBlockBuffer =
4420 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4422 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4424 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4427 * Step to the next big block.
4429 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4430 return FALSE;
4432 bufferWalker += bytesToReadInBuffer;
4433 size -= bytesToReadInBuffer;
4434 *bytesRead += bytesToReadInBuffer;
4435 offsetInBlock = 0; /* There is no offset on the next block */
4439 return (size == 0);
4442 /******************************************************************************
4443 * BlockChainStream_WriteAt
4445 * Writes the specified number of bytes to this chain at the specified offset.
4446 * bytesWritten may be NULL.
4447 * Will fail if not all specified number of bytes have been written.
4449 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4450 ULARGE_INTEGER offset,
4451 ULONG size,
4452 const void* buffer,
4453 ULONG* bytesWritten)
4455 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4456 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4457 ULONG bytesToWrite;
4458 ULONG blockIndex;
4459 const BYTE* bufferWalker;
4460 BYTE* bigBlockBuffer;
4463 * Find the first block in the stream that contains part of the buffer.
4465 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4466 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4467 (blockNoInSequence < This->lastBlockNoInSequence) )
4469 blockIndex = BlockChainStream_GetHeadOfChain(This);
4470 This->lastBlockNoInSequence = blockNoInSequence;
4472 else
4474 ULONG temp = blockNoInSequence;
4476 blockIndex = This->lastBlockNoInSequenceIndex;
4477 blockNoInSequence -= This->lastBlockNoInSequence;
4478 This->lastBlockNoInSequence = temp;
4481 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4483 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4484 &blockIndex)))
4485 return FALSE;
4486 blockNoInSequence--;
4489 This->lastBlockNoInSequenceIndex = blockIndex;
4492 * Here, I'm casting away the constness on the buffer variable
4493 * This is OK since we don't intend to modify that buffer.
4495 *bytesWritten = 0;
4496 bufferWalker = (const BYTE*)buffer;
4498 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4501 * Calculate how many bytes we can copy from this big block.
4503 bytesToWrite =
4504 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4507 * Copy those bytes to the buffer
4509 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4511 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4513 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4516 * Step to the next big block.
4518 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4519 &blockIndex)))
4520 return FALSE;
4521 bufferWalker += bytesToWrite;
4522 size -= bytesToWrite;
4523 *bytesWritten += bytesToWrite;
4524 offsetInBlock = 0; /* There is no offset on the next block */
4527 return (size == 0);
4530 /******************************************************************************
4531 * BlockChainStream_Shrink
4533 * Shrinks this chain in the big block depot.
4535 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4536 ULARGE_INTEGER newSize)
4538 ULONG blockIndex, extraBlock;
4539 ULONG numBlocks;
4540 ULONG count = 1;
4543 * Reset the last accessed block cache.
4545 This->lastBlockNoInSequence = 0xFFFFFFFF;
4546 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4549 * Figure out how many blocks are needed to contain the new size
4551 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4553 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4554 numBlocks++;
4556 blockIndex = BlockChainStream_GetHeadOfChain(This);
4559 * Go to the new end of chain
4561 while (count < numBlocks)
4563 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4564 &blockIndex)))
4565 return FALSE;
4566 count++;
4569 /* Get the next block before marking the new end */
4570 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4571 &extraBlock)))
4572 return FALSE;
4574 /* Mark the new end of chain */
4575 StorageImpl_SetNextBlockInChain(
4576 This->parentStorage,
4577 blockIndex,
4578 BLOCK_END_OF_CHAIN);
4580 This->tailIndex = blockIndex;
4581 This->numBlocks = numBlocks;
4584 * Mark the extra blocks as free
4586 while (extraBlock != BLOCK_END_OF_CHAIN)
4588 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4589 &blockIndex)))
4590 return FALSE;
4591 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4592 extraBlock = blockIndex;
4595 return TRUE;
4598 /******************************************************************************
4599 * BlockChainStream_Enlarge
4601 * Grows this chain in the big block depot.
4603 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4604 ULARGE_INTEGER newSize)
4606 ULONG blockIndex, currentBlock;
4607 ULONG newNumBlocks;
4608 ULONG oldNumBlocks = 0;
4610 blockIndex = BlockChainStream_GetHeadOfChain(This);
4613 * Empty chain. Create the head.
4615 if (blockIndex == BLOCK_END_OF_CHAIN)
4617 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4618 StorageImpl_SetNextBlockInChain(This->parentStorage,
4619 blockIndex,
4620 BLOCK_END_OF_CHAIN);
4622 if (This->headOfStreamPlaceHolder != 0)
4624 *(This->headOfStreamPlaceHolder) = blockIndex;
4626 else
4628 StgProperty chainProp;
4629 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4631 StorageImpl_ReadProperty(
4632 This->parentStorage,
4633 This->ownerPropertyIndex,
4634 &chainProp);
4636 chainProp.startingBlock = blockIndex;
4638 StorageImpl_WriteProperty(
4639 This->parentStorage,
4640 This->ownerPropertyIndex,
4641 &chainProp);
4644 This->tailIndex = blockIndex;
4645 This->numBlocks = 1;
4649 * Figure out how many blocks are needed to contain this stream
4651 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4653 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4654 newNumBlocks++;
4657 * Go to the current end of chain
4659 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4661 currentBlock = blockIndex;
4663 while (blockIndex != BLOCK_END_OF_CHAIN)
4665 This->numBlocks++;
4666 currentBlock = blockIndex;
4668 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4669 &blockIndex)))
4670 return FALSE;
4673 This->tailIndex = currentBlock;
4676 currentBlock = This->tailIndex;
4677 oldNumBlocks = This->numBlocks;
4680 * Add new blocks to the chain
4682 if (oldNumBlocks < newNumBlocks)
4684 while (oldNumBlocks < newNumBlocks)
4686 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4688 StorageImpl_SetNextBlockInChain(
4689 This->parentStorage,
4690 currentBlock,
4691 blockIndex);
4693 StorageImpl_SetNextBlockInChain(
4694 This->parentStorage,
4695 blockIndex,
4696 BLOCK_END_OF_CHAIN);
4698 currentBlock = blockIndex;
4699 oldNumBlocks++;
4702 This->tailIndex = blockIndex;
4703 This->numBlocks = newNumBlocks;
4706 return TRUE;
4709 /******************************************************************************
4710 * BlockChainStream_SetSize
4712 * Sets the size of this stream. The big block depot will be updated.
4713 * The file will grow if we grow the chain.
4715 * TODO: Free the actual blocks in the file when we shrink the chain.
4716 * Currently, the blocks are still in the file. So the file size
4717 * doesn't shrink even if we shrink streams.
4719 BOOL BlockChainStream_SetSize(
4720 BlockChainStream* This,
4721 ULARGE_INTEGER newSize)
4723 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4725 if (newSize.u.LowPart == size.u.LowPart)
4726 return TRUE;
4728 if (newSize.u.LowPart < size.u.LowPart)
4730 BlockChainStream_Shrink(This, newSize);
4732 else
4734 ULARGE_INTEGER fileSize =
4735 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4737 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4740 * Make sure the file stays a multiple of blocksize
4742 if ((diff % This->parentStorage->bigBlockSize) != 0)
4743 diff += (This->parentStorage->bigBlockSize -
4744 (diff % This->parentStorage->bigBlockSize) );
4746 fileSize.u.LowPart += diff;
4747 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4749 BlockChainStream_Enlarge(This, newSize);
4752 return TRUE;
4755 /******************************************************************************
4756 * BlockChainStream_GetSize
4758 * Returns the size of this chain.
4759 * Will return the block count if this chain doesn't have a property.
4761 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4763 StgProperty chainProperty;
4765 if(This->headOfStreamPlaceHolder == NULL)
4768 * This chain is a data stream read the property and return
4769 * the appropriate size
4771 StorageImpl_ReadProperty(
4772 This->parentStorage,
4773 This->ownerPropertyIndex,
4774 &chainProperty);
4776 return chainProperty.size;
4778 else
4781 * this chain is a chain that does not have a property, figure out the
4782 * size by making the product number of used blocks times the
4783 * size of them
4785 ULARGE_INTEGER result;
4786 result.u.HighPart = 0;
4788 result.u.LowPart =
4789 BlockChainStream_GetCount(This) *
4790 This->parentStorage->bigBlockSize;
4792 return result;
4796 /******************************************************************************
4797 ** SmallBlockChainStream implementation
4800 SmallBlockChainStream* SmallBlockChainStream_Construct(
4801 StorageImpl* parentStorage,
4802 ULONG propertyIndex)
4804 SmallBlockChainStream* newStream;
4806 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4808 newStream->parentStorage = parentStorage;
4809 newStream->ownerPropertyIndex = propertyIndex;
4811 return newStream;
4814 void SmallBlockChainStream_Destroy(
4815 SmallBlockChainStream* This)
4817 HeapFree(GetProcessHeap(), 0, This);
4820 /******************************************************************************
4821 * SmallBlockChainStream_GetHeadOfChain
4823 * Returns the head of this chain of small blocks.
4825 ULONG SmallBlockChainStream_GetHeadOfChain(
4826 SmallBlockChainStream* This)
4828 StgProperty chainProperty;
4829 BOOL readSuccessful;
4831 if (This->ownerPropertyIndex)
4833 readSuccessful = StorageImpl_ReadProperty(
4834 This->parentStorage,
4835 This->ownerPropertyIndex,
4836 &chainProperty);
4838 if (readSuccessful)
4840 return chainProperty.startingBlock;
4845 return BLOCK_END_OF_CHAIN;
4848 /******************************************************************************
4849 * SmallBlockChainStream_GetNextBlockInChain
4851 * Returns the index of the next small block in this chain.
4853 * Return Values:
4854 * - BLOCK_END_OF_CHAIN: end of this chain
4855 * - BLOCK_UNUSED: small block 'blockIndex' is free
4857 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4858 SmallBlockChainStream* This,
4859 ULONG blockIndex,
4860 ULONG* nextBlockInChain)
4862 ULARGE_INTEGER offsetOfBlockInDepot;
4863 DWORD buffer;
4864 ULONG bytesRead;
4865 BOOL success;
4867 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4869 offsetOfBlockInDepot.u.HighPart = 0;
4870 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4873 * Read those bytes in the buffer from the small block file.
4875 success = BlockChainStream_ReadAt(
4876 This->parentStorage->smallBlockDepotChain,
4877 offsetOfBlockInDepot,
4878 sizeof(DWORD),
4879 &buffer,
4880 &bytesRead);
4882 if (success)
4884 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4885 return S_OK;
4888 return STG_E_READFAULT;
4891 /******************************************************************************
4892 * SmallBlockChainStream_SetNextBlockInChain
4894 * Writes the index of the next block of the specified block in the small
4895 * block depot.
4896 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4897 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4899 void SmallBlockChainStream_SetNextBlockInChain(
4900 SmallBlockChainStream* This,
4901 ULONG blockIndex,
4902 ULONG nextBlock)
4904 ULARGE_INTEGER offsetOfBlockInDepot;
4905 DWORD buffer;
4906 ULONG bytesWritten;
4908 offsetOfBlockInDepot.u.HighPart = 0;
4909 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4911 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4914 * Read those bytes in the buffer from the small block file.
4916 BlockChainStream_WriteAt(
4917 This->parentStorage->smallBlockDepotChain,
4918 offsetOfBlockInDepot,
4919 sizeof(DWORD),
4920 &buffer,
4921 &bytesWritten);
4924 /******************************************************************************
4925 * SmallBlockChainStream_FreeBlock
4927 * Flag small block 'blockIndex' as free in the small block depot.
4929 void SmallBlockChainStream_FreeBlock(
4930 SmallBlockChainStream* This,
4931 ULONG blockIndex)
4933 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4936 /******************************************************************************
4937 * SmallBlockChainStream_GetNextFreeBlock
4939 * Returns the index of a free small block. The small block depot will be
4940 * enlarged if necessary. The small block chain will also be enlarged if
4941 * necessary.
4943 ULONG SmallBlockChainStream_GetNextFreeBlock(
4944 SmallBlockChainStream* This)
4946 ULARGE_INTEGER offsetOfBlockInDepot;
4947 DWORD buffer;
4948 ULONG bytesRead;
4949 ULONG blockIndex = 0;
4950 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4951 BOOL success = TRUE;
4952 ULONG smallBlocksPerBigBlock;
4954 offsetOfBlockInDepot.u.HighPart = 0;
4957 * Scan the small block depot for a free block
4959 while (nextBlockIndex != BLOCK_UNUSED)
4961 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4963 success = BlockChainStream_ReadAt(
4964 This->parentStorage->smallBlockDepotChain,
4965 offsetOfBlockInDepot,
4966 sizeof(DWORD),
4967 &buffer,
4968 &bytesRead);
4971 * If we run out of space for the small block depot, enlarge it
4973 if (success)
4975 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
4977 if (nextBlockIndex != BLOCK_UNUSED)
4978 blockIndex++;
4980 else
4982 ULONG count =
4983 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4985 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4986 ULONG nextBlock, newsbdIndex;
4987 BYTE* smallBlockDepot;
4989 nextBlock = sbdIndex;
4990 while (nextBlock != BLOCK_END_OF_CHAIN)
4992 sbdIndex = nextBlock;
4993 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4996 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4997 if (sbdIndex != BLOCK_END_OF_CHAIN)
4998 StorageImpl_SetNextBlockInChain(
4999 This->parentStorage,
5000 sbdIndex,
5001 newsbdIndex);
5003 StorageImpl_SetNextBlockInChain(
5004 This->parentStorage,
5005 newsbdIndex,
5006 BLOCK_END_OF_CHAIN);
5009 * Initialize all the small blocks to free
5011 smallBlockDepot =
5012 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5014 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5015 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5017 if (count == 0)
5020 * We have just created the small block depot.
5022 StgProperty rootProp;
5023 ULONG sbStartIndex;
5026 * Save it in the header
5028 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5029 StorageImpl_SaveFileHeader(This->parentStorage);
5032 * And allocate the first big block that will contain small blocks
5034 sbStartIndex =
5035 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5037 StorageImpl_SetNextBlockInChain(
5038 This->parentStorage,
5039 sbStartIndex,
5040 BLOCK_END_OF_CHAIN);
5042 StorageImpl_ReadProperty(
5043 This->parentStorage,
5044 This->parentStorage->base.rootPropertySetIndex,
5045 &rootProp);
5047 rootProp.startingBlock = sbStartIndex;
5048 rootProp.size.u.HighPart = 0;
5049 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5051 StorageImpl_WriteProperty(
5052 This->parentStorage,
5053 This->parentStorage->base.rootPropertySetIndex,
5054 &rootProp);
5059 smallBlocksPerBigBlock =
5060 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5063 * Verify if we have to allocate big blocks to contain small blocks
5065 if (blockIndex % smallBlocksPerBigBlock == 0)
5067 StgProperty rootProp;
5068 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5070 StorageImpl_ReadProperty(
5071 This->parentStorage,
5072 This->parentStorage->base.rootPropertySetIndex,
5073 &rootProp);
5075 if (rootProp.size.u.LowPart <
5076 (blocksRequired * This->parentStorage->bigBlockSize))
5078 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5080 BlockChainStream_SetSize(
5081 This->parentStorage->smallBlockRootChain,
5082 rootProp.size);
5084 StorageImpl_WriteProperty(
5085 This->parentStorage,
5086 This->parentStorage->base.rootPropertySetIndex,
5087 &rootProp);
5091 return blockIndex;
5094 /******************************************************************************
5095 * SmallBlockChainStream_ReadAt
5097 * Reads a specified number of bytes from this chain at the specified offset.
5098 * bytesRead may be NULL.
5099 * Failure will be returned if the specified number of bytes has not been read.
5101 BOOL SmallBlockChainStream_ReadAt(
5102 SmallBlockChainStream* This,
5103 ULARGE_INTEGER offset,
5104 ULONG size,
5105 void* buffer,
5106 ULONG* bytesRead)
5108 ULARGE_INTEGER offsetInBigBlockFile;
5109 ULONG blockNoInSequence =
5110 offset.u.LowPart / This->parentStorage->smallBlockSize;
5112 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5113 ULONG bytesToReadInBuffer;
5114 ULONG blockIndex;
5115 ULONG bytesReadFromBigBlockFile;
5116 BYTE* bufferWalker;
5119 * This should never happen on a small block file.
5121 assert(offset.u.HighPart==0);
5124 * Find the first block in the stream that contains part of the buffer.
5126 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5128 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5130 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5131 &blockIndex)))
5132 return FALSE;
5133 blockNoInSequence--;
5137 * Start reading the buffer.
5139 *bytesRead = 0;
5140 bufferWalker = buffer;
5142 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5145 * Calculate how many bytes we can copy from this small block.
5147 bytesToReadInBuffer =
5148 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5151 * Calculate the offset of the small block in the small block file.
5153 offsetInBigBlockFile.u.HighPart = 0;
5154 offsetInBigBlockFile.u.LowPart =
5155 blockIndex * This->parentStorage->smallBlockSize;
5157 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5160 * Read those bytes in the buffer from the small block file.
5162 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5163 offsetInBigBlockFile,
5164 bytesToReadInBuffer,
5165 bufferWalker,
5166 &bytesReadFromBigBlockFile);
5168 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5171 * Step to the next big block.
5173 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5174 return FALSE;
5175 bufferWalker += bytesToReadInBuffer;
5176 size -= bytesToReadInBuffer;
5177 *bytesRead += bytesToReadInBuffer;
5178 offsetInBlock = 0; /* There is no offset on the next block */
5181 return (size == 0);
5184 /******************************************************************************
5185 * SmallBlockChainStream_WriteAt
5187 * Writes the specified number of bytes to this chain at the specified offset.
5188 * bytesWritten may be NULL.
5189 * Will fail if not all specified number of bytes have been written.
5191 BOOL SmallBlockChainStream_WriteAt(
5192 SmallBlockChainStream* This,
5193 ULARGE_INTEGER offset,
5194 ULONG size,
5195 const void* buffer,
5196 ULONG* bytesWritten)
5198 ULARGE_INTEGER offsetInBigBlockFile;
5199 ULONG blockNoInSequence =
5200 offset.u.LowPart / This->parentStorage->smallBlockSize;
5202 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5203 ULONG bytesToWriteInBuffer;
5204 ULONG blockIndex;
5205 ULONG bytesWrittenFromBigBlockFile;
5206 const BYTE* bufferWalker;
5209 * This should never happen on a small block file.
5211 assert(offset.u.HighPart==0);
5214 * Find the first block in the stream that contains part of the buffer.
5216 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5218 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5220 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5221 return FALSE;
5222 blockNoInSequence--;
5226 * Start writing the buffer.
5228 * Here, I'm casting away the constness on the buffer variable
5229 * This is OK since we don't intend to modify that buffer.
5231 *bytesWritten = 0;
5232 bufferWalker = (const BYTE*)buffer;
5233 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5236 * Calculate how many bytes we can copy to this small block.
5238 bytesToWriteInBuffer =
5239 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5242 * Calculate the offset of the small block in the small block file.
5244 offsetInBigBlockFile.u.HighPart = 0;
5245 offsetInBigBlockFile.u.LowPart =
5246 blockIndex * This->parentStorage->smallBlockSize;
5248 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5251 * Write those bytes in the buffer to the small block file.
5253 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5254 offsetInBigBlockFile,
5255 bytesToWriteInBuffer,
5256 bufferWalker,
5257 &bytesWrittenFromBigBlockFile);
5259 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5262 * Step to the next big block.
5264 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5265 &blockIndex)))
5266 return FALSE;
5267 bufferWalker += bytesToWriteInBuffer;
5268 size -= bytesToWriteInBuffer;
5269 *bytesWritten += bytesToWriteInBuffer;
5270 offsetInBlock = 0; /* There is no offset on the next block */
5273 return (size == 0);
5276 /******************************************************************************
5277 * SmallBlockChainStream_Shrink
5279 * Shrinks this chain in the small block depot.
5281 BOOL SmallBlockChainStream_Shrink(
5282 SmallBlockChainStream* This,
5283 ULARGE_INTEGER newSize)
5285 ULONG blockIndex, extraBlock;
5286 ULONG numBlocks;
5287 ULONG count = 0;
5289 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5291 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5292 numBlocks++;
5294 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5297 * Go to the new end of chain
5299 while (count < numBlocks)
5301 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5302 &blockIndex)))
5303 return FALSE;
5304 count++;
5308 * If the count is 0, we have a special case, the head of the chain was
5309 * just freed.
5311 if (count == 0)
5313 StgProperty chainProp;
5315 StorageImpl_ReadProperty(This->parentStorage,
5316 This->ownerPropertyIndex,
5317 &chainProp);
5319 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5321 StorageImpl_WriteProperty(This->parentStorage,
5322 This->ownerPropertyIndex,
5323 &chainProp);
5326 * We start freeing the chain at the head block.
5328 extraBlock = blockIndex;
5330 else
5332 /* Get the next block before marking the new end */
5333 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5334 &extraBlock)))
5335 return FALSE;
5337 /* Mark the new end of chain */
5338 SmallBlockChainStream_SetNextBlockInChain(
5339 This,
5340 blockIndex,
5341 BLOCK_END_OF_CHAIN);
5345 * Mark the extra blocks as free
5347 while (extraBlock != BLOCK_END_OF_CHAIN)
5349 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5350 &blockIndex)))
5351 return FALSE;
5352 SmallBlockChainStream_FreeBlock(This, extraBlock);
5353 extraBlock = blockIndex;
5356 return TRUE;
5359 /******************************************************************************
5360 * SmallBlockChainStream_Enlarge
5362 * Grows this chain in the small block depot.
5364 BOOL SmallBlockChainStream_Enlarge(
5365 SmallBlockChainStream* This,
5366 ULARGE_INTEGER newSize)
5368 ULONG blockIndex, currentBlock;
5369 ULONG newNumBlocks;
5370 ULONG oldNumBlocks = 0;
5372 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5375 * Empty chain
5377 if (blockIndex == BLOCK_END_OF_CHAIN)
5380 StgProperty chainProp;
5382 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5383 &chainProp);
5385 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5387 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5388 &chainProp);
5390 blockIndex = chainProp.startingBlock;
5391 SmallBlockChainStream_SetNextBlockInChain(
5392 This,
5393 blockIndex,
5394 BLOCK_END_OF_CHAIN);
5397 currentBlock = blockIndex;
5400 * Figure out how many blocks are needed to contain this stream
5402 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5404 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5405 newNumBlocks++;
5408 * Go to the current end of chain
5410 while (blockIndex != BLOCK_END_OF_CHAIN)
5412 oldNumBlocks++;
5413 currentBlock = blockIndex;
5414 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5415 return FALSE;
5419 * Add new blocks to the chain
5421 while (oldNumBlocks < newNumBlocks)
5423 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5424 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5426 SmallBlockChainStream_SetNextBlockInChain(
5427 This,
5428 blockIndex,
5429 BLOCK_END_OF_CHAIN);
5431 currentBlock = blockIndex;
5432 oldNumBlocks++;
5435 return TRUE;
5438 /******************************************************************************
5439 * SmallBlockChainStream_GetCount
5441 * Returns the number of blocks that comprises this chain.
5442 * This is not the size of this chain as the last block may not be full!
5444 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5446 ULONG blockIndex;
5447 ULONG count = 0;
5449 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5451 while (blockIndex != BLOCK_END_OF_CHAIN)
5453 count++;
5455 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5456 return 0;
5459 return count;
5462 /******************************************************************************
5463 * SmallBlockChainStream_SetSize
5465 * Sets the size of this stream.
5466 * The file will grow if we grow the chain.
5468 * TODO: Free the actual blocks in the file when we shrink the chain.
5469 * Currently, the blocks are still in the file. So the file size
5470 * doesn't shrink even if we shrink streams.
5472 BOOL SmallBlockChainStream_SetSize(
5473 SmallBlockChainStream* This,
5474 ULARGE_INTEGER newSize)
5476 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5478 if (newSize.u.LowPart == size.u.LowPart)
5479 return TRUE;
5481 if (newSize.u.LowPart < size.u.LowPart)
5483 SmallBlockChainStream_Shrink(This, newSize);
5485 else
5487 SmallBlockChainStream_Enlarge(This, newSize);
5490 return TRUE;
5493 /******************************************************************************
5494 * SmallBlockChainStream_GetSize
5496 * Returns the size of this chain.
5498 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5500 StgProperty chainProperty;
5502 StorageImpl_ReadProperty(
5503 This->parentStorage,
5504 This->ownerPropertyIndex,
5505 &chainProperty);
5507 return chainProperty.size;
5510 /******************************************************************************
5511 * StgCreateDocfile [OLE32.@]
5513 HRESULT WINAPI StgCreateDocfile(
5514 LPCOLESTR pwcsName,
5515 DWORD grfMode,
5516 DWORD reserved,
5517 IStorage **ppstgOpen)
5519 StorageImpl* newStorage = 0;
5520 HANDLE hFile = INVALID_HANDLE_VALUE;
5521 HRESULT hr = STG_E_INVALIDFLAG;
5522 DWORD shareMode;
5523 DWORD accessMode;
5524 DWORD creationMode;
5525 DWORD fileAttributes;
5526 WCHAR tempFileName[MAX_PATH];
5528 TRACE("(%s, %lx, %ld, %p)\n",
5529 debugstr_w(pwcsName), grfMode,
5530 reserved, ppstgOpen);
5533 * Validate the parameters
5535 if (ppstgOpen == 0)
5536 return STG_E_INVALIDPOINTER;
5537 if (reserved != 0)
5538 return STG_E_INVALIDPARAMETER;
5541 * Validate the STGM flags
5543 if ( FAILED( validateSTGM(grfMode) ))
5544 goto end;
5546 /* StgCreateDocFile always opens for write */
5547 switch(STGM_ACCESS_MODE(grfMode))
5549 case STGM_WRITE:
5550 case STGM_READWRITE:
5551 break;
5552 default:
5553 goto end;
5556 /* can't share write */
5557 switch(STGM_SHARE_MODE(grfMode))
5559 case STGM_SHARE_EXCLUSIVE:
5560 case STGM_SHARE_DENY_WRITE:
5561 break;
5562 default:
5563 goto end;
5566 /* shared reading requires transacted mode */
5567 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5568 !(grfMode&STGM_TRANSACTED) )
5569 goto end;
5572 * Generate a unique name.
5574 if (pwcsName == 0)
5576 WCHAR tempPath[MAX_PATH];
5577 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5579 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
5580 goto end;
5582 memset(tempPath, 0, sizeof(tempPath));
5583 memset(tempFileName, 0, sizeof(tempFileName));
5585 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5586 tempPath[0] = '.';
5588 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5589 pwcsName = tempFileName;
5590 else
5592 hr = STG_E_INSUFFICIENTMEMORY;
5593 goto end;
5596 creationMode = TRUNCATE_EXISTING;
5598 else
5600 creationMode = GetCreationModeFromSTGM(grfMode);
5604 * Interpret the STGM value grfMode
5606 shareMode = GetShareModeFromSTGM(grfMode);
5607 accessMode = GetAccessModeFromSTGM(grfMode);
5609 if (grfMode & STGM_DELETEONRELEASE)
5610 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5611 else
5612 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5614 if (grfMode & STGM_TRANSACTED)
5615 FIXME("Transacted mode not implemented.\n");
5618 * Initialize the "out" parameter.
5620 *ppstgOpen = 0;
5622 hFile = CreateFileW(pwcsName,
5623 accessMode,
5624 shareMode,
5625 NULL,
5626 creationMode,
5627 fileAttributes,
5630 if (hFile == INVALID_HANDLE_VALUE)
5632 if(GetLastError() == ERROR_FILE_EXISTS)
5633 hr = STG_E_FILEALREADYEXISTS;
5634 else
5635 hr = E_FAIL;
5636 goto end;
5640 * Allocate and initialize the new IStorage32object.
5642 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5644 if (newStorage == 0)
5646 hr = STG_E_INSUFFICIENTMEMORY;
5647 goto end;
5650 hr = StorageImpl_Construct(
5651 newStorage,
5652 hFile,
5653 pwcsName,
5654 NULL,
5655 grfMode,
5656 TRUE,
5657 TRUE);
5659 if (FAILED(hr))
5661 HeapFree(GetProcessHeap(), 0, newStorage);
5662 goto end;
5666 * Get an "out" pointer for the caller.
5668 hr = StorageBaseImpl_QueryInterface(
5669 (IStorage*)newStorage,
5670 (REFIID)&IID_IStorage,
5671 (void**)ppstgOpen);
5672 end:
5673 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5675 return hr;
5678 /******************************************************************************
5679 * StgCreateStorageEx [OLE32.@]
5681 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5683 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5684 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5686 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5688 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5689 return STG_E_INVALIDPARAMETER;
5692 if (stgfmt != STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5694 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5695 return STG_E_INVALIDPARAMETER;
5698 if (stgfmt == STGFMT_FILE)
5700 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5701 return STG_E_INVALIDPARAMETER;
5704 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5706 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5707 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5710 ERR("Invalid stgfmt argument\n");
5711 return STG_E_INVALIDPARAMETER;
5714 /******************************************************************************
5715 * StgCreatePropSetStg [OLE32.@]
5717 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5718 IPropertySetStorage **ppPropSetStg)
5720 HRESULT hr;
5722 TRACE("(%p, 0x%lx, %p): stub\n", pstg, reserved, ppPropSetStg);
5723 if (reserved)
5724 hr = STG_E_INVALIDPARAMETER;
5725 else
5726 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5727 (void**)ppPropSetStg);
5728 return hr;
5731 /******************************************************************************
5732 * StgOpenStorageEx [OLE32.@]
5734 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5736 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5737 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5739 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5741 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5742 return STG_E_INVALIDPARAMETER;
5745 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5747 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5748 return STG_E_INVALIDPARAMETER;
5751 if (stgfmt == STGFMT_FILE)
5753 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5754 return STG_E_INVALIDPARAMETER;
5757 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE || stgfmt == STGFMT_ANY)
5759 if (stgfmt == STGFMT_ANY)
5760 WARN("STGFMT_ANY assuming storage\n");
5761 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5762 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5765 ERR("Invalid stgfmt argument\n");
5766 return STG_E_INVALIDPARAMETER;
5770 /******************************************************************************
5771 * StgOpenStorage [OLE32.@]
5773 HRESULT WINAPI StgOpenStorage(
5774 const OLECHAR *pwcsName,
5775 IStorage *pstgPriority,
5776 DWORD grfMode,
5777 SNB snbExclude,
5778 DWORD reserved,
5779 IStorage **ppstgOpen)
5781 StorageImpl* newStorage = 0;
5782 HRESULT hr = S_OK;
5783 HANDLE hFile = 0;
5784 DWORD shareMode;
5785 DWORD accessMode;
5786 WCHAR fullname[MAX_PATH];
5787 DWORD length;
5789 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5790 debugstr_w(pwcsName), pstgPriority, grfMode,
5791 snbExclude, reserved, ppstgOpen);
5794 * Perform sanity checks
5796 if (pwcsName == 0)
5798 hr = STG_E_INVALIDNAME;
5799 goto end;
5802 if (ppstgOpen == 0)
5804 hr = STG_E_INVALIDPOINTER;
5805 goto end;
5808 if (reserved)
5810 hr = STG_E_INVALIDPARAMETER;
5811 goto end;
5815 * Validate the sharing mode
5817 if (!(grfMode & STGM_TRANSACTED))
5818 switch(STGM_SHARE_MODE(grfMode))
5820 case STGM_SHARE_EXCLUSIVE:
5821 case STGM_SHARE_DENY_WRITE:
5822 break;
5823 default:
5824 hr = STG_E_INVALIDFLAG;
5825 goto end;
5829 * Validate the STGM flags
5831 if ( FAILED( validateSTGM(grfMode) ) ||
5832 (grfMode&STGM_CREATE))
5834 hr = STG_E_INVALIDFLAG;
5835 goto end;
5838 /* shared reading requires transacted mode */
5839 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5840 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5841 !(grfMode&STGM_TRANSACTED) )
5843 hr = STG_E_INVALIDFLAG;
5844 goto end;
5848 * Interpret the STGM value grfMode
5850 shareMode = GetShareModeFromSTGM(grfMode);
5851 accessMode = GetAccessModeFromSTGM(grfMode);
5854 * Initialize the "out" parameter.
5856 *ppstgOpen = 0;
5858 hFile = CreateFileW( pwcsName,
5859 accessMode,
5860 shareMode,
5861 NULL,
5862 OPEN_EXISTING,
5863 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5866 if (hFile==INVALID_HANDLE_VALUE)
5868 DWORD last_error = GetLastError();
5870 hr = E_FAIL;
5872 switch (last_error)
5874 case ERROR_FILE_NOT_FOUND:
5875 hr = STG_E_FILENOTFOUND;
5876 break;
5878 case ERROR_PATH_NOT_FOUND:
5879 hr = STG_E_PATHNOTFOUND;
5880 break;
5882 case ERROR_ACCESS_DENIED:
5883 case ERROR_WRITE_PROTECT:
5884 hr = STG_E_ACCESSDENIED;
5885 break;
5887 case ERROR_SHARING_VIOLATION:
5888 hr = STG_E_SHAREVIOLATION;
5889 break;
5891 default:
5892 hr = E_FAIL;
5895 goto end;
5899 * Refuse to open the file if it's too small to be a structured storage file
5900 * FIXME: verify the file when reading instead of here
5902 length = GetFileSize(hFile, NULL);
5903 if (length < 0x100)
5905 CloseHandle(hFile);
5906 hr = STG_E_FILEALREADYEXISTS;
5907 goto end;
5911 * Allocate and initialize the new IStorage32object.
5913 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5915 if (newStorage == 0)
5917 hr = STG_E_INSUFFICIENTMEMORY;
5918 goto end;
5921 /* if the file's length was zero, initialize the storage */
5922 hr = StorageImpl_Construct(
5923 newStorage,
5924 hFile,
5925 pwcsName,
5926 NULL,
5927 grfMode,
5928 TRUE,
5929 FALSE );
5931 if (FAILED(hr))
5933 HeapFree(GetProcessHeap(), 0, newStorage);
5935 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5937 if(hr == STG_E_INVALIDHEADER)
5938 hr = STG_E_FILEALREADYEXISTS;
5939 goto end;
5942 /* prepare the file name string given in lieu of the root property name */
5943 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5944 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5945 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5948 * Get an "out" pointer for the caller.
5950 hr = StorageBaseImpl_QueryInterface(
5951 (IStorage*)newStorage,
5952 (REFIID)&IID_IStorage,
5953 (void**)ppstgOpen);
5955 end:
5956 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5957 return hr;
5960 /******************************************************************************
5961 * StgCreateDocfileOnILockBytes [OLE32.@]
5963 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5964 ILockBytes *plkbyt,
5965 DWORD grfMode,
5966 DWORD reserved,
5967 IStorage** ppstgOpen)
5969 StorageImpl* newStorage = 0;
5970 HRESULT hr = S_OK;
5973 * Validate the parameters
5975 if ((ppstgOpen == 0) || (plkbyt == 0))
5976 return STG_E_INVALIDPOINTER;
5979 * Allocate and initialize the new IStorage object.
5981 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5983 if (newStorage == 0)
5984 return STG_E_INSUFFICIENTMEMORY;
5986 hr = StorageImpl_Construct(
5987 newStorage,
5990 plkbyt,
5991 grfMode,
5992 FALSE,
5993 TRUE);
5995 if (FAILED(hr))
5997 HeapFree(GetProcessHeap(), 0, newStorage);
5998 return hr;
6002 * Get an "out" pointer for the caller.
6004 hr = StorageBaseImpl_QueryInterface(
6005 (IStorage*)newStorage,
6006 (REFIID)&IID_IStorage,
6007 (void**)ppstgOpen);
6009 return hr;
6012 /******************************************************************************
6013 * StgOpenStorageOnILockBytes [OLE32.@]
6015 HRESULT WINAPI StgOpenStorageOnILockBytes(
6016 ILockBytes *plkbyt,
6017 IStorage *pstgPriority,
6018 DWORD grfMode,
6019 SNB snbExclude,
6020 DWORD reserved,
6021 IStorage **ppstgOpen)
6023 StorageImpl* newStorage = 0;
6024 HRESULT hr = S_OK;
6027 * Perform a sanity check
6029 if ((plkbyt == 0) || (ppstgOpen == 0))
6030 return STG_E_INVALIDPOINTER;
6033 * Validate the STGM flags
6035 if ( FAILED( validateSTGM(grfMode) ))
6036 return STG_E_INVALIDFLAG;
6039 * Initialize the "out" parameter.
6041 *ppstgOpen = 0;
6044 * Allocate and initialize the new IStorage object.
6046 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6048 if (newStorage == 0)
6049 return STG_E_INSUFFICIENTMEMORY;
6051 hr = StorageImpl_Construct(
6052 newStorage,
6055 plkbyt,
6056 grfMode,
6057 FALSE,
6058 FALSE);
6060 if (FAILED(hr))
6062 HeapFree(GetProcessHeap(), 0, newStorage);
6063 return hr;
6067 * Get an "out" pointer for the caller.
6069 hr = StorageBaseImpl_QueryInterface(
6070 (IStorage*)newStorage,
6071 (REFIID)&IID_IStorage,
6072 (void**)ppstgOpen);
6074 return hr;
6077 /******************************************************************************
6078 * StgSetTimes [ole32.@]
6079 * StgSetTimes [OLE32.@]
6083 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6084 FILETIME const *patime, FILETIME const *pmtime)
6086 IStorage *stg = NULL;
6087 HRESULT r;
6089 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6091 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6092 0, 0, &stg);
6093 if( SUCCEEDED(r) )
6095 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6096 IStorage_Release(stg);
6099 return r;
6102 /******************************************************************************
6103 * StgIsStorageILockBytes [OLE32.@]
6105 * Determines if the ILockBytes contains a storage object.
6107 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6109 BYTE sig[8];
6110 ULARGE_INTEGER offset;
6112 offset.u.HighPart = 0;
6113 offset.u.LowPart = 0;
6115 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6117 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6118 return S_OK;
6120 return S_FALSE;
6123 /******************************************************************************
6124 * WriteClassStg [OLE32.@]
6126 * This method will store the specified CLSID in the specified storage object
6128 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6130 HRESULT hRes;
6132 if(!pStg)
6133 return E_INVALIDARG;
6135 hRes = IStorage_SetClass(pStg, rclsid);
6137 return hRes;
6140 /***********************************************************************
6141 * ReadClassStg (OLE32.@)
6143 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
6145 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6147 STATSTG pstatstg;
6148 HRESULT hRes;
6150 TRACE("(%p, %p)\n", pstg, pclsid);
6152 if(!pstg || !pclsid)
6153 return E_INVALIDARG;
6156 * read a STATSTG structure (contains the clsid) from the storage
6158 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6160 if(SUCCEEDED(hRes))
6161 *pclsid=pstatstg.clsid;
6163 return hRes;
6166 /***********************************************************************
6167 * OleLoadFromStream (OLE32.@)
6169 * This function loads an object from stream
6171 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6173 CLSID clsid;
6174 HRESULT res;
6175 LPPERSISTSTREAM xstm;
6177 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6179 res=ReadClassStm(pStm,&clsid);
6180 if (!SUCCEEDED(res))
6181 return res;
6182 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6183 if (!SUCCEEDED(res))
6184 return res;
6185 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6186 if (!SUCCEEDED(res)) {
6187 IUnknown_Release((IUnknown*)*ppvObj);
6188 return res;
6190 res=IPersistStream_Load(xstm,pStm);
6191 IPersistStream_Release(xstm);
6192 /* FIXME: all refcounts ok at this point? I think they should be:
6193 * pStm : unchanged
6194 * ppvObj : 1
6195 * xstm : 0 (released)
6197 return res;
6200 /***********************************************************************
6201 * OleSaveToStream (OLE32.@)
6203 * This function saves an object with the IPersistStream interface on it
6204 * to the specified stream.
6206 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6209 CLSID clsid;
6210 HRESULT res;
6212 TRACE("(%p,%p)\n",pPStm,pStm);
6214 res=IPersistStream_GetClassID(pPStm,&clsid);
6216 if (SUCCEEDED(res)){
6218 res=WriteClassStm(pStm,&clsid);
6220 if (SUCCEEDED(res))
6222 res=IPersistStream_Save(pPStm,pStm,TRUE);
6225 TRACE("Finished Save\n");
6226 return res;
6229 /****************************************************************************
6230 * This method validate a STGM parameter that can contain the values below
6232 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6233 * The stgm values contained in 0xffff0000 are bitmasks.
6235 * STGM_DIRECT 0x00000000
6236 * STGM_TRANSACTED 0x00010000
6237 * STGM_SIMPLE 0x08000000
6239 * STGM_READ 0x00000000
6240 * STGM_WRITE 0x00000001
6241 * STGM_READWRITE 0x00000002
6243 * STGM_SHARE_DENY_NONE 0x00000040
6244 * STGM_SHARE_DENY_READ 0x00000030
6245 * STGM_SHARE_DENY_WRITE 0x00000020
6246 * STGM_SHARE_EXCLUSIVE 0x00000010
6248 * STGM_PRIORITY 0x00040000
6249 * STGM_DELETEONRELEASE 0x04000000
6251 * STGM_CREATE 0x00001000
6252 * STGM_CONVERT 0x00020000
6253 * STGM_FAILIFTHERE 0x00000000
6255 * STGM_NOSCRATCH 0x00100000
6256 * STGM_NOSNAPSHOT 0x00200000
6258 static HRESULT validateSTGM(DWORD stgm)
6260 DWORD access = STGM_ACCESS_MODE(stgm);
6261 DWORD share = STGM_SHARE_MODE(stgm);
6262 DWORD create = STGM_CREATE_MODE(stgm);
6264 if (stgm&~STGM_KNOWN_FLAGS)
6266 ERR("unknown flags %08lx\n", stgm);
6267 return E_FAIL;
6270 switch (access)
6272 case STGM_READ:
6273 case STGM_WRITE:
6274 case STGM_READWRITE:
6275 break;
6276 default:
6277 return E_FAIL;
6280 switch (share)
6282 case STGM_SHARE_DENY_NONE:
6283 case STGM_SHARE_DENY_READ:
6284 case STGM_SHARE_DENY_WRITE:
6285 case STGM_SHARE_EXCLUSIVE:
6286 break;
6287 default:
6288 return E_FAIL;
6291 switch (create)
6293 case STGM_CREATE:
6294 case STGM_FAILIFTHERE:
6295 break;
6296 default:
6297 return E_FAIL;
6301 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6303 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6304 return E_FAIL;
6307 * STGM_CREATE | STGM_CONVERT
6308 * if both are false, STGM_FAILIFTHERE is set to TRUE
6310 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6311 return E_FAIL;
6314 * STGM_NOSCRATCH requires STGM_TRANSACTED
6316 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6317 return E_FAIL;
6320 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6321 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6323 if ( (stgm & STGM_NOSNAPSHOT) &&
6324 (!(stgm & STGM_TRANSACTED) ||
6325 share == STGM_SHARE_EXCLUSIVE ||
6326 share == STGM_SHARE_DENY_WRITE) )
6327 return E_FAIL;
6329 return S_OK;
6332 /****************************************************************************
6333 * GetShareModeFromSTGM
6335 * This method will return a share mode flag from a STGM value.
6336 * The STGM value is assumed valid.
6338 static DWORD GetShareModeFromSTGM(DWORD stgm)
6340 switch (STGM_SHARE_MODE(stgm))
6342 case STGM_SHARE_DENY_NONE:
6343 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6344 case STGM_SHARE_DENY_READ:
6345 return FILE_SHARE_WRITE;
6346 case STGM_SHARE_DENY_WRITE:
6347 return FILE_SHARE_READ;
6348 case STGM_SHARE_EXCLUSIVE:
6349 return 0;
6351 ERR("Invalid share mode!\n");
6352 assert(0);
6353 return 0;
6356 /****************************************************************************
6357 * GetAccessModeFromSTGM
6359 * This method will return an access mode flag from a STGM value.
6360 * The STGM value is assumed valid.
6362 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6364 switch (STGM_ACCESS_MODE(stgm))
6366 case STGM_READ:
6367 return GENERIC_READ;
6368 case STGM_WRITE:
6369 case STGM_READWRITE:
6370 return GENERIC_READ | GENERIC_WRITE;
6372 ERR("Invalid access mode!\n");
6373 assert(0);
6374 return 0;
6377 /****************************************************************************
6378 * GetCreationModeFromSTGM
6380 * This method will return a creation mode flag from a STGM value.
6381 * The STGM value is assumed valid.
6383 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6385 switch(STGM_CREATE_MODE(stgm))
6387 case STGM_CREATE:
6388 return CREATE_ALWAYS;
6389 case STGM_CONVERT:
6390 FIXME("STGM_CONVERT not implemented!\n");
6391 return CREATE_NEW;
6392 case STGM_FAILIFTHERE:
6393 return CREATE_NEW;
6395 ERR("Invalid create mode!\n");
6396 assert(0);
6397 return 0;
6401 /*************************************************************************
6402 * OLECONVERT_LoadOLE10 [Internal]
6404 * Loads the OLE10 STREAM to memory
6406 * PARAMS
6407 * pOleStream [I] The OLESTREAM
6408 * pData [I] Data Structure for the OLESTREAM Data
6410 * RETURNS
6411 * Success: S_OK
6412 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6413 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6415 * NOTES
6416 * This function is used by OleConvertOLESTREAMToIStorage only.
6418 * Memory allocated for pData must be freed by the caller
6420 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6422 DWORD dwSize;
6423 HRESULT hRes = S_OK;
6424 int nTryCnt=0;
6425 int max_try = 6;
6427 pData->pData = NULL;
6428 pData->pstrOleObjFileName = (CHAR *) NULL;
6430 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6432 /* Get the OleID */
6433 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6434 if(dwSize != sizeof(pData->dwOleID))
6436 hRes = CONVERT10_E_OLESTREAM_GET;
6438 else if(pData->dwOleID != OLESTREAM_ID)
6440 hRes = CONVERT10_E_OLESTREAM_FMT;
6442 else
6444 hRes = S_OK;
6445 break;
6449 if(hRes == S_OK)
6451 /* Get the TypeID...more info needed for this field */
6452 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6453 if(dwSize != sizeof(pData->dwTypeID))
6455 hRes = CONVERT10_E_OLESTREAM_GET;
6458 if(hRes == S_OK)
6460 if(pData->dwTypeID != 0)
6462 /* Get the length of the OleTypeName */
6463 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6464 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6466 hRes = CONVERT10_E_OLESTREAM_GET;
6469 if(hRes == S_OK)
6471 if(pData->dwOleTypeNameLength > 0)
6473 /* Get the OleTypeName */
6474 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6475 if(dwSize != pData->dwOleTypeNameLength)
6477 hRes = CONVERT10_E_OLESTREAM_GET;
6481 if(bStrem1)
6483 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6484 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6486 hRes = CONVERT10_E_OLESTREAM_GET;
6488 if(hRes == S_OK)
6490 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6491 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6492 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6493 if(pData->pstrOleObjFileName)
6495 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6496 if(dwSize != pData->dwOleObjFileNameLength)
6498 hRes = CONVERT10_E_OLESTREAM_GET;
6501 else
6502 hRes = CONVERT10_E_OLESTREAM_GET;
6505 else
6507 /* Get the Width of the Metafile */
6508 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6509 if(dwSize != sizeof(pData->dwMetaFileWidth))
6511 hRes = CONVERT10_E_OLESTREAM_GET;
6513 if(hRes == S_OK)
6515 /* Get the Height of the Metafile */
6516 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6517 if(dwSize != sizeof(pData->dwMetaFileHeight))
6519 hRes = CONVERT10_E_OLESTREAM_GET;
6523 if(hRes == S_OK)
6525 /* Get the Length of the Data */
6526 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6527 if(dwSize != sizeof(pData->dwDataLength))
6529 hRes = CONVERT10_E_OLESTREAM_GET;
6533 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6535 if(!bStrem1) /* if it is a second OLE stream data */
6537 pData->dwDataLength -= 8;
6538 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6539 if(dwSize != sizeof(pData->strUnknown))
6541 hRes = CONVERT10_E_OLESTREAM_GET;
6545 if(hRes == S_OK)
6547 if(pData->dwDataLength > 0)
6549 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6551 /* Get Data (ex. IStorage, Metafile, or BMP) */
6552 if(pData->pData)
6554 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6555 if(dwSize != pData->dwDataLength)
6557 hRes = CONVERT10_E_OLESTREAM_GET;
6560 else
6562 hRes = CONVERT10_E_OLESTREAM_GET;
6568 return hRes;
6571 /*************************************************************************
6572 * OLECONVERT_SaveOLE10 [Internal]
6574 * Saves the OLE10 STREAM From memory
6576 * PARAMS
6577 * pData [I] Data Structure for the OLESTREAM Data
6578 * pOleStream [I] The OLESTREAM to save
6580 * RETURNS
6581 * Success: S_OK
6582 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6584 * NOTES
6585 * This function is used by OleConvertIStorageToOLESTREAM only.
6588 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6590 DWORD dwSize;
6591 HRESULT hRes = S_OK;
6594 /* Set the OleID */
6595 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6596 if(dwSize != sizeof(pData->dwOleID))
6598 hRes = CONVERT10_E_OLESTREAM_PUT;
6601 if(hRes == S_OK)
6603 /* Set the TypeID */
6604 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6605 if(dwSize != sizeof(pData->dwTypeID))
6607 hRes = CONVERT10_E_OLESTREAM_PUT;
6611 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6613 /* Set the Length of the OleTypeName */
6614 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6615 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6617 hRes = CONVERT10_E_OLESTREAM_PUT;
6620 if(hRes == S_OK)
6622 if(pData->dwOleTypeNameLength > 0)
6624 /* Set the OleTypeName */
6625 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6626 if(dwSize != pData->dwOleTypeNameLength)
6628 hRes = CONVERT10_E_OLESTREAM_PUT;
6633 if(hRes == S_OK)
6635 /* Set the width of the Metafile */
6636 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6637 if(dwSize != sizeof(pData->dwMetaFileWidth))
6639 hRes = CONVERT10_E_OLESTREAM_PUT;
6643 if(hRes == S_OK)
6645 /* Set the height of the Metafile */
6646 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6647 if(dwSize != sizeof(pData->dwMetaFileHeight))
6649 hRes = CONVERT10_E_OLESTREAM_PUT;
6653 if(hRes == S_OK)
6655 /* Set the length of the Data */
6656 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6657 if(dwSize != sizeof(pData->dwDataLength))
6659 hRes = CONVERT10_E_OLESTREAM_PUT;
6663 if(hRes == S_OK)
6665 if(pData->dwDataLength > 0)
6667 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6668 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6669 if(dwSize != pData->dwDataLength)
6671 hRes = CONVERT10_E_OLESTREAM_PUT;
6676 return hRes;
6679 /*************************************************************************
6680 * OLECONVERT_GetOLE20FromOLE10[Internal]
6682 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6683 * opens it, and copies the content to the dest IStorage for
6684 * OleConvertOLESTREAMToIStorage
6687 * PARAMS
6688 * pDestStorage [I] The IStorage to copy the data to
6689 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6690 * nBufferLength [I] The size of the buffer
6692 * RETURNS
6693 * Nothing
6695 * NOTES
6699 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6701 HRESULT hRes;
6702 HANDLE hFile;
6703 IStorage *pTempStorage;
6704 DWORD dwNumOfBytesWritten;
6705 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6706 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6708 /* Create a temp File */
6709 GetTempPathW(MAX_PATH, wstrTempDir);
6710 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6711 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6713 if(hFile != INVALID_HANDLE_VALUE)
6715 /* Write IStorage Data to File */
6716 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6717 CloseHandle(hFile);
6719 /* Open and copy temp storage to the Dest Storage */
6720 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6721 if(hRes == S_OK)
6723 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6724 StorageBaseImpl_Release(pTempStorage);
6726 DeleteFileW(wstrTempFile);
6731 /*************************************************************************
6732 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6734 * Saves the OLE10 STREAM From memory
6736 * PARAMS
6737 * pStorage [I] The Src IStorage to copy
6738 * pData [I] The Dest Memory to write to.
6740 * RETURNS
6741 * The size in bytes allocated for pData
6743 * NOTES
6744 * Memory allocated for pData must be freed by the caller
6746 * Used by OleConvertIStorageToOLESTREAM only.
6749 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6751 HANDLE hFile;
6752 HRESULT hRes;
6753 DWORD nDataLength = 0;
6754 IStorage *pTempStorage;
6755 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6756 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6758 *pData = NULL;
6760 /* Create temp Storage */
6761 GetTempPathW(MAX_PATH, wstrTempDir);
6762 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6763 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6765 if(hRes == S_OK)
6767 /* Copy Src Storage to the Temp Storage */
6768 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6769 StorageBaseImpl_Release(pTempStorage);
6771 /* Open Temp Storage as a file and copy to memory */
6772 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6773 if(hFile != INVALID_HANDLE_VALUE)
6775 nDataLength = GetFileSize(hFile, NULL);
6776 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6777 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6778 CloseHandle(hFile);
6780 DeleteFileW(wstrTempFile);
6782 return nDataLength;
6785 /*************************************************************************
6786 * OLECONVERT_CreateOleStream [Internal]
6788 * Creates the "\001OLE" stream in the IStorage if necessary.
6790 * PARAMS
6791 * pStorage [I] Dest storage to create the stream in
6793 * RETURNS
6794 * Nothing
6796 * NOTES
6797 * This function is used by OleConvertOLESTREAMToIStorage only.
6799 * This stream is still unknown, MS Word seems to have extra data
6800 * but since the data is stored in the OLESTREAM there should be
6801 * no need to recreate the stream. If the stream is manually
6802 * deleted it will create it with this default data.
6805 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6807 HRESULT hRes;
6808 IStream *pStream;
6809 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6810 BYTE pOleStreamHeader [] =
6812 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6813 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6814 0x00, 0x00, 0x00, 0x00
6817 /* Create stream if not present */
6818 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6819 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6821 if(hRes == S_OK)
6823 /* Write default Data */
6824 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6825 IStream_Release(pStream);
6829 /* write a string to a stream, preceded by its length */
6830 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6832 HRESULT r;
6833 LPSTR str;
6834 DWORD len = 0;
6836 if( string )
6837 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6838 r = IStream_Write( stm, &len, sizeof(len), NULL);
6839 if( FAILED( r ) )
6840 return r;
6841 if(len == 0)
6842 return r;
6843 str = CoTaskMemAlloc( len );
6844 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6845 r = IStream_Write( stm, str, len, NULL);
6846 CoTaskMemFree( str );
6847 return r;
6850 /* read a string preceded by its length from a stream */
6851 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6853 HRESULT r;
6854 DWORD len, count = 0;
6855 LPSTR str;
6856 LPWSTR wstr;
6858 r = IStream_Read( stm, &len, sizeof(len), &count );
6859 if( FAILED( r ) )
6860 return r;
6861 if( count != sizeof(len) )
6862 return E_OUTOFMEMORY;
6864 TRACE("%ld bytes\n",len);
6866 str = CoTaskMemAlloc( len );
6867 if( !str )
6868 return E_OUTOFMEMORY;
6869 count = 0;
6870 r = IStream_Read( stm, str, len, &count );
6871 if( FAILED( r ) )
6872 return r;
6873 if( count != len )
6875 CoTaskMemFree( str );
6876 return E_OUTOFMEMORY;
6879 TRACE("Read string %s\n",debugstr_an(str,len));
6881 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6882 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6883 if( wstr )
6884 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6885 CoTaskMemFree( str );
6887 *string = wstr;
6889 return r;
6893 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6894 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6896 IStream *pstm;
6897 HRESULT r = S_OK;
6898 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6900 static const BYTE unknown1[12] =
6901 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6902 0xFF, 0xFF, 0xFF, 0xFF};
6903 static const BYTE unknown2[16] =
6904 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6905 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6907 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6908 debugstr_w(lpszUserType), debugstr_w(szClipName),
6909 debugstr_w(szProgIDName));
6911 /* Create a CompObj stream if it doesn't exist */
6912 r = IStorage_CreateStream(pstg, szwStreamName,
6913 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6914 if( FAILED (r) )
6915 return r;
6917 /* Write CompObj Structure to stream */
6918 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6920 if( SUCCEEDED( r ) )
6921 r = WriteClassStm( pstm, clsid );
6923 if( SUCCEEDED( r ) )
6924 r = STREAM_WriteString( pstm, lpszUserType );
6925 if( SUCCEEDED( r ) )
6926 r = STREAM_WriteString( pstm, szClipName );
6927 if( SUCCEEDED( r ) )
6928 r = STREAM_WriteString( pstm, szProgIDName );
6929 if( SUCCEEDED( r ) )
6930 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6932 IStream_Release( pstm );
6934 return r;
6937 /***********************************************************************
6938 * WriteFmtUserTypeStg (OLE32.@)
6940 HRESULT WINAPI WriteFmtUserTypeStg(
6941 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6943 HRESULT r;
6944 WCHAR szwClipName[0x40];
6945 CLSID clsid = CLSID_NULL;
6946 LPWSTR wstrProgID = NULL;
6947 DWORD n;
6949 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6951 /* get the clipboard format name */
6952 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6953 szwClipName[n]=0;
6955 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6957 /* FIXME: There's room to save a CLSID and its ProgID, but
6958 the CLSID is not looked up in the registry and in all the
6959 tests I wrote it was CLSID_NULL. Where does it come from?
6962 /* get the real program ID. This may fail, but that's fine */
6963 ProgIDFromCLSID(&clsid, &wstrProgID);
6965 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6967 r = STORAGE_WriteCompObj( pstg, &clsid,
6968 lpszUserType, szwClipName, wstrProgID );
6970 CoTaskMemFree(wstrProgID);
6972 return r;
6976 /******************************************************************************
6977 * ReadFmtUserTypeStg [OLE32.@]
6979 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6981 HRESULT r;
6982 IStream *stm = 0;
6983 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6984 unsigned char unknown1[12];
6985 unsigned char unknown2[16];
6986 DWORD count;
6987 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6988 CLSID clsid;
6990 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6992 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6993 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6994 if( FAILED ( r ) )
6996 WARN("Failed to open stream r = %08lx\n", r);
6997 return r;
7000 /* read the various parts of the structure */
7001 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7002 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7003 goto end;
7004 r = ReadClassStm( stm, &clsid );
7005 if( FAILED( r ) )
7006 goto end;
7008 r = STREAM_ReadString( stm, &szCLSIDName );
7009 if( FAILED( r ) )
7010 goto end;
7012 r = STREAM_ReadString( stm, &szOleTypeName );
7013 if( FAILED( r ) )
7014 goto end;
7016 r = STREAM_ReadString( stm, &szProgIDName );
7017 if( FAILED( r ) )
7018 goto end;
7020 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7021 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7022 goto end;
7024 /* ok, success... now we just need to store what we found */
7025 if( pcf )
7026 *pcf = RegisterClipboardFormatW( szOleTypeName );
7027 CoTaskMemFree( szOleTypeName );
7029 if( lplpszUserType )
7030 *lplpszUserType = szCLSIDName;
7031 CoTaskMemFree( szProgIDName );
7033 end:
7034 IStream_Release( stm );
7036 return r;
7040 /*************************************************************************
7041 * OLECONVERT_CreateCompObjStream [Internal]
7043 * Creates a "\001CompObj" is the destination IStorage if necessary.
7045 * PARAMS
7046 * pStorage [I] The dest IStorage to create the CompObj Stream
7047 * if necessary.
7048 * strOleTypeName [I] The ProgID
7050 * RETURNS
7051 * Success: S_OK
7052 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7054 * NOTES
7055 * This function is used by OleConvertOLESTREAMToIStorage only.
7057 * The stream data is stored in the OLESTREAM and there should be
7058 * no need to recreate the stream. If the stream is manually
7059 * deleted it will attempt to create it by querying the registry.
7063 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7065 IStream *pStream;
7066 HRESULT hStorageRes, hRes = S_OK;
7067 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7068 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7069 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7071 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7072 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7074 /* Initialize the CompObj structure */
7075 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7076 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7077 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7080 /* Create a CompObj stream if it doesn't exist */
7081 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7082 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7083 if(hStorageRes == S_OK)
7085 /* copy the OleTypeName to the compobj struct */
7086 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7087 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7089 /* copy the OleTypeName to the compobj struct */
7090 /* Note: in the test made, these were Identical */
7091 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7092 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7094 /* Get the CLSID */
7095 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7096 bufferW, OLESTREAM_MAX_STR_LEN );
7097 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7099 if(hRes == S_OK)
7101 HKEY hKey;
7102 LONG hErr;
7103 /* Get the CLSID Default Name from the Registry */
7104 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7105 if(hErr == ERROR_SUCCESS)
7107 char strTemp[OLESTREAM_MAX_STR_LEN];
7108 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7109 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7110 if(hErr == ERROR_SUCCESS)
7112 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7114 RegCloseKey(hKey);
7118 /* Write CompObj Structure to stream */
7119 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7121 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7123 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7124 if(IStorageCompObj.dwCLSIDNameLength > 0)
7126 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7128 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7129 if(IStorageCompObj.dwOleTypeNameLength > 0)
7131 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7133 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7134 if(IStorageCompObj.dwProgIDNameLength > 0)
7136 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7138 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7139 IStream_Release(pStream);
7141 return hRes;
7145 /*************************************************************************
7146 * OLECONVERT_CreateOlePresStream[Internal]
7148 * Creates the "\002OlePres000" Stream with the Metafile data
7150 * PARAMS
7151 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7152 * dwExtentX [I] Width of the Metafile
7153 * dwExtentY [I] Height of the Metafile
7154 * pData [I] Metafile data
7155 * dwDataLength [I] Size of the Metafile data
7157 * RETURNS
7158 * Success: S_OK
7159 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7161 * NOTES
7162 * This function is used by OleConvertOLESTREAMToIStorage only.
7165 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7167 HRESULT hRes;
7168 IStream *pStream;
7169 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7170 BYTE pOlePresStreamHeader [] =
7172 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7173 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7174 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7175 0x00, 0x00, 0x00, 0x00
7178 BYTE pOlePresStreamHeaderEmpty [] =
7180 0x00, 0x00, 0x00, 0x00,
7181 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7182 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7183 0x00, 0x00, 0x00, 0x00
7186 /* Create the OlePres000 Stream */
7187 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7188 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7190 if(hRes == S_OK)
7192 DWORD nHeaderSize;
7193 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7195 memset(&OlePres, 0, sizeof(OlePres));
7196 /* Do we have any metafile data to save */
7197 if(dwDataLength > 0)
7199 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7200 nHeaderSize = sizeof(pOlePresStreamHeader);
7202 else
7204 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7205 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7207 /* Set width and height of the metafile */
7208 OlePres.dwExtentX = dwExtentX;
7209 OlePres.dwExtentY = -dwExtentY;
7211 /* Set Data and Length */
7212 if(dwDataLength > sizeof(METAFILEPICT16))
7214 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7215 OlePres.pData = &(pData[8]);
7217 /* Save OlePres000 Data to Stream */
7218 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7219 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7220 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7221 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7222 if(OlePres.dwSize > 0)
7224 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7226 IStream_Release(pStream);
7230 /*************************************************************************
7231 * OLECONVERT_CreateOle10NativeStream [Internal]
7233 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7235 * PARAMS
7236 * pStorage [I] Dest storage to create the stream in
7237 * pData [I] Ole10 Native Data (ex. bmp)
7238 * dwDataLength [I] Size of the Ole10 Native Data
7240 * RETURNS
7241 * Nothing
7243 * NOTES
7244 * This function is used by OleConvertOLESTREAMToIStorage only.
7246 * Might need to verify the data and return appropriate error message
7249 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7251 HRESULT hRes;
7252 IStream *pStream;
7253 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7255 /* Create the Ole10Native Stream */
7256 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7257 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7259 if(hRes == S_OK)
7261 /* Write info to stream */
7262 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7263 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7264 IStream_Release(pStream);
7269 /*************************************************************************
7270 * OLECONVERT_GetOLE10ProgID [Internal]
7272 * Finds the ProgID (or OleTypeID) from the IStorage
7274 * PARAMS
7275 * pStorage [I] The Src IStorage to get the ProgID
7276 * strProgID [I] the ProgID string to get
7277 * dwSize [I] the size of the string
7279 * RETURNS
7280 * Success: S_OK
7281 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7283 * NOTES
7284 * This function is used by OleConvertIStorageToOLESTREAM only.
7288 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7290 HRESULT hRes;
7291 IStream *pStream;
7292 LARGE_INTEGER iSeekPos;
7293 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7294 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7296 /* Open the CompObj Stream */
7297 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7298 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7299 if(hRes == S_OK)
7302 /*Get the OleType from the CompObj Stream */
7303 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7304 iSeekPos.u.HighPart = 0;
7306 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7307 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7308 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7309 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7310 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7311 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7312 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7314 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7315 if(*dwSize > 0)
7317 IStream_Read(pStream, strProgID, *dwSize, NULL);
7319 IStream_Release(pStream);
7321 else
7323 STATSTG stat;
7324 LPOLESTR wstrProgID;
7326 /* Get the OleType from the registry */
7327 REFCLSID clsid = &(stat.clsid);
7328 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7329 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7330 if(hRes == S_OK)
7332 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7336 return hRes;
7339 /*************************************************************************
7340 * OLECONVERT_GetOle10PresData [Internal]
7342 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7344 * PARAMS
7345 * pStorage [I] Src IStroage
7346 * pOleStream [I] Dest OleStream Mem Struct
7348 * RETURNS
7349 * Nothing
7351 * NOTES
7352 * This function is used by OleConvertIStorageToOLESTREAM only.
7354 * Memory allocated for pData must be freed by the caller
7358 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7361 HRESULT hRes;
7362 IStream *pStream;
7363 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7365 /* Initialize Default data for OLESTREAM */
7366 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7367 pOleStreamData[0].dwTypeID = 2;
7368 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7369 pOleStreamData[1].dwTypeID = 0;
7370 pOleStreamData[0].dwMetaFileWidth = 0;
7371 pOleStreamData[0].dwMetaFileHeight = 0;
7372 pOleStreamData[0].pData = NULL;
7373 pOleStreamData[1].pData = NULL;
7375 /* Open Ole10Native Stream */
7376 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7377 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7378 if(hRes == S_OK)
7381 /* Read Size and Data */
7382 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7383 if(pOleStreamData->dwDataLength > 0)
7385 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7386 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7388 IStream_Release(pStream);
7394 /*************************************************************************
7395 * OLECONVERT_GetOle20PresData[Internal]
7397 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7399 * PARAMS
7400 * pStorage [I] Src IStroage
7401 * pOleStreamData [I] Dest OleStream Mem Struct
7403 * RETURNS
7404 * Nothing
7406 * NOTES
7407 * This function is used by OleConvertIStorageToOLESTREAM only.
7409 * Memory allocated for pData must be freed by the caller
7411 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7413 HRESULT hRes;
7414 IStream *pStream;
7415 OLECONVERT_ISTORAGE_OLEPRES olePress;
7416 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7418 /* Initialize Default data for OLESTREAM */
7419 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7420 pOleStreamData[0].dwTypeID = 2;
7421 pOleStreamData[0].dwMetaFileWidth = 0;
7422 pOleStreamData[0].dwMetaFileHeight = 0;
7423 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7424 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7425 pOleStreamData[1].dwTypeID = 0;
7426 pOleStreamData[1].dwOleTypeNameLength = 0;
7427 pOleStreamData[1].strOleTypeName[0] = 0;
7428 pOleStreamData[1].dwMetaFileWidth = 0;
7429 pOleStreamData[1].dwMetaFileHeight = 0;
7430 pOleStreamData[1].pData = NULL;
7431 pOleStreamData[1].dwDataLength = 0;
7434 /* Open OlePress000 stream */
7435 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7436 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7437 if(hRes == S_OK)
7439 LARGE_INTEGER iSeekPos;
7440 METAFILEPICT16 MetaFilePict;
7441 static const char strMetafilePictName[] = "METAFILEPICT";
7443 /* Set the TypeID for a Metafile */
7444 pOleStreamData[1].dwTypeID = 5;
7446 /* Set the OleTypeName to Metafile */
7447 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7448 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7450 iSeekPos.u.HighPart = 0;
7451 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7453 /* Get Presentation Data */
7454 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7455 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7456 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7457 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7459 /*Set width and Height */
7460 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7461 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7462 if(olePress.dwSize > 0)
7464 /* Set Length */
7465 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7467 /* Set MetaFilePict struct */
7468 MetaFilePict.mm = 8;
7469 MetaFilePict.xExt = olePress.dwExtentX;
7470 MetaFilePict.yExt = olePress.dwExtentY;
7471 MetaFilePict.hMF = 0;
7473 /* Get Metafile Data */
7474 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7475 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7476 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7478 IStream_Release(pStream);
7482 /*************************************************************************
7483 * OleConvertOLESTREAMToIStorage [OLE32.@]
7485 * Read info on MSDN
7487 * TODO
7488 * DVTARGETDEVICE paramenter is not handled
7489 * Still unsure of some mem fields for OLE 10 Stream
7490 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7491 * and "\001OLE" streams
7494 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7495 LPOLESTREAM pOleStream,
7496 LPSTORAGE pstg,
7497 const DVTARGETDEVICE* ptd)
7499 int i;
7500 HRESULT hRes=S_OK;
7501 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7503 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7505 if(ptd != NULL)
7507 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7510 if(pstg == NULL || pOleStream == NULL)
7512 hRes = E_INVALIDARG;
7515 if(hRes == S_OK)
7517 /* Load the OLESTREAM to Memory */
7518 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7521 if(hRes == S_OK)
7523 /* Load the OLESTREAM to Memory (part 2)*/
7524 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7527 if(hRes == S_OK)
7530 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7532 /* Do we have the IStorage Data in the OLESTREAM */
7533 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7535 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7536 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7538 else
7540 /* It must be an original OLE 1.0 source */
7541 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7544 else
7546 /* It must be an original OLE 1.0 source */
7547 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7550 /* Create CompObj Stream if necessary */
7551 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7552 if(hRes == S_OK)
7554 /*Create the Ole Stream if necessary */
7555 OLECONVERT_CreateOleStream(pstg);
7560 /* Free allocated memory */
7561 for(i=0; i < 2; i++)
7563 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7564 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7565 pOleStreamData[i].pstrOleObjFileName = NULL;
7567 return hRes;
7570 /*************************************************************************
7571 * OleConvertIStorageToOLESTREAM [OLE32.@]
7573 * Read info on MSDN
7575 * Read info on MSDN
7577 * TODO
7578 * Still unsure of some mem fields for OLE 10 Stream
7579 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7580 * and "\001OLE" streams.
7583 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7584 LPSTORAGE pstg,
7585 LPOLESTREAM pOleStream)
7587 int i;
7588 HRESULT hRes = S_OK;
7589 IStream *pStream;
7590 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7591 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7594 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7596 if(pstg == NULL || pOleStream == NULL)
7598 hRes = E_INVALIDARG;
7600 if(hRes == S_OK)
7602 /* Get the ProgID */
7603 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7604 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7606 if(hRes == S_OK)
7608 /* Was it originally Ole10 */
7609 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7610 if(hRes == S_OK)
7612 IStream_Release(pStream);
7613 /* Get Presentation Data for Ole10Native */
7614 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7616 else
7618 /* Get Presentation Data (OLE20) */
7619 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7622 /* Save OLESTREAM */
7623 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7624 if(hRes == S_OK)
7626 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7631 /* Free allocated memory */
7632 for(i=0; i < 2; i++)
7634 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7637 return hRes;
7640 /***********************************************************************
7641 * GetConvertStg (OLE32.@)
7643 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7644 FIXME("unimplemented stub!\n");
7645 return E_FAIL;
7648 /******************************************************************************
7649 * StgIsStorageFile [OLE32.@]
7651 HRESULT WINAPI
7652 StgIsStorageFile(LPCOLESTR fn)
7654 HANDLE hf;
7655 BYTE magic[8];
7656 DWORD bytes_read;
7658 TRACE("(\'%s\')\n", debugstr_w(fn));
7659 hf = CreateFileW(fn, GENERIC_READ,
7660 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7661 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7663 if (hf == INVALID_HANDLE_VALUE)
7664 return STG_E_FILENOTFOUND;
7666 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7668 WARN(" unable to read file\n");
7669 CloseHandle(hf);
7670 return S_FALSE;
7673 CloseHandle(hf);
7675 if (bytes_read != 8) {
7676 WARN(" too short\n");
7677 return S_FALSE;
7680 if (!memcmp(magic,STORAGE_magic,8)) {
7681 WARN(" -> YES\n");
7682 return S_OK;
7685 WARN(" -> Invalid header.\n");
7686 return S_FALSE;