Added descriptions for a few more machine types.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob5f9a9097c680ba7a31cee38dbb9db2c78f7521d9
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 * Validate the STGM flags
1063 if ( FAILED( validateSTGM(grfMode) ) ||
1064 (grfMode & STGM_DELETEONRELEASE) )
1065 return STG_E_INVALIDFLAG;
1068 * Check that we're compatible with the parent's storage mode
1070 parent_grfMode = STGM_ACCESS_MODE( This->base.ancestorStorage->base.openFlags );
1071 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
1072 return STG_E_ACCESSDENIED;
1075 * Initialize the out parameter
1077 *ppstg = 0;
1080 * Create a property enumeration and search the properties
1082 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1083 This->base.rootPropertySetIndex);
1085 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1086 pwcsName,
1087 &currentProperty);
1088 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1090 if (foundPropertyIndex != PROPERTY_NULL)
1093 * An element with this name already exists
1095 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1096 IStorage_DestroyElement(iface, pwcsName);
1097 else
1098 return STG_E_FILEALREADYEXISTS;
1102 * memset the empty property
1104 memset(&newProperty, 0, sizeof(StgProperty));
1106 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1108 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1109 return STG_E_INVALIDNAME;
1111 strcpyW(newProperty.name, pwcsName);
1113 newProperty.propertyType = PROPTYPE_STORAGE;
1114 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1115 newProperty.size.u.LowPart = 0;
1116 newProperty.size.u.HighPart = 0;
1118 newProperty.previousProperty = PROPERTY_NULL;
1119 newProperty.nextProperty = PROPERTY_NULL;
1120 newProperty.dirProperty = PROPERTY_NULL;
1122 /* call CoFileTime to get the current time
1123 newProperty.timeStampS1
1124 newProperty.timeStampD1
1125 newProperty.timeStampS2
1126 newProperty.timeStampD2
1129 /* newStorageProperty.propertyUniqueID */
1132 * Obtain a free property in the property chain
1134 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1137 * Save the new property into the new property spot
1139 StorageImpl_WriteProperty(
1140 This->base.ancestorStorage,
1141 newPropertyIndex,
1142 &newProperty);
1145 * Find a spot in the property chain for our newly created property.
1147 updatePropertyChain(
1148 This,
1149 newPropertyIndex,
1150 newProperty);
1153 * Open it to get a pointer to return.
1155 hr = IStorage_OpenStorage(
1156 iface,
1157 (const OLECHAR*)pwcsName,
1159 grfMode,
1162 ppstg);
1164 if( (hr != S_OK) || (*ppstg == NULL))
1166 return hr;
1170 return S_OK;
1174 /***************************************************************************
1176 * Internal Method
1178 * Get a free property or create a new one.
1180 static ULONG getFreeProperty(
1181 StorageImpl *storage)
1183 ULONG currentPropertyIndex = 0;
1184 ULONG newPropertyIndex = PROPERTY_NULL;
1185 BOOL readSuccessful = TRUE;
1186 StgProperty currentProperty;
1191 * Start by reading the root property
1193 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1194 currentPropertyIndex,
1195 &currentProperty);
1196 if (readSuccessful)
1198 if (currentProperty.sizeOfNameString == 0)
1201 * The property existis and is available, we found it.
1203 newPropertyIndex = currentPropertyIndex;
1206 else
1209 * We exhausted the property list, we will create more space below
1211 newPropertyIndex = currentPropertyIndex;
1213 currentPropertyIndex++;
1215 } while (newPropertyIndex == PROPERTY_NULL);
1218 * grow the property chain
1220 if (! readSuccessful)
1222 StgProperty emptyProperty;
1223 ULARGE_INTEGER newSize;
1224 ULONG propertyIndex;
1225 ULONG lastProperty = 0;
1226 ULONG blockCount = 0;
1229 * obtain the new count of property blocks
1231 blockCount = BlockChainStream_GetCount(
1232 storage->base.ancestorStorage->rootBlockChain)+1;
1235 * initialize the size used by the property stream
1237 newSize.u.HighPart = 0;
1238 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1241 * add a property block to the property chain
1243 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1246 * memset the empty property in order to initialize the unused newly
1247 * created property
1249 memset(&emptyProperty, 0, sizeof(StgProperty));
1252 * initialize them
1254 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1256 for(
1257 propertyIndex = newPropertyIndex;
1258 propertyIndex < lastProperty;
1259 propertyIndex++)
1261 StorageImpl_WriteProperty(
1262 storage->base.ancestorStorage,
1263 propertyIndex,
1264 &emptyProperty);
1268 return newPropertyIndex;
1271 /****************************************************************************
1273 * Internal Method
1275 * Case insensitive comparaison of StgProperty.name by first considering
1276 * their size.
1278 * Returns <0 when newPrpoerty < currentProperty
1279 * >0 when newPrpoerty > currentProperty
1280 * 0 when newPrpoerty == currentProperty
1282 static LONG propertyNameCmp(
1283 const OLECHAR *newProperty,
1284 const OLECHAR *currentProperty)
1286 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1288 if (diff == 0)
1291 * We compare the string themselves only when they are of the same length
1293 diff = lstrcmpiW( newProperty, currentProperty);
1296 return diff;
1299 /****************************************************************************
1301 * Internal Method
1303 * Properly link this new element in the property chain.
1305 static void updatePropertyChain(
1306 StorageImpl *storage,
1307 ULONG newPropertyIndex,
1308 StgProperty newProperty)
1310 StgProperty currentProperty;
1313 * Read the root property
1315 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1316 storage->base.rootPropertySetIndex,
1317 &currentProperty);
1319 if (currentProperty.dirProperty != PROPERTY_NULL)
1322 * The root storage contains some element, therefore, start the research
1323 * for the appropriate location.
1325 BOOL found = 0;
1326 ULONG current, next, previous, currentPropertyId;
1329 * Keep the StgProperty sequence number of the storage first property
1331 currentPropertyId = currentProperty.dirProperty;
1334 * Read
1336 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1337 currentProperty.dirProperty,
1338 &currentProperty);
1340 previous = currentProperty.previousProperty;
1341 next = currentProperty.nextProperty;
1342 current = currentPropertyId;
1344 while (found == 0)
1346 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1348 if (diff < 0)
1350 if (previous != PROPERTY_NULL)
1352 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1353 previous,
1354 &currentProperty);
1355 current = previous;
1357 else
1359 currentProperty.previousProperty = newPropertyIndex;
1360 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1361 current,
1362 &currentProperty);
1363 found = 1;
1366 else if (diff > 0)
1368 if (next != PROPERTY_NULL)
1370 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1371 next,
1372 &currentProperty);
1373 current = next;
1375 else
1377 currentProperty.nextProperty = newPropertyIndex;
1378 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1379 current,
1380 &currentProperty);
1381 found = 1;
1384 else
1387 * Trying to insert an item with the same name in the
1388 * subtree structure.
1390 assert(FALSE);
1393 previous = currentProperty.previousProperty;
1394 next = currentProperty.nextProperty;
1397 else
1400 * The root storage is empty, link the new property to it's dir property
1402 currentProperty.dirProperty = newPropertyIndex;
1403 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1404 storage->base.rootPropertySetIndex,
1405 &currentProperty);
1410 /*************************************************************************
1411 * CopyTo (IStorage)
1413 HRESULT WINAPI StorageImpl_CopyTo(
1414 IStorage* iface,
1415 DWORD ciidExclude, /* [in] */
1416 const IID* rgiidExclude, /* [size_is][unique][in] */
1417 SNB snbExclude, /* [unique][in] */
1418 IStorage* pstgDest) /* [unique][in] */
1420 IEnumSTATSTG *elements = 0;
1421 STATSTG curElement, strStat;
1422 HRESULT hr;
1423 IStorage *pstgTmp, *pstgChild;
1424 IStream *pstrTmp, *pstrChild;
1426 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1427 FIXME("Exclude option not implemented\n");
1429 TRACE("(%p, %ld, %p, %p, %p)\n",
1430 iface, ciidExclude, rgiidExclude,
1431 snbExclude, pstgDest);
1434 * Perform a sanity check
1436 if ( pstgDest == 0 )
1437 return STG_E_INVALIDPOINTER;
1440 * Enumerate the elements
1442 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1444 if ( hr != S_OK )
1445 return hr;
1448 * set the class ID
1450 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1451 IStorage_SetClass( pstgDest, &curElement.clsid );
1456 * Obtain the next element
1458 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1460 if ( hr == S_FALSE )
1462 hr = S_OK; /* done, every element has been copied */
1463 break;
1466 if (curElement.type == STGTY_STORAGE)
1469 * open child source storage
1471 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1472 STGM_READ|STGM_SHARE_EXCLUSIVE,
1473 NULL, 0, &pstgChild );
1475 if (hr != S_OK)
1476 break;
1479 * Check if destination storage is not a child of the source
1480 * storage, which will cause an infinite loop
1482 if (pstgChild == pstgDest)
1484 IEnumSTATSTG_Release(elements);
1486 return STG_E_ACCESSDENIED;
1490 * create a new storage in destination storage
1492 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1493 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1494 0, 0,
1495 &pstgTmp );
1497 * if it already exist, don't create a new one use this one
1499 if (hr == STG_E_FILEALREADYEXISTS)
1501 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1502 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1503 NULL, 0, &pstgTmp );
1506 if (hr != S_OK)
1507 break;
1511 * do the copy recursively
1513 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1514 snbExclude, pstgTmp );
1516 IStorage_Release( pstgTmp );
1517 IStorage_Release( pstgChild );
1519 else if (curElement.type == STGTY_STREAM)
1522 * create a new stream in destination storage. If the stream already
1523 * exist, it will be deleted and a new one will be created.
1525 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1526 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1527 0, 0, &pstrTmp );
1529 if (hr != S_OK)
1530 break;
1533 * open child stream storage
1535 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1536 STGM_READ|STGM_SHARE_EXCLUSIVE,
1537 0, &pstrChild );
1539 if (hr != S_OK)
1540 break;
1543 * Get the size of the source stream
1545 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1548 * Set the size of the destination stream.
1550 IStream_SetSize(pstrTmp, strStat.cbSize);
1553 * do the copy
1555 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1556 NULL, NULL );
1558 IStream_Release( pstrTmp );
1559 IStream_Release( pstrChild );
1561 else
1563 WARN("unknown element type: %ld\n", curElement.type);
1566 } while (hr == S_OK);
1569 * Clean-up
1571 IEnumSTATSTG_Release(elements);
1573 return hr;
1576 /*************************************************************************
1577 * MoveElementTo (IStorage)
1579 HRESULT WINAPI StorageImpl_MoveElementTo(
1580 IStorage* iface,
1581 const OLECHAR *pwcsName, /* [string][in] */
1582 IStorage *pstgDest, /* [unique][in] */
1583 const OLECHAR *pwcsNewName,/* [string][in] */
1584 DWORD grfFlags) /* [in] */
1586 FIXME("not implemented!\n");
1587 return E_NOTIMPL;
1590 /*************************************************************************
1591 * Commit (IStorage)
1593 * Ensures that any changes made to a storage object open in transacted mode
1594 * are reflected in the parent storage
1596 * NOTES
1597 * Wine doesn't implement transacted mode, which seems to be a basic
1598 * optimization, so we can ignore this stub for now.
1600 HRESULT WINAPI StorageImpl_Commit(
1601 IStorage* iface,
1602 DWORD grfCommitFlags)/* [in] */
1604 FIXME("(%ld): stub!\n", grfCommitFlags);
1605 return S_OK;
1608 /*************************************************************************
1609 * Revert (IStorage)
1611 * Discard all changes that have been made since the last commit operation
1613 HRESULT WINAPI StorageImpl_Revert(
1614 IStorage* iface)
1616 FIXME("not implemented!\n");
1617 return E_NOTIMPL;
1620 /*************************************************************************
1621 * DestroyElement (IStorage)
1623 * Stategy: This implementation is build this way for simplicity not for speed.
1624 * I always delete the top most element of the enumeration and adjust
1625 * the deleted element pointer all the time. This takes longer to
1626 * do but allow to reinvoke DestroyElement whenever we encounter a
1627 * storage object. The optimisation reside in the usage of another
1628 * enumeration stategy that would give all the leaves of a storage
1629 * first. (postfix order)
1631 HRESULT WINAPI StorageImpl_DestroyElement(
1632 IStorage* iface,
1633 const OLECHAR *pwcsName)/* [string][in] */
1635 StorageImpl* const This=(StorageImpl*)iface;
1637 IEnumSTATSTGImpl* propertyEnumeration;
1638 HRESULT hr = S_OK;
1639 BOOL res;
1640 StgProperty propertyToDelete;
1641 StgProperty parentProperty;
1642 ULONG foundPropertyIndexToDelete;
1643 ULONG typeOfRelation;
1644 ULONG parentPropertyId;
1646 TRACE("(%p, %s)\n",
1647 iface, debugstr_w(pwcsName));
1650 * Perform a sanity check on the parameters.
1652 if (pwcsName==NULL)
1653 return STG_E_INVALIDPOINTER;
1656 * Create a property enumeration to search the property with the given name
1658 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1659 This->base.ancestorStorage,
1660 This->base.rootPropertySetIndex);
1662 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1663 propertyEnumeration,
1664 pwcsName,
1665 &propertyToDelete);
1667 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1669 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1671 return STG_E_FILENOTFOUND;
1675 * Find the parent property of the property to delete (the one that
1676 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1677 * the parent is This. Otherwise, the parent is one of it's sibling...
1681 * First, read This's StgProperty..
1683 res = StorageImpl_ReadProperty(
1684 This->base.ancestorStorage,
1685 This->base.rootPropertySetIndex,
1686 &parentProperty);
1688 assert(res);
1691 * Second, check to see if by any chance the actual storage (This) is not
1692 * the parent of the property to delete... We never know...
1694 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1697 * Set data as it would have been done in the else part...
1699 typeOfRelation = PROPERTY_RELATION_DIR;
1700 parentPropertyId = This->base.rootPropertySetIndex;
1702 else
1705 * Create a property enumeration to search the parent properties, and
1706 * delete it once done.
1708 IEnumSTATSTGImpl* propertyEnumeration2;
1710 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1711 This->base.ancestorStorage,
1712 This->base.rootPropertySetIndex);
1714 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1715 propertyEnumeration2,
1716 foundPropertyIndexToDelete,
1717 &parentProperty,
1718 &parentPropertyId);
1720 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1723 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1725 hr = deleteStorageProperty(
1726 This,
1727 foundPropertyIndexToDelete,
1728 propertyToDelete);
1730 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1732 hr = deleteStreamProperty(
1733 This,
1734 foundPropertyIndexToDelete,
1735 propertyToDelete);
1738 if (hr!=S_OK)
1739 return hr;
1742 * Adjust the property chain
1744 hr = adjustPropertyChain(
1745 This,
1746 propertyToDelete,
1747 parentProperty,
1748 parentPropertyId,
1749 typeOfRelation);
1751 return hr;
1755 /************************************************************************
1756 * StorageImpl_Stat (IStorage)
1758 * This method will retrieve information about this storage object.
1760 * See Windows documentation for more details on IStorage methods.
1762 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1763 STATSTG* pstatstg, /* [out] */
1764 DWORD grfStatFlag) /* [in] */
1766 StorageImpl* const This = (StorageImpl*)iface;
1767 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1769 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1771 CoTaskMemFree(pstatstg->pwcsName);
1772 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1773 strcpyW(pstatstg->pwcsName, This->pwcsName);
1776 return result;
1781 /*********************************************************************
1783 * Internal Method
1785 * Perform the deletion of a complete storage node
1788 static HRESULT deleteStorageProperty(
1789 StorageImpl *parentStorage,
1790 ULONG indexOfPropertyToDelete,
1791 StgProperty propertyToDelete)
1793 IEnumSTATSTG *elements = 0;
1794 IStorage *childStorage = 0;
1795 STATSTG currentElement;
1796 HRESULT hr;
1797 HRESULT destroyHr = S_OK;
1800 * Open the storage and enumerate it
1802 hr = StorageBaseImpl_OpenStorage(
1803 (IStorage*)parentStorage,
1804 propertyToDelete.name,
1806 STGM_SHARE_EXCLUSIVE,
1809 &childStorage);
1811 if (hr != S_OK)
1813 return hr;
1817 * Enumerate the elements
1819 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1824 * Obtain the next element
1826 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1827 if (hr==S_OK)
1829 destroyHr = StorageImpl_DestroyElement(
1830 (IStorage*)childStorage,
1831 (OLECHAR*)currentElement.pwcsName);
1833 CoTaskMemFree(currentElement.pwcsName);
1837 * We need to Reset the enumeration every time because we delete elements
1838 * and the enumeration could be invalid
1840 IEnumSTATSTG_Reset(elements);
1842 } while ((hr == S_OK) && (destroyHr == S_OK));
1845 * Invalidate the property by zeroing it's name member.
1847 propertyToDelete.sizeOfNameString = 0;
1849 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1850 indexOfPropertyToDelete,
1851 &propertyToDelete);
1853 IStorage_Release(childStorage);
1854 IEnumSTATSTG_Release(elements);
1856 return destroyHr;
1859 /*********************************************************************
1861 * Internal Method
1863 * Perform the deletion of a stream node
1866 static HRESULT deleteStreamProperty(
1867 StorageImpl *parentStorage,
1868 ULONG indexOfPropertyToDelete,
1869 StgProperty propertyToDelete)
1871 IStream *pis;
1872 HRESULT hr;
1873 ULARGE_INTEGER size;
1875 size.u.HighPart = 0;
1876 size.u.LowPart = 0;
1878 hr = StorageBaseImpl_OpenStream(
1879 (IStorage*)parentStorage,
1880 (OLECHAR*)propertyToDelete.name,
1881 NULL,
1882 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1884 &pis);
1886 if (hr!=S_OK)
1888 return(hr);
1892 * Zap the stream
1894 hr = IStream_SetSize(pis, size);
1896 if(hr != S_OK)
1898 return hr;
1902 * Release the stream object.
1904 IStream_Release(pis);
1907 * Invalidate the property by zeroing it's name member.
1909 propertyToDelete.sizeOfNameString = 0;
1912 * Here we should re-read the property so we get the updated pointer
1913 * but since we are here to zap it, I don't do it...
1915 StorageImpl_WriteProperty(
1916 parentStorage->base.ancestorStorage,
1917 indexOfPropertyToDelete,
1918 &propertyToDelete);
1920 return S_OK;
1923 /*********************************************************************
1925 * Internal Method
1927 * Finds a placeholder for the StgProperty within the Storage
1930 static HRESULT findPlaceholder(
1931 StorageImpl *storage,
1932 ULONG propertyIndexToStore,
1933 ULONG storePropertyIndex,
1934 INT typeOfRelation)
1936 StgProperty storeProperty;
1937 HRESULT hr = S_OK;
1938 BOOL res = TRUE;
1941 * Read the storage property
1943 res = StorageImpl_ReadProperty(
1944 storage->base.ancestorStorage,
1945 storePropertyIndex,
1946 &storeProperty);
1948 if(! res)
1950 return E_FAIL;
1953 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1955 if (storeProperty.previousProperty != PROPERTY_NULL)
1957 return findPlaceholder(
1958 storage,
1959 propertyIndexToStore,
1960 storeProperty.previousProperty,
1961 typeOfRelation);
1963 else
1965 storeProperty.previousProperty = propertyIndexToStore;
1968 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1970 if (storeProperty.nextProperty != PROPERTY_NULL)
1972 return findPlaceholder(
1973 storage,
1974 propertyIndexToStore,
1975 storeProperty.nextProperty,
1976 typeOfRelation);
1978 else
1980 storeProperty.nextProperty = propertyIndexToStore;
1983 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1985 if (storeProperty.dirProperty != PROPERTY_NULL)
1987 return findPlaceholder(
1988 storage,
1989 propertyIndexToStore,
1990 storeProperty.dirProperty,
1991 typeOfRelation);
1993 else
1995 storeProperty.dirProperty = propertyIndexToStore;
1999 hr = StorageImpl_WriteProperty(
2000 storage->base.ancestorStorage,
2001 storePropertyIndex,
2002 &storeProperty);
2004 if(! hr)
2006 return E_FAIL;
2009 return S_OK;
2012 /*************************************************************************
2014 * Internal Method
2016 * This method takes the previous and the next property link of a property
2017 * to be deleted and find them a place in the Storage.
2019 static HRESULT adjustPropertyChain(
2020 StorageImpl *This,
2021 StgProperty propertyToDelete,
2022 StgProperty parentProperty,
2023 ULONG parentPropertyId,
2024 INT typeOfRelation)
2026 ULONG newLinkProperty = PROPERTY_NULL;
2027 BOOL needToFindAPlaceholder = FALSE;
2028 ULONG storeNode = PROPERTY_NULL;
2029 ULONG toStoreNode = PROPERTY_NULL;
2030 INT relationType = 0;
2031 HRESULT hr = S_OK;
2032 BOOL res = TRUE;
2034 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2036 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2039 * Set the parent previous to the property to delete previous
2041 newLinkProperty = propertyToDelete.previousProperty;
2043 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2046 * We also need to find a storage for the other link, setup variables
2047 * to do this at the end...
2049 needToFindAPlaceholder = TRUE;
2050 storeNode = propertyToDelete.previousProperty;
2051 toStoreNode = propertyToDelete.nextProperty;
2052 relationType = PROPERTY_RELATION_NEXT;
2055 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2058 * Set the parent previous to the property to delete next
2060 newLinkProperty = propertyToDelete.nextProperty;
2064 * Link it for real...
2066 parentProperty.previousProperty = newLinkProperty;
2069 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2071 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2074 * Set the parent next to the property to delete next previous
2076 newLinkProperty = propertyToDelete.previousProperty;
2078 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2081 * We also need to find a storage for the other link, setup variables
2082 * to do this at the end...
2084 needToFindAPlaceholder = TRUE;
2085 storeNode = propertyToDelete.previousProperty;
2086 toStoreNode = propertyToDelete.nextProperty;
2087 relationType = PROPERTY_RELATION_NEXT;
2090 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2093 * Set the parent next to the property to delete next
2095 newLinkProperty = propertyToDelete.nextProperty;
2099 * Link it for real...
2101 parentProperty.nextProperty = newLinkProperty;
2103 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2105 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2108 * Set the parent dir to the property to delete previous
2110 newLinkProperty = propertyToDelete.previousProperty;
2112 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2115 * We also need to find a storage for the other link, setup variables
2116 * to do this at the end...
2118 needToFindAPlaceholder = TRUE;
2119 storeNode = propertyToDelete.previousProperty;
2120 toStoreNode = propertyToDelete.nextProperty;
2121 relationType = PROPERTY_RELATION_NEXT;
2124 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2127 * Set the parent dir to the property to delete next
2129 newLinkProperty = propertyToDelete.nextProperty;
2133 * Link it for real...
2135 parentProperty.dirProperty = newLinkProperty;
2139 * Write back the parent property
2141 res = StorageImpl_WriteProperty(
2142 This->base.ancestorStorage,
2143 parentPropertyId,
2144 &parentProperty);
2145 if(! res)
2147 return E_FAIL;
2151 * If a placeholder is required for the other link, then, find one and
2152 * get out of here...
2154 if (needToFindAPlaceholder)
2156 hr = findPlaceholder(
2157 This,
2158 toStoreNode,
2159 storeNode,
2160 relationType);
2163 return hr;
2167 /******************************************************************************
2168 * SetElementTimes (IStorage)
2170 HRESULT WINAPI StorageImpl_SetElementTimes(
2171 IStorage* iface,
2172 const OLECHAR *pwcsName,/* [string][in] */
2173 const FILETIME *pctime, /* [in] */
2174 const FILETIME *patime, /* [in] */
2175 const FILETIME *pmtime) /* [in] */
2177 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2178 return S_OK;
2181 /******************************************************************************
2182 * SetStateBits (IStorage)
2184 HRESULT WINAPI StorageImpl_SetStateBits(
2185 IStorage* iface,
2186 DWORD grfStateBits,/* [in] */
2187 DWORD grfMask) /* [in] */
2189 FIXME("not implemented!\n");
2190 return E_NOTIMPL;
2194 * Virtual function table for the IStorage32Impl class.
2196 static const IStorageVtbl Storage32Impl_Vtbl =
2198 StorageBaseImpl_QueryInterface,
2199 StorageBaseImpl_AddRef,
2200 StorageBaseImpl_Release,
2201 StorageBaseImpl_CreateStream,
2202 StorageBaseImpl_OpenStream,
2203 StorageImpl_CreateStorage,
2204 StorageBaseImpl_OpenStorage,
2205 StorageImpl_CopyTo,
2206 StorageImpl_MoveElementTo,
2207 StorageImpl_Commit,
2208 StorageImpl_Revert,
2209 StorageBaseImpl_EnumElements,
2210 StorageImpl_DestroyElement,
2211 StorageBaseImpl_RenameElement,
2212 StorageImpl_SetElementTimes,
2213 StorageBaseImpl_SetClass,
2214 StorageImpl_SetStateBits,
2215 StorageImpl_Stat
2218 HRESULT StorageImpl_Construct(
2219 StorageImpl* This,
2220 HANDLE hFile,
2221 LPCOLESTR pwcsName,
2222 ILockBytes* pLkbyt,
2223 DWORD openFlags,
2224 BOOL fileBased,
2225 BOOL fileCreate)
2227 HRESULT hr = S_OK;
2228 StgProperty currentProperty;
2229 BOOL readSuccessful;
2230 ULONG currentPropertyIndex;
2232 if ( FAILED( validateSTGM(openFlags) ))
2233 return STG_E_INVALIDFLAG;
2235 memset(This, 0, sizeof(StorageImpl));
2238 * Initialize the virtual function table.
2240 This->base.lpVtbl = &Storage32Impl_Vtbl;
2241 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2242 This->base.v_destructor = &StorageImpl_Destroy;
2243 This->base.openFlags = openFlags;
2246 * This is the top-level storage so initialize the ancestor pointer
2247 * to this.
2249 This->base.ancestorStorage = This;
2252 * Initialize the physical support of the storage.
2254 This->hFile = hFile;
2257 * Store copy of file path.
2259 if(pwcsName) {
2260 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2261 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2262 if (!This->pwcsName)
2263 return STG_E_INSUFFICIENTMEMORY;
2264 strcpyW(This->pwcsName, pwcsName);
2268 * Initialize the big block cache.
2270 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2271 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2272 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2273 pLkbyt,
2274 openFlags,
2275 This->bigBlockSize,
2276 fileBased);
2278 if (This->bigBlockFile == 0)
2279 return E_FAIL;
2281 if (fileCreate)
2283 ULARGE_INTEGER size;
2284 BYTE* bigBlockBuffer;
2287 * Initialize all header variables:
2288 * - The big block depot consists of one block and it is at block 0
2289 * - The properties start at block 1
2290 * - There is no small block depot
2292 memset( This->bigBlockDepotStart,
2293 BLOCK_UNUSED,
2294 sizeof(This->bigBlockDepotStart));
2296 This->bigBlockDepotCount = 1;
2297 This->bigBlockDepotStart[0] = 0;
2298 This->rootStartBlock = 1;
2299 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2300 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2301 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2302 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2303 This->extBigBlockDepotCount = 0;
2305 StorageImpl_SaveFileHeader(This);
2308 * Add one block for the big block depot and one block for the properties
2310 size.u.HighPart = 0;
2311 size.u.LowPart = This->bigBlockSize * 3;
2312 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2315 * Initialize the big block depot
2317 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2318 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2319 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2320 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2321 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2323 else
2326 * Load the header for the file.
2328 hr = StorageImpl_LoadFileHeader(This);
2330 if (FAILED(hr))
2332 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2334 return hr;
2339 * There is no block depot cached yet.
2341 This->indexBlockDepotCached = 0xFFFFFFFF;
2344 * Start searching for free blocks with block 0.
2346 This->prevFreeBlock = 0;
2349 * Create the block chain abstractions.
2351 if(!(This->rootBlockChain =
2352 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2353 return STG_E_READFAULT;
2355 if(!(This->smallBlockDepotChain =
2356 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2357 PROPERTY_NULL)))
2358 return STG_E_READFAULT;
2361 * Write the root property
2363 if (fileCreate)
2365 StgProperty rootProp;
2367 * Initialize the property chain
2369 memset(&rootProp, 0, sizeof(rootProp));
2370 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2371 sizeof(rootProp.name)/sizeof(WCHAR) );
2372 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2373 rootProp.propertyType = PROPTYPE_ROOT;
2374 rootProp.previousProperty = PROPERTY_NULL;
2375 rootProp.nextProperty = PROPERTY_NULL;
2376 rootProp.dirProperty = PROPERTY_NULL;
2377 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2378 rootProp.size.u.HighPart = 0;
2379 rootProp.size.u.LowPart = 0;
2381 StorageImpl_WriteProperty(This, 0, &rootProp);
2385 * Find the ID of the root in the property sets.
2387 currentPropertyIndex = 0;
2391 readSuccessful = StorageImpl_ReadProperty(
2392 This,
2393 currentPropertyIndex,
2394 &currentProperty);
2396 if (readSuccessful)
2398 if ( (currentProperty.sizeOfNameString != 0 ) &&
2399 (currentProperty.propertyType == PROPTYPE_ROOT) )
2401 This->base.rootPropertySetIndex = currentPropertyIndex;
2405 currentPropertyIndex++;
2407 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2409 if (!readSuccessful)
2411 /* TODO CLEANUP */
2412 return STG_E_READFAULT;
2416 * Create the block chain abstraction for the small block root chain.
2418 if(!(This->smallBlockRootChain =
2419 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2420 return STG_E_READFAULT;
2422 return hr;
2425 void StorageImpl_Destroy(StorageBaseImpl* iface)
2427 StorageImpl *This = (StorageImpl*) iface;
2428 TRACE("(%p)\n", This);
2430 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2432 BlockChainStream_Destroy(This->smallBlockRootChain);
2433 BlockChainStream_Destroy(This->rootBlockChain);
2434 BlockChainStream_Destroy(This->smallBlockDepotChain);
2436 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2437 HeapFree(GetProcessHeap(), 0, This);
2440 /******************************************************************************
2441 * Storage32Impl_GetNextFreeBigBlock
2443 * Returns the index of the next free big block.
2444 * If the big block depot is filled, this method will enlarge it.
2447 ULONG StorageImpl_GetNextFreeBigBlock(
2448 StorageImpl* This)
2450 ULONG depotBlockIndexPos;
2451 void *depotBuffer;
2452 ULONG depotBlockOffset;
2453 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2454 ULONG nextBlockIndex = BLOCK_SPECIAL;
2455 int depotIndex = 0;
2456 ULONG freeBlock = BLOCK_UNUSED;
2458 depotIndex = This->prevFreeBlock / blocksPerDepot;
2459 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2462 * Scan the entire big block depot until we find a block marked free
2464 while (nextBlockIndex != BLOCK_UNUSED)
2466 if (depotIndex < COUNT_BBDEPOTINHEADER)
2468 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2471 * Grow the primary depot.
2473 if (depotBlockIndexPos == BLOCK_UNUSED)
2475 depotBlockIndexPos = depotIndex*blocksPerDepot;
2478 * Add a block depot.
2480 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2481 This->bigBlockDepotCount++;
2482 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2485 * Flag it as a block depot.
2487 StorageImpl_SetNextBlockInChain(This,
2488 depotBlockIndexPos,
2489 BLOCK_SPECIAL);
2491 /* Save new header information.
2493 StorageImpl_SaveFileHeader(This);
2496 else
2498 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2500 if (depotBlockIndexPos == BLOCK_UNUSED)
2503 * Grow the extended depot.
2505 ULONG extIndex = BLOCK_UNUSED;
2506 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2507 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2509 if (extBlockOffset == 0)
2511 /* We need an extended block.
2513 extIndex = Storage32Impl_AddExtBlockDepot(This);
2514 This->extBigBlockDepotCount++;
2515 depotBlockIndexPos = extIndex + 1;
2517 else
2518 depotBlockIndexPos = depotIndex * blocksPerDepot;
2521 * Add a block depot and mark it in the extended block.
2523 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2524 This->bigBlockDepotCount++;
2525 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2527 /* Flag the block depot.
2529 StorageImpl_SetNextBlockInChain(This,
2530 depotBlockIndexPos,
2531 BLOCK_SPECIAL);
2533 /* If necessary, flag the extended depot block.
2535 if (extIndex != BLOCK_UNUSED)
2536 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2538 /* Save header information.
2540 StorageImpl_SaveFileHeader(This);
2544 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2546 if (depotBuffer != 0)
2548 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2549 ( nextBlockIndex != BLOCK_UNUSED))
2551 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2553 if (nextBlockIndex == BLOCK_UNUSED)
2555 freeBlock = (depotIndex * blocksPerDepot) +
2556 (depotBlockOffset/sizeof(ULONG));
2559 depotBlockOffset += sizeof(ULONG);
2562 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2565 depotIndex++;
2566 depotBlockOffset = 0;
2569 This->prevFreeBlock = freeBlock;
2571 return freeBlock;
2574 /******************************************************************************
2575 * Storage32Impl_AddBlockDepot
2577 * This will create a depot block, essentially it is a block initialized
2578 * to BLOCK_UNUSEDs.
2580 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2582 BYTE* blockBuffer;
2584 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2587 * Initialize blocks as free
2589 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2591 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2594 /******************************************************************************
2595 * Storage32Impl_GetExtDepotBlock
2597 * Returns the index of the block that corresponds to the specified depot
2598 * index. This method is only for depot indexes equal or greater than
2599 * COUNT_BBDEPOTINHEADER.
2601 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2603 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2604 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2605 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2606 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2607 ULONG blockIndex = BLOCK_UNUSED;
2608 ULONG extBlockIndex = This->extBigBlockDepotStart;
2610 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2612 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2613 return BLOCK_UNUSED;
2615 while (extBlockCount > 0)
2617 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2618 extBlockCount--;
2621 if (extBlockIndex != BLOCK_UNUSED)
2623 BYTE* depotBuffer;
2625 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2627 if (depotBuffer != 0)
2629 StorageUtl_ReadDWord(depotBuffer,
2630 extBlockOffset * sizeof(ULONG),
2631 &blockIndex);
2633 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2637 return blockIndex;
2640 /******************************************************************************
2641 * Storage32Impl_SetExtDepotBlock
2643 * Associates the specified block index to the specified depot index.
2644 * This method is only for depot indexes equal or greater than
2645 * COUNT_BBDEPOTINHEADER.
2647 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2648 ULONG depotIndex,
2649 ULONG blockIndex)
2651 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2652 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2653 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2654 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2655 ULONG extBlockIndex = This->extBigBlockDepotStart;
2657 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2659 while (extBlockCount > 0)
2661 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2662 extBlockCount--;
2665 if (extBlockIndex != BLOCK_UNUSED)
2667 BYTE* depotBuffer;
2669 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2671 if (depotBuffer != 0)
2673 StorageUtl_WriteDWord(depotBuffer,
2674 extBlockOffset * sizeof(ULONG),
2675 blockIndex);
2677 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2682 /******************************************************************************
2683 * Storage32Impl_AddExtBlockDepot
2685 * Creates an extended depot block.
2687 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2689 ULONG numExtBlocks = This->extBigBlockDepotCount;
2690 ULONG nextExtBlock = This->extBigBlockDepotStart;
2691 BYTE* depotBuffer = NULL;
2692 ULONG index = BLOCK_UNUSED;
2693 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2694 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2695 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2697 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2698 blocksPerDepotBlock;
2700 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2703 * The first extended block.
2705 This->extBigBlockDepotStart = index;
2707 else
2709 unsigned int i;
2711 * Follow the chain to the last one.
2713 for (i = 0; i < (numExtBlocks - 1); i++)
2715 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2719 * Add the new extended block to the chain.
2721 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2722 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2723 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2727 * Initialize this block.
2729 depotBuffer = StorageImpl_GetBigBlock(This, index);
2730 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2731 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2733 return index;
2736 /******************************************************************************
2737 * Storage32Impl_FreeBigBlock
2739 * This method will flag the specified block as free in the big block depot.
2741 void StorageImpl_FreeBigBlock(
2742 StorageImpl* This,
2743 ULONG blockIndex)
2745 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2747 if (blockIndex < This->prevFreeBlock)
2748 This->prevFreeBlock = blockIndex;
2751 /************************************************************************
2752 * Storage32Impl_GetNextBlockInChain
2754 * This method will retrieve the block index of the next big block in
2755 * in the chain.
2757 * Params: This - Pointer to the Storage object.
2758 * blockIndex - Index of the block to retrieve the chain
2759 * for.
2760 * nextBlockIndex - receives the return value.
2762 * Returns: This method returns the index of the next block in the chain.
2763 * It will return the constants:
2764 * BLOCK_SPECIAL - If the block given was not part of a
2765 * chain.
2766 * BLOCK_END_OF_CHAIN - If the block given was the last in
2767 * a chain.
2768 * BLOCK_UNUSED - If the block given was not past of a chain
2769 * and is available.
2770 * BLOCK_EXTBBDEPOT - This block is part of the extended
2771 * big block depot.
2773 * See Windows documentation for more details on IStorage methods.
2775 HRESULT StorageImpl_GetNextBlockInChain(
2776 StorageImpl* This,
2777 ULONG blockIndex,
2778 ULONG* nextBlockIndex)
2780 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2781 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2782 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2783 void* depotBuffer;
2784 ULONG depotBlockIndexPos;
2785 int index;
2787 *nextBlockIndex = BLOCK_SPECIAL;
2789 if(depotBlockCount >= This->bigBlockDepotCount)
2791 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2792 This->bigBlockDepotCount);
2793 return STG_E_READFAULT;
2797 * Cache the currently accessed depot block.
2799 if (depotBlockCount != This->indexBlockDepotCached)
2801 This->indexBlockDepotCached = depotBlockCount;
2803 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2805 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2807 else
2810 * We have to look in the extended depot.
2812 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2815 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2817 if (!depotBuffer)
2818 return STG_E_READFAULT;
2820 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2822 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2823 This->blockDepotCached[index] = *nextBlockIndex;
2825 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2828 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2830 return S_OK;
2833 /******************************************************************************
2834 * Storage32Impl_GetNextExtendedBlock
2836 * Given an extended block this method will return the next extended block.
2838 * NOTES:
2839 * The last ULONG of an extended block is the block index of the next
2840 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2841 * depot.
2843 * Return values:
2844 * - The index of the next extended block
2845 * - BLOCK_UNUSED: there is no next extended block.
2846 * - Any other return values denotes failure.
2848 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2850 ULONG nextBlockIndex = BLOCK_SPECIAL;
2851 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2852 void* depotBuffer;
2854 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2856 if (depotBuffer!=0)
2858 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2860 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2863 return nextBlockIndex;
2866 /******************************************************************************
2867 * Storage32Impl_SetNextBlockInChain
2869 * This method will write the index of the specified block's next block
2870 * in the big block depot.
2872 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2873 * do the following
2875 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2876 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2877 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2880 void StorageImpl_SetNextBlockInChain(
2881 StorageImpl* This,
2882 ULONG blockIndex,
2883 ULONG nextBlock)
2885 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2886 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2887 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2888 ULONG depotBlockIndexPos;
2889 void* depotBuffer;
2891 assert(depotBlockCount < This->bigBlockDepotCount);
2892 assert(blockIndex != nextBlock);
2894 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2896 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2898 else
2901 * We have to look in the extended depot.
2903 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2906 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2908 if (depotBuffer!=0)
2910 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2911 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2915 * Update the cached block depot, if necessary.
2917 if (depotBlockCount == This->indexBlockDepotCached)
2919 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2923 /******************************************************************************
2924 * Storage32Impl_LoadFileHeader
2926 * This method will read in the file header, i.e. big block index -1.
2928 HRESULT StorageImpl_LoadFileHeader(
2929 StorageImpl* This)
2931 HRESULT hr = STG_E_FILENOTFOUND;
2932 void* headerBigBlock = NULL;
2933 int index;
2936 * Get a pointer to the big block of data containing the header.
2938 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2941 * Extract the information from the header.
2943 if (headerBigBlock!=0)
2946 * Check for the "magic number" signature and return an error if it is not
2947 * found.
2949 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2951 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2952 return STG_E_OLDFORMAT;
2955 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2957 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2958 return STG_E_INVALIDHEADER;
2961 StorageUtl_ReadWord(
2962 headerBigBlock,
2963 OFFSET_BIGBLOCKSIZEBITS,
2964 &This->bigBlockSizeBits);
2966 StorageUtl_ReadWord(
2967 headerBigBlock,
2968 OFFSET_SMALLBLOCKSIZEBITS,
2969 &This->smallBlockSizeBits);
2971 StorageUtl_ReadDWord(
2972 headerBigBlock,
2973 OFFSET_BBDEPOTCOUNT,
2974 &This->bigBlockDepotCount);
2976 StorageUtl_ReadDWord(
2977 headerBigBlock,
2978 OFFSET_ROOTSTARTBLOCK,
2979 &This->rootStartBlock);
2981 StorageUtl_ReadDWord(
2982 headerBigBlock,
2983 OFFSET_SBDEPOTSTART,
2984 &This->smallBlockDepotStart);
2986 StorageUtl_ReadDWord(
2987 headerBigBlock,
2988 OFFSET_EXTBBDEPOTSTART,
2989 &This->extBigBlockDepotStart);
2991 StorageUtl_ReadDWord(
2992 headerBigBlock,
2993 OFFSET_EXTBBDEPOTCOUNT,
2994 &This->extBigBlockDepotCount);
2996 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2998 StorageUtl_ReadDWord(
2999 headerBigBlock,
3000 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3001 &(This->bigBlockDepotStart[index]));
3005 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3007 if ((1 << 2) == 4)
3009 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3010 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3012 else
3014 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3015 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3019 * Right now, the code is making some assumptions about the size of the
3020 * blocks, just make sure they are what we're expecting.
3022 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3023 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3025 WARN("Broken OLE storage file\n");
3026 hr = STG_E_INVALIDHEADER;
3028 else
3029 hr = S_OK;
3032 * Release the block.
3034 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3037 return hr;
3040 /******************************************************************************
3041 * Storage32Impl_SaveFileHeader
3043 * This method will save to the file the header, i.e. big block -1.
3045 void StorageImpl_SaveFileHeader(
3046 StorageImpl* This)
3048 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3049 int index;
3050 BOOL success;
3053 * Get a pointer to the big block of data containing the header.
3055 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3058 * If the block read failed, the file is probably new.
3060 if (!success)
3063 * Initialize for all unknown fields.
3065 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3068 * Initialize the magic number.
3070 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3073 * And a bunch of things we don't know what they mean
3075 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3076 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3077 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3078 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3082 * Write the information to the header.
3084 StorageUtl_WriteWord(
3085 headerBigBlock,
3086 OFFSET_BIGBLOCKSIZEBITS,
3087 This->bigBlockSizeBits);
3089 StorageUtl_WriteWord(
3090 headerBigBlock,
3091 OFFSET_SMALLBLOCKSIZEBITS,
3092 This->smallBlockSizeBits);
3094 StorageUtl_WriteDWord(
3095 headerBigBlock,
3096 OFFSET_BBDEPOTCOUNT,
3097 This->bigBlockDepotCount);
3099 StorageUtl_WriteDWord(
3100 headerBigBlock,
3101 OFFSET_ROOTSTARTBLOCK,
3102 This->rootStartBlock);
3104 StorageUtl_WriteDWord(
3105 headerBigBlock,
3106 OFFSET_SBDEPOTSTART,
3107 This->smallBlockDepotStart);
3109 StorageUtl_WriteDWord(
3110 headerBigBlock,
3111 OFFSET_SBDEPOTCOUNT,
3112 This->smallBlockDepotChain ?
3113 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3115 StorageUtl_WriteDWord(
3116 headerBigBlock,
3117 OFFSET_EXTBBDEPOTSTART,
3118 This->extBigBlockDepotStart);
3120 StorageUtl_WriteDWord(
3121 headerBigBlock,
3122 OFFSET_EXTBBDEPOTCOUNT,
3123 This->extBigBlockDepotCount);
3125 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3127 StorageUtl_WriteDWord(
3128 headerBigBlock,
3129 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3130 (This->bigBlockDepotStart[index]));
3134 * Write the big block back to the file.
3136 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3139 /******************************************************************************
3140 * Storage32Impl_ReadProperty
3142 * This method will read the specified property from the property chain.
3144 BOOL StorageImpl_ReadProperty(
3145 StorageImpl* This,
3146 ULONG index,
3147 StgProperty* buffer)
3149 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3150 ULARGE_INTEGER offsetInPropSet;
3151 BOOL readSuccessful;
3152 ULONG bytesRead;
3154 offsetInPropSet.u.HighPart = 0;
3155 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3157 readSuccessful = BlockChainStream_ReadAt(
3158 This->rootBlockChain,
3159 offsetInPropSet,
3160 PROPSET_BLOCK_SIZE,
3161 currentProperty,
3162 &bytesRead);
3164 if (readSuccessful)
3166 /* replace the name of root entry (often "Root Entry") by the file name */
3167 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3168 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3170 memset(buffer->name, 0, sizeof(buffer->name));
3171 memcpy(
3172 buffer->name,
3173 propName,
3174 PROPERTY_NAME_BUFFER_LEN );
3175 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3177 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3179 StorageUtl_ReadWord(
3180 currentProperty,
3181 OFFSET_PS_NAMELENGTH,
3182 &buffer->sizeOfNameString);
3184 StorageUtl_ReadDWord(
3185 currentProperty,
3186 OFFSET_PS_PREVIOUSPROP,
3187 &buffer->previousProperty);
3189 StorageUtl_ReadDWord(
3190 currentProperty,
3191 OFFSET_PS_NEXTPROP,
3192 &buffer->nextProperty);
3194 StorageUtl_ReadDWord(
3195 currentProperty,
3196 OFFSET_PS_DIRPROP,
3197 &buffer->dirProperty);
3199 StorageUtl_ReadGUID(
3200 currentProperty,
3201 OFFSET_PS_GUID,
3202 &buffer->propertyUniqueID);
3204 StorageUtl_ReadDWord(
3205 currentProperty,
3206 OFFSET_PS_TSS1,
3207 &buffer->timeStampS1);
3209 StorageUtl_ReadDWord(
3210 currentProperty,
3211 OFFSET_PS_TSD1,
3212 &buffer->timeStampD1);
3214 StorageUtl_ReadDWord(
3215 currentProperty,
3216 OFFSET_PS_TSS2,
3217 &buffer->timeStampS2);
3219 StorageUtl_ReadDWord(
3220 currentProperty,
3221 OFFSET_PS_TSD2,
3222 &buffer->timeStampD2);
3224 StorageUtl_ReadDWord(
3225 currentProperty,
3226 OFFSET_PS_STARTBLOCK,
3227 &buffer->startingBlock);
3229 StorageUtl_ReadDWord(
3230 currentProperty,
3231 OFFSET_PS_SIZE,
3232 &buffer->size.u.LowPart);
3234 buffer->size.u.HighPart = 0;
3237 return readSuccessful;
3240 /*********************************************************************
3241 * Write the specified property into the property chain
3243 BOOL StorageImpl_WriteProperty(
3244 StorageImpl* This,
3245 ULONG index,
3246 StgProperty* buffer)
3248 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3249 ULARGE_INTEGER offsetInPropSet;
3250 BOOL writeSuccessful;
3251 ULONG bytesWritten;
3253 offsetInPropSet.u.HighPart = 0;
3254 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3256 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3258 memcpy(
3259 currentProperty + OFFSET_PS_NAME,
3260 buffer->name,
3261 PROPERTY_NAME_BUFFER_LEN );
3263 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3265 StorageUtl_WriteWord(
3266 currentProperty,
3267 OFFSET_PS_NAMELENGTH,
3268 buffer->sizeOfNameString);
3270 StorageUtl_WriteDWord(
3271 currentProperty,
3272 OFFSET_PS_PREVIOUSPROP,
3273 buffer->previousProperty);
3275 StorageUtl_WriteDWord(
3276 currentProperty,
3277 OFFSET_PS_NEXTPROP,
3278 buffer->nextProperty);
3280 StorageUtl_WriteDWord(
3281 currentProperty,
3282 OFFSET_PS_DIRPROP,
3283 buffer->dirProperty);
3285 StorageUtl_WriteGUID(
3286 currentProperty,
3287 OFFSET_PS_GUID,
3288 &buffer->propertyUniqueID);
3290 StorageUtl_WriteDWord(
3291 currentProperty,
3292 OFFSET_PS_TSS1,
3293 buffer->timeStampS1);
3295 StorageUtl_WriteDWord(
3296 currentProperty,
3297 OFFSET_PS_TSD1,
3298 buffer->timeStampD1);
3300 StorageUtl_WriteDWord(
3301 currentProperty,
3302 OFFSET_PS_TSS2,
3303 buffer->timeStampS2);
3305 StorageUtl_WriteDWord(
3306 currentProperty,
3307 OFFSET_PS_TSD2,
3308 buffer->timeStampD2);
3310 StorageUtl_WriteDWord(
3311 currentProperty,
3312 OFFSET_PS_STARTBLOCK,
3313 buffer->startingBlock);
3315 StorageUtl_WriteDWord(
3316 currentProperty,
3317 OFFSET_PS_SIZE,
3318 buffer->size.u.LowPart);
3320 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3321 offsetInPropSet,
3322 PROPSET_BLOCK_SIZE,
3323 currentProperty,
3324 &bytesWritten);
3325 return writeSuccessful;
3328 BOOL StorageImpl_ReadBigBlock(
3329 StorageImpl* This,
3330 ULONG blockIndex,
3331 void* buffer)
3333 void* bigBlockBuffer;
3335 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3337 if (bigBlockBuffer!=0)
3339 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3341 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3343 return TRUE;
3346 return FALSE;
3349 BOOL StorageImpl_WriteBigBlock(
3350 StorageImpl* This,
3351 ULONG blockIndex,
3352 void* buffer)
3354 void* bigBlockBuffer;
3356 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3358 if (bigBlockBuffer!=0)
3360 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3362 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3364 return TRUE;
3367 return FALSE;
3370 void* StorageImpl_GetROBigBlock(
3371 StorageImpl* This,
3372 ULONG blockIndex)
3374 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3377 void* StorageImpl_GetBigBlock(
3378 StorageImpl* This,
3379 ULONG blockIndex)
3381 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3384 void StorageImpl_ReleaseBigBlock(
3385 StorageImpl* This,
3386 void* pBigBlock)
3388 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3391 /******************************************************************************
3392 * Storage32Impl_SmallBlocksToBigBlocks
3394 * This method will convert a small block chain to a big block chain.
3395 * The small block chain will be destroyed.
3397 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3398 StorageImpl* This,
3399 SmallBlockChainStream** ppsbChain)
3401 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3402 ULARGE_INTEGER size, offset;
3403 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3404 ULONG propertyIndex;
3405 BOOL successRead, successWrite;
3406 StgProperty chainProperty;
3407 BYTE *buffer;
3408 BlockChainStream *bbTempChain = NULL;
3409 BlockChainStream *bigBlockChain = NULL;
3412 * Create a temporary big block chain that doesn't have
3413 * an associated property. This temporary chain will be
3414 * used to copy data from small blocks to big blocks.
3416 bbTempChain = BlockChainStream_Construct(This,
3417 &bbHeadOfChain,
3418 PROPERTY_NULL);
3419 if(!bbTempChain) return NULL;
3421 * Grow the big block chain.
3423 size = SmallBlockChainStream_GetSize(*ppsbChain);
3424 BlockChainStream_SetSize(bbTempChain, size);
3427 * Copy the contents of the small block chain to the big block chain
3428 * by small block size increments.
3430 offset.u.LowPart = 0;
3431 offset.u.HighPart = 0;
3432 cbTotalRead = 0;
3433 cbTotalWritten = 0;
3435 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3438 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3439 offset,
3440 DEF_SMALL_BLOCK_SIZE,
3441 buffer,
3442 &cbRead);
3443 cbTotalRead += cbRead;
3445 successWrite = BlockChainStream_WriteAt(bbTempChain,
3446 offset,
3447 cbRead,
3448 buffer,
3449 &cbWritten);
3450 cbTotalWritten += cbWritten;
3452 offset.u.LowPart += This->smallBlockSize;
3454 } while (successRead && successWrite);
3455 HeapFree(GetProcessHeap(),0,buffer);
3457 assert(cbTotalRead == cbTotalWritten);
3460 * Destroy the small block chain.
3462 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3463 size.u.HighPart = 0;
3464 size.u.LowPart = 0;
3465 SmallBlockChainStream_SetSize(*ppsbChain, size);
3466 SmallBlockChainStream_Destroy(*ppsbChain);
3467 *ppsbChain = 0;
3470 * Change the property information. This chain is now a big block chain
3471 * and it doesn't reside in the small blocks chain anymore.
3473 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3475 chainProperty.startingBlock = bbHeadOfChain;
3477 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3480 * Destroy the temporary propertyless big block chain.
3481 * Create a new big block chain associated with this property.
3483 BlockChainStream_Destroy(bbTempChain);
3484 bigBlockChain = BlockChainStream_Construct(This,
3485 NULL,
3486 propertyIndex);
3488 return bigBlockChain;
3491 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3493 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3495 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3496 HeapFree(GetProcessHeap(), 0, This);
3499 /******************************************************************************
3501 ** Storage32InternalImpl_Commit
3503 ** The non-root storages cannot be opened in transacted mode thus this function
3504 ** does nothing.
3506 HRESULT WINAPI StorageInternalImpl_Commit(
3507 IStorage* iface,
3508 DWORD grfCommitFlags) /* [in] */
3510 return S_OK;
3513 /******************************************************************************
3515 ** Storage32InternalImpl_Revert
3517 ** The non-root storages cannot be opened in transacted mode thus this function
3518 ** does nothing.
3520 HRESULT WINAPI StorageInternalImpl_Revert(
3521 IStorage* iface)
3523 return S_OK;
3526 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3528 IStorage_Release((IStorage*)This->parentStorage);
3529 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3530 HeapFree(GetProcessHeap(), 0, This);
3533 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3534 IEnumSTATSTG* iface,
3535 REFIID riid,
3536 void** ppvObject)
3538 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3541 * Perform a sanity check on the parameters.
3543 if (ppvObject==0)
3544 return E_INVALIDARG;
3547 * Initialize the return parameter.
3549 *ppvObject = 0;
3552 * Compare the riid with the interface IDs implemented by this object.
3554 if (IsEqualGUID(&IID_IUnknown, riid) ||
3555 IsEqualGUID(&IID_IStorage, riid))
3557 *ppvObject = (IEnumSTATSTG*)This;
3558 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3559 return S_OK;
3562 return E_NOINTERFACE;
3565 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3566 IEnumSTATSTG* iface)
3568 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3569 return InterlockedIncrement(&This->ref);
3572 ULONG WINAPI IEnumSTATSTGImpl_Release(
3573 IEnumSTATSTG* iface)
3575 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3577 ULONG newRef;
3579 newRef = InterlockedDecrement(&This->ref);
3582 * If the reference count goes down to 0, perform suicide.
3584 if (newRef==0)
3586 IEnumSTATSTGImpl_Destroy(This);
3589 return newRef;
3592 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3593 IEnumSTATSTG* iface,
3594 ULONG celt,
3595 STATSTG* rgelt,
3596 ULONG* pceltFetched)
3598 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3600 StgProperty currentProperty;
3601 STATSTG* currentReturnStruct = rgelt;
3602 ULONG objectFetched = 0;
3603 ULONG currentSearchNode;
3606 * Perform a sanity check on the parameters.
3608 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3609 return E_INVALIDARG;
3612 * To avoid the special case, get another pointer to a ULONG value if
3613 * the caller didn't supply one.
3615 if (pceltFetched==0)
3616 pceltFetched = &objectFetched;
3619 * Start the iteration, we will iterate until we hit the end of the
3620 * linked list or until we hit the number of items to iterate through
3622 *pceltFetched = 0;
3625 * Start with the node at the top of the stack.
3627 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3629 while ( ( *pceltFetched < celt) &&
3630 ( currentSearchNode!=PROPERTY_NULL) )
3633 * Remove the top node from the stack
3635 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3638 * Read the property from the storage.
3640 StorageImpl_ReadProperty(This->parentStorage,
3641 currentSearchNode,
3642 &currentProperty);
3645 * Copy the information to the return buffer.
3647 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3648 &currentProperty,
3649 STATFLAG_DEFAULT);
3652 * Step to the next item in the iteration
3654 (*pceltFetched)++;
3655 currentReturnStruct++;
3658 * Push the next search node in the search stack.
3660 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3663 * continue the iteration.
3665 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3668 if (*pceltFetched == celt)
3669 return S_OK;
3671 return S_FALSE;
3675 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3676 IEnumSTATSTG* iface,
3677 ULONG celt)
3679 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3681 StgProperty currentProperty;
3682 ULONG objectFetched = 0;
3683 ULONG currentSearchNode;
3686 * Start with the node at the top of the stack.
3688 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3690 while ( (objectFetched < celt) &&
3691 (currentSearchNode!=PROPERTY_NULL) )
3694 * Remove the top node from the stack
3696 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3699 * Read the property from the storage.
3701 StorageImpl_ReadProperty(This->parentStorage,
3702 currentSearchNode,
3703 &currentProperty);
3706 * Step to the next item in the iteration
3708 objectFetched++;
3711 * Push the next search node in the search stack.
3713 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3716 * continue the iteration.
3718 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3721 if (objectFetched == celt)
3722 return S_OK;
3724 return S_FALSE;
3727 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3728 IEnumSTATSTG* iface)
3730 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3732 StgProperty rootProperty;
3733 BOOL readSuccessful;
3736 * Re-initialize the search stack to an empty stack
3738 This->stackSize = 0;
3741 * Read the root property from the storage.
3743 readSuccessful = StorageImpl_ReadProperty(
3744 This->parentStorage,
3745 This->firstPropertyNode,
3746 &rootProperty);
3748 if (readSuccessful)
3750 assert(rootProperty.sizeOfNameString!=0);
3753 * Push the search node in the search stack.
3755 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3758 return S_OK;
3761 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3762 IEnumSTATSTG* iface,
3763 IEnumSTATSTG** ppenum)
3765 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3767 IEnumSTATSTGImpl* newClone;
3770 * Perform a sanity check on the parameters.
3772 if (ppenum==0)
3773 return E_INVALIDARG;
3775 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3776 This->firstPropertyNode);
3780 * The new clone enumeration must point to the same current node as
3781 * the ole one.
3783 newClone->stackSize = This->stackSize ;
3784 newClone->stackMaxSize = This->stackMaxSize ;
3785 newClone->stackToVisit =
3786 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3788 memcpy(
3789 newClone->stackToVisit,
3790 This->stackToVisit,
3791 sizeof(ULONG) * newClone->stackSize);
3793 *ppenum = (IEnumSTATSTG*)newClone;
3796 * Don't forget to nail down a reference to the clone before
3797 * returning it.
3799 IEnumSTATSTGImpl_AddRef(*ppenum);
3801 return S_OK;
3804 INT IEnumSTATSTGImpl_FindParentProperty(
3805 IEnumSTATSTGImpl *This,
3806 ULONG childProperty,
3807 StgProperty *currentProperty,
3808 ULONG *thisNodeId)
3810 ULONG currentSearchNode;
3811 ULONG foundNode;
3814 * To avoid the special case, get another pointer to a ULONG value if
3815 * the caller didn't supply one.
3817 if (thisNodeId==0)
3818 thisNodeId = &foundNode;
3821 * Start with the node at the top of the stack.
3823 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3826 while (currentSearchNode!=PROPERTY_NULL)
3829 * Store the current node in the returned parameters
3831 *thisNodeId = currentSearchNode;
3834 * Remove the top node from the stack
3836 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3839 * Read the property from the storage.
3841 StorageImpl_ReadProperty(
3842 This->parentStorage,
3843 currentSearchNode,
3844 currentProperty);
3846 if (currentProperty->previousProperty == childProperty)
3847 return PROPERTY_RELATION_PREVIOUS;
3849 else if (currentProperty->nextProperty == childProperty)
3850 return PROPERTY_RELATION_NEXT;
3852 else if (currentProperty->dirProperty == childProperty)
3853 return PROPERTY_RELATION_DIR;
3856 * Push the next search node in the search stack.
3858 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3861 * continue the iteration.
3863 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3866 return PROPERTY_NULL;
3869 ULONG IEnumSTATSTGImpl_FindProperty(
3870 IEnumSTATSTGImpl* This,
3871 const OLECHAR* lpszPropName,
3872 StgProperty* currentProperty)
3874 ULONG currentSearchNode;
3877 * Start with the node at the top of the stack.
3879 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3881 while (currentSearchNode!=PROPERTY_NULL)
3884 * Remove the top node from the stack
3886 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3889 * Read the property from the storage.
3891 StorageImpl_ReadProperty(This->parentStorage,
3892 currentSearchNode,
3893 currentProperty);
3895 if ( propertyNameCmp(
3896 (const OLECHAR*)currentProperty->name,
3897 (const OLECHAR*)lpszPropName) == 0)
3898 return currentSearchNode;
3901 * Push the next search node in the search stack.
3903 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3906 * continue the iteration.
3908 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3911 return PROPERTY_NULL;
3914 void IEnumSTATSTGImpl_PushSearchNode(
3915 IEnumSTATSTGImpl* This,
3916 ULONG nodeToPush)
3918 StgProperty rootProperty;
3919 BOOL readSuccessful;
3922 * First, make sure we're not trying to push an unexisting node.
3924 if (nodeToPush==PROPERTY_NULL)
3925 return;
3928 * First push the node to the stack
3930 if (This->stackSize == This->stackMaxSize)
3932 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3934 This->stackToVisit = HeapReAlloc(
3935 GetProcessHeap(),
3937 This->stackToVisit,
3938 sizeof(ULONG) * This->stackMaxSize);
3941 This->stackToVisit[This->stackSize] = nodeToPush;
3942 This->stackSize++;
3945 * Read the root property from the storage.
3947 readSuccessful = StorageImpl_ReadProperty(
3948 This->parentStorage,
3949 nodeToPush,
3950 &rootProperty);
3952 if (readSuccessful)
3954 assert(rootProperty.sizeOfNameString!=0);
3957 * Push the previous search node in the search stack.
3959 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3963 ULONG IEnumSTATSTGImpl_PopSearchNode(
3964 IEnumSTATSTGImpl* This,
3965 BOOL remove)
3967 ULONG topNode;
3969 if (This->stackSize == 0)
3970 return PROPERTY_NULL;
3972 topNode = This->stackToVisit[This->stackSize-1];
3974 if (remove)
3975 This->stackSize--;
3977 return topNode;
3981 * Virtual function table for the IEnumSTATSTGImpl class.
3983 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
3985 IEnumSTATSTGImpl_QueryInterface,
3986 IEnumSTATSTGImpl_AddRef,
3987 IEnumSTATSTGImpl_Release,
3988 IEnumSTATSTGImpl_Next,
3989 IEnumSTATSTGImpl_Skip,
3990 IEnumSTATSTGImpl_Reset,
3991 IEnumSTATSTGImpl_Clone
3994 /******************************************************************************
3995 ** IEnumSTATSTGImpl implementation
3998 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3999 StorageImpl* parentStorage,
4000 ULONG firstPropertyNode)
4002 IEnumSTATSTGImpl* newEnumeration;
4004 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4006 if (newEnumeration!=0)
4009 * Set-up the virtual function table and reference count.
4011 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4012 newEnumeration->ref = 0;
4015 * We want to nail-down the reference to the storage in case the
4016 * enumeration out-lives the storage in the client application.
4018 newEnumeration->parentStorage = parentStorage;
4019 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4021 newEnumeration->firstPropertyNode = firstPropertyNode;
4024 * Initialize the search stack
4026 newEnumeration->stackSize = 0;
4027 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4028 newEnumeration->stackToVisit =
4029 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4032 * Make sure the current node of the iterator is the first one.
4034 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4037 return newEnumeration;
4041 * Virtual function table for the Storage32InternalImpl class.
4043 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4045 StorageBaseImpl_QueryInterface,
4046 StorageBaseImpl_AddRef,
4047 StorageBaseImpl_Release,
4048 StorageBaseImpl_CreateStream,
4049 StorageBaseImpl_OpenStream,
4050 StorageImpl_CreateStorage,
4051 StorageBaseImpl_OpenStorage,
4052 StorageImpl_CopyTo,
4053 StorageImpl_MoveElementTo,
4054 StorageInternalImpl_Commit,
4055 StorageInternalImpl_Revert,
4056 StorageBaseImpl_EnumElements,
4057 StorageImpl_DestroyElement,
4058 StorageBaseImpl_RenameElement,
4059 StorageImpl_SetElementTimes,
4060 StorageBaseImpl_SetClass,
4061 StorageImpl_SetStateBits,
4062 StorageBaseImpl_Stat
4065 /******************************************************************************
4066 ** Storage32InternalImpl implementation
4069 StorageInternalImpl* StorageInternalImpl_Construct(
4070 StorageImpl* ancestorStorage,
4071 DWORD openFlags,
4072 ULONG rootPropertyIndex)
4074 StorageInternalImpl* newStorage;
4077 * Allocate space for the new storage object
4079 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4081 if (newStorage!=0)
4083 memset(newStorage, 0, sizeof(StorageInternalImpl));
4086 * Initialize the virtual function table.
4088 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4089 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4090 newStorage->base.openFlags = openFlags;
4093 * Keep the ancestor storage pointer and nail a reference to it.
4095 newStorage->base.ancestorStorage = ancestorStorage;
4096 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4099 * Keep the index of the root property set for this storage,
4101 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4103 return newStorage;
4106 return 0;
4109 /******************************************************************************
4110 ** StorageUtl implementation
4113 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4115 WORD tmp;
4117 memcpy(&tmp, buffer+offset, sizeof(WORD));
4118 *value = le16toh(tmp);
4121 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4123 value = htole16(value);
4124 memcpy(buffer+offset, &value, sizeof(WORD));
4127 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4129 DWORD tmp;
4131 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4132 *value = le32toh(tmp);
4135 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4137 value = htole32(value);
4138 memcpy(buffer+offset, &value, sizeof(DWORD));
4141 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4142 ULARGE_INTEGER* value)
4144 #ifdef WORDS_BIGENDIAN
4145 ULARGE_INTEGER tmp;
4147 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4148 value->u.LowPart = htole32(tmp.u.HighPart);
4149 value->u.HighPart = htole32(tmp.u.LowPart);
4150 #else
4151 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4152 #endif
4155 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4156 const ULARGE_INTEGER *value)
4158 #ifdef WORDS_BIGENDIAN
4159 ULARGE_INTEGER tmp;
4161 tmp.u.LowPart = htole32(value->u.HighPart);
4162 tmp.u.HighPart = htole32(value->u.LowPart);
4163 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4164 #else
4165 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4166 #endif
4169 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4171 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4172 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4173 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4175 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4178 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4180 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4181 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4182 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4184 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4187 void StorageUtl_CopyPropertyToSTATSTG(
4188 STATSTG* destination,
4189 StgProperty* source,
4190 int statFlags)
4193 * The copy of the string occurs only when the flag is not set
4195 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4196 (source->name == NULL) ||
4197 (source->name[0] == 0) )
4199 destination->pwcsName = 0;
4201 else
4203 destination->pwcsName =
4204 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4206 strcpyW((LPWSTR)destination->pwcsName, source->name);
4209 switch (source->propertyType)
4211 case PROPTYPE_STORAGE:
4212 case PROPTYPE_ROOT:
4213 destination->type = STGTY_STORAGE;
4214 break;
4215 case PROPTYPE_STREAM:
4216 destination->type = STGTY_STREAM;
4217 break;
4218 default:
4219 destination->type = STGTY_STREAM;
4220 break;
4223 destination->cbSize = source->size;
4225 currentReturnStruct->mtime = {0}; TODO
4226 currentReturnStruct->ctime = {0};
4227 currentReturnStruct->atime = {0};
4229 destination->grfMode = 0;
4230 destination->grfLocksSupported = 0;
4231 destination->clsid = source->propertyUniqueID;
4232 destination->grfStateBits = 0;
4233 destination->reserved = 0;
4236 /******************************************************************************
4237 ** BlockChainStream implementation
4240 BlockChainStream* BlockChainStream_Construct(
4241 StorageImpl* parentStorage,
4242 ULONG* headOfStreamPlaceHolder,
4243 ULONG propertyIndex)
4245 BlockChainStream* newStream;
4246 ULONG blockIndex;
4248 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4250 newStream->parentStorage = parentStorage;
4251 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4252 newStream->ownerPropertyIndex = propertyIndex;
4253 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4254 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4255 newStream->numBlocks = 0;
4257 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4259 while (blockIndex != BLOCK_END_OF_CHAIN)
4261 newStream->numBlocks++;
4262 newStream->tailIndex = blockIndex;
4264 if(FAILED(StorageImpl_GetNextBlockInChain(
4265 parentStorage,
4266 blockIndex,
4267 &blockIndex)))
4269 HeapFree(GetProcessHeap(), 0, newStream);
4270 return NULL;
4274 return newStream;
4277 void BlockChainStream_Destroy(BlockChainStream* This)
4279 HeapFree(GetProcessHeap(), 0, This);
4282 /******************************************************************************
4283 * BlockChainStream_GetHeadOfChain
4285 * Returns the head of this stream chain.
4286 * Some special chains don't have properties, their heads are kept in
4287 * This->headOfStreamPlaceHolder.
4290 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4292 StgProperty chainProperty;
4293 BOOL readSuccessful;
4295 if (This->headOfStreamPlaceHolder != 0)
4296 return *(This->headOfStreamPlaceHolder);
4298 if (This->ownerPropertyIndex != PROPERTY_NULL)
4300 readSuccessful = StorageImpl_ReadProperty(
4301 This->parentStorage,
4302 This->ownerPropertyIndex,
4303 &chainProperty);
4305 if (readSuccessful)
4307 return chainProperty.startingBlock;
4311 return BLOCK_END_OF_CHAIN;
4314 /******************************************************************************
4315 * BlockChainStream_GetCount
4317 * Returns the number of blocks that comprises this chain.
4318 * This is not the size of the stream as the last block may not be full!
4321 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4323 ULONG blockIndex;
4324 ULONG count = 0;
4326 blockIndex = BlockChainStream_GetHeadOfChain(This);
4328 while (blockIndex != BLOCK_END_OF_CHAIN)
4330 count++;
4332 if(FAILED(StorageImpl_GetNextBlockInChain(
4333 This->parentStorage,
4334 blockIndex,
4335 &blockIndex)))
4336 return 0;
4339 return count;
4342 /******************************************************************************
4343 * BlockChainStream_ReadAt
4345 * Reads a specified number of bytes from this chain at the specified offset.
4346 * bytesRead may be NULL.
4347 * Failure will be returned if the specified number of bytes has not been read.
4349 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4350 ULARGE_INTEGER offset,
4351 ULONG size,
4352 void* buffer,
4353 ULONG* bytesRead)
4355 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4356 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4357 ULONG bytesToReadInBuffer;
4358 ULONG blockIndex;
4359 BYTE* bufferWalker;
4360 BYTE* bigBlockBuffer;
4363 * Find the first block in the stream that contains part of the buffer.
4365 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4366 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4367 (blockNoInSequence < This->lastBlockNoInSequence) )
4369 blockIndex = BlockChainStream_GetHeadOfChain(This);
4370 This->lastBlockNoInSequence = blockNoInSequence;
4372 else
4374 ULONG temp = blockNoInSequence;
4376 blockIndex = This->lastBlockNoInSequenceIndex;
4377 blockNoInSequence -= This->lastBlockNoInSequence;
4378 This->lastBlockNoInSequence = temp;
4381 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4383 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4384 return FALSE;
4385 blockNoInSequence--;
4388 This->lastBlockNoInSequenceIndex = blockIndex;
4391 * Start reading the buffer.
4393 *bytesRead = 0;
4394 bufferWalker = buffer;
4396 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4399 * Calculate how many bytes we can copy from this big block.
4401 bytesToReadInBuffer =
4402 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4405 * Copy those bytes to the buffer
4407 bigBlockBuffer =
4408 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4410 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4412 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4415 * Step to the next big block.
4417 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4418 return FALSE;
4420 bufferWalker += bytesToReadInBuffer;
4421 size -= bytesToReadInBuffer;
4422 *bytesRead += bytesToReadInBuffer;
4423 offsetInBlock = 0; /* There is no offset on the next block */
4427 return (size == 0);
4430 /******************************************************************************
4431 * BlockChainStream_WriteAt
4433 * Writes the specified number of bytes to this chain at the specified offset.
4434 * bytesWritten may be NULL.
4435 * Will fail if not all specified number of bytes have been written.
4437 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4438 ULARGE_INTEGER offset,
4439 ULONG size,
4440 const void* buffer,
4441 ULONG* bytesWritten)
4443 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4444 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4445 ULONG bytesToWrite;
4446 ULONG blockIndex;
4447 const BYTE* bufferWalker;
4448 BYTE* bigBlockBuffer;
4451 * Find the first block in the stream that contains part of the buffer.
4453 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4454 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4455 (blockNoInSequence < This->lastBlockNoInSequence) )
4457 blockIndex = BlockChainStream_GetHeadOfChain(This);
4458 This->lastBlockNoInSequence = blockNoInSequence;
4460 else
4462 ULONG temp = blockNoInSequence;
4464 blockIndex = This->lastBlockNoInSequenceIndex;
4465 blockNoInSequence -= This->lastBlockNoInSequence;
4466 This->lastBlockNoInSequence = temp;
4469 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4471 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4472 &blockIndex)))
4473 return FALSE;
4474 blockNoInSequence--;
4477 This->lastBlockNoInSequenceIndex = blockIndex;
4480 * Here, I'm casting away the constness on the buffer variable
4481 * This is OK since we don't intend to modify that buffer.
4483 *bytesWritten = 0;
4484 bufferWalker = (const BYTE*)buffer;
4486 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4489 * Calculate how many bytes we can copy from this big block.
4491 bytesToWrite =
4492 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4495 * Copy those bytes to the buffer
4497 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4499 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4501 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4504 * Step to the next big block.
4506 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4507 &blockIndex)))
4508 return FALSE;
4509 bufferWalker += bytesToWrite;
4510 size -= bytesToWrite;
4511 *bytesWritten += bytesToWrite;
4512 offsetInBlock = 0; /* There is no offset on the next block */
4515 return (size == 0);
4518 /******************************************************************************
4519 * BlockChainStream_Shrink
4521 * Shrinks this chain in the big block depot.
4523 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4524 ULARGE_INTEGER newSize)
4526 ULONG blockIndex, extraBlock;
4527 ULONG numBlocks;
4528 ULONG count = 1;
4531 * Reset the last accessed block cache.
4533 This->lastBlockNoInSequence = 0xFFFFFFFF;
4534 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4537 * Figure out how many blocks are needed to contain the new size
4539 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4541 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4542 numBlocks++;
4544 blockIndex = BlockChainStream_GetHeadOfChain(This);
4547 * Go to the new end of chain
4549 while (count < numBlocks)
4551 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4552 &blockIndex)))
4553 return FALSE;
4554 count++;
4557 /* Get the next block before marking the new end */
4558 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4559 &extraBlock)))
4560 return FALSE;
4562 /* Mark the new end of chain */
4563 StorageImpl_SetNextBlockInChain(
4564 This->parentStorage,
4565 blockIndex,
4566 BLOCK_END_OF_CHAIN);
4568 This->tailIndex = blockIndex;
4569 This->numBlocks = numBlocks;
4572 * Mark the extra blocks as free
4574 while (extraBlock != BLOCK_END_OF_CHAIN)
4576 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4577 &blockIndex)))
4578 return FALSE;
4579 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4580 extraBlock = blockIndex;
4583 return TRUE;
4586 /******************************************************************************
4587 * BlockChainStream_Enlarge
4589 * Grows this chain in the big block depot.
4591 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4592 ULARGE_INTEGER newSize)
4594 ULONG blockIndex, currentBlock;
4595 ULONG newNumBlocks;
4596 ULONG oldNumBlocks = 0;
4598 blockIndex = BlockChainStream_GetHeadOfChain(This);
4601 * Empty chain. Create the head.
4603 if (blockIndex == BLOCK_END_OF_CHAIN)
4605 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4606 StorageImpl_SetNextBlockInChain(This->parentStorage,
4607 blockIndex,
4608 BLOCK_END_OF_CHAIN);
4610 if (This->headOfStreamPlaceHolder != 0)
4612 *(This->headOfStreamPlaceHolder) = blockIndex;
4614 else
4616 StgProperty chainProp;
4617 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4619 StorageImpl_ReadProperty(
4620 This->parentStorage,
4621 This->ownerPropertyIndex,
4622 &chainProp);
4624 chainProp.startingBlock = blockIndex;
4626 StorageImpl_WriteProperty(
4627 This->parentStorage,
4628 This->ownerPropertyIndex,
4629 &chainProp);
4632 This->tailIndex = blockIndex;
4633 This->numBlocks = 1;
4637 * Figure out how many blocks are needed to contain this stream
4639 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4641 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4642 newNumBlocks++;
4645 * Go to the current end of chain
4647 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4649 currentBlock = blockIndex;
4651 while (blockIndex != BLOCK_END_OF_CHAIN)
4653 This->numBlocks++;
4654 currentBlock = blockIndex;
4656 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4657 &blockIndex)))
4658 return FALSE;
4661 This->tailIndex = currentBlock;
4664 currentBlock = This->tailIndex;
4665 oldNumBlocks = This->numBlocks;
4668 * Add new blocks to the chain
4670 if (oldNumBlocks < newNumBlocks)
4672 while (oldNumBlocks < newNumBlocks)
4674 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4676 StorageImpl_SetNextBlockInChain(
4677 This->parentStorage,
4678 currentBlock,
4679 blockIndex);
4681 StorageImpl_SetNextBlockInChain(
4682 This->parentStorage,
4683 blockIndex,
4684 BLOCK_END_OF_CHAIN);
4686 currentBlock = blockIndex;
4687 oldNumBlocks++;
4690 This->tailIndex = blockIndex;
4691 This->numBlocks = newNumBlocks;
4694 return TRUE;
4697 /******************************************************************************
4698 * BlockChainStream_SetSize
4700 * Sets the size of this stream. The big block depot will be updated.
4701 * The file will grow if we grow the chain.
4703 * TODO: Free the actual blocks in the file when we shrink the chain.
4704 * Currently, the blocks are still in the file. So the file size
4705 * doesn't shrink even if we shrink streams.
4707 BOOL BlockChainStream_SetSize(
4708 BlockChainStream* This,
4709 ULARGE_INTEGER newSize)
4711 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4713 if (newSize.u.LowPart == size.u.LowPart)
4714 return TRUE;
4716 if (newSize.u.LowPart < size.u.LowPart)
4718 BlockChainStream_Shrink(This, newSize);
4720 else
4722 ULARGE_INTEGER fileSize =
4723 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4725 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4728 * Make sure the file stays a multiple of blocksize
4730 if ((diff % This->parentStorage->bigBlockSize) != 0)
4731 diff += (This->parentStorage->bigBlockSize -
4732 (diff % This->parentStorage->bigBlockSize) );
4734 fileSize.u.LowPart += diff;
4735 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4737 BlockChainStream_Enlarge(This, newSize);
4740 return TRUE;
4743 /******************************************************************************
4744 * BlockChainStream_GetSize
4746 * Returns the size of this chain.
4747 * Will return the block count if this chain doesn't have a property.
4749 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4751 StgProperty chainProperty;
4753 if(This->headOfStreamPlaceHolder == NULL)
4756 * This chain is a data stream read the property and return
4757 * the appropriate size
4759 StorageImpl_ReadProperty(
4760 This->parentStorage,
4761 This->ownerPropertyIndex,
4762 &chainProperty);
4764 return chainProperty.size;
4766 else
4769 * this chain is a chain that does not have a property, figure out the
4770 * size by making the product number of used blocks times the
4771 * size of them
4773 ULARGE_INTEGER result;
4774 result.u.HighPart = 0;
4776 result.u.LowPart =
4777 BlockChainStream_GetCount(This) *
4778 This->parentStorage->bigBlockSize;
4780 return result;
4784 /******************************************************************************
4785 ** SmallBlockChainStream implementation
4788 SmallBlockChainStream* SmallBlockChainStream_Construct(
4789 StorageImpl* parentStorage,
4790 ULONG propertyIndex)
4792 SmallBlockChainStream* newStream;
4794 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4796 newStream->parentStorage = parentStorage;
4797 newStream->ownerPropertyIndex = propertyIndex;
4799 return newStream;
4802 void SmallBlockChainStream_Destroy(
4803 SmallBlockChainStream* This)
4805 HeapFree(GetProcessHeap(), 0, This);
4808 /******************************************************************************
4809 * SmallBlockChainStream_GetHeadOfChain
4811 * Returns the head of this chain of small blocks.
4813 ULONG SmallBlockChainStream_GetHeadOfChain(
4814 SmallBlockChainStream* This)
4816 StgProperty chainProperty;
4817 BOOL readSuccessful;
4819 if (This->ownerPropertyIndex)
4821 readSuccessful = StorageImpl_ReadProperty(
4822 This->parentStorage,
4823 This->ownerPropertyIndex,
4824 &chainProperty);
4826 if (readSuccessful)
4828 return chainProperty.startingBlock;
4833 return BLOCK_END_OF_CHAIN;
4836 /******************************************************************************
4837 * SmallBlockChainStream_GetNextBlockInChain
4839 * Returns the index of the next small block in this chain.
4841 * Return Values:
4842 * - BLOCK_END_OF_CHAIN: end of this chain
4843 * - BLOCK_UNUSED: small block 'blockIndex' is free
4845 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4846 SmallBlockChainStream* This,
4847 ULONG blockIndex,
4848 ULONG* nextBlockInChain)
4850 ULARGE_INTEGER offsetOfBlockInDepot;
4851 DWORD buffer;
4852 ULONG bytesRead;
4853 BOOL success;
4855 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4857 offsetOfBlockInDepot.u.HighPart = 0;
4858 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4861 * Read those bytes in the buffer from the small block file.
4863 success = BlockChainStream_ReadAt(
4864 This->parentStorage->smallBlockDepotChain,
4865 offsetOfBlockInDepot,
4866 sizeof(DWORD),
4867 &buffer,
4868 &bytesRead);
4870 if (success)
4872 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4873 return S_OK;
4876 return STG_E_READFAULT;
4879 /******************************************************************************
4880 * SmallBlockChainStream_SetNextBlockInChain
4882 * Writes the index of the next block of the specified block in the small
4883 * block depot.
4884 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4885 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4887 void SmallBlockChainStream_SetNextBlockInChain(
4888 SmallBlockChainStream* This,
4889 ULONG blockIndex,
4890 ULONG nextBlock)
4892 ULARGE_INTEGER offsetOfBlockInDepot;
4893 DWORD buffer;
4894 ULONG bytesWritten;
4896 offsetOfBlockInDepot.u.HighPart = 0;
4897 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4899 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4902 * Read those bytes in the buffer from the small block file.
4904 BlockChainStream_WriteAt(
4905 This->parentStorage->smallBlockDepotChain,
4906 offsetOfBlockInDepot,
4907 sizeof(DWORD),
4908 &buffer,
4909 &bytesWritten);
4912 /******************************************************************************
4913 * SmallBlockChainStream_FreeBlock
4915 * Flag small block 'blockIndex' as free in the small block depot.
4917 void SmallBlockChainStream_FreeBlock(
4918 SmallBlockChainStream* This,
4919 ULONG blockIndex)
4921 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4924 /******************************************************************************
4925 * SmallBlockChainStream_GetNextFreeBlock
4927 * Returns the index of a free small block. The small block depot will be
4928 * enlarged if necessary. The small block chain will also be enlarged if
4929 * necessary.
4931 ULONG SmallBlockChainStream_GetNextFreeBlock(
4932 SmallBlockChainStream* This)
4934 ULARGE_INTEGER offsetOfBlockInDepot;
4935 DWORD buffer;
4936 ULONG bytesRead;
4937 ULONG blockIndex = 0;
4938 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4939 BOOL success = TRUE;
4940 ULONG smallBlocksPerBigBlock;
4942 offsetOfBlockInDepot.u.HighPart = 0;
4945 * Scan the small block depot for a free block
4947 while (nextBlockIndex != BLOCK_UNUSED)
4949 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4951 success = BlockChainStream_ReadAt(
4952 This->parentStorage->smallBlockDepotChain,
4953 offsetOfBlockInDepot,
4954 sizeof(DWORD),
4955 &buffer,
4956 &bytesRead);
4959 * If we run out of space for the small block depot, enlarge it
4961 if (success)
4963 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
4965 if (nextBlockIndex != BLOCK_UNUSED)
4966 blockIndex++;
4968 else
4970 ULONG count =
4971 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4973 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4974 ULONG nextBlock, newsbdIndex;
4975 BYTE* smallBlockDepot;
4977 nextBlock = sbdIndex;
4978 while (nextBlock != BLOCK_END_OF_CHAIN)
4980 sbdIndex = nextBlock;
4981 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4984 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4985 if (sbdIndex != BLOCK_END_OF_CHAIN)
4986 StorageImpl_SetNextBlockInChain(
4987 This->parentStorage,
4988 sbdIndex,
4989 newsbdIndex);
4991 StorageImpl_SetNextBlockInChain(
4992 This->parentStorage,
4993 newsbdIndex,
4994 BLOCK_END_OF_CHAIN);
4997 * Initialize all the small blocks to free
4999 smallBlockDepot =
5000 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5002 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5003 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5005 if (count == 0)
5008 * We have just created the small block depot.
5010 StgProperty rootProp;
5011 ULONG sbStartIndex;
5014 * Save it in the header
5016 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5017 StorageImpl_SaveFileHeader(This->parentStorage);
5020 * And allocate the first big block that will contain small blocks
5022 sbStartIndex =
5023 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5025 StorageImpl_SetNextBlockInChain(
5026 This->parentStorage,
5027 sbStartIndex,
5028 BLOCK_END_OF_CHAIN);
5030 StorageImpl_ReadProperty(
5031 This->parentStorage,
5032 This->parentStorage->base.rootPropertySetIndex,
5033 &rootProp);
5035 rootProp.startingBlock = sbStartIndex;
5036 rootProp.size.u.HighPart = 0;
5037 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5039 StorageImpl_WriteProperty(
5040 This->parentStorage,
5041 This->parentStorage->base.rootPropertySetIndex,
5042 &rootProp);
5047 smallBlocksPerBigBlock =
5048 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5051 * Verify if we have to allocate big blocks to contain small blocks
5053 if (blockIndex % smallBlocksPerBigBlock == 0)
5055 StgProperty rootProp;
5056 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5058 StorageImpl_ReadProperty(
5059 This->parentStorage,
5060 This->parentStorage->base.rootPropertySetIndex,
5061 &rootProp);
5063 if (rootProp.size.u.LowPart <
5064 (blocksRequired * This->parentStorage->bigBlockSize))
5066 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5068 BlockChainStream_SetSize(
5069 This->parentStorage->smallBlockRootChain,
5070 rootProp.size);
5072 StorageImpl_WriteProperty(
5073 This->parentStorage,
5074 This->parentStorage->base.rootPropertySetIndex,
5075 &rootProp);
5079 return blockIndex;
5082 /******************************************************************************
5083 * SmallBlockChainStream_ReadAt
5085 * Reads a specified number of bytes from this chain at the specified offset.
5086 * bytesRead may be NULL.
5087 * Failure will be returned if the specified number of bytes has not been read.
5089 BOOL SmallBlockChainStream_ReadAt(
5090 SmallBlockChainStream* This,
5091 ULARGE_INTEGER offset,
5092 ULONG size,
5093 void* buffer,
5094 ULONG* bytesRead)
5096 ULARGE_INTEGER offsetInBigBlockFile;
5097 ULONG blockNoInSequence =
5098 offset.u.LowPart / This->parentStorage->smallBlockSize;
5100 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5101 ULONG bytesToReadInBuffer;
5102 ULONG blockIndex;
5103 ULONG bytesReadFromBigBlockFile;
5104 BYTE* bufferWalker;
5107 * This should never happen on a small block file.
5109 assert(offset.u.HighPart==0);
5112 * Find the first block in the stream that contains part of the buffer.
5114 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5116 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5118 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5119 &blockIndex)))
5120 return FALSE;
5121 blockNoInSequence--;
5125 * Start reading the buffer.
5127 *bytesRead = 0;
5128 bufferWalker = buffer;
5130 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5133 * Calculate how many bytes we can copy from this small block.
5135 bytesToReadInBuffer =
5136 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5139 * Calculate the offset of the small block in the small block file.
5141 offsetInBigBlockFile.u.HighPart = 0;
5142 offsetInBigBlockFile.u.LowPart =
5143 blockIndex * This->parentStorage->smallBlockSize;
5145 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5148 * Read those bytes in the buffer from the small block file.
5150 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5151 offsetInBigBlockFile,
5152 bytesToReadInBuffer,
5153 bufferWalker,
5154 &bytesReadFromBigBlockFile);
5156 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5159 * Step to the next big block.
5161 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5162 return FALSE;
5163 bufferWalker += bytesToReadInBuffer;
5164 size -= bytesToReadInBuffer;
5165 *bytesRead += bytesToReadInBuffer;
5166 offsetInBlock = 0; /* There is no offset on the next block */
5169 return (size == 0);
5172 /******************************************************************************
5173 * SmallBlockChainStream_WriteAt
5175 * Writes the specified number of bytes to this chain at the specified offset.
5176 * bytesWritten may be NULL.
5177 * Will fail if not all specified number of bytes have been written.
5179 BOOL SmallBlockChainStream_WriteAt(
5180 SmallBlockChainStream* This,
5181 ULARGE_INTEGER offset,
5182 ULONG size,
5183 const void* buffer,
5184 ULONG* bytesWritten)
5186 ULARGE_INTEGER offsetInBigBlockFile;
5187 ULONG blockNoInSequence =
5188 offset.u.LowPart / This->parentStorage->smallBlockSize;
5190 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5191 ULONG bytesToWriteInBuffer;
5192 ULONG blockIndex;
5193 ULONG bytesWrittenFromBigBlockFile;
5194 const BYTE* bufferWalker;
5197 * This should never happen on a small block file.
5199 assert(offset.u.HighPart==0);
5202 * Find the first block in the stream that contains part of the buffer.
5204 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5206 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5208 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5209 return FALSE;
5210 blockNoInSequence--;
5214 * Start writing the buffer.
5216 * Here, I'm casting away the constness on the buffer variable
5217 * This is OK since we don't intend to modify that buffer.
5219 *bytesWritten = 0;
5220 bufferWalker = (const BYTE*)buffer;
5221 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5224 * Calculate how many bytes we can copy to this small block.
5226 bytesToWriteInBuffer =
5227 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5230 * Calculate the offset of the small block in the small block file.
5232 offsetInBigBlockFile.u.HighPart = 0;
5233 offsetInBigBlockFile.u.LowPart =
5234 blockIndex * This->parentStorage->smallBlockSize;
5236 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5239 * Write those bytes in the buffer to the small block file.
5241 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5242 offsetInBigBlockFile,
5243 bytesToWriteInBuffer,
5244 bufferWalker,
5245 &bytesWrittenFromBigBlockFile);
5247 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5250 * Step to the next big block.
5252 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5253 &blockIndex)))
5254 return FALSE;
5255 bufferWalker += bytesToWriteInBuffer;
5256 size -= bytesToWriteInBuffer;
5257 *bytesWritten += bytesToWriteInBuffer;
5258 offsetInBlock = 0; /* There is no offset on the next block */
5261 return (size == 0);
5264 /******************************************************************************
5265 * SmallBlockChainStream_Shrink
5267 * Shrinks this chain in the small block depot.
5269 BOOL SmallBlockChainStream_Shrink(
5270 SmallBlockChainStream* This,
5271 ULARGE_INTEGER newSize)
5273 ULONG blockIndex, extraBlock;
5274 ULONG numBlocks;
5275 ULONG count = 0;
5277 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5279 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5280 numBlocks++;
5282 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5285 * Go to the new end of chain
5287 while (count < numBlocks)
5289 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5290 &blockIndex)))
5291 return FALSE;
5292 count++;
5296 * If the count is 0, we have a special case, the head of the chain was
5297 * just freed.
5299 if (count == 0)
5301 StgProperty chainProp;
5303 StorageImpl_ReadProperty(This->parentStorage,
5304 This->ownerPropertyIndex,
5305 &chainProp);
5307 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5309 StorageImpl_WriteProperty(This->parentStorage,
5310 This->ownerPropertyIndex,
5311 &chainProp);
5314 * We start freeing the chain at the head block.
5316 extraBlock = blockIndex;
5318 else
5320 /* Get the next block before marking the new end */
5321 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5322 &extraBlock)))
5323 return FALSE;
5325 /* Mark the new end of chain */
5326 SmallBlockChainStream_SetNextBlockInChain(
5327 This,
5328 blockIndex,
5329 BLOCK_END_OF_CHAIN);
5333 * Mark the extra blocks as free
5335 while (extraBlock != BLOCK_END_OF_CHAIN)
5337 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5338 &blockIndex)))
5339 return FALSE;
5340 SmallBlockChainStream_FreeBlock(This, extraBlock);
5341 extraBlock = blockIndex;
5344 return TRUE;
5347 /******************************************************************************
5348 * SmallBlockChainStream_Enlarge
5350 * Grows this chain in the small block depot.
5352 BOOL SmallBlockChainStream_Enlarge(
5353 SmallBlockChainStream* This,
5354 ULARGE_INTEGER newSize)
5356 ULONG blockIndex, currentBlock;
5357 ULONG newNumBlocks;
5358 ULONG oldNumBlocks = 0;
5360 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5363 * Empty chain
5365 if (blockIndex == BLOCK_END_OF_CHAIN)
5368 StgProperty chainProp;
5370 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5371 &chainProp);
5373 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5375 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5376 &chainProp);
5378 blockIndex = chainProp.startingBlock;
5379 SmallBlockChainStream_SetNextBlockInChain(
5380 This,
5381 blockIndex,
5382 BLOCK_END_OF_CHAIN);
5385 currentBlock = blockIndex;
5388 * Figure out how many blocks are needed to contain this stream
5390 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5392 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5393 newNumBlocks++;
5396 * Go to the current end of chain
5398 while (blockIndex != BLOCK_END_OF_CHAIN)
5400 oldNumBlocks++;
5401 currentBlock = blockIndex;
5402 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5403 return FALSE;
5407 * Add new blocks to the chain
5409 while (oldNumBlocks < newNumBlocks)
5411 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5412 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5414 SmallBlockChainStream_SetNextBlockInChain(
5415 This,
5416 blockIndex,
5417 BLOCK_END_OF_CHAIN);
5419 currentBlock = blockIndex;
5420 oldNumBlocks++;
5423 return TRUE;
5426 /******************************************************************************
5427 * SmallBlockChainStream_GetCount
5429 * Returns the number of blocks that comprises this chain.
5430 * This is not the size of this chain as the last block may not be full!
5432 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5434 ULONG blockIndex;
5435 ULONG count = 0;
5437 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5439 while (blockIndex != BLOCK_END_OF_CHAIN)
5441 count++;
5443 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5444 return 0;
5447 return count;
5450 /******************************************************************************
5451 * SmallBlockChainStream_SetSize
5453 * Sets the size of this stream.
5454 * The file will grow if we grow the chain.
5456 * TODO: Free the actual blocks in the file when we shrink the chain.
5457 * Currently, the blocks are still in the file. So the file size
5458 * doesn't shrink even if we shrink streams.
5460 BOOL SmallBlockChainStream_SetSize(
5461 SmallBlockChainStream* This,
5462 ULARGE_INTEGER newSize)
5464 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5466 if (newSize.u.LowPart == size.u.LowPart)
5467 return TRUE;
5469 if (newSize.u.LowPart < size.u.LowPart)
5471 SmallBlockChainStream_Shrink(This, newSize);
5473 else
5475 SmallBlockChainStream_Enlarge(This, newSize);
5478 return TRUE;
5481 /******************************************************************************
5482 * SmallBlockChainStream_GetSize
5484 * Returns the size of this chain.
5486 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5488 StgProperty chainProperty;
5490 StorageImpl_ReadProperty(
5491 This->parentStorage,
5492 This->ownerPropertyIndex,
5493 &chainProperty);
5495 return chainProperty.size;
5498 /******************************************************************************
5499 * StgCreateDocfile [OLE32.@]
5501 HRESULT WINAPI StgCreateDocfile(
5502 LPCOLESTR pwcsName,
5503 DWORD grfMode,
5504 DWORD reserved,
5505 IStorage **ppstgOpen)
5507 StorageImpl* newStorage = 0;
5508 HANDLE hFile = INVALID_HANDLE_VALUE;
5509 HRESULT hr = STG_E_INVALIDFLAG;
5510 DWORD shareMode;
5511 DWORD accessMode;
5512 DWORD creationMode;
5513 DWORD fileAttributes;
5514 WCHAR tempFileName[MAX_PATH];
5516 TRACE("(%s, %lx, %ld, %p)\n",
5517 debugstr_w(pwcsName), grfMode,
5518 reserved, ppstgOpen);
5521 * Validate the parameters
5523 if (ppstgOpen == 0)
5524 return STG_E_INVALIDPOINTER;
5525 if (reserved != 0)
5526 return STG_E_INVALIDPARAMETER;
5529 * Validate the STGM flags
5531 if ( FAILED( validateSTGM(grfMode) ))
5532 goto end;
5534 /* StgCreateDocFile always opens for write */
5535 switch(STGM_ACCESS_MODE(grfMode))
5537 case STGM_WRITE:
5538 case STGM_READWRITE:
5539 break;
5540 default:
5541 goto end;
5544 /* can't share write */
5545 switch(STGM_SHARE_MODE(grfMode))
5547 case STGM_SHARE_EXCLUSIVE:
5548 case STGM_SHARE_DENY_WRITE:
5549 break;
5550 default:
5551 goto end;
5554 /* shared reading requires transacted mode */
5555 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5556 !(grfMode&STGM_TRANSACTED) )
5557 goto end;
5560 * Generate a unique name.
5562 if (pwcsName == 0)
5564 WCHAR tempPath[MAX_PATH];
5565 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5567 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
5568 goto end;
5570 memset(tempPath, 0, sizeof(tempPath));
5571 memset(tempFileName, 0, sizeof(tempFileName));
5573 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5574 tempPath[0] = '.';
5576 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5577 pwcsName = tempFileName;
5578 else
5580 hr = STG_E_INSUFFICIENTMEMORY;
5581 goto end;
5584 creationMode = TRUNCATE_EXISTING;
5586 else
5588 creationMode = GetCreationModeFromSTGM(grfMode);
5592 * Interpret the STGM value grfMode
5594 shareMode = GetShareModeFromSTGM(grfMode);
5595 accessMode = GetAccessModeFromSTGM(grfMode);
5597 if (grfMode & STGM_DELETEONRELEASE)
5598 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5599 else
5600 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5602 if (grfMode & STGM_TRANSACTED)
5603 FIXME("Transacted mode not implemented.\n");
5606 * Initialize the "out" parameter.
5608 *ppstgOpen = 0;
5610 hFile = CreateFileW(pwcsName,
5611 accessMode,
5612 shareMode,
5613 NULL,
5614 creationMode,
5615 fileAttributes,
5618 if (hFile == INVALID_HANDLE_VALUE)
5620 if(GetLastError() == ERROR_FILE_EXISTS)
5621 hr = STG_E_FILEALREADYEXISTS;
5622 else
5623 hr = E_FAIL;
5624 goto end;
5628 * Allocate and initialize the new IStorage32object.
5630 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5632 if (newStorage == 0)
5634 hr = STG_E_INSUFFICIENTMEMORY;
5635 goto end;
5638 hr = StorageImpl_Construct(
5639 newStorage,
5640 hFile,
5641 pwcsName,
5642 NULL,
5643 grfMode,
5644 TRUE,
5645 TRUE);
5647 if (FAILED(hr))
5649 HeapFree(GetProcessHeap(), 0, newStorage);
5650 goto end;
5654 * Get an "out" pointer for the caller.
5656 hr = StorageBaseImpl_QueryInterface(
5657 (IStorage*)newStorage,
5658 (REFIID)&IID_IStorage,
5659 (void**)ppstgOpen);
5660 end:
5661 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5663 return hr;
5666 /******************************************************************************
5667 * StgCreateStorageEx [OLE32.@]
5669 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5671 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5672 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5674 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5676 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5677 return STG_E_INVALIDPARAMETER;
5680 if (stgfmt != STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5682 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5683 return STG_E_INVALIDPARAMETER;
5686 if (stgfmt == STGFMT_FILE)
5688 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5689 return STG_E_INVALIDPARAMETER;
5692 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5694 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5695 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5698 ERR("Invalid stgfmt argument\n");
5699 return STG_E_INVALIDPARAMETER;
5702 /******************************************************************************
5703 * StgCreatePropSetStg [OLE32.@]
5705 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5706 IPropertySetStorage **ppPropSetStg)
5708 HRESULT hr;
5710 TRACE("(%p, 0x%lx, %p): stub\n", pstg, reserved, ppPropSetStg);
5711 if (reserved)
5712 hr = STG_E_INVALIDPARAMETER;
5713 else
5714 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5715 (void**)ppPropSetStg);
5716 return hr;
5719 /******************************************************************************
5720 * StgOpenStorageEx [OLE32.@]
5722 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5724 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5725 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5727 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5729 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5730 return STG_E_INVALIDPARAMETER;
5733 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5735 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5736 return STG_E_INVALIDPARAMETER;
5739 if (stgfmt == STGFMT_FILE)
5741 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5742 return STG_E_INVALIDPARAMETER;
5745 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE || stgfmt == STGFMT_ANY)
5747 if (stgfmt == STGFMT_ANY)
5748 WARN("STGFMT_ANY assuming storage\n");
5749 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5750 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5753 ERR("Invalid stgfmt argument\n");
5754 return STG_E_INVALIDPARAMETER;
5758 /******************************************************************************
5759 * StgOpenStorage [OLE32.@]
5761 HRESULT WINAPI StgOpenStorage(
5762 const OLECHAR *pwcsName,
5763 IStorage *pstgPriority,
5764 DWORD grfMode,
5765 SNB snbExclude,
5766 DWORD reserved,
5767 IStorage **ppstgOpen)
5769 StorageImpl* newStorage = 0;
5770 HRESULT hr = S_OK;
5771 HANDLE hFile = 0;
5772 DWORD shareMode;
5773 DWORD accessMode;
5774 WCHAR fullname[MAX_PATH];
5775 DWORD length;
5777 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5778 debugstr_w(pwcsName), pstgPriority, grfMode,
5779 snbExclude, reserved, ppstgOpen);
5782 * Perform sanity checks
5784 if (pwcsName == 0)
5786 hr = STG_E_INVALIDNAME;
5787 goto end;
5790 if (ppstgOpen == 0)
5792 hr = STG_E_INVALIDPOINTER;
5793 goto end;
5796 if (reserved)
5798 hr = STG_E_INVALIDPARAMETER;
5799 goto end;
5803 * Validate the sharing mode
5805 if (!(grfMode & STGM_TRANSACTED))
5806 switch(STGM_SHARE_MODE(grfMode))
5808 case STGM_SHARE_EXCLUSIVE:
5809 case STGM_SHARE_DENY_WRITE:
5810 break;
5811 default:
5812 hr = STG_E_INVALIDFLAG;
5813 goto end;
5817 * Validate the STGM flags
5819 if ( FAILED( validateSTGM(grfMode) ) ||
5820 (grfMode&STGM_CREATE))
5822 hr = STG_E_INVALIDFLAG;
5823 goto end;
5826 /* shared reading requires transacted mode */
5827 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5828 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5829 !(grfMode&STGM_TRANSACTED) )
5831 hr = STG_E_INVALIDFLAG;
5832 goto end;
5836 * Interpret the STGM value grfMode
5838 shareMode = GetShareModeFromSTGM(grfMode);
5839 accessMode = GetAccessModeFromSTGM(grfMode);
5842 * Initialize the "out" parameter.
5844 *ppstgOpen = 0;
5846 hFile = CreateFileW( pwcsName,
5847 accessMode,
5848 shareMode,
5849 NULL,
5850 OPEN_EXISTING,
5851 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5854 if (hFile==INVALID_HANDLE_VALUE)
5856 DWORD last_error = GetLastError();
5858 hr = E_FAIL;
5860 switch (last_error)
5862 case ERROR_FILE_NOT_FOUND:
5863 hr = STG_E_FILENOTFOUND;
5864 break;
5866 case ERROR_PATH_NOT_FOUND:
5867 hr = STG_E_PATHNOTFOUND;
5868 break;
5870 case ERROR_ACCESS_DENIED:
5871 case ERROR_WRITE_PROTECT:
5872 hr = STG_E_ACCESSDENIED;
5873 break;
5875 case ERROR_SHARING_VIOLATION:
5876 hr = STG_E_SHAREVIOLATION;
5877 break;
5879 default:
5880 hr = E_FAIL;
5883 goto end;
5887 * Refuse to open the file if it's too small to be a structured storage file
5888 * FIXME: verify the file when reading instead of here
5890 length = GetFileSize(hFile, NULL);
5891 if (length < 0x100)
5893 CloseHandle(hFile);
5894 hr = STG_E_FILEALREADYEXISTS;
5895 goto end;
5899 * Allocate and initialize the new IStorage32object.
5901 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5903 if (newStorage == 0)
5905 hr = STG_E_INSUFFICIENTMEMORY;
5906 goto end;
5909 /* if the file's length was zero, initialize the storage */
5910 hr = StorageImpl_Construct(
5911 newStorage,
5912 hFile,
5913 pwcsName,
5914 NULL,
5915 grfMode,
5916 TRUE,
5917 FALSE );
5919 if (FAILED(hr))
5921 HeapFree(GetProcessHeap(), 0, newStorage);
5923 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5925 if(hr == STG_E_INVALIDHEADER)
5926 hr = STG_E_FILEALREADYEXISTS;
5927 goto end;
5930 /* prepare the file name string given in lieu of the root property name */
5931 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5932 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5933 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5936 * Get an "out" pointer for the caller.
5938 hr = StorageBaseImpl_QueryInterface(
5939 (IStorage*)newStorage,
5940 (REFIID)&IID_IStorage,
5941 (void**)ppstgOpen);
5943 end:
5944 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5945 return hr;
5948 /******************************************************************************
5949 * StgCreateDocfileOnILockBytes [OLE32.@]
5951 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5952 ILockBytes *plkbyt,
5953 DWORD grfMode,
5954 DWORD reserved,
5955 IStorage** ppstgOpen)
5957 StorageImpl* newStorage = 0;
5958 HRESULT hr = S_OK;
5961 * Validate the parameters
5963 if ((ppstgOpen == 0) || (plkbyt == 0))
5964 return STG_E_INVALIDPOINTER;
5967 * Allocate and initialize the new IStorage object.
5969 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5971 if (newStorage == 0)
5972 return STG_E_INSUFFICIENTMEMORY;
5974 hr = StorageImpl_Construct(
5975 newStorage,
5978 plkbyt,
5979 grfMode,
5980 FALSE,
5981 TRUE);
5983 if (FAILED(hr))
5985 HeapFree(GetProcessHeap(), 0, newStorage);
5986 return hr;
5990 * Get an "out" pointer for the caller.
5992 hr = StorageBaseImpl_QueryInterface(
5993 (IStorage*)newStorage,
5994 (REFIID)&IID_IStorage,
5995 (void**)ppstgOpen);
5997 return hr;
6000 /******************************************************************************
6001 * StgOpenStorageOnILockBytes [OLE32.@]
6003 HRESULT WINAPI StgOpenStorageOnILockBytes(
6004 ILockBytes *plkbyt,
6005 IStorage *pstgPriority,
6006 DWORD grfMode,
6007 SNB snbExclude,
6008 DWORD reserved,
6009 IStorage **ppstgOpen)
6011 StorageImpl* newStorage = 0;
6012 HRESULT hr = S_OK;
6015 * Perform a sanity check
6017 if ((plkbyt == 0) || (ppstgOpen == 0))
6018 return STG_E_INVALIDPOINTER;
6021 * Validate the STGM flags
6023 if ( FAILED( validateSTGM(grfMode) ))
6024 return STG_E_INVALIDFLAG;
6027 * Initialize the "out" parameter.
6029 *ppstgOpen = 0;
6032 * Allocate and initialize the new IStorage object.
6034 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6036 if (newStorage == 0)
6037 return STG_E_INSUFFICIENTMEMORY;
6039 hr = StorageImpl_Construct(
6040 newStorage,
6043 plkbyt,
6044 grfMode,
6045 FALSE,
6046 FALSE);
6048 if (FAILED(hr))
6050 HeapFree(GetProcessHeap(), 0, newStorage);
6051 return hr;
6055 * Get an "out" pointer for the caller.
6057 hr = StorageBaseImpl_QueryInterface(
6058 (IStorage*)newStorage,
6059 (REFIID)&IID_IStorage,
6060 (void**)ppstgOpen);
6062 return hr;
6065 /******************************************************************************
6066 * StgSetTimes [ole32.@]
6067 * StgSetTimes [OLE32.@]
6071 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6072 FILETIME const *patime, FILETIME const *pmtime)
6074 IStorage *stg = NULL;
6075 HRESULT r;
6077 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6079 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6080 0, 0, &stg);
6081 if( SUCCEEDED(r) )
6083 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6084 IStorage_Release(stg);
6087 return r;
6090 /******************************************************************************
6091 * StgIsStorageILockBytes [OLE32.@]
6093 * Determines if the ILockBytes contains a storage object.
6095 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6097 BYTE sig[8];
6098 ULARGE_INTEGER offset;
6100 offset.u.HighPart = 0;
6101 offset.u.LowPart = 0;
6103 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6105 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6106 return S_OK;
6108 return S_FALSE;
6111 /******************************************************************************
6112 * WriteClassStg [OLE32.@]
6114 * This method will store the specified CLSID in the specified storage object
6116 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6118 HRESULT hRes;
6120 assert(pStg != 0);
6122 hRes = IStorage_SetClass(pStg, rclsid);
6124 return hRes;
6127 /***********************************************************************
6128 * ReadClassStg (OLE32.@)
6130 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
6132 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6134 STATSTG pstatstg;
6135 HRESULT hRes;
6137 TRACE("()\n");
6139 if(pclsid==NULL)
6140 return E_POINTER;
6142 * read a STATSTG structure (contains the clsid) from the storage
6144 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6146 if(SUCCEEDED(hRes))
6147 *pclsid=pstatstg.clsid;
6149 return hRes;
6152 /***********************************************************************
6153 * OleLoadFromStream (OLE32.@)
6155 * This function loads an object from stream
6157 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6159 CLSID clsid;
6160 HRESULT res;
6161 LPPERSISTSTREAM xstm;
6163 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6165 res=ReadClassStm(pStm,&clsid);
6166 if (!SUCCEEDED(res))
6167 return res;
6168 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6169 if (!SUCCEEDED(res))
6170 return res;
6171 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6172 if (!SUCCEEDED(res)) {
6173 IUnknown_Release((IUnknown*)*ppvObj);
6174 return res;
6176 res=IPersistStream_Load(xstm,pStm);
6177 IPersistStream_Release(xstm);
6178 /* FIXME: all refcounts ok at this point? I think they should be:
6179 * pStm : unchanged
6180 * ppvObj : 1
6181 * xstm : 0 (released)
6183 return res;
6186 /***********************************************************************
6187 * OleSaveToStream (OLE32.@)
6189 * This function saves an object with the IPersistStream interface on it
6190 * to the specified stream.
6192 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6195 CLSID clsid;
6196 HRESULT res;
6198 TRACE("(%p,%p)\n",pPStm,pStm);
6200 res=IPersistStream_GetClassID(pPStm,&clsid);
6202 if (SUCCEEDED(res)){
6204 res=WriteClassStm(pStm,&clsid);
6206 if (SUCCEEDED(res))
6208 res=IPersistStream_Save(pPStm,pStm,TRUE);
6211 TRACE("Finished Save\n");
6212 return res;
6215 /****************************************************************************
6216 * This method validate a STGM parameter that can contain the values below
6218 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6219 * The stgm values contained in 0xffff0000 are bitmasks.
6221 * STGM_DIRECT 0x00000000
6222 * STGM_TRANSACTED 0x00010000
6223 * STGM_SIMPLE 0x08000000
6225 * STGM_READ 0x00000000
6226 * STGM_WRITE 0x00000001
6227 * STGM_READWRITE 0x00000002
6229 * STGM_SHARE_DENY_NONE 0x00000040
6230 * STGM_SHARE_DENY_READ 0x00000030
6231 * STGM_SHARE_DENY_WRITE 0x00000020
6232 * STGM_SHARE_EXCLUSIVE 0x00000010
6234 * STGM_PRIORITY 0x00040000
6235 * STGM_DELETEONRELEASE 0x04000000
6237 * STGM_CREATE 0x00001000
6238 * STGM_CONVERT 0x00020000
6239 * STGM_FAILIFTHERE 0x00000000
6241 * STGM_NOSCRATCH 0x00100000
6242 * STGM_NOSNAPSHOT 0x00200000
6244 static HRESULT validateSTGM(DWORD stgm)
6246 DWORD access = STGM_ACCESS_MODE(stgm);
6247 DWORD share = STGM_SHARE_MODE(stgm);
6248 DWORD create = STGM_CREATE_MODE(stgm);
6250 if (stgm&~STGM_KNOWN_FLAGS)
6252 ERR("unknown flags %08lx\n", stgm);
6253 return E_FAIL;
6256 switch (access)
6258 case STGM_READ:
6259 case STGM_WRITE:
6260 case STGM_READWRITE:
6261 break;
6262 default:
6263 return E_FAIL;
6266 switch (share)
6268 case STGM_SHARE_DENY_NONE:
6269 case STGM_SHARE_DENY_READ:
6270 case STGM_SHARE_DENY_WRITE:
6271 case STGM_SHARE_EXCLUSIVE:
6272 break;
6273 default:
6274 return E_FAIL;
6277 switch (create)
6279 case STGM_CREATE:
6280 case STGM_FAILIFTHERE:
6281 break;
6282 default:
6283 return E_FAIL;
6287 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6289 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6290 return E_FAIL;
6293 * STGM_CREATE | STGM_CONVERT
6294 * if both are false, STGM_FAILIFTHERE is set to TRUE
6296 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6297 return E_FAIL;
6300 * STGM_NOSCRATCH requires STGM_TRANSACTED
6302 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6303 return E_FAIL;
6306 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6307 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6309 if ( (stgm & STGM_NOSNAPSHOT) &&
6310 (!(stgm & STGM_TRANSACTED) ||
6311 share == STGM_SHARE_EXCLUSIVE ||
6312 share == STGM_SHARE_DENY_WRITE) )
6313 return E_FAIL;
6315 return S_OK;
6318 /****************************************************************************
6319 * GetShareModeFromSTGM
6321 * This method will return a share mode flag from a STGM value.
6322 * The STGM value is assumed valid.
6324 static DWORD GetShareModeFromSTGM(DWORD stgm)
6326 switch (STGM_SHARE_MODE(stgm))
6328 case STGM_SHARE_DENY_NONE:
6329 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6330 case STGM_SHARE_DENY_READ:
6331 return FILE_SHARE_WRITE;
6332 case STGM_SHARE_DENY_WRITE:
6333 return FILE_SHARE_READ;
6334 case STGM_SHARE_EXCLUSIVE:
6335 return 0;
6337 ERR("Invalid share mode!\n");
6338 assert(0);
6339 return 0;
6342 /****************************************************************************
6343 * GetAccessModeFromSTGM
6345 * This method will return an access mode flag from a STGM value.
6346 * The STGM value is assumed valid.
6348 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6350 switch (STGM_ACCESS_MODE(stgm))
6352 case STGM_READ:
6353 return GENERIC_READ;
6354 case STGM_WRITE:
6355 case STGM_READWRITE:
6356 return GENERIC_READ | GENERIC_WRITE;
6358 ERR("Invalid access mode!\n");
6359 assert(0);
6360 return 0;
6363 /****************************************************************************
6364 * GetCreationModeFromSTGM
6366 * This method will return a creation mode flag from a STGM value.
6367 * The STGM value is assumed valid.
6369 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6371 switch(STGM_CREATE_MODE(stgm))
6373 case STGM_CREATE:
6374 return CREATE_ALWAYS;
6375 case STGM_CONVERT:
6376 FIXME("STGM_CONVERT not implemented!\n");
6377 return CREATE_NEW;
6378 case STGM_FAILIFTHERE:
6379 return CREATE_NEW;
6381 ERR("Invalid create mode!\n");
6382 assert(0);
6383 return 0;
6387 /*************************************************************************
6388 * OLECONVERT_LoadOLE10 [Internal]
6390 * Loads the OLE10 STREAM to memory
6392 * PARAMS
6393 * pOleStream [I] The OLESTREAM
6394 * pData [I] Data Structure for the OLESTREAM Data
6396 * RETURNS
6397 * Success: S_OK
6398 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6399 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6401 * NOTES
6402 * This function is used by OleConvertOLESTREAMToIStorage only.
6404 * Memory allocated for pData must be freed by the caller
6406 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6408 DWORD dwSize;
6409 HRESULT hRes = S_OK;
6410 int nTryCnt=0;
6411 int max_try = 6;
6413 pData->pData = NULL;
6414 pData->pstrOleObjFileName = (CHAR *) NULL;
6416 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6418 /* Get the OleID */
6419 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6420 if(dwSize != sizeof(pData->dwOleID))
6422 hRes = CONVERT10_E_OLESTREAM_GET;
6424 else if(pData->dwOleID != OLESTREAM_ID)
6426 hRes = CONVERT10_E_OLESTREAM_FMT;
6428 else
6430 hRes = S_OK;
6431 break;
6435 if(hRes == S_OK)
6437 /* Get the TypeID...more info needed for this field */
6438 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6439 if(dwSize != sizeof(pData->dwTypeID))
6441 hRes = CONVERT10_E_OLESTREAM_GET;
6444 if(hRes == S_OK)
6446 if(pData->dwTypeID != 0)
6448 /* Get the length of the OleTypeName */
6449 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6450 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6452 hRes = CONVERT10_E_OLESTREAM_GET;
6455 if(hRes == S_OK)
6457 if(pData->dwOleTypeNameLength > 0)
6459 /* Get the OleTypeName */
6460 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6461 if(dwSize != pData->dwOleTypeNameLength)
6463 hRes = CONVERT10_E_OLESTREAM_GET;
6467 if(bStrem1)
6469 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6470 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6472 hRes = CONVERT10_E_OLESTREAM_GET;
6474 if(hRes == S_OK)
6476 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6477 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6478 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6479 if(pData->pstrOleObjFileName)
6481 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6482 if(dwSize != pData->dwOleObjFileNameLength)
6484 hRes = CONVERT10_E_OLESTREAM_GET;
6487 else
6488 hRes = CONVERT10_E_OLESTREAM_GET;
6491 else
6493 /* Get the Width of the Metafile */
6494 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6495 if(dwSize != sizeof(pData->dwMetaFileWidth))
6497 hRes = CONVERT10_E_OLESTREAM_GET;
6499 if(hRes == S_OK)
6501 /* Get the Height of the Metafile */
6502 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6503 if(dwSize != sizeof(pData->dwMetaFileHeight))
6505 hRes = CONVERT10_E_OLESTREAM_GET;
6509 if(hRes == S_OK)
6511 /* Get the Length of the Data */
6512 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6513 if(dwSize != sizeof(pData->dwDataLength))
6515 hRes = CONVERT10_E_OLESTREAM_GET;
6519 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6521 if(!bStrem1) /* if it is a second OLE stream data */
6523 pData->dwDataLength -= 8;
6524 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6525 if(dwSize != sizeof(pData->strUnknown))
6527 hRes = CONVERT10_E_OLESTREAM_GET;
6531 if(hRes == S_OK)
6533 if(pData->dwDataLength > 0)
6535 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6537 /* Get Data (ex. IStorage, Metafile, or BMP) */
6538 if(pData->pData)
6540 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6541 if(dwSize != pData->dwDataLength)
6543 hRes = CONVERT10_E_OLESTREAM_GET;
6546 else
6548 hRes = CONVERT10_E_OLESTREAM_GET;
6554 return hRes;
6557 /*************************************************************************
6558 * OLECONVERT_SaveOLE10 [Internal]
6560 * Saves the OLE10 STREAM From memory
6562 * PARAMS
6563 * pData [I] Data Structure for the OLESTREAM Data
6564 * pOleStream [I] The OLESTREAM to save
6566 * RETURNS
6567 * Success: S_OK
6568 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6570 * NOTES
6571 * This function is used by OleConvertIStorageToOLESTREAM only.
6574 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6576 DWORD dwSize;
6577 HRESULT hRes = S_OK;
6580 /* Set the OleID */
6581 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6582 if(dwSize != sizeof(pData->dwOleID))
6584 hRes = CONVERT10_E_OLESTREAM_PUT;
6587 if(hRes == S_OK)
6589 /* Set the TypeID */
6590 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6591 if(dwSize != sizeof(pData->dwTypeID))
6593 hRes = CONVERT10_E_OLESTREAM_PUT;
6597 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6599 /* Set the Length of the OleTypeName */
6600 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6601 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6603 hRes = CONVERT10_E_OLESTREAM_PUT;
6606 if(hRes == S_OK)
6608 if(pData->dwOleTypeNameLength > 0)
6610 /* Set the OleTypeName */
6611 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6612 if(dwSize != pData->dwOleTypeNameLength)
6614 hRes = CONVERT10_E_OLESTREAM_PUT;
6619 if(hRes == S_OK)
6621 /* Set the width of the Metafile */
6622 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6623 if(dwSize != sizeof(pData->dwMetaFileWidth))
6625 hRes = CONVERT10_E_OLESTREAM_PUT;
6629 if(hRes == S_OK)
6631 /* Set the height of the Metafile */
6632 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6633 if(dwSize != sizeof(pData->dwMetaFileHeight))
6635 hRes = CONVERT10_E_OLESTREAM_PUT;
6639 if(hRes == S_OK)
6641 /* Set the length of the Data */
6642 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6643 if(dwSize != sizeof(pData->dwDataLength))
6645 hRes = CONVERT10_E_OLESTREAM_PUT;
6649 if(hRes == S_OK)
6651 if(pData->dwDataLength > 0)
6653 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6654 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6655 if(dwSize != pData->dwDataLength)
6657 hRes = CONVERT10_E_OLESTREAM_PUT;
6662 return hRes;
6665 /*************************************************************************
6666 * OLECONVERT_GetOLE20FromOLE10[Internal]
6668 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6669 * opens it, and copies the content to the dest IStorage for
6670 * OleConvertOLESTREAMToIStorage
6673 * PARAMS
6674 * pDestStorage [I] The IStorage to copy the data to
6675 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6676 * nBufferLength [I] The size of the buffer
6678 * RETURNS
6679 * Nothing
6681 * NOTES
6685 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6687 HRESULT hRes;
6688 HANDLE hFile;
6689 IStorage *pTempStorage;
6690 DWORD dwNumOfBytesWritten;
6691 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6692 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6694 /* Create a temp File */
6695 GetTempPathW(MAX_PATH, wstrTempDir);
6696 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6697 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6699 if(hFile != INVALID_HANDLE_VALUE)
6701 /* Write IStorage Data to File */
6702 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6703 CloseHandle(hFile);
6705 /* Open and copy temp storage to the Dest Storage */
6706 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6707 if(hRes == S_OK)
6709 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6710 StorageBaseImpl_Release(pTempStorage);
6712 DeleteFileW(wstrTempFile);
6717 /*************************************************************************
6718 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6720 * Saves the OLE10 STREAM From memory
6722 * PARAMS
6723 * pStorage [I] The Src IStorage to copy
6724 * pData [I] The Dest Memory to write to.
6726 * RETURNS
6727 * The size in bytes allocated for pData
6729 * NOTES
6730 * Memory allocated for pData must be freed by the caller
6732 * Used by OleConvertIStorageToOLESTREAM only.
6735 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6737 HANDLE hFile;
6738 HRESULT hRes;
6739 DWORD nDataLength = 0;
6740 IStorage *pTempStorage;
6741 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6742 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6744 *pData = NULL;
6746 /* Create temp Storage */
6747 GetTempPathW(MAX_PATH, wstrTempDir);
6748 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6749 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6751 if(hRes == S_OK)
6753 /* Copy Src Storage to the Temp Storage */
6754 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6755 StorageBaseImpl_Release(pTempStorage);
6757 /* Open Temp Storage as a file and copy to memory */
6758 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6759 if(hFile != INVALID_HANDLE_VALUE)
6761 nDataLength = GetFileSize(hFile, NULL);
6762 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6763 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6764 CloseHandle(hFile);
6766 DeleteFileW(wstrTempFile);
6768 return nDataLength;
6771 /*************************************************************************
6772 * OLECONVERT_CreateOleStream [Internal]
6774 * Creates the "\001OLE" stream in the IStorage if necessary.
6776 * PARAMS
6777 * pStorage [I] Dest storage to create the stream in
6779 * RETURNS
6780 * Nothing
6782 * NOTES
6783 * This function is used by OleConvertOLESTREAMToIStorage only.
6785 * This stream is still unknown, MS Word seems to have extra data
6786 * but since the data is stored in the OLESTREAM there should be
6787 * no need to recreate the stream. If the stream is manually
6788 * deleted it will create it with this default data.
6791 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6793 HRESULT hRes;
6794 IStream *pStream;
6795 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6796 BYTE pOleStreamHeader [] =
6798 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6799 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6800 0x00, 0x00, 0x00, 0x00
6803 /* Create stream if not present */
6804 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6805 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6807 if(hRes == S_OK)
6809 /* Write default Data */
6810 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6811 IStream_Release(pStream);
6815 /* write a string to a stream, preceded by its length */
6816 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6818 HRESULT r;
6819 LPSTR str;
6820 DWORD len = 0;
6822 if( string )
6823 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6824 r = IStream_Write( stm, &len, sizeof(len), NULL);
6825 if( FAILED( r ) )
6826 return r;
6827 if(len == 0)
6828 return r;
6829 str = CoTaskMemAlloc( len );
6830 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6831 r = IStream_Write( stm, str, len, NULL);
6832 CoTaskMemFree( str );
6833 return r;
6836 /* read a string preceded by its length from a stream */
6837 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6839 HRESULT r;
6840 DWORD len, count = 0;
6841 LPSTR str;
6842 LPWSTR wstr;
6844 r = IStream_Read( stm, &len, sizeof(len), &count );
6845 if( FAILED( r ) )
6846 return r;
6847 if( count != sizeof(len) )
6848 return E_OUTOFMEMORY;
6850 TRACE("%ld bytes\n",len);
6852 str = CoTaskMemAlloc( len );
6853 if( !str )
6854 return E_OUTOFMEMORY;
6855 count = 0;
6856 r = IStream_Read( stm, str, len, &count );
6857 if( FAILED( r ) )
6858 return r;
6859 if( count != len )
6861 CoTaskMemFree( str );
6862 return E_OUTOFMEMORY;
6865 TRACE("Read string %s\n",debugstr_an(str,len));
6867 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6868 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6869 if( wstr )
6870 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6871 CoTaskMemFree( str );
6873 *string = wstr;
6875 return r;
6879 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6880 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6882 IStream *pstm;
6883 HRESULT r = S_OK;
6884 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6886 static const BYTE unknown1[12] =
6887 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6888 0xFF, 0xFF, 0xFF, 0xFF};
6889 static const BYTE unknown2[16] =
6890 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6891 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6893 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6894 debugstr_w(lpszUserType), debugstr_w(szClipName),
6895 debugstr_w(szProgIDName));
6897 /* Create a CompObj stream if it doesn't exist */
6898 r = IStorage_CreateStream(pstg, szwStreamName,
6899 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6900 if( FAILED (r) )
6901 return r;
6903 /* Write CompObj Structure to stream */
6904 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6906 if( SUCCEEDED( r ) )
6907 r = WriteClassStm( pstm, clsid );
6909 if( SUCCEEDED( r ) )
6910 r = STREAM_WriteString( pstm, lpszUserType );
6911 if( SUCCEEDED( r ) )
6912 r = STREAM_WriteString( pstm, szClipName );
6913 if( SUCCEEDED( r ) )
6914 r = STREAM_WriteString( pstm, szProgIDName );
6915 if( SUCCEEDED( r ) )
6916 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6918 IStream_Release( pstm );
6920 return r;
6923 /***********************************************************************
6924 * WriteFmtUserTypeStg (OLE32.@)
6926 HRESULT WINAPI WriteFmtUserTypeStg(
6927 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6929 HRESULT r;
6930 WCHAR szwClipName[0x40];
6931 CLSID clsid = CLSID_NULL;
6932 LPWSTR wstrProgID = NULL;
6933 DWORD n;
6935 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6937 /* get the clipboard format name */
6938 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6939 szwClipName[n]=0;
6941 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6943 /* FIXME: There's room to save a CLSID and its ProgID, but
6944 the CLSID is not looked up in the registry and in all the
6945 tests I wrote it was CLSID_NULL. Where does it come from?
6948 /* get the real program ID. This may fail, but that's fine */
6949 ProgIDFromCLSID(&clsid, &wstrProgID);
6951 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6953 r = STORAGE_WriteCompObj( pstg, &clsid,
6954 lpszUserType, szwClipName, wstrProgID );
6956 CoTaskMemFree(wstrProgID);
6958 return r;
6962 /******************************************************************************
6963 * ReadFmtUserTypeStg [OLE32.@]
6965 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6967 HRESULT r;
6968 IStream *stm = 0;
6969 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6970 unsigned char unknown1[12];
6971 unsigned char unknown2[16];
6972 DWORD count;
6973 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6974 CLSID clsid;
6976 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6978 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6979 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6980 if( FAILED ( r ) )
6982 WARN("Failed to open stream r = %08lx\n", r);
6983 return r;
6986 /* read the various parts of the structure */
6987 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6988 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6989 goto end;
6990 r = ReadClassStm( stm, &clsid );
6991 if( FAILED( r ) )
6992 goto end;
6994 r = STREAM_ReadString( stm, &szCLSIDName );
6995 if( FAILED( r ) )
6996 goto end;
6998 r = STREAM_ReadString( stm, &szOleTypeName );
6999 if( FAILED( r ) )
7000 goto end;
7002 r = STREAM_ReadString( stm, &szProgIDName );
7003 if( FAILED( r ) )
7004 goto end;
7006 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7007 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7008 goto end;
7010 /* ok, success... now we just need to store what we found */
7011 if( pcf )
7012 *pcf = RegisterClipboardFormatW( szOleTypeName );
7013 CoTaskMemFree( szOleTypeName );
7015 if( lplpszUserType )
7016 *lplpszUserType = szCLSIDName;
7017 CoTaskMemFree( szProgIDName );
7019 end:
7020 IStream_Release( stm );
7022 return r;
7026 /*************************************************************************
7027 * OLECONVERT_CreateCompObjStream [Internal]
7029 * Creates a "\001CompObj" is the destination IStorage if necessary.
7031 * PARAMS
7032 * pStorage [I] The dest IStorage to create the CompObj Stream
7033 * if necessary.
7034 * strOleTypeName [I] The ProgID
7036 * RETURNS
7037 * Success: S_OK
7038 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7040 * NOTES
7041 * This function is used by OleConvertOLESTREAMToIStorage only.
7043 * The stream data is stored in the OLESTREAM and there should be
7044 * no need to recreate the stream. If the stream is manually
7045 * deleted it will attempt to create it by querying the registry.
7049 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7051 IStream *pStream;
7052 HRESULT hStorageRes, hRes = S_OK;
7053 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7054 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7055 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7057 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7058 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7060 /* Initialize the CompObj structure */
7061 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7062 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7063 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7066 /* Create a CompObj stream if it doesn't exist */
7067 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7068 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7069 if(hStorageRes == S_OK)
7071 /* copy the OleTypeName to the compobj struct */
7072 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7073 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7075 /* copy the OleTypeName to the compobj struct */
7076 /* Note: in the test made, these were Identical */
7077 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7078 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7080 /* Get the CLSID */
7081 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7082 bufferW, OLESTREAM_MAX_STR_LEN );
7083 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7085 if(hRes == S_OK)
7087 HKEY hKey;
7088 LONG hErr;
7089 /* Get the CLSID Default Name from the Registry */
7090 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7091 if(hErr == ERROR_SUCCESS)
7093 char strTemp[OLESTREAM_MAX_STR_LEN];
7094 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7095 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7096 if(hErr == ERROR_SUCCESS)
7098 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7100 RegCloseKey(hKey);
7104 /* Write CompObj Structure to stream */
7105 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7107 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7109 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7110 if(IStorageCompObj.dwCLSIDNameLength > 0)
7112 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7114 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7115 if(IStorageCompObj.dwOleTypeNameLength > 0)
7117 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7119 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7120 if(IStorageCompObj.dwProgIDNameLength > 0)
7122 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7124 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7125 IStream_Release(pStream);
7127 return hRes;
7131 /*************************************************************************
7132 * OLECONVERT_CreateOlePresStream[Internal]
7134 * Creates the "\002OlePres000" Stream with the Metafile data
7136 * PARAMS
7137 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7138 * dwExtentX [I] Width of the Metafile
7139 * dwExtentY [I] Height of the Metafile
7140 * pData [I] Metafile data
7141 * dwDataLength [I] Size of the Metafile data
7143 * RETURNS
7144 * Success: S_OK
7145 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7147 * NOTES
7148 * This function is used by OleConvertOLESTREAMToIStorage only.
7151 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7153 HRESULT hRes;
7154 IStream *pStream;
7155 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7156 BYTE pOlePresStreamHeader [] =
7158 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7159 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7160 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7161 0x00, 0x00, 0x00, 0x00
7164 BYTE pOlePresStreamHeaderEmpty [] =
7166 0x00, 0x00, 0x00, 0x00,
7167 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7168 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7169 0x00, 0x00, 0x00, 0x00
7172 /* Create the OlePres000 Stream */
7173 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7174 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7176 if(hRes == S_OK)
7178 DWORD nHeaderSize;
7179 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7181 memset(&OlePres, 0, sizeof(OlePres));
7182 /* Do we have any metafile data to save */
7183 if(dwDataLength > 0)
7185 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7186 nHeaderSize = sizeof(pOlePresStreamHeader);
7188 else
7190 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7191 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7193 /* Set width and height of the metafile */
7194 OlePres.dwExtentX = dwExtentX;
7195 OlePres.dwExtentY = -dwExtentY;
7197 /* Set Data and Length */
7198 if(dwDataLength > sizeof(METAFILEPICT16))
7200 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7201 OlePres.pData = &(pData[8]);
7203 /* Save OlePres000 Data to Stream */
7204 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7205 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7206 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7207 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7208 if(OlePres.dwSize > 0)
7210 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7212 IStream_Release(pStream);
7216 /*************************************************************************
7217 * OLECONVERT_CreateOle10NativeStream [Internal]
7219 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7221 * PARAMS
7222 * pStorage [I] Dest storage to create the stream in
7223 * pData [I] Ole10 Native Data (ex. bmp)
7224 * dwDataLength [I] Size of the Ole10 Native Data
7226 * RETURNS
7227 * Nothing
7229 * NOTES
7230 * This function is used by OleConvertOLESTREAMToIStorage only.
7232 * Might need to verify the data and return appropriate error message
7235 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7237 HRESULT hRes;
7238 IStream *pStream;
7239 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7241 /* Create the Ole10Native Stream */
7242 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7243 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7245 if(hRes == S_OK)
7247 /* Write info to stream */
7248 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7249 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7250 IStream_Release(pStream);
7255 /*************************************************************************
7256 * OLECONVERT_GetOLE10ProgID [Internal]
7258 * Finds the ProgID (or OleTypeID) from the IStorage
7260 * PARAMS
7261 * pStorage [I] The Src IStorage to get the ProgID
7262 * strProgID [I] the ProgID string to get
7263 * dwSize [I] the size of the string
7265 * RETURNS
7266 * Success: S_OK
7267 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7269 * NOTES
7270 * This function is used by OleConvertIStorageToOLESTREAM only.
7274 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7276 HRESULT hRes;
7277 IStream *pStream;
7278 LARGE_INTEGER iSeekPos;
7279 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7280 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7282 /* Open the CompObj Stream */
7283 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7284 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7285 if(hRes == S_OK)
7288 /*Get the OleType from the CompObj Stream */
7289 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7290 iSeekPos.u.HighPart = 0;
7292 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7293 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7294 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7295 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7296 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7297 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7298 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7300 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7301 if(*dwSize > 0)
7303 IStream_Read(pStream, strProgID, *dwSize, NULL);
7305 IStream_Release(pStream);
7307 else
7309 STATSTG stat;
7310 LPOLESTR wstrProgID;
7312 /* Get the OleType from the registry */
7313 REFCLSID clsid = &(stat.clsid);
7314 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7315 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7316 if(hRes == S_OK)
7318 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7322 return hRes;
7325 /*************************************************************************
7326 * OLECONVERT_GetOle10PresData [Internal]
7328 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7330 * PARAMS
7331 * pStorage [I] Src IStroage
7332 * pOleStream [I] Dest OleStream Mem Struct
7334 * RETURNS
7335 * Nothing
7337 * NOTES
7338 * This function is used by OleConvertIStorageToOLESTREAM only.
7340 * Memory allocated for pData must be freed by the caller
7344 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7347 HRESULT hRes;
7348 IStream *pStream;
7349 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7351 /* Initialize Default data for OLESTREAM */
7352 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7353 pOleStreamData[0].dwTypeID = 2;
7354 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7355 pOleStreamData[1].dwTypeID = 0;
7356 pOleStreamData[0].dwMetaFileWidth = 0;
7357 pOleStreamData[0].dwMetaFileHeight = 0;
7358 pOleStreamData[0].pData = NULL;
7359 pOleStreamData[1].pData = NULL;
7361 /* Open Ole10Native Stream */
7362 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7363 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7364 if(hRes == S_OK)
7367 /* Read Size and Data */
7368 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7369 if(pOleStreamData->dwDataLength > 0)
7371 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7372 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7374 IStream_Release(pStream);
7380 /*************************************************************************
7381 * OLECONVERT_GetOle20PresData[Internal]
7383 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7385 * PARAMS
7386 * pStorage [I] Src IStroage
7387 * pOleStreamData [I] Dest OleStream Mem Struct
7389 * RETURNS
7390 * Nothing
7392 * NOTES
7393 * This function is used by OleConvertIStorageToOLESTREAM only.
7395 * Memory allocated for pData must be freed by the caller
7397 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7399 HRESULT hRes;
7400 IStream *pStream;
7401 OLECONVERT_ISTORAGE_OLEPRES olePress;
7402 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7404 /* Initialize Default data for OLESTREAM */
7405 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7406 pOleStreamData[0].dwTypeID = 2;
7407 pOleStreamData[0].dwMetaFileWidth = 0;
7408 pOleStreamData[0].dwMetaFileHeight = 0;
7409 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7410 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7411 pOleStreamData[1].dwTypeID = 0;
7412 pOleStreamData[1].dwOleTypeNameLength = 0;
7413 pOleStreamData[1].strOleTypeName[0] = 0;
7414 pOleStreamData[1].dwMetaFileWidth = 0;
7415 pOleStreamData[1].dwMetaFileHeight = 0;
7416 pOleStreamData[1].pData = NULL;
7417 pOleStreamData[1].dwDataLength = 0;
7420 /* Open OlePress000 stream */
7421 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7422 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7423 if(hRes == S_OK)
7425 LARGE_INTEGER iSeekPos;
7426 METAFILEPICT16 MetaFilePict;
7427 static const char strMetafilePictName[] = "METAFILEPICT";
7429 /* Set the TypeID for a Metafile */
7430 pOleStreamData[1].dwTypeID = 5;
7432 /* Set the OleTypeName to Metafile */
7433 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7434 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7436 iSeekPos.u.HighPart = 0;
7437 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7439 /* Get Presentation Data */
7440 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7441 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7442 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7443 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7445 /*Set width and Height */
7446 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7447 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7448 if(olePress.dwSize > 0)
7450 /* Set Length */
7451 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7453 /* Set MetaFilePict struct */
7454 MetaFilePict.mm = 8;
7455 MetaFilePict.xExt = olePress.dwExtentX;
7456 MetaFilePict.yExt = olePress.dwExtentY;
7457 MetaFilePict.hMF = 0;
7459 /* Get Metafile Data */
7460 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7461 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7462 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7464 IStream_Release(pStream);
7468 /*************************************************************************
7469 * OleConvertOLESTREAMToIStorage [OLE32.@]
7471 * Read info on MSDN
7473 * TODO
7474 * DVTARGETDEVICE paramenter is not handled
7475 * Still unsure of some mem fields for OLE 10 Stream
7476 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7477 * and "\001OLE" streams
7480 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7481 LPOLESTREAM pOleStream,
7482 LPSTORAGE pstg,
7483 const DVTARGETDEVICE* ptd)
7485 int i;
7486 HRESULT hRes=S_OK;
7487 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7489 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7491 if(ptd != NULL)
7493 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7496 if(pstg == NULL || pOleStream == NULL)
7498 hRes = E_INVALIDARG;
7501 if(hRes == S_OK)
7503 /* Load the OLESTREAM to Memory */
7504 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7507 if(hRes == S_OK)
7509 /* Load the OLESTREAM to Memory (part 2)*/
7510 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7513 if(hRes == S_OK)
7516 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7518 /* Do we have the IStorage Data in the OLESTREAM */
7519 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7521 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7522 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7524 else
7526 /* It must be an original OLE 1.0 source */
7527 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7530 else
7532 /* It must be an original OLE 1.0 source */
7533 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7536 /* Create CompObj Stream if necessary */
7537 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7538 if(hRes == S_OK)
7540 /*Create the Ole Stream if necessary */
7541 OLECONVERT_CreateOleStream(pstg);
7546 /* Free allocated memory */
7547 for(i=0; i < 2; i++)
7549 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7550 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7551 pOleStreamData[i].pstrOleObjFileName = NULL;
7553 return hRes;
7556 /*************************************************************************
7557 * OleConvertIStorageToOLESTREAM [OLE32.@]
7559 * Read info on MSDN
7561 * Read info on MSDN
7563 * TODO
7564 * Still unsure of some mem fields for OLE 10 Stream
7565 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7566 * and "\001OLE" streams.
7569 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7570 LPSTORAGE pstg,
7571 LPOLESTREAM pOleStream)
7573 int i;
7574 HRESULT hRes = S_OK;
7575 IStream *pStream;
7576 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7577 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7580 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7582 if(pstg == NULL || pOleStream == NULL)
7584 hRes = E_INVALIDARG;
7586 if(hRes == S_OK)
7588 /* Get the ProgID */
7589 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7590 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7592 if(hRes == S_OK)
7594 /* Was it originally Ole10 */
7595 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7596 if(hRes == S_OK)
7598 IStream_Release(pStream);
7599 /* Get Presentation Data for Ole10Native */
7600 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7602 else
7604 /* Get Presentation Data (OLE20) */
7605 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7608 /* Save OLESTREAM */
7609 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7610 if(hRes == S_OK)
7612 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7617 /* Free allocated memory */
7618 for(i=0; i < 2; i++)
7620 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7623 return hRes;
7626 /***********************************************************************
7627 * GetConvertStg (OLE32.@)
7629 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7630 FIXME("unimplemented stub!\n");
7631 return E_FAIL;
7634 /******************************************************************************
7635 * StgIsStorageFile [OLE32.@]
7637 HRESULT WINAPI
7638 StgIsStorageFile(LPCOLESTR fn)
7640 HANDLE hf;
7641 BYTE magic[8];
7642 DWORD bytes_read;
7644 TRACE("(\'%s\')\n", debugstr_w(fn));
7645 hf = CreateFileW(fn, GENERIC_READ,
7646 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7647 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7649 if (hf == INVALID_HANDLE_VALUE)
7650 return STG_E_FILENOTFOUND;
7652 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7654 WARN(" unable to read file\n");
7655 CloseHandle(hf);
7656 return S_FALSE;
7659 CloseHandle(hf);
7661 if (bytes_read != 8) {
7662 WARN(" too short\n");
7663 return S_FALSE;
7666 if (!memcmp(magic,STORAGE_magic,8)) {
7667 WARN(" -> YES\n");
7668 return S_OK;
7671 WARN(" -> Invalid header.\n");
7672 return S_FALSE;