ntdll: More compatible exception information for protection faults.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob708d99442e2ada8bdb61ea1075588c72c40d8ddd
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
6144 * the WriteClassStg.
6146 * PARAMS
6147 * pstg [I] IStorage pointer
6148 * pclsid [O] Pointer to where the CLSID is written
6150 * RETURNS
6151 * Success: S_OK.
6152 * Failure: HRESULT code.
6154 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6156 STATSTG pstatstg;
6157 HRESULT hRes;
6159 TRACE("(%p, %p)\n", pstg, pclsid);
6161 if(!pstg || !pclsid)
6162 return E_INVALIDARG;
6165 * read a STATSTG structure (contains the clsid) from the storage
6167 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6169 if(SUCCEEDED(hRes))
6170 *pclsid=pstatstg.clsid;
6172 return hRes;
6175 /***********************************************************************
6176 * OleLoadFromStream (OLE32.@)
6178 * This function loads an object from stream
6180 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6182 CLSID clsid;
6183 HRESULT res;
6184 LPPERSISTSTREAM xstm;
6186 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6188 res=ReadClassStm(pStm,&clsid);
6189 if (!SUCCEEDED(res))
6190 return res;
6191 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6192 if (!SUCCEEDED(res))
6193 return res;
6194 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6195 if (!SUCCEEDED(res)) {
6196 IUnknown_Release((IUnknown*)*ppvObj);
6197 return res;
6199 res=IPersistStream_Load(xstm,pStm);
6200 IPersistStream_Release(xstm);
6201 /* FIXME: all refcounts ok at this point? I think they should be:
6202 * pStm : unchanged
6203 * ppvObj : 1
6204 * xstm : 0 (released)
6206 return res;
6209 /***********************************************************************
6210 * OleSaveToStream (OLE32.@)
6212 * This function saves an object with the IPersistStream interface on it
6213 * to the specified stream.
6215 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6218 CLSID clsid;
6219 HRESULT res;
6221 TRACE("(%p,%p)\n",pPStm,pStm);
6223 res=IPersistStream_GetClassID(pPStm,&clsid);
6225 if (SUCCEEDED(res)){
6227 res=WriteClassStm(pStm,&clsid);
6229 if (SUCCEEDED(res))
6231 res=IPersistStream_Save(pPStm,pStm,TRUE);
6234 TRACE("Finished Save\n");
6235 return res;
6238 /****************************************************************************
6239 * This method validate a STGM parameter that can contain the values below
6241 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6242 * The stgm values contained in 0xffff0000 are bitmasks.
6244 * STGM_DIRECT 0x00000000
6245 * STGM_TRANSACTED 0x00010000
6246 * STGM_SIMPLE 0x08000000
6248 * STGM_READ 0x00000000
6249 * STGM_WRITE 0x00000001
6250 * STGM_READWRITE 0x00000002
6252 * STGM_SHARE_DENY_NONE 0x00000040
6253 * STGM_SHARE_DENY_READ 0x00000030
6254 * STGM_SHARE_DENY_WRITE 0x00000020
6255 * STGM_SHARE_EXCLUSIVE 0x00000010
6257 * STGM_PRIORITY 0x00040000
6258 * STGM_DELETEONRELEASE 0x04000000
6260 * STGM_CREATE 0x00001000
6261 * STGM_CONVERT 0x00020000
6262 * STGM_FAILIFTHERE 0x00000000
6264 * STGM_NOSCRATCH 0x00100000
6265 * STGM_NOSNAPSHOT 0x00200000
6267 static HRESULT validateSTGM(DWORD stgm)
6269 DWORD access = STGM_ACCESS_MODE(stgm);
6270 DWORD share = STGM_SHARE_MODE(stgm);
6271 DWORD create = STGM_CREATE_MODE(stgm);
6273 if (stgm&~STGM_KNOWN_FLAGS)
6275 ERR("unknown flags %08lx\n", stgm);
6276 return E_FAIL;
6279 switch (access)
6281 case STGM_READ:
6282 case STGM_WRITE:
6283 case STGM_READWRITE:
6284 break;
6285 default:
6286 return E_FAIL;
6289 switch (share)
6291 case STGM_SHARE_DENY_NONE:
6292 case STGM_SHARE_DENY_READ:
6293 case STGM_SHARE_DENY_WRITE:
6294 case STGM_SHARE_EXCLUSIVE:
6295 break;
6296 default:
6297 return E_FAIL;
6300 switch (create)
6302 case STGM_CREATE:
6303 case STGM_FAILIFTHERE:
6304 break;
6305 default:
6306 return E_FAIL;
6310 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6312 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6313 return E_FAIL;
6316 * STGM_CREATE | STGM_CONVERT
6317 * if both are false, STGM_FAILIFTHERE is set to TRUE
6319 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6320 return E_FAIL;
6323 * STGM_NOSCRATCH requires STGM_TRANSACTED
6325 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6326 return E_FAIL;
6329 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6330 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6332 if ( (stgm & STGM_NOSNAPSHOT) &&
6333 (!(stgm & STGM_TRANSACTED) ||
6334 share == STGM_SHARE_EXCLUSIVE ||
6335 share == STGM_SHARE_DENY_WRITE) )
6336 return E_FAIL;
6338 return S_OK;
6341 /****************************************************************************
6342 * GetShareModeFromSTGM
6344 * This method will return a share mode flag from a STGM value.
6345 * The STGM value is assumed valid.
6347 static DWORD GetShareModeFromSTGM(DWORD stgm)
6349 switch (STGM_SHARE_MODE(stgm))
6351 case STGM_SHARE_DENY_NONE:
6352 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6353 case STGM_SHARE_DENY_READ:
6354 return FILE_SHARE_WRITE;
6355 case STGM_SHARE_DENY_WRITE:
6356 return FILE_SHARE_READ;
6357 case STGM_SHARE_EXCLUSIVE:
6358 return 0;
6360 ERR("Invalid share mode!\n");
6361 assert(0);
6362 return 0;
6365 /****************************************************************************
6366 * GetAccessModeFromSTGM
6368 * This method will return an access mode flag from a STGM value.
6369 * The STGM value is assumed valid.
6371 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6373 switch (STGM_ACCESS_MODE(stgm))
6375 case STGM_READ:
6376 return GENERIC_READ;
6377 case STGM_WRITE:
6378 case STGM_READWRITE:
6379 return GENERIC_READ | GENERIC_WRITE;
6381 ERR("Invalid access mode!\n");
6382 assert(0);
6383 return 0;
6386 /****************************************************************************
6387 * GetCreationModeFromSTGM
6389 * This method will return a creation mode flag from a STGM value.
6390 * The STGM value is assumed valid.
6392 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6394 switch(STGM_CREATE_MODE(stgm))
6396 case STGM_CREATE:
6397 return CREATE_ALWAYS;
6398 case STGM_CONVERT:
6399 FIXME("STGM_CONVERT not implemented!\n");
6400 return CREATE_NEW;
6401 case STGM_FAILIFTHERE:
6402 return CREATE_NEW;
6404 ERR("Invalid create mode!\n");
6405 assert(0);
6406 return 0;
6410 /*************************************************************************
6411 * OLECONVERT_LoadOLE10 [Internal]
6413 * Loads the OLE10 STREAM to memory
6415 * PARAMS
6416 * pOleStream [I] The OLESTREAM
6417 * pData [I] Data Structure for the OLESTREAM Data
6419 * RETURNS
6420 * Success: S_OK
6421 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6422 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6424 * NOTES
6425 * This function is used by OleConvertOLESTREAMToIStorage only.
6427 * Memory allocated for pData must be freed by the caller
6429 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6431 DWORD dwSize;
6432 HRESULT hRes = S_OK;
6433 int nTryCnt=0;
6434 int max_try = 6;
6436 pData->pData = NULL;
6437 pData->pstrOleObjFileName = (CHAR *) NULL;
6439 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6441 /* Get the OleID */
6442 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6443 if(dwSize != sizeof(pData->dwOleID))
6445 hRes = CONVERT10_E_OLESTREAM_GET;
6447 else if(pData->dwOleID != OLESTREAM_ID)
6449 hRes = CONVERT10_E_OLESTREAM_FMT;
6451 else
6453 hRes = S_OK;
6454 break;
6458 if(hRes == S_OK)
6460 /* Get the TypeID...more info needed for this field */
6461 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6462 if(dwSize != sizeof(pData->dwTypeID))
6464 hRes = CONVERT10_E_OLESTREAM_GET;
6467 if(hRes == S_OK)
6469 if(pData->dwTypeID != 0)
6471 /* Get the length of the OleTypeName */
6472 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6473 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6475 hRes = CONVERT10_E_OLESTREAM_GET;
6478 if(hRes == S_OK)
6480 if(pData->dwOleTypeNameLength > 0)
6482 /* Get the OleTypeName */
6483 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6484 if(dwSize != pData->dwOleTypeNameLength)
6486 hRes = CONVERT10_E_OLESTREAM_GET;
6490 if(bStrem1)
6492 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6493 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6495 hRes = CONVERT10_E_OLESTREAM_GET;
6497 if(hRes == S_OK)
6499 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6500 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6501 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6502 if(pData->pstrOleObjFileName)
6504 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6505 if(dwSize != pData->dwOleObjFileNameLength)
6507 hRes = CONVERT10_E_OLESTREAM_GET;
6510 else
6511 hRes = CONVERT10_E_OLESTREAM_GET;
6514 else
6516 /* Get the Width of the Metafile */
6517 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6518 if(dwSize != sizeof(pData->dwMetaFileWidth))
6520 hRes = CONVERT10_E_OLESTREAM_GET;
6522 if(hRes == S_OK)
6524 /* Get the Height of the Metafile */
6525 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6526 if(dwSize != sizeof(pData->dwMetaFileHeight))
6528 hRes = CONVERT10_E_OLESTREAM_GET;
6532 if(hRes == S_OK)
6534 /* Get the Length of the Data */
6535 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6536 if(dwSize != sizeof(pData->dwDataLength))
6538 hRes = CONVERT10_E_OLESTREAM_GET;
6542 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6544 if(!bStrem1) /* if it is a second OLE stream data */
6546 pData->dwDataLength -= 8;
6547 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6548 if(dwSize != sizeof(pData->strUnknown))
6550 hRes = CONVERT10_E_OLESTREAM_GET;
6554 if(hRes == S_OK)
6556 if(pData->dwDataLength > 0)
6558 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6560 /* Get Data (ex. IStorage, Metafile, or BMP) */
6561 if(pData->pData)
6563 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6564 if(dwSize != pData->dwDataLength)
6566 hRes = CONVERT10_E_OLESTREAM_GET;
6569 else
6571 hRes = CONVERT10_E_OLESTREAM_GET;
6577 return hRes;
6580 /*************************************************************************
6581 * OLECONVERT_SaveOLE10 [Internal]
6583 * Saves the OLE10 STREAM From memory
6585 * PARAMS
6586 * pData [I] Data Structure for the OLESTREAM Data
6587 * pOleStream [I] The OLESTREAM to save
6589 * RETURNS
6590 * Success: S_OK
6591 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6593 * NOTES
6594 * This function is used by OleConvertIStorageToOLESTREAM only.
6597 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6599 DWORD dwSize;
6600 HRESULT hRes = S_OK;
6603 /* Set the OleID */
6604 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6605 if(dwSize != sizeof(pData->dwOleID))
6607 hRes = CONVERT10_E_OLESTREAM_PUT;
6610 if(hRes == S_OK)
6612 /* Set the TypeID */
6613 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6614 if(dwSize != sizeof(pData->dwTypeID))
6616 hRes = CONVERT10_E_OLESTREAM_PUT;
6620 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6622 /* Set the Length of the OleTypeName */
6623 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6624 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6626 hRes = CONVERT10_E_OLESTREAM_PUT;
6629 if(hRes == S_OK)
6631 if(pData->dwOleTypeNameLength > 0)
6633 /* Set the OleTypeName */
6634 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6635 if(dwSize != pData->dwOleTypeNameLength)
6637 hRes = CONVERT10_E_OLESTREAM_PUT;
6642 if(hRes == S_OK)
6644 /* Set the width of the Metafile */
6645 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6646 if(dwSize != sizeof(pData->dwMetaFileWidth))
6648 hRes = CONVERT10_E_OLESTREAM_PUT;
6652 if(hRes == S_OK)
6654 /* Set the height of the Metafile */
6655 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6656 if(dwSize != sizeof(pData->dwMetaFileHeight))
6658 hRes = CONVERT10_E_OLESTREAM_PUT;
6662 if(hRes == S_OK)
6664 /* Set the length of the Data */
6665 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6666 if(dwSize != sizeof(pData->dwDataLength))
6668 hRes = CONVERT10_E_OLESTREAM_PUT;
6672 if(hRes == S_OK)
6674 if(pData->dwDataLength > 0)
6676 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6677 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6678 if(dwSize != pData->dwDataLength)
6680 hRes = CONVERT10_E_OLESTREAM_PUT;
6685 return hRes;
6688 /*************************************************************************
6689 * OLECONVERT_GetOLE20FromOLE10[Internal]
6691 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6692 * opens it, and copies the content to the dest IStorage for
6693 * OleConvertOLESTREAMToIStorage
6696 * PARAMS
6697 * pDestStorage [I] The IStorage to copy the data to
6698 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6699 * nBufferLength [I] The size of the buffer
6701 * RETURNS
6702 * Nothing
6704 * NOTES
6708 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6710 HRESULT hRes;
6711 HANDLE hFile;
6712 IStorage *pTempStorage;
6713 DWORD dwNumOfBytesWritten;
6714 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6715 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6717 /* Create a temp File */
6718 GetTempPathW(MAX_PATH, wstrTempDir);
6719 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6720 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6722 if(hFile != INVALID_HANDLE_VALUE)
6724 /* Write IStorage Data to File */
6725 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6726 CloseHandle(hFile);
6728 /* Open and copy temp storage to the Dest Storage */
6729 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6730 if(hRes == S_OK)
6732 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6733 StorageBaseImpl_Release(pTempStorage);
6735 DeleteFileW(wstrTempFile);
6740 /*************************************************************************
6741 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6743 * Saves the OLE10 STREAM From memory
6745 * PARAMS
6746 * pStorage [I] The Src IStorage to copy
6747 * pData [I] The Dest Memory to write to.
6749 * RETURNS
6750 * The size in bytes allocated for pData
6752 * NOTES
6753 * Memory allocated for pData must be freed by the caller
6755 * Used by OleConvertIStorageToOLESTREAM only.
6758 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6760 HANDLE hFile;
6761 HRESULT hRes;
6762 DWORD nDataLength = 0;
6763 IStorage *pTempStorage;
6764 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6765 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6767 *pData = NULL;
6769 /* Create temp Storage */
6770 GetTempPathW(MAX_PATH, wstrTempDir);
6771 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6772 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6774 if(hRes == S_OK)
6776 /* Copy Src Storage to the Temp Storage */
6777 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6778 StorageBaseImpl_Release(pTempStorage);
6780 /* Open Temp Storage as a file and copy to memory */
6781 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6782 if(hFile != INVALID_HANDLE_VALUE)
6784 nDataLength = GetFileSize(hFile, NULL);
6785 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6786 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6787 CloseHandle(hFile);
6789 DeleteFileW(wstrTempFile);
6791 return nDataLength;
6794 /*************************************************************************
6795 * OLECONVERT_CreateOleStream [Internal]
6797 * Creates the "\001OLE" stream in the IStorage if necessary.
6799 * PARAMS
6800 * pStorage [I] Dest storage to create the stream in
6802 * RETURNS
6803 * Nothing
6805 * NOTES
6806 * This function is used by OleConvertOLESTREAMToIStorage only.
6808 * This stream is still unknown, MS Word seems to have extra data
6809 * but since the data is stored in the OLESTREAM there should be
6810 * no need to recreate the stream. If the stream is manually
6811 * deleted it will create it with this default data.
6814 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6816 HRESULT hRes;
6817 IStream *pStream;
6818 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6819 BYTE pOleStreamHeader [] =
6821 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6822 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6823 0x00, 0x00, 0x00, 0x00
6826 /* Create stream if not present */
6827 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6828 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6830 if(hRes == S_OK)
6832 /* Write default Data */
6833 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6834 IStream_Release(pStream);
6838 /* write a string to a stream, preceded by its length */
6839 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6841 HRESULT r;
6842 LPSTR str;
6843 DWORD len = 0;
6845 if( string )
6846 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6847 r = IStream_Write( stm, &len, sizeof(len), NULL);
6848 if( FAILED( r ) )
6849 return r;
6850 if(len == 0)
6851 return r;
6852 str = CoTaskMemAlloc( len );
6853 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6854 r = IStream_Write( stm, str, len, NULL);
6855 CoTaskMemFree( str );
6856 return r;
6859 /* read a string preceded by its length from a stream */
6860 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6862 HRESULT r;
6863 DWORD len, count = 0;
6864 LPSTR str;
6865 LPWSTR wstr;
6867 r = IStream_Read( stm, &len, sizeof(len), &count );
6868 if( FAILED( r ) )
6869 return r;
6870 if( count != sizeof(len) )
6871 return E_OUTOFMEMORY;
6873 TRACE("%ld bytes\n",len);
6875 str = CoTaskMemAlloc( len );
6876 if( !str )
6877 return E_OUTOFMEMORY;
6878 count = 0;
6879 r = IStream_Read( stm, str, len, &count );
6880 if( FAILED( r ) )
6881 return r;
6882 if( count != len )
6884 CoTaskMemFree( str );
6885 return E_OUTOFMEMORY;
6888 TRACE("Read string %s\n",debugstr_an(str,len));
6890 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6891 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6892 if( wstr )
6893 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6894 CoTaskMemFree( str );
6896 *string = wstr;
6898 return r;
6902 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6903 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6905 IStream *pstm;
6906 HRESULT r = S_OK;
6907 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6909 static const BYTE unknown1[12] =
6910 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6911 0xFF, 0xFF, 0xFF, 0xFF};
6912 static const BYTE unknown2[16] =
6913 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6914 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6916 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6917 debugstr_w(lpszUserType), debugstr_w(szClipName),
6918 debugstr_w(szProgIDName));
6920 /* Create a CompObj stream if it doesn't exist */
6921 r = IStorage_CreateStream(pstg, szwStreamName,
6922 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6923 if( FAILED (r) )
6924 return r;
6926 /* Write CompObj Structure to stream */
6927 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6929 if( SUCCEEDED( r ) )
6930 r = WriteClassStm( pstm, clsid );
6932 if( SUCCEEDED( r ) )
6933 r = STREAM_WriteString( pstm, lpszUserType );
6934 if( SUCCEEDED( r ) )
6935 r = STREAM_WriteString( pstm, szClipName );
6936 if( SUCCEEDED( r ) )
6937 r = STREAM_WriteString( pstm, szProgIDName );
6938 if( SUCCEEDED( r ) )
6939 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6941 IStream_Release( pstm );
6943 return r;
6946 /***********************************************************************
6947 * WriteFmtUserTypeStg (OLE32.@)
6949 HRESULT WINAPI WriteFmtUserTypeStg(
6950 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6952 HRESULT r;
6953 WCHAR szwClipName[0x40];
6954 CLSID clsid = CLSID_NULL;
6955 LPWSTR wstrProgID = NULL;
6956 DWORD n;
6958 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6960 /* get the clipboard format name */
6961 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6962 szwClipName[n]=0;
6964 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6966 /* FIXME: There's room to save a CLSID and its ProgID, but
6967 the CLSID is not looked up in the registry and in all the
6968 tests I wrote it was CLSID_NULL. Where does it come from?
6971 /* get the real program ID. This may fail, but that's fine */
6972 ProgIDFromCLSID(&clsid, &wstrProgID);
6974 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6976 r = STORAGE_WriteCompObj( pstg, &clsid,
6977 lpszUserType, szwClipName, wstrProgID );
6979 CoTaskMemFree(wstrProgID);
6981 return r;
6985 /******************************************************************************
6986 * ReadFmtUserTypeStg [OLE32.@]
6988 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6990 HRESULT r;
6991 IStream *stm = 0;
6992 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6993 unsigned char unknown1[12];
6994 unsigned char unknown2[16];
6995 DWORD count;
6996 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6997 CLSID clsid;
6999 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7001 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7002 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7003 if( FAILED ( r ) )
7005 WARN("Failed to open stream r = %08lx\n", r);
7006 return r;
7009 /* read the various parts of the structure */
7010 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7011 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7012 goto end;
7013 r = ReadClassStm( stm, &clsid );
7014 if( FAILED( r ) )
7015 goto end;
7017 r = STREAM_ReadString( stm, &szCLSIDName );
7018 if( FAILED( r ) )
7019 goto end;
7021 r = STREAM_ReadString( stm, &szOleTypeName );
7022 if( FAILED( r ) )
7023 goto end;
7025 r = STREAM_ReadString( stm, &szProgIDName );
7026 if( FAILED( r ) )
7027 goto end;
7029 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7030 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7031 goto end;
7033 /* ok, success... now we just need to store what we found */
7034 if( pcf )
7035 *pcf = RegisterClipboardFormatW( szOleTypeName );
7036 CoTaskMemFree( szOleTypeName );
7038 if( lplpszUserType )
7039 *lplpszUserType = szCLSIDName;
7040 CoTaskMemFree( szProgIDName );
7042 end:
7043 IStream_Release( stm );
7045 return r;
7049 /*************************************************************************
7050 * OLECONVERT_CreateCompObjStream [Internal]
7052 * Creates a "\001CompObj" is the destination IStorage if necessary.
7054 * PARAMS
7055 * pStorage [I] The dest IStorage to create the CompObj Stream
7056 * if necessary.
7057 * strOleTypeName [I] The ProgID
7059 * RETURNS
7060 * Success: S_OK
7061 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7063 * NOTES
7064 * This function is used by OleConvertOLESTREAMToIStorage only.
7066 * The stream data is stored in the OLESTREAM and there should be
7067 * no need to recreate the stream. If the stream is manually
7068 * deleted it will attempt to create it by querying the registry.
7072 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7074 IStream *pStream;
7075 HRESULT hStorageRes, hRes = S_OK;
7076 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7077 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7078 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7080 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7081 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7083 /* Initialize the CompObj structure */
7084 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7085 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7086 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7089 /* Create a CompObj stream if it doesn't exist */
7090 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7091 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7092 if(hStorageRes == S_OK)
7094 /* copy the OleTypeName to the compobj struct */
7095 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7096 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7098 /* copy the OleTypeName to the compobj struct */
7099 /* Note: in the test made, these were Identical */
7100 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7101 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7103 /* Get the CLSID */
7104 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7105 bufferW, OLESTREAM_MAX_STR_LEN );
7106 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7108 if(hRes == S_OK)
7110 HKEY hKey;
7111 LONG hErr;
7112 /* Get the CLSID Default Name from the Registry */
7113 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7114 if(hErr == ERROR_SUCCESS)
7116 char strTemp[OLESTREAM_MAX_STR_LEN];
7117 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7118 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7119 if(hErr == ERROR_SUCCESS)
7121 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7123 RegCloseKey(hKey);
7127 /* Write CompObj Structure to stream */
7128 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7130 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7132 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7133 if(IStorageCompObj.dwCLSIDNameLength > 0)
7135 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7137 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7138 if(IStorageCompObj.dwOleTypeNameLength > 0)
7140 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7142 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7143 if(IStorageCompObj.dwProgIDNameLength > 0)
7145 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7147 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7148 IStream_Release(pStream);
7150 return hRes;
7154 /*************************************************************************
7155 * OLECONVERT_CreateOlePresStream[Internal]
7157 * Creates the "\002OlePres000" Stream with the Metafile data
7159 * PARAMS
7160 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7161 * dwExtentX [I] Width of the Metafile
7162 * dwExtentY [I] Height of the Metafile
7163 * pData [I] Metafile data
7164 * dwDataLength [I] Size of the Metafile data
7166 * RETURNS
7167 * Success: S_OK
7168 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7170 * NOTES
7171 * This function is used by OleConvertOLESTREAMToIStorage only.
7174 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7176 HRESULT hRes;
7177 IStream *pStream;
7178 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7179 BYTE pOlePresStreamHeader [] =
7181 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7182 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7183 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7184 0x00, 0x00, 0x00, 0x00
7187 BYTE pOlePresStreamHeaderEmpty [] =
7189 0x00, 0x00, 0x00, 0x00,
7190 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7191 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7192 0x00, 0x00, 0x00, 0x00
7195 /* Create the OlePres000 Stream */
7196 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7197 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7199 if(hRes == S_OK)
7201 DWORD nHeaderSize;
7202 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7204 memset(&OlePres, 0, sizeof(OlePres));
7205 /* Do we have any metafile data to save */
7206 if(dwDataLength > 0)
7208 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7209 nHeaderSize = sizeof(pOlePresStreamHeader);
7211 else
7213 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7214 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7216 /* Set width and height of the metafile */
7217 OlePres.dwExtentX = dwExtentX;
7218 OlePres.dwExtentY = -dwExtentY;
7220 /* Set Data and Length */
7221 if(dwDataLength > sizeof(METAFILEPICT16))
7223 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7224 OlePres.pData = &(pData[8]);
7226 /* Save OlePres000 Data to Stream */
7227 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7228 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7229 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7230 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7231 if(OlePres.dwSize > 0)
7233 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7235 IStream_Release(pStream);
7239 /*************************************************************************
7240 * OLECONVERT_CreateOle10NativeStream [Internal]
7242 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7244 * PARAMS
7245 * pStorage [I] Dest storage to create the stream in
7246 * pData [I] Ole10 Native Data (ex. bmp)
7247 * dwDataLength [I] Size of the Ole10 Native Data
7249 * RETURNS
7250 * Nothing
7252 * NOTES
7253 * This function is used by OleConvertOLESTREAMToIStorage only.
7255 * Might need to verify the data and return appropriate error message
7258 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7260 HRESULT hRes;
7261 IStream *pStream;
7262 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7264 /* Create the Ole10Native Stream */
7265 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7266 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7268 if(hRes == S_OK)
7270 /* Write info to stream */
7271 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7272 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7273 IStream_Release(pStream);
7278 /*************************************************************************
7279 * OLECONVERT_GetOLE10ProgID [Internal]
7281 * Finds the ProgID (or OleTypeID) from the IStorage
7283 * PARAMS
7284 * pStorage [I] The Src IStorage to get the ProgID
7285 * strProgID [I] the ProgID string to get
7286 * dwSize [I] the size of the string
7288 * RETURNS
7289 * Success: S_OK
7290 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7292 * NOTES
7293 * This function is used by OleConvertIStorageToOLESTREAM only.
7297 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7299 HRESULT hRes;
7300 IStream *pStream;
7301 LARGE_INTEGER iSeekPos;
7302 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7303 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7305 /* Open the CompObj Stream */
7306 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7307 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7308 if(hRes == S_OK)
7311 /*Get the OleType from the CompObj Stream */
7312 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7313 iSeekPos.u.HighPart = 0;
7315 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7316 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7317 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7318 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7319 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7320 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7321 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7323 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7324 if(*dwSize > 0)
7326 IStream_Read(pStream, strProgID, *dwSize, NULL);
7328 IStream_Release(pStream);
7330 else
7332 STATSTG stat;
7333 LPOLESTR wstrProgID;
7335 /* Get the OleType from the registry */
7336 REFCLSID clsid = &(stat.clsid);
7337 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7338 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7339 if(hRes == S_OK)
7341 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7345 return hRes;
7348 /*************************************************************************
7349 * OLECONVERT_GetOle10PresData [Internal]
7351 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7353 * PARAMS
7354 * pStorage [I] Src IStroage
7355 * pOleStream [I] Dest OleStream Mem Struct
7357 * RETURNS
7358 * Nothing
7360 * NOTES
7361 * This function is used by OleConvertIStorageToOLESTREAM only.
7363 * Memory allocated for pData must be freed by the caller
7367 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7370 HRESULT hRes;
7371 IStream *pStream;
7372 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7374 /* Initialize Default data for OLESTREAM */
7375 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7376 pOleStreamData[0].dwTypeID = 2;
7377 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7378 pOleStreamData[1].dwTypeID = 0;
7379 pOleStreamData[0].dwMetaFileWidth = 0;
7380 pOleStreamData[0].dwMetaFileHeight = 0;
7381 pOleStreamData[0].pData = NULL;
7382 pOleStreamData[1].pData = NULL;
7384 /* Open Ole10Native Stream */
7385 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7386 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7387 if(hRes == S_OK)
7390 /* Read Size and Data */
7391 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7392 if(pOleStreamData->dwDataLength > 0)
7394 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7395 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7397 IStream_Release(pStream);
7403 /*************************************************************************
7404 * OLECONVERT_GetOle20PresData[Internal]
7406 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7408 * PARAMS
7409 * pStorage [I] Src IStroage
7410 * pOleStreamData [I] Dest OleStream Mem Struct
7412 * RETURNS
7413 * Nothing
7415 * NOTES
7416 * This function is used by OleConvertIStorageToOLESTREAM only.
7418 * Memory allocated for pData must be freed by the caller
7420 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7422 HRESULT hRes;
7423 IStream *pStream;
7424 OLECONVERT_ISTORAGE_OLEPRES olePress;
7425 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7427 /* Initialize Default data for OLESTREAM */
7428 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7429 pOleStreamData[0].dwTypeID = 2;
7430 pOleStreamData[0].dwMetaFileWidth = 0;
7431 pOleStreamData[0].dwMetaFileHeight = 0;
7432 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7433 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7434 pOleStreamData[1].dwTypeID = 0;
7435 pOleStreamData[1].dwOleTypeNameLength = 0;
7436 pOleStreamData[1].strOleTypeName[0] = 0;
7437 pOleStreamData[1].dwMetaFileWidth = 0;
7438 pOleStreamData[1].dwMetaFileHeight = 0;
7439 pOleStreamData[1].pData = NULL;
7440 pOleStreamData[1].dwDataLength = 0;
7443 /* Open OlePress000 stream */
7444 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7445 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7446 if(hRes == S_OK)
7448 LARGE_INTEGER iSeekPos;
7449 METAFILEPICT16 MetaFilePict;
7450 static const char strMetafilePictName[] = "METAFILEPICT";
7452 /* Set the TypeID for a Metafile */
7453 pOleStreamData[1].dwTypeID = 5;
7455 /* Set the OleTypeName to Metafile */
7456 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7457 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7459 iSeekPos.u.HighPart = 0;
7460 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7462 /* Get Presentation Data */
7463 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7464 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7465 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7466 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7468 /*Set width and Height */
7469 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7470 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7471 if(olePress.dwSize > 0)
7473 /* Set Length */
7474 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7476 /* Set MetaFilePict struct */
7477 MetaFilePict.mm = 8;
7478 MetaFilePict.xExt = olePress.dwExtentX;
7479 MetaFilePict.yExt = olePress.dwExtentY;
7480 MetaFilePict.hMF = 0;
7482 /* Get Metafile Data */
7483 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7484 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7485 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7487 IStream_Release(pStream);
7491 /*************************************************************************
7492 * OleConvertOLESTREAMToIStorage [OLE32.@]
7494 * Read info on MSDN
7496 * TODO
7497 * DVTARGETDEVICE paramenter is not handled
7498 * Still unsure of some mem fields for OLE 10 Stream
7499 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7500 * and "\001OLE" streams
7503 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7504 LPOLESTREAM pOleStream,
7505 LPSTORAGE pstg,
7506 const DVTARGETDEVICE* ptd)
7508 int i;
7509 HRESULT hRes=S_OK;
7510 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7512 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7514 if(ptd != NULL)
7516 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7519 if(pstg == NULL || pOleStream == NULL)
7521 hRes = E_INVALIDARG;
7524 if(hRes == S_OK)
7526 /* Load the OLESTREAM to Memory */
7527 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7530 if(hRes == S_OK)
7532 /* Load the OLESTREAM to Memory (part 2)*/
7533 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7536 if(hRes == S_OK)
7539 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7541 /* Do we have the IStorage Data in the OLESTREAM */
7542 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7544 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7545 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7547 else
7549 /* It must be an original OLE 1.0 source */
7550 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7553 else
7555 /* It must be an original OLE 1.0 source */
7556 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7559 /* Create CompObj Stream if necessary */
7560 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7561 if(hRes == S_OK)
7563 /*Create the Ole Stream if necessary */
7564 OLECONVERT_CreateOleStream(pstg);
7569 /* Free allocated memory */
7570 for(i=0; i < 2; i++)
7572 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7573 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7574 pOleStreamData[i].pstrOleObjFileName = NULL;
7576 return hRes;
7579 /*************************************************************************
7580 * OleConvertIStorageToOLESTREAM [OLE32.@]
7582 * Read info on MSDN
7584 * Read info on MSDN
7586 * TODO
7587 * Still unsure of some mem fields for OLE 10 Stream
7588 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7589 * and "\001OLE" streams.
7592 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7593 LPSTORAGE pstg,
7594 LPOLESTREAM pOleStream)
7596 int i;
7597 HRESULT hRes = S_OK;
7598 IStream *pStream;
7599 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7600 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7603 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7605 if(pstg == NULL || pOleStream == NULL)
7607 hRes = E_INVALIDARG;
7609 if(hRes == S_OK)
7611 /* Get the ProgID */
7612 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7613 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7615 if(hRes == S_OK)
7617 /* Was it originally Ole10 */
7618 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7619 if(hRes == S_OK)
7621 IStream_Release(pStream);
7622 /* Get Presentation Data for Ole10Native */
7623 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7625 else
7627 /* Get Presentation Data (OLE20) */
7628 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7631 /* Save OLESTREAM */
7632 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7633 if(hRes == S_OK)
7635 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7640 /* Free allocated memory */
7641 for(i=0; i < 2; i++)
7643 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7646 return hRes;
7649 /***********************************************************************
7650 * GetConvertStg (OLE32.@)
7652 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7653 FIXME("unimplemented stub!\n");
7654 return E_FAIL;
7657 /******************************************************************************
7658 * StgIsStorageFile [OLE32.@]
7660 HRESULT WINAPI
7661 StgIsStorageFile(LPCOLESTR fn)
7663 HANDLE hf;
7664 BYTE magic[8];
7665 DWORD bytes_read;
7667 TRACE("(\'%s\')\n", debugstr_w(fn));
7668 hf = CreateFileW(fn, GENERIC_READ,
7669 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7670 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7672 if (hf == INVALID_HANDLE_VALUE)
7673 return STG_E_FILENOTFOUND;
7675 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7677 WARN(" unable to read file\n");
7678 CloseHandle(hf);
7679 return S_FALSE;
7682 CloseHandle(hf);
7684 if (bytes_read != 8) {
7685 WARN(" too short\n");
7686 return S_FALSE;
7689 if (!memcmp(magic,STORAGE_magic,8)) {
7690 WARN(" -> YES\n");
7691 return S_OK;
7694 WARN(" -> Invalid header.\n");
7695 return S_FALSE;