ntdll: Add a test for NtNotifyChangeDirectoryFile.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob8512667bc50a274d641d35af9e6a49ba4d4cdcad
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
32 * MSDN
33 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
36 #include <assert.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
42 #define COBJMACROS
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
46 #include "windef.h"
47 #include "winbase.h"
48 #include "winnls.h"
49 #include "winuser.h"
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 #include "storage32.h"
54 #include "ole2.h" /* For Write/ReadClassStm */
56 #include "winreg.h"
57 #include "wine/wingdi16.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(storage);
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 #define OLESTREAM_ID 0x501
63 #define OLESTREAM_MAX_STR_LEN 255
65 static const char rootPropertyName[] = "Root Entry";
68 /* OLESTREAM memory structure to use for Get and Put Routines */
69 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
70 typedef struct
72 DWORD dwOleID;
73 DWORD dwTypeID;
74 DWORD dwOleTypeNameLength;
75 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
76 CHAR *pstrOleObjFileName;
77 DWORD dwOleObjFileNameLength;
78 DWORD dwMetaFileWidth;
79 DWORD dwMetaFileHeight;
80 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
81 DWORD dwDataLength;
82 BYTE *pData;
83 }OLECONVERT_OLESTREAM_DATA;
85 /* CompObj Stream structure */
86 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
87 typedef struct
89 BYTE byUnknown1[12];
90 CLSID clsid;
91 DWORD dwCLSIDNameLength;
92 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
93 DWORD dwOleTypeNameLength;
94 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
95 DWORD dwProgIDNameLength;
96 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
97 BYTE byUnknown2[16];
98 }OLECONVERT_ISTORAGE_COMPOBJ;
101 /* Ole Presention Stream structure */
102 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
103 typedef struct
105 BYTE byUnknown1[28];
106 DWORD dwExtentX;
107 DWORD dwExtentY;
108 DWORD dwSize;
109 BYTE *pData;
110 }OLECONVERT_ISTORAGE_OLEPRES;
114 /***********************************************************************
115 * Forward declaration of internal functions used by the method DestroyElement
117 static HRESULT deleteStorageProperty(
118 StorageImpl *parentStorage,
119 ULONG foundPropertyIndexToDelete,
120 StgProperty propertyToDelete);
122 static HRESULT deleteStreamProperty(
123 StorageImpl *parentStorage,
124 ULONG foundPropertyIndexToDelete,
125 StgProperty propertyToDelete);
127 static HRESULT findPlaceholder(
128 StorageImpl *storage,
129 ULONG propertyIndexToStore,
130 ULONG storagePropertyIndex,
131 INT typeOfRelation);
133 static HRESULT adjustPropertyChain(
134 StorageImpl *This,
135 StgProperty propertyToDelete,
136 StgProperty parentProperty,
137 ULONG parentPropertyId,
138 INT typeOfRelation);
140 /***********************************************************************
141 * Declaration of the functions used to manipulate StgProperty
144 static ULONG getFreeProperty(
145 StorageImpl *storage);
147 static void updatePropertyChain(
148 StorageImpl *storage,
149 ULONG newPropertyIndex,
150 StgProperty newProperty);
152 static LONG propertyNameCmp(
153 const OLECHAR *newProperty,
154 const OLECHAR *currentProperty);
157 /***********************************************************************
158 * Declaration of miscellaneous functions...
160 static HRESULT validateSTGM(DWORD stgmValue);
162 static DWORD GetShareModeFromSTGM(DWORD stgm);
163 static DWORD GetAccessModeFromSTGM(DWORD stgm);
164 static DWORD GetCreationModeFromSTGM(DWORD stgm);
166 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
170 /************************************************************************
171 ** Storage32BaseImpl implementatiion
174 /************************************************************************
175 * Storage32BaseImpl_QueryInterface (IUnknown)
177 * This method implements the common QueryInterface for all IStorage32
178 * implementations contained in this file.
180 * See Windows documentation for more details on IUnknown methods.
182 HRESULT WINAPI StorageBaseImpl_QueryInterface(
183 IStorage* iface,
184 REFIID riid,
185 void** ppvObject)
187 StorageBaseImpl *This = (StorageBaseImpl *)iface;
189 * Perform a sanity check on the parameters.
191 if ( (This==0) || (ppvObject==0) )
192 return E_INVALIDARG;
195 * Initialize the return parameter.
197 *ppvObject = 0;
200 * Compare the riid with the interface IDs implemented by this object.
202 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
204 *ppvObject = (IStorage*)This;
206 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
208 *ppvObject = (IStorage*)This;
210 else if (memcmp(&IID_IPropertySetStorage, riid, sizeof(IID_IPropertySetStorage)) == 0)
212 *ppvObject = (IStorage*)&This->pssVtbl;
216 * Check that we obtained an interface.
218 if ((*ppvObject)==0)
219 return E_NOINTERFACE;
222 * Query Interface always increases the reference count by one when it is
223 * successful
225 IStorage_AddRef(iface);
227 return S_OK;
230 /************************************************************************
231 * Storage32BaseImpl_AddRef (IUnknown)
233 * This method implements the common AddRef for all IStorage32
234 * implementations contained in this file.
236 * See Windows documentation for more details on IUnknown methods.
238 ULONG WINAPI StorageBaseImpl_AddRef(
239 IStorage* iface)
241 StorageBaseImpl *This = (StorageBaseImpl *)iface;
242 ULONG ref = InterlockedIncrement(&This->ref);
244 TRACE("(%p) AddRef to %ld\n", This, ref);
246 return ref;
249 /************************************************************************
250 * Storage32BaseImpl_Release (IUnknown)
252 * This method implements the common Release for all IStorage32
253 * implementations contained in this file.
255 * See Windows documentation for more details on IUnknown methods.
257 ULONG WINAPI StorageBaseImpl_Release(
258 IStorage* iface)
260 StorageBaseImpl *This = (StorageBaseImpl *)iface;
262 * Decrease the reference count on this object.
264 ULONG ref = InterlockedDecrement(&This->ref);
266 TRACE("(%p) ReleaseRef to %ld\n", This, ref);
269 * If the reference count goes down to 0, perform suicide.
271 if (ref == 0)
274 * Since we are using a system of base-classes, we want to call the
275 * destructor of the appropriate derived class. To do this, we are
276 * using virtual functions to implement the destructor.
278 This->v_destructor(This);
281 return ref;
284 /************************************************************************
285 * Storage32BaseImpl_OpenStream (IStorage)
287 * This method will open the specified stream object from the current storage.
289 * See Windows documentation for more details on IStorage methods.
291 HRESULT WINAPI StorageBaseImpl_OpenStream(
292 IStorage* iface,
293 const OLECHAR* pwcsName, /* [string][in] */
294 void* reserved1, /* [unique][in] */
295 DWORD grfMode, /* [in] */
296 DWORD reserved2, /* [in] */
297 IStream** ppstm) /* [out] */
299 StorageBaseImpl *This = (StorageBaseImpl *)iface;
300 IEnumSTATSTGImpl* propertyEnumeration;
301 StgStreamImpl* newStream;
302 StgProperty currentProperty;
303 ULONG foundPropertyIndex;
304 HRESULT res = STG_E_UNKNOWN;
305 DWORD parent_grfMode;
307 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
308 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
311 * Perform a sanity check on the parameters.
313 if ( (pwcsName==NULL) || (ppstm==0) )
315 res = E_INVALIDARG;
316 goto end;
320 * Initialize the out parameter
322 *ppstm = NULL;
325 * Validate the STGM flags
327 if ( FAILED( validateSTGM(grfMode) ))
329 res = STG_E_INVALIDFLAG;
330 goto end;
334 * As documented.
336 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
337 (grfMode & STGM_DELETEONRELEASE) ||
338 (grfMode & STGM_TRANSACTED) )
340 res = STG_E_INVALIDFUNCTION;
341 goto end;
345 * Check that we're compatible with the parent's storage mode
347 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
348 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
350 res = STG_E_ACCESSDENIED;
351 goto end;
355 * Create a property enumeration to search the properties
357 propertyEnumeration = IEnumSTATSTGImpl_Construct(
358 This->ancestorStorage,
359 This->rootPropertySetIndex);
362 * Search the enumeration for the property with the given name
364 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
365 propertyEnumeration,
366 pwcsName,
367 &currentProperty);
370 * Delete the property enumeration since we don't need it anymore
372 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
375 * If it was found, construct the stream object and return a pointer to it.
377 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
378 (currentProperty.propertyType==PROPTYPE_STREAM) )
380 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
382 if (newStream!=0)
384 newStream->grfMode = grfMode;
385 *ppstm = (IStream*)newStream;
388 * Since we are returning a pointer to the interface, we have to
389 * nail down the reference.
391 IStream_AddRef(*ppstm);
393 res = S_OK;
394 goto end;
397 res = E_OUTOFMEMORY;
398 goto end;
401 res = STG_E_FILENOTFOUND;
403 end:
404 if (res == S_OK)
405 TRACE("<-- IStream %p\n", *ppstm);
406 TRACE("<-- %08lx\n", res);
407 return res;
410 /************************************************************************
411 * Storage32BaseImpl_OpenStorage (IStorage)
413 * This method will open a new storage object from the current storage.
415 * See Windows documentation for more details on IStorage methods.
417 HRESULT WINAPI StorageBaseImpl_OpenStorage(
418 IStorage* iface,
419 const OLECHAR* pwcsName, /* [string][unique][in] */
420 IStorage* pstgPriority, /* [unique][in] */
421 DWORD grfMode, /* [in] */
422 SNB snbExclude, /* [unique][in] */
423 DWORD reserved, /* [in] */
424 IStorage** ppstg) /* [out] */
426 StorageBaseImpl *This = (StorageBaseImpl *)iface;
427 StorageInternalImpl* newStorage;
428 IEnumSTATSTGImpl* propertyEnumeration;
429 StgProperty currentProperty;
430 ULONG foundPropertyIndex;
431 HRESULT res = STG_E_UNKNOWN;
432 DWORD parent_grfMode;
434 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
435 iface, debugstr_w(pwcsName), pstgPriority,
436 grfMode, snbExclude, reserved, ppstg);
439 * Perform a sanity check on the parameters.
441 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
443 res = E_INVALIDARG;
444 goto end;
447 /* as documented */
448 if (snbExclude != NULL)
450 res = STG_E_INVALIDPARAMETER;
451 goto end;
455 * Validate the STGM flags
457 if ( FAILED( validateSTGM(grfMode) ))
459 res = STG_E_INVALIDFLAG;
460 goto end;
464 * As documented.
466 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
467 (grfMode & STGM_DELETEONRELEASE) ||
468 (grfMode & STGM_PRIORITY) )
470 res = STG_E_INVALIDFUNCTION;
471 goto end;
475 * Check that we're compatible with the parent's storage mode
477 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
478 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
480 res = STG_E_ACCESSDENIED;
481 goto end;
485 * Initialize the out parameter
487 *ppstg = NULL;
490 * Create a property enumeration to search the properties
492 propertyEnumeration = IEnumSTATSTGImpl_Construct(
493 This->ancestorStorage,
494 This->rootPropertySetIndex);
497 * Search the enumeration for the property with the given name
499 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
500 propertyEnumeration,
501 pwcsName,
502 &currentProperty);
505 * Delete the property enumeration since we don't need it anymore
507 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
510 * If it was found, construct the stream object and return a pointer to it.
512 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
513 (currentProperty.propertyType==PROPTYPE_STORAGE) )
516 * Construct a new Storage object
518 newStorage = StorageInternalImpl_Construct(
519 This->ancestorStorage,
520 grfMode,
521 foundPropertyIndex);
523 if (newStorage != 0)
525 *ppstg = (IStorage*)newStorage;
528 * Since we are returning a pointer to the interface,
529 * we have to nail down the reference.
531 StorageBaseImpl_AddRef(*ppstg);
533 res = S_OK;
534 goto end;
537 res = STG_E_INSUFFICIENTMEMORY;
538 goto end;
541 res = STG_E_FILENOTFOUND;
543 end:
544 TRACE("<-- %08lx\n", res);
545 return res;
548 /************************************************************************
549 * Storage32BaseImpl_EnumElements (IStorage)
551 * This method will create an enumerator object that can be used to
552 * retrieve informatino about all the properties in the storage object.
554 * See Windows documentation for more details on IStorage methods.
556 HRESULT WINAPI StorageBaseImpl_EnumElements(
557 IStorage* iface,
558 DWORD reserved1, /* [in] */
559 void* reserved2, /* [size_is][unique][in] */
560 DWORD reserved3, /* [in] */
561 IEnumSTATSTG** ppenum) /* [out] */
563 StorageBaseImpl *This = (StorageBaseImpl *)iface;
564 IEnumSTATSTGImpl* newEnum;
566 TRACE("(%p, %ld, %p, %ld, %p)\n",
567 iface, reserved1, reserved2, reserved3, ppenum);
570 * Perform a sanity check on the parameters.
572 if ( (This==0) || (ppenum==0))
573 return E_INVALIDARG;
576 * Construct the enumerator.
578 newEnum = IEnumSTATSTGImpl_Construct(
579 This->ancestorStorage,
580 This->rootPropertySetIndex);
582 if (newEnum!=0)
584 *ppenum = (IEnumSTATSTG*)newEnum;
587 * Don't forget to nail down a reference to the new object before
588 * returning it.
590 IEnumSTATSTG_AddRef(*ppenum);
592 return S_OK;
595 return E_OUTOFMEMORY;
598 /************************************************************************
599 * Storage32BaseImpl_Stat (IStorage)
601 * This method will retrieve information about this storage object.
603 * See Windows documentation for more details on IStorage methods.
605 HRESULT WINAPI StorageBaseImpl_Stat(
606 IStorage* iface,
607 STATSTG* pstatstg, /* [out] */
608 DWORD grfStatFlag) /* [in] */
610 StorageBaseImpl *This = (StorageBaseImpl *)iface;
611 StgProperty curProperty;
612 BOOL readSuccessful;
613 HRESULT res = STG_E_UNKNOWN;
615 TRACE("(%p, %p, %lx)\n",
616 iface, pstatstg, grfStatFlag);
619 * Perform a sanity check on the parameters.
621 if ( (This==0) || (pstatstg==0))
623 res = E_INVALIDARG;
624 goto end;
628 * Read the information from the property.
630 readSuccessful = StorageImpl_ReadProperty(
631 This->ancestorStorage,
632 This->rootPropertySetIndex,
633 &curProperty);
635 if (readSuccessful)
637 StorageUtl_CopyPropertyToSTATSTG(
638 pstatstg,
639 &curProperty,
640 grfStatFlag);
642 res = S_OK;
643 goto end;
646 res = E_FAIL;
648 end:
649 if (res == S_OK)
651 TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %08lx, grfLocksSupported: %ld, grfStateBits: %08lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
653 TRACE("<-- %08lx\n", res);
654 return res;
657 /************************************************************************
658 * Storage32BaseImpl_RenameElement (IStorage)
660 * This method will rename the specified element.
662 * See Windows documentation for more details on IStorage methods.
664 * Implementation notes: The method used to rename consists of creating a clone
665 * of the deleted StgProperty object setting it with the new name and to
666 * perform a DestroyElement of the old StgProperty.
668 HRESULT WINAPI StorageBaseImpl_RenameElement(
669 IStorage* iface,
670 const OLECHAR* pwcsOldName, /* [in] */
671 const OLECHAR* pwcsNewName) /* [in] */
673 StorageBaseImpl *This = (StorageBaseImpl *)iface;
674 IEnumSTATSTGImpl* propertyEnumeration;
675 StgProperty currentProperty;
676 ULONG foundPropertyIndex;
678 TRACE("(%p, %s, %s)\n",
679 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
682 * Create a property enumeration to search the properties
684 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
685 This->rootPropertySetIndex);
688 * Search the enumeration for the new property name
690 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
691 pwcsNewName,
692 &currentProperty);
694 if (foundPropertyIndex != PROPERTY_NULL)
697 * There is already a property with the new name
699 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
700 return STG_E_FILEALREADYEXISTS;
703 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
706 * Search the enumeration for the old property name
708 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
709 pwcsOldName,
710 &currentProperty);
713 * Delete the property enumeration since we don't need it anymore
715 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
717 if (foundPropertyIndex != PROPERTY_NULL)
719 StgProperty renamedProperty;
720 ULONG renamedPropertyIndex;
723 * Setup a new property for the renamed property
725 renamedProperty.sizeOfNameString =
726 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
728 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
729 return STG_E_INVALIDNAME;
731 strcpyW(renamedProperty.name, pwcsNewName);
733 renamedProperty.propertyType = currentProperty.propertyType;
734 renamedProperty.startingBlock = currentProperty.startingBlock;
735 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
736 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
738 renamedProperty.previousProperty = PROPERTY_NULL;
739 renamedProperty.nextProperty = PROPERTY_NULL;
742 * Bring the dirProperty link in case it is a storage and in which
743 * case the renamed storage elements don't require to be reorganized.
745 renamedProperty.dirProperty = currentProperty.dirProperty;
747 /* call CoFileTime to get the current time
748 renamedProperty.timeStampS1
749 renamedProperty.timeStampD1
750 renamedProperty.timeStampS2
751 renamedProperty.timeStampD2
752 renamedProperty.propertyUniqueID
756 * Obtain a free property in the property chain
758 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
761 * Save the new property into the new property spot
763 StorageImpl_WriteProperty(
764 This->ancestorStorage,
765 renamedPropertyIndex,
766 &renamedProperty);
769 * Find a spot in the property chain for our newly created property.
771 updatePropertyChain(
772 (StorageImpl*)This,
773 renamedPropertyIndex,
774 renamedProperty);
777 * At this point the renamed property has been inserted in the tree,
778 * now, before to Destroy the old property we must zeroed it's dirProperty
779 * otherwise the DestroyProperty below will zap it all and we do not want
780 * this to happen.
781 * Also, we fake that the old property is a storage so the DestroyProperty
782 * will not do a SetSize(0) on the stream data.
784 * This means that we need to tweek the StgProperty if it is a stream or a
785 * non empty storage.
787 StorageImpl_ReadProperty(This->ancestorStorage,
788 foundPropertyIndex,
789 &currentProperty);
791 currentProperty.dirProperty = PROPERTY_NULL;
792 currentProperty.propertyType = PROPTYPE_STORAGE;
793 StorageImpl_WriteProperty(
794 This->ancestorStorage,
795 foundPropertyIndex,
796 &currentProperty);
799 * Invoke Destroy to get rid of the ole property and automatically redo
800 * the linking of it's previous and next members...
802 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
805 else
808 * There is no property with the old name
810 return STG_E_FILENOTFOUND;
813 return S_OK;
816 /************************************************************************
817 * Storage32BaseImpl_CreateStream (IStorage)
819 * This method will create a stream object within this storage
821 * See Windows documentation for more details on IStorage methods.
823 HRESULT WINAPI StorageBaseImpl_CreateStream(
824 IStorage* iface,
825 const OLECHAR* pwcsName, /* [string][in] */
826 DWORD grfMode, /* [in] */
827 DWORD reserved1, /* [in] */
828 DWORD reserved2, /* [in] */
829 IStream** ppstm) /* [out] */
831 StorageBaseImpl *This = (StorageBaseImpl *)iface;
832 IEnumSTATSTGImpl* propertyEnumeration;
833 StgStreamImpl* newStream;
834 StgProperty currentProperty, newStreamProperty;
835 ULONG foundPropertyIndex, newPropertyIndex;
836 DWORD parent_grfMode;
838 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
839 iface, debugstr_w(pwcsName), grfMode,
840 reserved1, reserved2, ppstm);
843 * Validate parameters
845 if (ppstm == 0)
846 return STG_E_INVALIDPOINTER;
848 if (pwcsName == 0)
849 return STG_E_INVALIDNAME;
851 if (reserved1 || reserved2)
852 return STG_E_INVALIDPARAMETER;
855 * Validate the STGM flags
857 if ( FAILED( validateSTGM(grfMode) ))
858 return STG_E_INVALIDFLAG;
860 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
861 return STG_E_INVALIDFLAG;
864 * As documented.
866 if ((grfMode & STGM_DELETEONRELEASE) ||
867 (grfMode & STGM_TRANSACTED))
868 return STG_E_INVALIDFUNCTION;
871 * Check that we're compatible with the parent's storage mode
873 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
874 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
875 return STG_E_ACCESSDENIED;
878 * Initialize the out parameter
880 *ppstm = 0;
883 * Create a property enumeration to search the properties
885 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
886 This->rootPropertySetIndex);
888 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
889 pwcsName,
890 &currentProperty);
892 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
894 if (foundPropertyIndex != PROPERTY_NULL)
897 * An element with this name already exists
899 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
901 IStorage_DestroyElement(iface, pwcsName);
903 else
904 return STG_E_FILEALREADYEXISTS;
908 * memset the empty property
910 memset(&newStreamProperty, 0, sizeof(StgProperty));
912 newStreamProperty.sizeOfNameString =
913 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
915 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
916 return STG_E_INVALIDNAME;
918 strcpyW(newStreamProperty.name, pwcsName);
920 newStreamProperty.propertyType = PROPTYPE_STREAM;
921 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
922 newStreamProperty.size.u.LowPart = 0;
923 newStreamProperty.size.u.HighPart = 0;
925 newStreamProperty.previousProperty = PROPERTY_NULL;
926 newStreamProperty.nextProperty = PROPERTY_NULL;
927 newStreamProperty.dirProperty = PROPERTY_NULL;
929 /* call CoFileTime to get the current time
930 newStreamProperty.timeStampS1
931 newStreamProperty.timeStampD1
932 newStreamProperty.timeStampS2
933 newStreamProperty.timeStampD2
936 /* newStreamProperty.propertyUniqueID */
939 * Get a free property or create a new one
941 newPropertyIndex = getFreeProperty(This->ancestorStorage);
944 * Save the new property into the new property spot
946 StorageImpl_WriteProperty(
947 This->ancestorStorage,
948 newPropertyIndex,
949 &newStreamProperty);
952 * Find a spot in the property chain for our newly created property.
954 updatePropertyChain(
955 (StorageImpl*)This,
956 newPropertyIndex,
957 newStreamProperty);
960 * Open the stream to return it.
962 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
964 if (newStream != 0)
966 *ppstm = (IStream*)newStream;
969 * Since we are returning a pointer to the interface, we have to nail down
970 * the reference.
972 IStream_AddRef(*ppstm);
974 else
976 return STG_E_INSUFFICIENTMEMORY;
979 return S_OK;
982 /************************************************************************
983 * Storage32BaseImpl_SetClass (IStorage)
985 * This method will write the specified CLSID in the property of this
986 * storage.
988 * See Windows documentation for more details on IStorage methods.
990 HRESULT WINAPI StorageBaseImpl_SetClass(
991 IStorage* iface,
992 REFCLSID clsid) /* [in] */
994 StorageBaseImpl *This = (StorageBaseImpl *)iface;
995 HRESULT hRes = E_FAIL;
996 StgProperty curProperty;
997 BOOL success;
999 TRACE("(%p, %p)\n", iface, clsid);
1001 success = StorageImpl_ReadProperty(This->ancestorStorage,
1002 This->rootPropertySetIndex,
1003 &curProperty);
1004 if (success)
1006 curProperty.propertyUniqueID = *clsid;
1008 success = StorageImpl_WriteProperty(This->ancestorStorage,
1009 This->rootPropertySetIndex,
1010 &curProperty);
1011 if (success)
1012 hRes = S_OK;
1015 return hRes;
1018 /************************************************************************
1019 ** Storage32Impl implementation
1022 /************************************************************************
1023 * Storage32Impl_CreateStorage (IStorage)
1025 * This method will create the storage object within the provided storage.
1027 * See Windows documentation for more details on IStorage methods.
1029 HRESULT WINAPI StorageImpl_CreateStorage(
1030 IStorage* iface,
1031 const OLECHAR *pwcsName, /* [string][in] */
1032 DWORD grfMode, /* [in] */
1033 DWORD reserved1, /* [in] */
1034 DWORD reserved2, /* [in] */
1035 IStorage **ppstg) /* [out] */
1037 StorageImpl* const This=(StorageImpl*)iface;
1039 IEnumSTATSTGImpl *propertyEnumeration;
1040 StgProperty currentProperty;
1041 StgProperty newProperty;
1042 ULONG foundPropertyIndex;
1043 ULONG newPropertyIndex;
1044 HRESULT hr;
1045 DWORD parent_grfMode;
1047 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1048 iface, debugstr_w(pwcsName), grfMode,
1049 reserved1, reserved2, ppstg);
1052 * Validate parameters
1054 if (ppstg == 0)
1055 return STG_E_INVALIDPOINTER;
1057 if (pwcsName == 0)
1058 return STG_E_INVALIDNAME;
1061 * Initialize the out parameter
1063 *ppstg = NULL;
1066 * Validate the STGM flags
1068 if ( FAILED( validateSTGM(grfMode) ) ||
1069 (grfMode & STGM_DELETEONRELEASE) )
1071 WARN("bad grfMode: 0x%lx\n", grfMode);
1072 return STG_E_INVALIDFLAG;
1076 * Check that we're compatible with the parent's storage mode
1078 parent_grfMode = STGM_ACCESS_MODE( This->base.ancestorStorage->base.openFlags );
1079 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
1081 WARN("access denied\n");
1082 return STG_E_ACCESSDENIED;
1086 * Create a property enumeration and search the properties
1088 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1089 This->base.rootPropertySetIndex);
1091 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1092 pwcsName,
1093 &currentProperty);
1094 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1096 if (foundPropertyIndex != PROPERTY_NULL)
1099 * An element with this name already exists
1101 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1102 IStorage_DestroyElement(iface, pwcsName);
1103 else
1105 WARN("file already exists\n");
1106 return STG_E_FILEALREADYEXISTS;
1111 * memset the empty property
1113 memset(&newProperty, 0, sizeof(StgProperty));
1115 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1117 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1119 FIXME("name too long\n");
1120 return STG_E_INVALIDNAME;
1123 strcpyW(newProperty.name, pwcsName);
1125 newProperty.propertyType = PROPTYPE_STORAGE;
1126 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1127 newProperty.size.u.LowPart = 0;
1128 newProperty.size.u.HighPart = 0;
1130 newProperty.previousProperty = PROPERTY_NULL;
1131 newProperty.nextProperty = PROPERTY_NULL;
1132 newProperty.dirProperty = PROPERTY_NULL;
1134 /* call CoFileTime to get the current time
1135 newProperty.timeStampS1
1136 newProperty.timeStampD1
1137 newProperty.timeStampS2
1138 newProperty.timeStampD2
1141 /* newStorageProperty.propertyUniqueID */
1144 * Obtain a free property in the property chain
1146 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1149 * Save the new property into the new property spot
1151 StorageImpl_WriteProperty(
1152 This->base.ancestorStorage,
1153 newPropertyIndex,
1154 &newProperty);
1157 * Find a spot in the property chain for our newly created property.
1159 updatePropertyChain(
1160 This,
1161 newPropertyIndex,
1162 newProperty);
1165 * Open it to get a pointer to return.
1167 hr = IStorage_OpenStorage(
1168 iface,
1169 (const OLECHAR*)pwcsName,
1171 grfMode,
1174 ppstg);
1176 if( (hr != S_OK) || (*ppstg == NULL))
1178 return hr;
1182 return S_OK;
1186 /***************************************************************************
1188 * Internal Method
1190 * Get a free property or create a new one.
1192 static ULONG getFreeProperty(
1193 StorageImpl *storage)
1195 ULONG currentPropertyIndex = 0;
1196 ULONG newPropertyIndex = PROPERTY_NULL;
1197 BOOL readSuccessful = TRUE;
1198 StgProperty currentProperty;
1203 * Start by reading the root property
1205 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1206 currentPropertyIndex,
1207 &currentProperty);
1208 if (readSuccessful)
1210 if (currentProperty.sizeOfNameString == 0)
1213 * The property existis and is available, we found it.
1215 newPropertyIndex = currentPropertyIndex;
1218 else
1221 * We exhausted the property list, we will create more space below
1223 newPropertyIndex = currentPropertyIndex;
1225 currentPropertyIndex++;
1227 } while (newPropertyIndex == PROPERTY_NULL);
1230 * grow the property chain
1232 if (! readSuccessful)
1234 StgProperty emptyProperty;
1235 ULARGE_INTEGER newSize;
1236 ULONG propertyIndex;
1237 ULONG lastProperty = 0;
1238 ULONG blockCount = 0;
1241 * obtain the new count of property blocks
1243 blockCount = BlockChainStream_GetCount(
1244 storage->base.ancestorStorage->rootBlockChain)+1;
1247 * initialize the size used by the property stream
1249 newSize.u.HighPart = 0;
1250 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1253 * add a property block to the property chain
1255 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1258 * memset the empty property in order to initialize the unused newly
1259 * created property
1261 memset(&emptyProperty, 0, sizeof(StgProperty));
1264 * initialize them
1266 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1268 for(
1269 propertyIndex = newPropertyIndex;
1270 propertyIndex < lastProperty;
1271 propertyIndex++)
1273 StorageImpl_WriteProperty(
1274 storage->base.ancestorStorage,
1275 propertyIndex,
1276 &emptyProperty);
1280 return newPropertyIndex;
1283 /****************************************************************************
1285 * Internal Method
1287 * Case insensitive comparaison of StgProperty.name by first considering
1288 * their size.
1290 * Returns <0 when newPrpoerty < currentProperty
1291 * >0 when newPrpoerty > currentProperty
1292 * 0 when newPrpoerty == currentProperty
1294 static LONG propertyNameCmp(
1295 const OLECHAR *newProperty,
1296 const OLECHAR *currentProperty)
1298 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1300 if (diff == 0)
1303 * We compare the string themselves only when they are of the same length
1305 diff = lstrcmpiW( newProperty, currentProperty);
1308 return diff;
1311 /****************************************************************************
1313 * Internal Method
1315 * Properly link this new element in the property chain.
1317 static void updatePropertyChain(
1318 StorageImpl *storage,
1319 ULONG newPropertyIndex,
1320 StgProperty newProperty)
1322 StgProperty currentProperty;
1325 * Read the root property
1327 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1328 storage->base.rootPropertySetIndex,
1329 &currentProperty);
1331 if (currentProperty.dirProperty != PROPERTY_NULL)
1334 * The root storage contains some element, therefore, start the research
1335 * for the appropriate location.
1337 BOOL found = 0;
1338 ULONG current, next, previous, currentPropertyId;
1341 * Keep the StgProperty sequence number of the storage first property
1343 currentPropertyId = currentProperty.dirProperty;
1346 * Read
1348 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1349 currentProperty.dirProperty,
1350 &currentProperty);
1352 previous = currentProperty.previousProperty;
1353 next = currentProperty.nextProperty;
1354 current = currentPropertyId;
1356 while (found == 0)
1358 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1360 if (diff < 0)
1362 if (previous != PROPERTY_NULL)
1364 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1365 previous,
1366 &currentProperty);
1367 current = previous;
1369 else
1371 currentProperty.previousProperty = newPropertyIndex;
1372 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1373 current,
1374 &currentProperty);
1375 found = 1;
1378 else if (diff > 0)
1380 if (next != PROPERTY_NULL)
1382 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1383 next,
1384 &currentProperty);
1385 current = next;
1387 else
1389 currentProperty.nextProperty = newPropertyIndex;
1390 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1391 current,
1392 &currentProperty);
1393 found = 1;
1396 else
1399 * Trying to insert an item with the same name in the
1400 * subtree structure.
1402 assert(FALSE);
1405 previous = currentProperty.previousProperty;
1406 next = currentProperty.nextProperty;
1409 else
1412 * The root storage is empty, link the new property to it's dir property
1414 currentProperty.dirProperty = newPropertyIndex;
1415 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1416 storage->base.rootPropertySetIndex,
1417 &currentProperty);
1422 /*************************************************************************
1423 * CopyTo (IStorage)
1425 HRESULT WINAPI StorageImpl_CopyTo(
1426 IStorage* iface,
1427 DWORD ciidExclude, /* [in] */
1428 const IID* rgiidExclude, /* [size_is][unique][in] */
1429 SNB snbExclude, /* [unique][in] */
1430 IStorage* pstgDest) /* [unique][in] */
1432 IEnumSTATSTG *elements = 0;
1433 STATSTG curElement, strStat;
1434 HRESULT hr;
1435 IStorage *pstgTmp, *pstgChild;
1436 IStream *pstrTmp, *pstrChild;
1438 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1439 FIXME("Exclude option not implemented\n");
1441 TRACE("(%p, %ld, %p, %p, %p)\n",
1442 iface, ciidExclude, rgiidExclude,
1443 snbExclude, pstgDest);
1446 * Perform a sanity check
1448 if ( pstgDest == 0 )
1449 return STG_E_INVALIDPOINTER;
1452 * Enumerate the elements
1454 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1456 if ( hr != S_OK )
1457 return hr;
1460 * set the class ID
1462 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1463 IStorage_SetClass( pstgDest, &curElement.clsid );
1468 * Obtain the next element
1470 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1472 if ( hr == S_FALSE )
1474 hr = S_OK; /* done, every element has been copied */
1475 break;
1478 if (curElement.type == STGTY_STORAGE)
1481 * open child source storage
1483 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1484 STGM_READ|STGM_SHARE_EXCLUSIVE,
1485 NULL, 0, &pstgChild );
1487 if (hr != S_OK)
1488 break;
1491 * Check if destination storage is not a child of the source
1492 * storage, which will cause an infinite loop
1494 if (pstgChild == pstgDest)
1496 IEnumSTATSTG_Release(elements);
1498 return STG_E_ACCESSDENIED;
1502 * create a new storage in destination storage
1504 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1505 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1506 0, 0,
1507 &pstgTmp );
1509 * if it already exist, don't create a new one use this one
1511 if (hr == STG_E_FILEALREADYEXISTS)
1513 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1514 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1515 NULL, 0, &pstgTmp );
1518 if (hr != S_OK)
1519 break;
1523 * do the copy recursively
1525 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1526 snbExclude, pstgTmp );
1528 IStorage_Release( pstgTmp );
1529 IStorage_Release( pstgChild );
1531 else if (curElement.type == STGTY_STREAM)
1534 * create a new stream in destination storage. If the stream already
1535 * exist, it will be deleted and a new one will be created.
1537 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1538 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1539 0, 0, &pstrTmp );
1541 if (hr != S_OK)
1542 break;
1545 * open child stream storage
1547 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1548 STGM_READ|STGM_SHARE_EXCLUSIVE,
1549 0, &pstrChild );
1551 if (hr != S_OK)
1552 break;
1555 * Get the size of the source stream
1557 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1560 * Set the size of the destination stream.
1562 IStream_SetSize(pstrTmp, strStat.cbSize);
1565 * do the copy
1567 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1568 NULL, NULL );
1570 IStream_Release( pstrTmp );
1571 IStream_Release( pstrChild );
1573 else
1575 WARN("unknown element type: %ld\n", curElement.type);
1578 } while (hr == S_OK);
1581 * Clean-up
1583 IEnumSTATSTG_Release(elements);
1585 return hr;
1588 /*************************************************************************
1589 * MoveElementTo (IStorage)
1591 HRESULT WINAPI StorageImpl_MoveElementTo(
1592 IStorage* iface,
1593 const OLECHAR *pwcsName, /* [string][in] */
1594 IStorage *pstgDest, /* [unique][in] */
1595 const OLECHAR *pwcsNewName,/* [string][in] */
1596 DWORD grfFlags) /* [in] */
1598 FIXME("not implemented!\n");
1599 return E_NOTIMPL;
1602 /*************************************************************************
1603 * Commit (IStorage)
1605 * Ensures that any changes made to a storage object open in transacted mode
1606 * are reflected in the parent storage
1608 * NOTES
1609 * Wine doesn't implement transacted mode, which seems to be a basic
1610 * optimization, so we can ignore this stub for now.
1612 HRESULT WINAPI StorageImpl_Commit(
1613 IStorage* iface,
1614 DWORD grfCommitFlags)/* [in] */
1616 FIXME("(%ld): stub!\n", grfCommitFlags);
1617 return S_OK;
1620 /*************************************************************************
1621 * Revert (IStorage)
1623 * Discard all changes that have been made since the last commit operation
1625 HRESULT WINAPI StorageImpl_Revert(
1626 IStorage* iface)
1628 FIXME("not implemented!\n");
1629 return E_NOTIMPL;
1632 /*************************************************************************
1633 * DestroyElement (IStorage)
1635 * Stategy: This implementation is build this way for simplicity not for speed.
1636 * I always delete the top most element of the enumeration and adjust
1637 * the deleted element pointer all the time. This takes longer to
1638 * do but allow to reinvoke DestroyElement whenever we encounter a
1639 * storage object. The optimisation reside in the usage of another
1640 * enumeration stategy that would give all the leaves of a storage
1641 * first. (postfix order)
1643 HRESULT WINAPI StorageImpl_DestroyElement(
1644 IStorage* iface,
1645 const OLECHAR *pwcsName)/* [string][in] */
1647 StorageImpl* const This=(StorageImpl*)iface;
1649 IEnumSTATSTGImpl* propertyEnumeration;
1650 HRESULT hr = S_OK;
1651 BOOL res;
1652 StgProperty propertyToDelete;
1653 StgProperty parentProperty;
1654 ULONG foundPropertyIndexToDelete;
1655 ULONG typeOfRelation;
1656 ULONG parentPropertyId;
1658 TRACE("(%p, %s)\n",
1659 iface, debugstr_w(pwcsName));
1662 * Perform a sanity check on the parameters.
1664 if (pwcsName==NULL)
1665 return STG_E_INVALIDPOINTER;
1668 * Create a property enumeration to search the property with the given name
1670 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1671 This->base.ancestorStorage,
1672 This->base.rootPropertySetIndex);
1674 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1675 propertyEnumeration,
1676 pwcsName,
1677 &propertyToDelete);
1679 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1681 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1683 return STG_E_FILENOTFOUND;
1687 * Find the parent property of the property to delete (the one that
1688 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1689 * the parent is This. Otherwise, the parent is one of it's sibling...
1693 * First, read This's StgProperty..
1695 res = StorageImpl_ReadProperty(
1696 This->base.ancestorStorage,
1697 This->base.rootPropertySetIndex,
1698 &parentProperty);
1700 assert(res);
1703 * Second, check to see if by any chance the actual storage (This) is not
1704 * the parent of the property to delete... We never know...
1706 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1709 * Set data as it would have been done in the else part...
1711 typeOfRelation = PROPERTY_RELATION_DIR;
1712 parentPropertyId = This->base.rootPropertySetIndex;
1714 else
1717 * Create a property enumeration to search the parent properties, and
1718 * delete it once done.
1720 IEnumSTATSTGImpl* propertyEnumeration2;
1722 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1723 This->base.ancestorStorage,
1724 This->base.rootPropertySetIndex);
1726 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1727 propertyEnumeration2,
1728 foundPropertyIndexToDelete,
1729 &parentProperty,
1730 &parentPropertyId);
1732 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1735 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1737 hr = deleteStorageProperty(
1738 This,
1739 foundPropertyIndexToDelete,
1740 propertyToDelete);
1742 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1744 hr = deleteStreamProperty(
1745 This,
1746 foundPropertyIndexToDelete,
1747 propertyToDelete);
1750 if (hr!=S_OK)
1751 return hr;
1754 * Adjust the property chain
1756 hr = adjustPropertyChain(
1757 This,
1758 propertyToDelete,
1759 parentProperty,
1760 parentPropertyId,
1761 typeOfRelation);
1763 return hr;
1767 /************************************************************************
1768 * StorageImpl_Stat (IStorage)
1770 * This method will retrieve information about this storage object.
1772 * See Windows documentation for more details on IStorage methods.
1774 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1775 STATSTG* pstatstg, /* [out] */
1776 DWORD grfStatFlag) /* [in] */
1778 StorageImpl* const This = (StorageImpl*)iface;
1779 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1781 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1783 CoTaskMemFree(pstatstg->pwcsName);
1784 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1785 strcpyW(pstatstg->pwcsName, This->pwcsName);
1788 return result;
1793 /*********************************************************************
1795 * Internal Method
1797 * Perform the deletion of a complete storage node
1800 static HRESULT deleteStorageProperty(
1801 StorageImpl *parentStorage,
1802 ULONG indexOfPropertyToDelete,
1803 StgProperty propertyToDelete)
1805 IEnumSTATSTG *elements = 0;
1806 IStorage *childStorage = 0;
1807 STATSTG currentElement;
1808 HRESULT hr;
1809 HRESULT destroyHr = S_OK;
1812 * Open the storage and enumerate it
1814 hr = StorageBaseImpl_OpenStorage(
1815 (IStorage*)parentStorage,
1816 propertyToDelete.name,
1818 STGM_SHARE_EXCLUSIVE,
1821 &childStorage);
1823 if (hr != S_OK)
1825 return hr;
1829 * Enumerate the elements
1831 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1836 * Obtain the next element
1838 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1839 if (hr==S_OK)
1841 destroyHr = StorageImpl_DestroyElement(
1842 (IStorage*)childStorage,
1843 (OLECHAR*)currentElement.pwcsName);
1845 CoTaskMemFree(currentElement.pwcsName);
1849 * We need to Reset the enumeration every time because we delete elements
1850 * and the enumeration could be invalid
1852 IEnumSTATSTG_Reset(elements);
1854 } while ((hr == S_OK) && (destroyHr == S_OK));
1857 * Invalidate the property by zeroing it's name member.
1859 propertyToDelete.sizeOfNameString = 0;
1861 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1862 indexOfPropertyToDelete,
1863 &propertyToDelete);
1865 IStorage_Release(childStorage);
1866 IEnumSTATSTG_Release(elements);
1868 return destroyHr;
1871 /*********************************************************************
1873 * Internal Method
1875 * Perform the deletion of a stream node
1878 static HRESULT deleteStreamProperty(
1879 StorageImpl *parentStorage,
1880 ULONG indexOfPropertyToDelete,
1881 StgProperty propertyToDelete)
1883 IStream *pis;
1884 HRESULT hr;
1885 ULARGE_INTEGER size;
1887 size.u.HighPart = 0;
1888 size.u.LowPart = 0;
1890 hr = StorageBaseImpl_OpenStream(
1891 (IStorage*)parentStorage,
1892 (OLECHAR*)propertyToDelete.name,
1893 NULL,
1894 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1896 &pis);
1898 if (hr!=S_OK)
1900 return(hr);
1904 * Zap the stream
1906 hr = IStream_SetSize(pis, size);
1908 if(hr != S_OK)
1910 return hr;
1914 * Release the stream object.
1916 IStream_Release(pis);
1919 * Invalidate the property by zeroing it's name member.
1921 propertyToDelete.sizeOfNameString = 0;
1924 * Here we should re-read the property so we get the updated pointer
1925 * but since we are here to zap it, I don't do it...
1927 StorageImpl_WriteProperty(
1928 parentStorage->base.ancestorStorage,
1929 indexOfPropertyToDelete,
1930 &propertyToDelete);
1932 return S_OK;
1935 /*********************************************************************
1937 * Internal Method
1939 * Finds a placeholder for the StgProperty within the Storage
1942 static HRESULT findPlaceholder(
1943 StorageImpl *storage,
1944 ULONG propertyIndexToStore,
1945 ULONG storePropertyIndex,
1946 INT typeOfRelation)
1948 StgProperty storeProperty;
1949 HRESULT hr = S_OK;
1950 BOOL res = TRUE;
1953 * Read the storage property
1955 res = StorageImpl_ReadProperty(
1956 storage->base.ancestorStorage,
1957 storePropertyIndex,
1958 &storeProperty);
1960 if(! res)
1962 return E_FAIL;
1965 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1967 if (storeProperty.previousProperty != PROPERTY_NULL)
1969 return findPlaceholder(
1970 storage,
1971 propertyIndexToStore,
1972 storeProperty.previousProperty,
1973 typeOfRelation);
1975 else
1977 storeProperty.previousProperty = propertyIndexToStore;
1980 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1982 if (storeProperty.nextProperty != PROPERTY_NULL)
1984 return findPlaceholder(
1985 storage,
1986 propertyIndexToStore,
1987 storeProperty.nextProperty,
1988 typeOfRelation);
1990 else
1992 storeProperty.nextProperty = propertyIndexToStore;
1995 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1997 if (storeProperty.dirProperty != PROPERTY_NULL)
1999 return findPlaceholder(
2000 storage,
2001 propertyIndexToStore,
2002 storeProperty.dirProperty,
2003 typeOfRelation);
2005 else
2007 storeProperty.dirProperty = propertyIndexToStore;
2011 hr = StorageImpl_WriteProperty(
2012 storage->base.ancestorStorage,
2013 storePropertyIndex,
2014 &storeProperty);
2016 if(! hr)
2018 return E_FAIL;
2021 return S_OK;
2024 /*************************************************************************
2026 * Internal Method
2028 * This method takes the previous and the next property link of a property
2029 * to be deleted and find them a place in the Storage.
2031 static HRESULT adjustPropertyChain(
2032 StorageImpl *This,
2033 StgProperty propertyToDelete,
2034 StgProperty parentProperty,
2035 ULONG parentPropertyId,
2036 INT typeOfRelation)
2038 ULONG newLinkProperty = PROPERTY_NULL;
2039 BOOL needToFindAPlaceholder = FALSE;
2040 ULONG storeNode = PROPERTY_NULL;
2041 ULONG toStoreNode = PROPERTY_NULL;
2042 INT relationType = 0;
2043 HRESULT hr = S_OK;
2044 BOOL res = TRUE;
2046 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2048 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2051 * Set the parent previous to the property to delete previous
2053 newLinkProperty = propertyToDelete.previousProperty;
2055 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2058 * We also need to find a storage for the other link, setup variables
2059 * to do this at the end...
2061 needToFindAPlaceholder = TRUE;
2062 storeNode = propertyToDelete.previousProperty;
2063 toStoreNode = propertyToDelete.nextProperty;
2064 relationType = PROPERTY_RELATION_NEXT;
2067 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2070 * Set the parent previous to the property to delete next
2072 newLinkProperty = propertyToDelete.nextProperty;
2076 * Link it for real...
2078 parentProperty.previousProperty = newLinkProperty;
2081 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2083 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2086 * Set the parent next to the property to delete next previous
2088 newLinkProperty = propertyToDelete.previousProperty;
2090 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2093 * We also need to find a storage for the other link, setup variables
2094 * to do this at the end...
2096 needToFindAPlaceholder = TRUE;
2097 storeNode = propertyToDelete.previousProperty;
2098 toStoreNode = propertyToDelete.nextProperty;
2099 relationType = PROPERTY_RELATION_NEXT;
2102 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2105 * Set the parent next to the property to delete next
2107 newLinkProperty = propertyToDelete.nextProperty;
2111 * Link it for real...
2113 parentProperty.nextProperty = newLinkProperty;
2115 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2117 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2120 * Set the parent dir to the property to delete previous
2122 newLinkProperty = propertyToDelete.previousProperty;
2124 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2127 * We also need to find a storage for the other link, setup variables
2128 * to do this at the end...
2130 needToFindAPlaceholder = TRUE;
2131 storeNode = propertyToDelete.previousProperty;
2132 toStoreNode = propertyToDelete.nextProperty;
2133 relationType = PROPERTY_RELATION_NEXT;
2136 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2139 * Set the parent dir to the property to delete next
2141 newLinkProperty = propertyToDelete.nextProperty;
2145 * Link it for real...
2147 parentProperty.dirProperty = newLinkProperty;
2151 * Write back the parent property
2153 res = StorageImpl_WriteProperty(
2154 This->base.ancestorStorage,
2155 parentPropertyId,
2156 &parentProperty);
2157 if(! res)
2159 return E_FAIL;
2163 * If a placeholder is required for the other link, then, find one and
2164 * get out of here...
2166 if (needToFindAPlaceholder)
2168 hr = findPlaceholder(
2169 This,
2170 toStoreNode,
2171 storeNode,
2172 relationType);
2175 return hr;
2179 /******************************************************************************
2180 * SetElementTimes (IStorage)
2182 HRESULT WINAPI StorageImpl_SetElementTimes(
2183 IStorage* iface,
2184 const OLECHAR *pwcsName,/* [string][in] */
2185 const FILETIME *pctime, /* [in] */
2186 const FILETIME *patime, /* [in] */
2187 const FILETIME *pmtime) /* [in] */
2189 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2190 return S_OK;
2193 /******************************************************************************
2194 * SetStateBits (IStorage)
2196 HRESULT WINAPI StorageImpl_SetStateBits(
2197 IStorage* iface,
2198 DWORD grfStateBits,/* [in] */
2199 DWORD grfMask) /* [in] */
2201 FIXME("not implemented!\n");
2202 return E_NOTIMPL;
2206 * Virtual function table for the IStorage32Impl class.
2208 static const IStorageVtbl Storage32Impl_Vtbl =
2210 StorageBaseImpl_QueryInterface,
2211 StorageBaseImpl_AddRef,
2212 StorageBaseImpl_Release,
2213 StorageBaseImpl_CreateStream,
2214 StorageBaseImpl_OpenStream,
2215 StorageImpl_CreateStorage,
2216 StorageBaseImpl_OpenStorage,
2217 StorageImpl_CopyTo,
2218 StorageImpl_MoveElementTo,
2219 StorageImpl_Commit,
2220 StorageImpl_Revert,
2221 StorageBaseImpl_EnumElements,
2222 StorageImpl_DestroyElement,
2223 StorageBaseImpl_RenameElement,
2224 StorageImpl_SetElementTimes,
2225 StorageBaseImpl_SetClass,
2226 StorageImpl_SetStateBits,
2227 StorageImpl_Stat
2230 HRESULT StorageImpl_Construct(
2231 StorageImpl* This,
2232 HANDLE hFile,
2233 LPCOLESTR pwcsName,
2234 ILockBytes* pLkbyt,
2235 DWORD openFlags,
2236 BOOL fileBased,
2237 BOOL fileCreate)
2239 HRESULT hr = S_OK;
2240 StgProperty currentProperty;
2241 BOOL readSuccessful;
2242 ULONG currentPropertyIndex;
2244 if ( FAILED( validateSTGM(openFlags) ))
2245 return STG_E_INVALIDFLAG;
2247 memset(This, 0, sizeof(StorageImpl));
2250 * Initialize the virtual function table.
2252 This->base.lpVtbl = &Storage32Impl_Vtbl;
2253 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2254 This->base.v_destructor = &StorageImpl_Destroy;
2255 This->base.openFlags = openFlags;
2258 * This is the top-level storage so initialize the ancestor pointer
2259 * to this.
2261 This->base.ancestorStorage = This;
2264 * Initialize the physical support of the storage.
2266 This->hFile = hFile;
2269 * Store copy of file path.
2271 if(pwcsName) {
2272 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2273 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2274 if (!This->pwcsName)
2275 return STG_E_INSUFFICIENTMEMORY;
2276 strcpyW(This->pwcsName, pwcsName);
2280 * Initialize the big block cache.
2282 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2283 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2284 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2285 pLkbyt,
2286 openFlags,
2287 This->bigBlockSize,
2288 fileBased);
2290 if (This->bigBlockFile == 0)
2291 return E_FAIL;
2293 if (fileCreate)
2295 ULARGE_INTEGER size;
2296 BYTE* bigBlockBuffer;
2299 * Initialize all header variables:
2300 * - The big block depot consists of one block and it is at block 0
2301 * - The properties start at block 1
2302 * - There is no small block depot
2304 memset( This->bigBlockDepotStart,
2305 BLOCK_UNUSED,
2306 sizeof(This->bigBlockDepotStart));
2308 This->bigBlockDepotCount = 1;
2309 This->bigBlockDepotStart[0] = 0;
2310 This->rootStartBlock = 1;
2311 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2312 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2313 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2314 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2315 This->extBigBlockDepotCount = 0;
2317 StorageImpl_SaveFileHeader(This);
2320 * Add one block for the big block depot and one block for the properties
2322 size.u.HighPart = 0;
2323 size.u.LowPart = This->bigBlockSize * 3;
2324 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2327 * Initialize the big block depot
2329 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2330 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2331 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2332 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2333 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2335 else
2338 * Load the header for the file.
2340 hr = StorageImpl_LoadFileHeader(This);
2342 if (FAILED(hr))
2344 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2346 return hr;
2351 * There is no block depot cached yet.
2353 This->indexBlockDepotCached = 0xFFFFFFFF;
2356 * Start searching for free blocks with block 0.
2358 This->prevFreeBlock = 0;
2361 * Create the block chain abstractions.
2363 if(!(This->rootBlockChain =
2364 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2365 return STG_E_READFAULT;
2367 if(!(This->smallBlockDepotChain =
2368 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2369 PROPERTY_NULL)))
2370 return STG_E_READFAULT;
2373 * Write the root property
2375 if (fileCreate)
2377 StgProperty rootProp;
2379 * Initialize the property chain
2381 memset(&rootProp, 0, sizeof(rootProp));
2382 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2383 sizeof(rootProp.name)/sizeof(WCHAR) );
2384 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2385 rootProp.propertyType = PROPTYPE_ROOT;
2386 rootProp.previousProperty = PROPERTY_NULL;
2387 rootProp.nextProperty = PROPERTY_NULL;
2388 rootProp.dirProperty = PROPERTY_NULL;
2389 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2390 rootProp.size.u.HighPart = 0;
2391 rootProp.size.u.LowPart = 0;
2393 StorageImpl_WriteProperty(This, 0, &rootProp);
2397 * Find the ID of the root in the property sets.
2399 currentPropertyIndex = 0;
2403 readSuccessful = StorageImpl_ReadProperty(
2404 This,
2405 currentPropertyIndex,
2406 &currentProperty);
2408 if (readSuccessful)
2410 if ( (currentProperty.sizeOfNameString != 0 ) &&
2411 (currentProperty.propertyType == PROPTYPE_ROOT) )
2413 This->base.rootPropertySetIndex = currentPropertyIndex;
2417 currentPropertyIndex++;
2419 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2421 if (!readSuccessful)
2423 /* TODO CLEANUP */
2424 return STG_E_READFAULT;
2428 * Create the block chain abstraction for the small block root chain.
2430 if(!(This->smallBlockRootChain =
2431 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2432 return STG_E_READFAULT;
2434 return hr;
2437 void StorageImpl_Destroy(StorageBaseImpl* iface)
2439 StorageImpl *This = (StorageImpl*) iface;
2440 TRACE("(%p)\n", This);
2442 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2444 BlockChainStream_Destroy(This->smallBlockRootChain);
2445 BlockChainStream_Destroy(This->rootBlockChain);
2446 BlockChainStream_Destroy(This->smallBlockDepotChain);
2448 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2449 HeapFree(GetProcessHeap(), 0, This);
2452 /******************************************************************************
2453 * Storage32Impl_GetNextFreeBigBlock
2455 * Returns the index of the next free big block.
2456 * If the big block depot is filled, this method will enlarge it.
2459 ULONG StorageImpl_GetNextFreeBigBlock(
2460 StorageImpl* This)
2462 ULONG depotBlockIndexPos;
2463 void *depotBuffer;
2464 ULONG depotBlockOffset;
2465 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2466 ULONG nextBlockIndex = BLOCK_SPECIAL;
2467 int depotIndex = 0;
2468 ULONG freeBlock = BLOCK_UNUSED;
2470 depotIndex = This->prevFreeBlock / blocksPerDepot;
2471 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2474 * Scan the entire big block depot until we find a block marked free
2476 while (nextBlockIndex != BLOCK_UNUSED)
2478 if (depotIndex < COUNT_BBDEPOTINHEADER)
2480 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2483 * Grow the primary depot.
2485 if (depotBlockIndexPos == BLOCK_UNUSED)
2487 depotBlockIndexPos = depotIndex*blocksPerDepot;
2490 * Add a block depot.
2492 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2493 This->bigBlockDepotCount++;
2494 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2497 * Flag it as a block depot.
2499 StorageImpl_SetNextBlockInChain(This,
2500 depotBlockIndexPos,
2501 BLOCK_SPECIAL);
2503 /* Save new header information.
2505 StorageImpl_SaveFileHeader(This);
2508 else
2510 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2512 if (depotBlockIndexPos == BLOCK_UNUSED)
2515 * Grow the extended depot.
2517 ULONG extIndex = BLOCK_UNUSED;
2518 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2519 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2521 if (extBlockOffset == 0)
2523 /* We need an extended block.
2525 extIndex = Storage32Impl_AddExtBlockDepot(This);
2526 This->extBigBlockDepotCount++;
2527 depotBlockIndexPos = extIndex + 1;
2529 else
2530 depotBlockIndexPos = depotIndex * blocksPerDepot;
2533 * Add a block depot and mark it in the extended block.
2535 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2536 This->bigBlockDepotCount++;
2537 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2539 /* Flag the block depot.
2541 StorageImpl_SetNextBlockInChain(This,
2542 depotBlockIndexPos,
2543 BLOCK_SPECIAL);
2545 /* If necessary, flag the extended depot block.
2547 if (extIndex != BLOCK_UNUSED)
2548 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2550 /* Save header information.
2552 StorageImpl_SaveFileHeader(This);
2556 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2558 if (depotBuffer != 0)
2560 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2561 ( nextBlockIndex != BLOCK_UNUSED))
2563 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2565 if (nextBlockIndex == BLOCK_UNUSED)
2567 freeBlock = (depotIndex * blocksPerDepot) +
2568 (depotBlockOffset/sizeof(ULONG));
2571 depotBlockOffset += sizeof(ULONG);
2574 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2577 depotIndex++;
2578 depotBlockOffset = 0;
2581 This->prevFreeBlock = freeBlock;
2583 return freeBlock;
2586 /******************************************************************************
2587 * Storage32Impl_AddBlockDepot
2589 * This will create a depot block, essentially it is a block initialized
2590 * to BLOCK_UNUSEDs.
2592 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2594 BYTE* blockBuffer;
2596 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2599 * Initialize blocks as free
2601 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2603 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2606 /******************************************************************************
2607 * Storage32Impl_GetExtDepotBlock
2609 * Returns the index of the block that corresponds to the specified depot
2610 * index. This method is only for depot indexes equal or greater than
2611 * COUNT_BBDEPOTINHEADER.
2613 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2615 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2616 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2617 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2618 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2619 ULONG blockIndex = BLOCK_UNUSED;
2620 ULONG extBlockIndex = This->extBigBlockDepotStart;
2622 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2624 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2625 return BLOCK_UNUSED;
2627 while (extBlockCount > 0)
2629 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2630 extBlockCount--;
2633 if (extBlockIndex != BLOCK_UNUSED)
2635 BYTE* depotBuffer;
2637 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2639 if (depotBuffer != 0)
2641 StorageUtl_ReadDWord(depotBuffer,
2642 extBlockOffset * sizeof(ULONG),
2643 &blockIndex);
2645 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2649 return blockIndex;
2652 /******************************************************************************
2653 * Storage32Impl_SetExtDepotBlock
2655 * Associates the specified block index to the specified depot index.
2656 * This method is only for depot indexes equal or greater than
2657 * COUNT_BBDEPOTINHEADER.
2659 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2660 ULONG depotIndex,
2661 ULONG blockIndex)
2663 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2664 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2665 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2666 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2667 ULONG extBlockIndex = This->extBigBlockDepotStart;
2669 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2671 while (extBlockCount > 0)
2673 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2674 extBlockCount--;
2677 if (extBlockIndex != BLOCK_UNUSED)
2679 BYTE* depotBuffer;
2681 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2683 if (depotBuffer != 0)
2685 StorageUtl_WriteDWord(depotBuffer,
2686 extBlockOffset * sizeof(ULONG),
2687 blockIndex);
2689 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2694 /******************************************************************************
2695 * Storage32Impl_AddExtBlockDepot
2697 * Creates an extended depot block.
2699 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2701 ULONG numExtBlocks = This->extBigBlockDepotCount;
2702 ULONG nextExtBlock = This->extBigBlockDepotStart;
2703 BYTE* depotBuffer = NULL;
2704 ULONG index = BLOCK_UNUSED;
2705 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2706 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2707 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2709 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2710 blocksPerDepotBlock;
2712 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2715 * The first extended block.
2717 This->extBigBlockDepotStart = index;
2719 else
2721 unsigned int i;
2723 * Follow the chain to the last one.
2725 for (i = 0; i < (numExtBlocks - 1); i++)
2727 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2731 * Add the new extended block to the chain.
2733 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2734 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2735 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2739 * Initialize this block.
2741 depotBuffer = StorageImpl_GetBigBlock(This, index);
2742 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2743 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2745 return index;
2748 /******************************************************************************
2749 * Storage32Impl_FreeBigBlock
2751 * This method will flag the specified block as free in the big block depot.
2753 void StorageImpl_FreeBigBlock(
2754 StorageImpl* This,
2755 ULONG blockIndex)
2757 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2759 if (blockIndex < This->prevFreeBlock)
2760 This->prevFreeBlock = blockIndex;
2763 /************************************************************************
2764 * Storage32Impl_GetNextBlockInChain
2766 * This method will retrieve the block index of the next big block in
2767 * in the chain.
2769 * Params: This - Pointer to the Storage object.
2770 * blockIndex - Index of the block to retrieve the chain
2771 * for.
2772 * nextBlockIndex - receives the return value.
2774 * Returns: This method returns the index of the next block in the chain.
2775 * It will return the constants:
2776 * BLOCK_SPECIAL - If the block given was not part of a
2777 * chain.
2778 * BLOCK_END_OF_CHAIN - If the block given was the last in
2779 * a chain.
2780 * BLOCK_UNUSED - If the block given was not past of a chain
2781 * and is available.
2782 * BLOCK_EXTBBDEPOT - This block is part of the extended
2783 * big block depot.
2785 * See Windows documentation for more details on IStorage methods.
2787 HRESULT StorageImpl_GetNextBlockInChain(
2788 StorageImpl* This,
2789 ULONG blockIndex,
2790 ULONG* nextBlockIndex)
2792 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2793 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2794 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2795 void* depotBuffer;
2796 ULONG depotBlockIndexPos;
2797 int index;
2799 *nextBlockIndex = BLOCK_SPECIAL;
2801 if(depotBlockCount >= This->bigBlockDepotCount)
2803 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2804 This->bigBlockDepotCount);
2805 return STG_E_READFAULT;
2809 * Cache the currently accessed depot block.
2811 if (depotBlockCount != This->indexBlockDepotCached)
2813 This->indexBlockDepotCached = depotBlockCount;
2815 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2817 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2819 else
2822 * We have to look in the extended depot.
2824 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2827 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2829 if (!depotBuffer)
2830 return STG_E_READFAULT;
2832 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2834 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2835 This->blockDepotCached[index] = *nextBlockIndex;
2837 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2840 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2842 return S_OK;
2845 /******************************************************************************
2846 * Storage32Impl_GetNextExtendedBlock
2848 * Given an extended block this method will return the next extended block.
2850 * NOTES:
2851 * The last ULONG of an extended block is the block index of the next
2852 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2853 * depot.
2855 * Return values:
2856 * - The index of the next extended block
2857 * - BLOCK_UNUSED: there is no next extended block.
2858 * - Any other return values denotes failure.
2860 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2862 ULONG nextBlockIndex = BLOCK_SPECIAL;
2863 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2864 void* depotBuffer;
2866 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2868 if (depotBuffer!=0)
2870 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2872 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2875 return nextBlockIndex;
2878 /******************************************************************************
2879 * Storage32Impl_SetNextBlockInChain
2881 * This method will write the index of the specified block's next block
2882 * in the big block depot.
2884 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2885 * do the following
2887 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2888 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2889 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2892 void StorageImpl_SetNextBlockInChain(
2893 StorageImpl* This,
2894 ULONG blockIndex,
2895 ULONG nextBlock)
2897 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2898 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2899 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2900 ULONG depotBlockIndexPos;
2901 void* depotBuffer;
2903 assert(depotBlockCount < This->bigBlockDepotCount);
2904 assert(blockIndex != nextBlock);
2906 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2908 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2910 else
2913 * We have to look in the extended depot.
2915 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2918 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2920 if (depotBuffer!=0)
2922 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2923 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2927 * Update the cached block depot, if necessary.
2929 if (depotBlockCount == This->indexBlockDepotCached)
2931 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2935 /******************************************************************************
2936 * Storage32Impl_LoadFileHeader
2938 * This method will read in the file header, i.e. big block index -1.
2940 HRESULT StorageImpl_LoadFileHeader(
2941 StorageImpl* This)
2943 HRESULT hr = STG_E_FILENOTFOUND;
2944 void* headerBigBlock = NULL;
2945 int index;
2948 * Get a pointer to the big block of data containing the header.
2950 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2953 * Extract the information from the header.
2955 if (headerBigBlock!=0)
2958 * Check for the "magic number" signature and return an error if it is not
2959 * found.
2961 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2963 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2964 return STG_E_OLDFORMAT;
2967 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2969 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2970 return STG_E_INVALIDHEADER;
2973 StorageUtl_ReadWord(
2974 headerBigBlock,
2975 OFFSET_BIGBLOCKSIZEBITS,
2976 &This->bigBlockSizeBits);
2978 StorageUtl_ReadWord(
2979 headerBigBlock,
2980 OFFSET_SMALLBLOCKSIZEBITS,
2981 &This->smallBlockSizeBits);
2983 StorageUtl_ReadDWord(
2984 headerBigBlock,
2985 OFFSET_BBDEPOTCOUNT,
2986 &This->bigBlockDepotCount);
2988 StorageUtl_ReadDWord(
2989 headerBigBlock,
2990 OFFSET_ROOTSTARTBLOCK,
2991 &This->rootStartBlock);
2993 StorageUtl_ReadDWord(
2994 headerBigBlock,
2995 OFFSET_SBDEPOTSTART,
2996 &This->smallBlockDepotStart);
2998 StorageUtl_ReadDWord(
2999 headerBigBlock,
3000 OFFSET_EXTBBDEPOTSTART,
3001 &This->extBigBlockDepotStart);
3003 StorageUtl_ReadDWord(
3004 headerBigBlock,
3005 OFFSET_EXTBBDEPOTCOUNT,
3006 &This->extBigBlockDepotCount);
3008 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3010 StorageUtl_ReadDWord(
3011 headerBigBlock,
3012 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3013 &(This->bigBlockDepotStart[index]));
3017 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3019 if ((1 << 2) == 4)
3021 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3022 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3024 else
3026 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3027 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3031 * Right now, the code is making some assumptions about the size of the
3032 * blocks, just make sure they are what we're expecting.
3034 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3035 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3037 WARN("Broken OLE storage file\n");
3038 hr = STG_E_INVALIDHEADER;
3040 else
3041 hr = S_OK;
3044 * Release the block.
3046 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3049 return hr;
3052 /******************************************************************************
3053 * Storage32Impl_SaveFileHeader
3055 * This method will save to the file the header, i.e. big block -1.
3057 void StorageImpl_SaveFileHeader(
3058 StorageImpl* This)
3060 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3061 int index;
3062 BOOL success;
3065 * Get a pointer to the big block of data containing the header.
3067 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3070 * If the block read failed, the file is probably new.
3072 if (!success)
3075 * Initialize for all unknown fields.
3077 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3080 * Initialize the magic number.
3082 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3085 * And a bunch of things we don't know what they mean
3087 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3088 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3089 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3090 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3094 * Write the information to the header.
3096 StorageUtl_WriteWord(
3097 headerBigBlock,
3098 OFFSET_BIGBLOCKSIZEBITS,
3099 This->bigBlockSizeBits);
3101 StorageUtl_WriteWord(
3102 headerBigBlock,
3103 OFFSET_SMALLBLOCKSIZEBITS,
3104 This->smallBlockSizeBits);
3106 StorageUtl_WriteDWord(
3107 headerBigBlock,
3108 OFFSET_BBDEPOTCOUNT,
3109 This->bigBlockDepotCount);
3111 StorageUtl_WriteDWord(
3112 headerBigBlock,
3113 OFFSET_ROOTSTARTBLOCK,
3114 This->rootStartBlock);
3116 StorageUtl_WriteDWord(
3117 headerBigBlock,
3118 OFFSET_SBDEPOTSTART,
3119 This->smallBlockDepotStart);
3121 StorageUtl_WriteDWord(
3122 headerBigBlock,
3123 OFFSET_SBDEPOTCOUNT,
3124 This->smallBlockDepotChain ?
3125 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3127 StorageUtl_WriteDWord(
3128 headerBigBlock,
3129 OFFSET_EXTBBDEPOTSTART,
3130 This->extBigBlockDepotStart);
3132 StorageUtl_WriteDWord(
3133 headerBigBlock,
3134 OFFSET_EXTBBDEPOTCOUNT,
3135 This->extBigBlockDepotCount);
3137 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3139 StorageUtl_WriteDWord(
3140 headerBigBlock,
3141 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3142 (This->bigBlockDepotStart[index]));
3146 * Write the big block back to the file.
3148 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3151 /******************************************************************************
3152 * Storage32Impl_ReadProperty
3154 * This method will read the specified property from the property chain.
3156 BOOL StorageImpl_ReadProperty(
3157 StorageImpl* This,
3158 ULONG index,
3159 StgProperty* buffer)
3161 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3162 ULARGE_INTEGER offsetInPropSet;
3163 BOOL readSuccessful;
3164 ULONG bytesRead;
3166 offsetInPropSet.u.HighPart = 0;
3167 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3169 readSuccessful = BlockChainStream_ReadAt(
3170 This->rootBlockChain,
3171 offsetInPropSet,
3172 PROPSET_BLOCK_SIZE,
3173 currentProperty,
3174 &bytesRead);
3176 if (readSuccessful)
3178 /* replace the name of root entry (often "Root Entry") by the file name */
3179 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3180 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3182 memset(buffer->name, 0, sizeof(buffer->name));
3183 memcpy(
3184 buffer->name,
3185 propName,
3186 PROPERTY_NAME_BUFFER_LEN );
3187 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3189 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3191 StorageUtl_ReadWord(
3192 currentProperty,
3193 OFFSET_PS_NAMELENGTH,
3194 &buffer->sizeOfNameString);
3196 StorageUtl_ReadDWord(
3197 currentProperty,
3198 OFFSET_PS_PREVIOUSPROP,
3199 &buffer->previousProperty);
3201 StorageUtl_ReadDWord(
3202 currentProperty,
3203 OFFSET_PS_NEXTPROP,
3204 &buffer->nextProperty);
3206 StorageUtl_ReadDWord(
3207 currentProperty,
3208 OFFSET_PS_DIRPROP,
3209 &buffer->dirProperty);
3211 StorageUtl_ReadGUID(
3212 currentProperty,
3213 OFFSET_PS_GUID,
3214 &buffer->propertyUniqueID);
3216 StorageUtl_ReadDWord(
3217 currentProperty,
3218 OFFSET_PS_TSS1,
3219 &buffer->timeStampS1);
3221 StorageUtl_ReadDWord(
3222 currentProperty,
3223 OFFSET_PS_TSD1,
3224 &buffer->timeStampD1);
3226 StorageUtl_ReadDWord(
3227 currentProperty,
3228 OFFSET_PS_TSS2,
3229 &buffer->timeStampS2);
3231 StorageUtl_ReadDWord(
3232 currentProperty,
3233 OFFSET_PS_TSD2,
3234 &buffer->timeStampD2);
3236 StorageUtl_ReadDWord(
3237 currentProperty,
3238 OFFSET_PS_STARTBLOCK,
3239 &buffer->startingBlock);
3241 StorageUtl_ReadDWord(
3242 currentProperty,
3243 OFFSET_PS_SIZE,
3244 &buffer->size.u.LowPart);
3246 buffer->size.u.HighPart = 0;
3249 return readSuccessful;
3252 /*********************************************************************
3253 * Write the specified property into the property chain
3255 BOOL StorageImpl_WriteProperty(
3256 StorageImpl* This,
3257 ULONG index,
3258 StgProperty* buffer)
3260 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3261 ULARGE_INTEGER offsetInPropSet;
3262 BOOL writeSuccessful;
3263 ULONG bytesWritten;
3265 offsetInPropSet.u.HighPart = 0;
3266 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3268 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3270 memcpy(
3271 currentProperty + OFFSET_PS_NAME,
3272 buffer->name,
3273 PROPERTY_NAME_BUFFER_LEN );
3275 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3277 StorageUtl_WriteWord(
3278 currentProperty,
3279 OFFSET_PS_NAMELENGTH,
3280 buffer->sizeOfNameString);
3282 StorageUtl_WriteDWord(
3283 currentProperty,
3284 OFFSET_PS_PREVIOUSPROP,
3285 buffer->previousProperty);
3287 StorageUtl_WriteDWord(
3288 currentProperty,
3289 OFFSET_PS_NEXTPROP,
3290 buffer->nextProperty);
3292 StorageUtl_WriteDWord(
3293 currentProperty,
3294 OFFSET_PS_DIRPROP,
3295 buffer->dirProperty);
3297 StorageUtl_WriteGUID(
3298 currentProperty,
3299 OFFSET_PS_GUID,
3300 &buffer->propertyUniqueID);
3302 StorageUtl_WriteDWord(
3303 currentProperty,
3304 OFFSET_PS_TSS1,
3305 buffer->timeStampS1);
3307 StorageUtl_WriteDWord(
3308 currentProperty,
3309 OFFSET_PS_TSD1,
3310 buffer->timeStampD1);
3312 StorageUtl_WriteDWord(
3313 currentProperty,
3314 OFFSET_PS_TSS2,
3315 buffer->timeStampS2);
3317 StorageUtl_WriteDWord(
3318 currentProperty,
3319 OFFSET_PS_TSD2,
3320 buffer->timeStampD2);
3322 StorageUtl_WriteDWord(
3323 currentProperty,
3324 OFFSET_PS_STARTBLOCK,
3325 buffer->startingBlock);
3327 StorageUtl_WriteDWord(
3328 currentProperty,
3329 OFFSET_PS_SIZE,
3330 buffer->size.u.LowPart);
3332 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3333 offsetInPropSet,
3334 PROPSET_BLOCK_SIZE,
3335 currentProperty,
3336 &bytesWritten);
3337 return writeSuccessful;
3340 BOOL StorageImpl_ReadBigBlock(
3341 StorageImpl* This,
3342 ULONG blockIndex,
3343 void* buffer)
3345 void* bigBlockBuffer;
3347 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3349 if (bigBlockBuffer!=0)
3351 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3353 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3355 return TRUE;
3358 return FALSE;
3361 BOOL StorageImpl_WriteBigBlock(
3362 StorageImpl* This,
3363 ULONG blockIndex,
3364 void* buffer)
3366 void* bigBlockBuffer;
3368 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3370 if (bigBlockBuffer!=0)
3372 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3374 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3376 return TRUE;
3379 return FALSE;
3382 void* StorageImpl_GetROBigBlock(
3383 StorageImpl* This,
3384 ULONG blockIndex)
3386 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3389 void* StorageImpl_GetBigBlock(
3390 StorageImpl* This,
3391 ULONG blockIndex)
3393 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3396 void StorageImpl_ReleaseBigBlock(
3397 StorageImpl* This,
3398 void* pBigBlock)
3400 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3403 /******************************************************************************
3404 * Storage32Impl_SmallBlocksToBigBlocks
3406 * This method will convert a small block chain to a big block chain.
3407 * The small block chain will be destroyed.
3409 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3410 StorageImpl* This,
3411 SmallBlockChainStream** ppsbChain)
3413 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3414 ULARGE_INTEGER size, offset;
3415 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3416 ULONG propertyIndex;
3417 BOOL successWrite;
3418 HRESULT successRead;
3419 StgProperty chainProperty;
3420 BYTE *buffer;
3421 BlockChainStream *bbTempChain = NULL;
3422 BlockChainStream *bigBlockChain = NULL;
3425 * Create a temporary big block chain that doesn't have
3426 * an associated property. This temporary chain will be
3427 * used to copy data from small blocks to big blocks.
3429 bbTempChain = BlockChainStream_Construct(This,
3430 &bbHeadOfChain,
3431 PROPERTY_NULL);
3432 if(!bbTempChain) return NULL;
3434 * Grow the big block chain.
3436 size = SmallBlockChainStream_GetSize(*ppsbChain);
3437 BlockChainStream_SetSize(bbTempChain, size);
3440 * Copy the contents of the small block chain to the big block chain
3441 * by small block size increments.
3443 offset.u.LowPart = 0;
3444 offset.u.HighPart = 0;
3445 cbTotalRead = 0;
3446 cbTotalWritten = 0;
3448 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3451 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3452 offset,
3453 DEF_SMALL_BLOCK_SIZE,
3454 buffer,
3455 &cbRead);
3456 if (FAILED(successRead))
3457 break;
3459 if (cbRead > 0)
3461 cbTotalRead += cbRead;
3463 successWrite = BlockChainStream_WriteAt(bbTempChain,
3464 offset,
3465 cbRead,
3466 buffer,
3467 &cbWritten);
3469 if (!successWrite)
3470 break;
3472 cbTotalWritten += cbWritten;
3473 offset.u.LowPart += This->smallBlockSize;
3475 } while (cbRead > 0);
3476 HeapFree(GetProcessHeap(),0,buffer);
3478 assert(cbTotalRead == cbTotalWritten);
3481 * Destroy the small block chain.
3483 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3484 size.u.HighPart = 0;
3485 size.u.LowPart = 0;
3486 SmallBlockChainStream_SetSize(*ppsbChain, size);
3487 SmallBlockChainStream_Destroy(*ppsbChain);
3488 *ppsbChain = 0;
3491 * Change the property information. This chain is now a big block chain
3492 * and it doesn't reside in the small blocks chain anymore.
3494 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3496 chainProperty.startingBlock = bbHeadOfChain;
3498 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3501 * Destroy the temporary propertyless big block chain.
3502 * Create a new big block chain associated with this property.
3504 BlockChainStream_Destroy(bbTempChain);
3505 bigBlockChain = BlockChainStream_Construct(This,
3506 NULL,
3507 propertyIndex);
3509 return bigBlockChain;
3512 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3514 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3516 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3517 HeapFree(GetProcessHeap(), 0, This);
3520 /******************************************************************************
3522 ** Storage32InternalImpl_Commit
3524 ** The non-root storages cannot be opened in transacted mode thus this function
3525 ** does nothing.
3527 HRESULT WINAPI StorageInternalImpl_Commit(
3528 IStorage* iface,
3529 DWORD grfCommitFlags) /* [in] */
3531 return S_OK;
3534 /******************************************************************************
3536 ** Storage32InternalImpl_Revert
3538 ** The non-root storages cannot be opened in transacted mode thus this function
3539 ** does nothing.
3541 HRESULT WINAPI StorageInternalImpl_Revert(
3542 IStorage* iface)
3544 return S_OK;
3547 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3549 IStorage_Release((IStorage*)This->parentStorage);
3550 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3551 HeapFree(GetProcessHeap(), 0, This);
3554 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3555 IEnumSTATSTG* iface,
3556 REFIID riid,
3557 void** ppvObject)
3559 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3562 * Perform a sanity check on the parameters.
3564 if (ppvObject==0)
3565 return E_INVALIDARG;
3568 * Initialize the return parameter.
3570 *ppvObject = 0;
3573 * Compare the riid with the interface IDs implemented by this object.
3575 if (IsEqualGUID(&IID_IUnknown, riid) ||
3576 IsEqualGUID(&IID_IStorage, riid))
3578 *ppvObject = (IEnumSTATSTG*)This;
3579 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3580 return S_OK;
3583 return E_NOINTERFACE;
3586 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3587 IEnumSTATSTG* iface)
3589 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3590 return InterlockedIncrement(&This->ref);
3593 ULONG WINAPI IEnumSTATSTGImpl_Release(
3594 IEnumSTATSTG* iface)
3596 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3598 ULONG newRef;
3600 newRef = InterlockedDecrement(&This->ref);
3603 * If the reference count goes down to 0, perform suicide.
3605 if (newRef==0)
3607 IEnumSTATSTGImpl_Destroy(This);
3610 return newRef;
3613 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3614 IEnumSTATSTG* iface,
3615 ULONG celt,
3616 STATSTG* rgelt,
3617 ULONG* pceltFetched)
3619 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3621 StgProperty currentProperty;
3622 STATSTG* currentReturnStruct = rgelt;
3623 ULONG objectFetched = 0;
3624 ULONG currentSearchNode;
3627 * Perform a sanity check on the parameters.
3629 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3630 return E_INVALIDARG;
3633 * To avoid the special case, get another pointer to a ULONG value if
3634 * the caller didn't supply one.
3636 if (pceltFetched==0)
3637 pceltFetched = &objectFetched;
3640 * Start the iteration, we will iterate until we hit the end of the
3641 * linked list or until we hit the number of items to iterate through
3643 *pceltFetched = 0;
3646 * Start with the node at the top of the stack.
3648 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3650 while ( ( *pceltFetched < celt) &&
3651 ( currentSearchNode!=PROPERTY_NULL) )
3654 * Remove the top node from the stack
3656 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3659 * Read the property from the storage.
3661 StorageImpl_ReadProperty(This->parentStorage,
3662 currentSearchNode,
3663 &currentProperty);
3666 * Copy the information to the return buffer.
3668 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3669 &currentProperty,
3670 STATFLAG_DEFAULT);
3673 * Step to the next item in the iteration
3675 (*pceltFetched)++;
3676 currentReturnStruct++;
3679 * Push the next search node in the search stack.
3681 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3684 * continue the iteration.
3686 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3689 if (*pceltFetched == celt)
3690 return S_OK;
3692 return S_FALSE;
3696 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3697 IEnumSTATSTG* iface,
3698 ULONG celt)
3700 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3702 StgProperty currentProperty;
3703 ULONG objectFetched = 0;
3704 ULONG currentSearchNode;
3707 * Start with the node at the top of the stack.
3709 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3711 while ( (objectFetched < celt) &&
3712 (currentSearchNode!=PROPERTY_NULL) )
3715 * Remove the top node from the stack
3717 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3720 * Read the property from the storage.
3722 StorageImpl_ReadProperty(This->parentStorage,
3723 currentSearchNode,
3724 &currentProperty);
3727 * Step to the next item in the iteration
3729 objectFetched++;
3732 * Push the next search node in the search stack.
3734 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3737 * continue the iteration.
3739 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3742 if (objectFetched == celt)
3743 return S_OK;
3745 return S_FALSE;
3748 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3749 IEnumSTATSTG* iface)
3751 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3753 StgProperty rootProperty;
3754 BOOL readSuccessful;
3757 * Re-initialize the search stack to an empty stack
3759 This->stackSize = 0;
3762 * Read the root property from the storage.
3764 readSuccessful = StorageImpl_ReadProperty(
3765 This->parentStorage,
3766 This->firstPropertyNode,
3767 &rootProperty);
3769 if (readSuccessful)
3771 assert(rootProperty.sizeOfNameString!=0);
3774 * Push the search node in the search stack.
3776 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3779 return S_OK;
3782 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3783 IEnumSTATSTG* iface,
3784 IEnumSTATSTG** ppenum)
3786 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3788 IEnumSTATSTGImpl* newClone;
3791 * Perform a sanity check on the parameters.
3793 if (ppenum==0)
3794 return E_INVALIDARG;
3796 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3797 This->firstPropertyNode);
3801 * The new clone enumeration must point to the same current node as
3802 * the ole one.
3804 newClone->stackSize = This->stackSize ;
3805 newClone->stackMaxSize = This->stackMaxSize ;
3806 newClone->stackToVisit =
3807 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3809 memcpy(
3810 newClone->stackToVisit,
3811 This->stackToVisit,
3812 sizeof(ULONG) * newClone->stackSize);
3814 *ppenum = (IEnumSTATSTG*)newClone;
3817 * Don't forget to nail down a reference to the clone before
3818 * returning it.
3820 IEnumSTATSTGImpl_AddRef(*ppenum);
3822 return S_OK;
3825 INT IEnumSTATSTGImpl_FindParentProperty(
3826 IEnumSTATSTGImpl *This,
3827 ULONG childProperty,
3828 StgProperty *currentProperty,
3829 ULONG *thisNodeId)
3831 ULONG currentSearchNode;
3832 ULONG foundNode;
3835 * To avoid the special case, get another pointer to a ULONG value if
3836 * the caller didn't supply one.
3838 if (thisNodeId==0)
3839 thisNodeId = &foundNode;
3842 * Start with the node at the top of the stack.
3844 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3847 while (currentSearchNode!=PROPERTY_NULL)
3850 * Store the current node in the returned parameters
3852 *thisNodeId = currentSearchNode;
3855 * Remove the top node from the stack
3857 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3860 * Read the property from the storage.
3862 StorageImpl_ReadProperty(
3863 This->parentStorage,
3864 currentSearchNode,
3865 currentProperty);
3867 if (currentProperty->previousProperty == childProperty)
3868 return PROPERTY_RELATION_PREVIOUS;
3870 else if (currentProperty->nextProperty == childProperty)
3871 return PROPERTY_RELATION_NEXT;
3873 else if (currentProperty->dirProperty == childProperty)
3874 return PROPERTY_RELATION_DIR;
3877 * Push the next search node in the search stack.
3879 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3882 * continue the iteration.
3884 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3887 return PROPERTY_NULL;
3890 ULONG IEnumSTATSTGImpl_FindProperty(
3891 IEnumSTATSTGImpl* This,
3892 const OLECHAR* lpszPropName,
3893 StgProperty* currentProperty)
3895 ULONG currentSearchNode;
3898 * Start with the node at the top of the stack.
3900 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3902 while (currentSearchNode!=PROPERTY_NULL)
3905 * Remove the top node from the stack
3907 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3910 * Read the property from the storage.
3912 StorageImpl_ReadProperty(This->parentStorage,
3913 currentSearchNode,
3914 currentProperty);
3916 if ( propertyNameCmp(
3917 (const OLECHAR*)currentProperty->name,
3918 (const OLECHAR*)lpszPropName) == 0)
3919 return currentSearchNode;
3922 * Push the next search node in the search stack.
3924 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3927 * continue the iteration.
3929 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3932 return PROPERTY_NULL;
3935 void IEnumSTATSTGImpl_PushSearchNode(
3936 IEnumSTATSTGImpl* This,
3937 ULONG nodeToPush)
3939 StgProperty rootProperty;
3940 BOOL readSuccessful;
3943 * First, make sure we're not trying to push an unexisting node.
3945 if (nodeToPush==PROPERTY_NULL)
3946 return;
3949 * First push the node to the stack
3951 if (This->stackSize == This->stackMaxSize)
3953 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3955 This->stackToVisit = HeapReAlloc(
3956 GetProcessHeap(),
3958 This->stackToVisit,
3959 sizeof(ULONG) * This->stackMaxSize);
3962 This->stackToVisit[This->stackSize] = nodeToPush;
3963 This->stackSize++;
3966 * Read the root property from the storage.
3968 readSuccessful = StorageImpl_ReadProperty(
3969 This->parentStorage,
3970 nodeToPush,
3971 &rootProperty);
3973 if (readSuccessful)
3975 assert(rootProperty.sizeOfNameString!=0);
3978 * Push the previous search node in the search stack.
3980 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3984 ULONG IEnumSTATSTGImpl_PopSearchNode(
3985 IEnumSTATSTGImpl* This,
3986 BOOL remove)
3988 ULONG topNode;
3990 if (This->stackSize == 0)
3991 return PROPERTY_NULL;
3993 topNode = This->stackToVisit[This->stackSize-1];
3995 if (remove)
3996 This->stackSize--;
3998 return topNode;
4002 * Virtual function table for the IEnumSTATSTGImpl class.
4004 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4006 IEnumSTATSTGImpl_QueryInterface,
4007 IEnumSTATSTGImpl_AddRef,
4008 IEnumSTATSTGImpl_Release,
4009 IEnumSTATSTGImpl_Next,
4010 IEnumSTATSTGImpl_Skip,
4011 IEnumSTATSTGImpl_Reset,
4012 IEnumSTATSTGImpl_Clone
4015 /******************************************************************************
4016 ** IEnumSTATSTGImpl implementation
4019 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4020 StorageImpl* parentStorage,
4021 ULONG firstPropertyNode)
4023 IEnumSTATSTGImpl* newEnumeration;
4025 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4027 if (newEnumeration!=0)
4030 * Set-up the virtual function table and reference count.
4032 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4033 newEnumeration->ref = 0;
4036 * We want to nail-down the reference to the storage in case the
4037 * enumeration out-lives the storage in the client application.
4039 newEnumeration->parentStorage = parentStorage;
4040 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4042 newEnumeration->firstPropertyNode = firstPropertyNode;
4045 * Initialize the search stack
4047 newEnumeration->stackSize = 0;
4048 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4049 newEnumeration->stackToVisit =
4050 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4053 * Make sure the current node of the iterator is the first one.
4055 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4058 return newEnumeration;
4062 * Virtual function table for the Storage32InternalImpl class.
4064 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4066 StorageBaseImpl_QueryInterface,
4067 StorageBaseImpl_AddRef,
4068 StorageBaseImpl_Release,
4069 StorageBaseImpl_CreateStream,
4070 StorageBaseImpl_OpenStream,
4071 StorageImpl_CreateStorage,
4072 StorageBaseImpl_OpenStorage,
4073 StorageImpl_CopyTo,
4074 StorageImpl_MoveElementTo,
4075 StorageInternalImpl_Commit,
4076 StorageInternalImpl_Revert,
4077 StorageBaseImpl_EnumElements,
4078 StorageImpl_DestroyElement,
4079 StorageBaseImpl_RenameElement,
4080 StorageImpl_SetElementTimes,
4081 StorageBaseImpl_SetClass,
4082 StorageImpl_SetStateBits,
4083 StorageBaseImpl_Stat
4086 /******************************************************************************
4087 ** Storage32InternalImpl implementation
4090 StorageInternalImpl* StorageInternalImpl_Construct(
4091 StorageImpl* ancestorStorage,
4092 DWORD openFlags,
4093 ULONG rootPropertyIndex)
4095 StorageInternalImpl* newStorage;
4098 * Allocate space for the new storage object
4100 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4102 if (newStorage!=0)
4104 memset(newStorage, 0, sizeof(StorageInternalImpl));
4107 * Initialize the virtual function table.
4109 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4110 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4111 newStorage->base.openFlags = openFlags;
4114 * Keep the ancestor storage pointer and nail a reference to it.
4116 newStorage->base.ancestorStorage = ancestorStorage;
4117 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4120 * Keep the index of the root property set for this storage,
4122 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4124 return newStorage;
4127 return 0;
4130 /******************************************************************************
4131 ** StorageUtl implementation
4134 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4136 WORD tmp;
4138 memcpy(&tmp, buffer+offset, sizeof(WORD));
4139 *value = le16toh(tmp);
4142 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4144 value = htole16(value);
4145 memcpy(buffer+offset, &value, sizeof(WORD));
4148 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4150 DWORD tmp;
4152 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4153 *value = le32toh(tmp);
4156 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4158 value = htole32(value);
4159 memcpy(buffer+offset, &value, sizeof(DWORD));
4162 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4163 ULARGE_INTEGER* value)
4165 #ifdef WORDS_BIGENDIAN
4166 ULARGE_INTEGER tmp;
4168 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4169 value->u.LowPart = htole32(tmp.u.HighPart);
4170 value->u.HighPart = htole32(tmp.u.LowPart);
4171 #else
4172 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4173 #endif
4176 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4177 const ULARGE_INTEGER *value)
4179 #ifdef WORDS_BIGENDIAN
4180 ULARGE_INTEGER tmp;
4182 tmp.u.LowPart = htole32(value->u.HighPart);
4183 tmp.u.HighPart = htole32(value->u.LowPart);
4184 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4185 #else
4186 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4187 #endif
4190 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4192 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4193 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4194 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4196 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4199 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4201 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4202 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4203 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4205 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4208 void StorageUtl_CopyPropertyToSTATSTG(
4209 STATSTG* destination,
4210 StgProperty* source,
4211 int statFlags)
4214 * The copy of the string occurs only when the flag is not set
4216 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4217 (source->name == NULL) ||
4218 (source->name[0] == 0) )
4220 destination->pwcsName = 0;
4222 else
4224 destination->pwcsName =
4225 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4227 strcpyW((LPWSTR)destination->pwcsName, source->name);
4230 switch (source->propertyType)
4232 case PROPTYPE_STORAGE:
4233 case PROPTYPE_ROOT:
4234 destination->type = STGTY_STORAGE;
4235 break;
4236 case PROPTYPE_STREAM:
4237 destination->type = STGTY_STREAM;
4238 break;
4239 default:
4240 destination->type = STGTY_STREAM;
4241 break;
4244 destination->cbSize = source->size;
4246 currentReturnStruct->mtime = {0}; TODO
4247 currentReturnStruct->ctime = {0};
4248 currentReturnStruct->atime = {0};
4250 destination->grfMode = 0;
4251 destination->grfLocksSupported = 0;
4252 destination->clsid = source->propertyUniqueID;
4253 destination->grfStateBits = 0;
4254 destination->reserved = 0;
4257 /******************************************************************************
4258 ** BlockChainStream implementation
4261 BlockChainStream* BlockChainStream_Construct(
4262 StorageImpl* parentStorage,
4263 ULONG* headOfStreamPlaceHolder,
4264 ULONG propertyIndex)
4266 BlockChainStream* newStream;
4267 ULONG blockIndex;
4269 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4271 newStream->parentStorage = parentStorage;
4272 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4273 newStream->ownerPropertyIndex = propertyIndex;
4274 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4275 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4276 newStream->numBlocks = 0;
4278 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4280 while (blockIndex != BLOCK_END_OF_CHAIN)
4282 newStream->numBlocks++;
4283 newStream->tailIndex = blockIndex;
4285 if(FAILED(StorageImpl_GetNextBlockInChain(
4286 parentStorage,
4287 blockIndex,
4288 &blockIndex)))
4290 HeapFree(GetProcessHeap(), 0, newStream);
4291 return NULL;
4295 return newStream;
4298 void BlockChainStream_Destroy(BlockChainStream* This)
4300 HeapFree(GetProcessHeap(), 0, This);
4303 /******************************************************************************
4304 * BlockChainStream_GetHeadOfChain
4306 * Returns the head of this stream chain.
4307 * Some special chains don't have properties, their heads are kept in
4308 * This->headOfStreamPlaceHolder.
4311 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4313 StgProperty chainProperty;
4314 BOOL readSuccessful;
4316 if (This->headOfStreamPlaceHolder != 0)
4317 return *(This->headOfStreamPlaceHolder);
4319 if (This->ownerPropertyIndex != PROPERTY_NULL)
4321 readSuccessful = StorageImpl_ReadProperty(
4322 This->parentStorage,
4323 This->ownerPropertyIndex,
4324 &chainProperty);
4326 if (readSuccessful)
4328 return chainProperty.startingBlock;
4332 return BLOCK_END_OF_CHAIN;
4335 /******************************************************************************
4336 * BlockChainStream_GetCount
4338 * Returns the number of blocks that comprises this chain.
4339 * This is not the size of the stream as the last block may not be full!
4342 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4344 ULONG blockIndex;
4345 ULONG count = 0;
4347 blockIndex = BlockChainStream_GetHeadOfChain(This);
4349 while (blockIndex != BLOCK_END_OF_CHAIN)
4351 count++;
4353 if(FAILED(StorageImpl_GetNextBlockInChain(
4354 This->parentStorage,
4355 blockIndex,
4356 &blockIndex)))
4357 return 0;
4360 return count;
4363 /******************************************************************************
4364 * BlockChainStream_ReadAt
4366 * Reads a specified number of bytes from this chain at the specified offset.
4367 * bytesRead may be NULL.
4368 * Failure will be returned if the specified number of bytes has not been read.
4370 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4371 ULARGE_INTEGER offset,
4372 ULONG size,
4373 void* buffer,
4374 ULONG* bytesRead)
4376 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4377 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4378 ULONG bytesToReadInBuffer;
4379 ULONG blockIndex;
4380 BYTE* bufferWalker;
4381 BYTE* bigBlockBuffer;
4384 * Find the first block in the stream that contains part of the buffer.
4386 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4387 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4388 (blockNoInSequence < This->lastBlockNoInSequence) )
4390 blockIndex = BlockChainStream_GetHeadOfChain(This);
4391 This->lastBlockNoInSequence = blockNoInSequence;
4393 else
4395 ULONG temp = blockNoInSequence;
4397 blockIndex = This->lastBlockNoInSequenceIndex;
4398 blockNoInSequence -= This->lastBlockNoInSequence;
4399 This->lastBlockNoInSequence = temp;
4402 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4404 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4405 return FALSE;
4406 blockNoInSequence--;
4409 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4410 return FALSE; /* We failed to find the starting block */
4412 This->lastBlockNoInSequenceIndex = blockIndex;
4415 * Start reading the buffer.
4417 *bytesRead = 0;
4418 bufferWalker = buffer;
4420 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4423 * Calculate how many bytes we can copy from this big block.
4425 bytesToReadInBuffer =
4426 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4429 * Copy those bytes to the buffer
4431 bigBlockBuffer =
4432 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4434 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4436 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4439 * Step to the next big block.
4441 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4442 return FALSE;
4444 bufferWalker += bytesToReadInBuffer;
4445 size -= bytesToReadInBuffer;
4446 *bytesRead += bytesToReadInBuffer;
4447 offsetInBlock = 0; /* There is no offset on the next block */
4451 return (size == 0);
4454 /******************************************************************************
4455 * BlockChainStream_WriteAt
4457 * Writes the specified number of bytes to this chain at the specified offset.
4458 * bytesWritten may be NULL.
4459 * Will fail if not all specified number of bytes have been written.
4461 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4462 ULARGE_INTEGER offset,
4463 ULONG size,
4464 const void* buffer,
4465 ULONG* bytesWritten)
4467 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4468 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4469 ULONG bytesToWrite;
4470 ULONG blockIndex;
4471 const BYTE* bufferWalker;
4472 BYTE* bigBlockBuffer;
4475 * Find the first block in the stream that contains part of the buffer.
4477 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4478 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4479 (blockNoInSequence < This->lastBlockNoInSequence) )
4481 blockIndex = BlockChainStream_GetHeadOfChain(This);
4482 This->lastBlockNoInSequence = blockNoInSequence;
4484 else
4486 ULONG temp = blockNoInSequence;
4488 blockIndex = This->lastBlockNoInSequenceIndex;
4489 blockNoInSequence -= This->lastBlockNoInSequence;
4490 This->lastBlockNoInSequence = temp;
4493 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4495 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4496 &blockIndex)))
4497 return FALSE;
4498 blockNoInSequence--;
4501 This->lastBlockNoInSequenceIndex = blockIndex;
4504 * Here, I'm casting away the constness on the buffer variable
4505 * This is OK since we don't intend to modify that buffer.
4507 *bytesWritten = 0;
4508 bufferWalker = (const BYTE*)buffer;
4510 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4513 * Calculate how many bytes we can copy from this big block.
4515 bytesToWrite =
4516 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4519 * Copy those bytes to the buffer
4521 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4523 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4525 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4528 * Step to the next big block.
4530 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4531 &blockIndex)))
4532 return FALSE;
4533 bufferWalker += bytesToWrite;
4534 size -= bytesToWrite;
4535 *bytesWritten += bytesToWrite;
4536 offsetInBlock = 0; /* There is no offset on the next block */
4539 return (size == 0);
4542 /******************************************************************************
4543 * BlockChainStream_Shrink
4545 * Shrinks this chain in the big block depot.
4547 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4548 ULARGE_INTEGER newSize)
4550 ULONG blockIndex, extraBlock;
4551 ULONG numBlocks;
4552 ULONG count = 1;
4555 * Reset the last accessed block cache.
4557 This->lastBlockNoInSequence = 0xFFFFFFFF;
4558 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4561 * Figure out how many blocks are needed to contain the new size
4563 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4565 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4566 numBlocks++;
4568 blockIndex = BlockChainStream_GetHeadOfChain(This);
4571 * Go to the new end of chain
4573 while (count < numBlocks)
4575 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4576 &blockIndex)))
4577 return FALSE;
4578 count++;
4581 /* Get the next block before marking the new end */
4582 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4583 &extraBlock)))
4584 return FALSE;
4586 /* Mark the new end of chain */
4587 StorageImpl_SetNextBlockInChain(
4588 This->parentStorage,
4589 blockIndex,
4590 BLOCK_END_OF_CHAIN);
4592 This->tailIndex = blockIndex;
4593 This->numBlocks = numBlocks;
4596 * Mark the extra blocks as free
4598 while (extraBlock != BLOCK_END_OF_CHAIN)
4600 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4601 &blockIndex)))
4602 return FALSE;
4603 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4604 extraBlock = blockIndex;
4607 return TRUE;
4610 /******************************************************************************
4611 * BlockChainStream_Enlarge
4613 * Grows this chain in the big block depot.
4615 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4616 ULARGE_INTEGER newSize)
4618 ULONG blockIndex, currentBlock;
4619 ULONG newNumBlocks;
4620 ULONG oldNumBlocks = 0;
4622 blockIndex = BlockChainStream_GetHeadOfChain(This);
4625 * Empty chain. Create the head.
4627 if (blockIndex == BLOCK_END_OF_CHAIN)
4629 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4630 StorageImpl_SetNextBlockInChain(This->parentStorage,
4631 blockIndex,
4632 BLOCK_END_OF_CHAIN);
4634 if (This->headOfStreamPlaceHolder != 0)
4636 *(This->headOfStreamPlaceHolder) = blockIndex;
4638 else
4640 StgProperty chainProp;
4641 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4643 StorageImpl_ReadProperty(
4644 This->parentStorage,
4645 This->ownerPropertyIndex,
4646 &chainProp);
4648 chainProp.startingBlock = blockIndex;
4650 StorageImpl_WriteProperty(
4651 This->parentStorage,
4652 This->ownerPropertyIndex,
4653 &chainProp);
4656 This->tailIndex = blockIndex;
4657 This->numBlocks = 1;
4661 * Figure out how many blocks are needed to contain this stream
4663 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4665 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4666 newNumBlocks++;
4669 * Go to the current end of chain
4671 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4673 currentBlock = blockIndex;
4675 while (blockIndex != BLOCK_END_OF_CHAIN)
4677 This->numBlocks++;
4678 currentBlock = blockIndex;
4680 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4681 &blockIndex)))
4682 return FALSE;
4685 This->tailIndex = currentBlock;
4688 currentBlock = This->tailIndex;
4689 oldNumBlocks = This->numBlocks;
4692 * Add new blocks to the chain
4694 if (oldNumBlocks < newNumBlocks)
4696 while (oldNumBlocks < newNumBlocks)
4698 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4700 StorageImpl_SetNextBlockInChain(
4701 This->parentStorage,
4702 currentBlock,
4703 blockIndex);
4705 StorageImpl_SetNextBlockInChain(
4706 This->parentStorage,
4707 blockIndex,
4708 BLOCK_END_OF_CHAIN);
4710 currentBlock = blockIndex;
4711 oldNumBlocks++;
4714 This->tailIndex = blockIndex;
4715 This->numBlocks = newNumBlocks;
4718 return TRUE;
4721 /******************************************************************************
4722 * BlockChainStream_SetSize
4724 * Sets the size of this stream. The big block depot will be updated.
4725 * The file will grow if we grow the chain.
4727 * TODO: Free the actual blocks in the file when we shrink the chain.
4728 * Currently, the blocks are still in the file. So the file size
4729 * doesn't shrink even if we shrink streams.
4731 BOOL BlockChainStream_SetSize(
4732 BlockChainStream* This,
4733 ULARGE_INTEGER newSize)
4735 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4737 if (newSize.u.LowPart == size.u.LowPart)
4738 return TRUE;
4740 if (newSize.u.LowPart < size.u.LowPart)
4742 BlockChainStream_Shrink(This, newSize);
4744 else
4746 ULARGE_INTEGER fileSize =
4747 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4749 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4752 * Make sure the file stays a multiple of blocksize
4754 if ((diff % This->parentStorage->bigBlockSize) != 0)
4755 diff += (This->parentStorage->bigBlockSize -
4756 (diff % This->parentStorage->bigBlockSize) );
4758 fileSize.u.LowPart += diff;
4759 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4761 BlockChainStream_Enlarge(This, newSize);
4764 return TRUE;
4767 /******************************************************************************
4768 * BlockChainStream_GetSize
4770 * Returns the size of this chain.
4771 * Will return the block count if this chain doesn't have a property.
4773 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4775 StgProperty chainProperty;
4777 if(This->headOfStreamPlaceHolder == NULL)
4780 * This chain is a data stream read the property and return
4781 * the appropriate size
4783 StorageImpl_ReadProperty(
4784 This->parentStorage,
4785 This->ownerPropertyIndex,
4786 &chainProperty);
4788 return chainProperty.size;
4790 else
4793 * this chain is a chain that does not have a property, figure out the
4794 * size by making the product number of used blocks times the
4795 * size of them
4797 ULARGE_INTEGER result;
4798 result.u.HighPart = 0;
4800 result.u.LowPart =
4801 BlockChainStream_GetCount(This) *
4802 This->parentStorage->bigBlockSize;
4804 return result;
4808 /******************************************************************************
4809 ** SmallBlockChainStream implementation
4812 SmallBlockChainStream* SmallBlockChainStream_Construct(
4813 StorageImpl* parentStorage,
4814 ULONG propertyIndex)
4816 SmallBlockChainStream* newStream;
4818 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4820 newStream->parentStorage = parentStorage;
4821 newStream->ownerPropertyIndex = propertyIndex;
4823 return newStream;
4826 void SmallBlockChainStream_Destroy(
4827 SmallBlockChainStream* This)
4829 HeapFree(GetProcessHeap(), 0, This);
4832 /******************************************************************************
4833 * SmallBlockChainStream_GetHeadOfChain
4835 * Returns the head of this chain of small blocks.
4837 ULONG SmallBlockChainStream_GetHeadOfChain(
4838 SmallBlockChainStream* This)
4840 StgProperty chainProperty;
4841 BOOL readSuccessful;
4843 if (This->ownerPropertyIndex)
4845 readSuccessful = StorageImpl_ReadProperty(
4846 This->parentStorage,
4847 This->ownerPropertyIndex,
4848 &chainProperty);
4850 if (readSuccessful)
4852 return chainProperty.startingBlock;
4857 return BLOCK_END_OF_CHAIN;
4860 /******************************************************************************
4861 * SmallBlockChainStream_GetNextBlockInChain
4863 * Returns the index of the next small block in this chain.
4865 * Return Values:
4866 * - BLOCK_END_OF_CHAIN: end of this chain
4867 * - BLOCK_UNUSED: small block 'blockIndex' is free
4869 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4870 SmallBlockChainStream* This,
4871 ULONG blockIndex,
4872 ULONG* nextBlockInChain)
4874 ULARGE_INTEGER offsetOfBlockInDepot;
4875 DWORD buffer;
4876 ULONG bytesRead;
4877 BOOL success;
4879 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4881 offsetOfBlockInDepot.u.HighPart = 0;
4882 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4885 * Read those bytes in the buffer from the small block file.
4887 success = BlockChainStream_ReadAt(
4888 This->parentStorage->smallBlockDepotChain,
4889 offsetOfBlockInDepot,
4890 sizeof(DWORD),
4891 &buffer,
4892 &bytesRead);
4894 if (success)
4896 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4897 return S_OK;
4900 return STG_E_READFAULT;
4903 /******************************************************************************
4904 * SmallBlockChainStream_SetNextBlockInChain
4906 * Writes the index of the next block of the specified block in the small
4907 * block depot.
4908 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4909 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4911 void SmallBlockChainStream_SetNextBlockInChain(
4912 SmallBlockChainStream* This,
4913 ULONG blockIndex,
4914 ULONG nextBlock)
4916 ULARGE_INTEGER offsetOfBlockInDepot;
4917 DWORD buffer;
4918 ULONG bytesWritten;
4920 offsetOfBlockInDepot.u.HighPart = 0;
4921 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4923 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4926 * Read those bytes in the buffer from the small block file.
4928 BlockChainStream_WriteAt(
4929 This->parentStorage->smallBlockDepotChain,
4930 offsetOfBlockInDepot,
4931 sizeof(DWORD),
4932 &buffer,
4933 &bytesWritten);
4936 /******************************************************************************
4937 * SmallBlockChainStream_FreeBlock
4939 * Flag small block 'blockIndex' as free in the small block depot.
4941 void SmallBlockChainStream_FreeBlock(
4942 SmallBlockChainStream* This,
4943 ULONG blockIndex)
4945 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4948 /******************************************************************************
4949 * SmallBlockChainStream_GetNextFreeBlock
4951 * Returns the index of a free small block. The small block depot will be
4952 * enlarged if necessary. The small block chain will also be enlarged if
4953 * necessary.
4955 ULONG SmallBlockChainStream_GetNextFreeBlock(
4956 SmallBlockChainStream* This)
4958 ULARGE_INTEGER offsetOfBlockInDepot;
4959 DWORD buffer;
4960 ULONG bytesRead;
4961 ULONG blockIndex = 0;
4962 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4963 BOOL success = TRUE;
4964 ULONG smallBlocksPerBigBlock;
4966 offsetOfBlockInDepot.u.HighPart = 0;
4969 * Scan the small block depot for a free block
4971 while (nextBlockIndex != BLOCK_UNUSED)
4973 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4975 success = BlockChainStream_ReadAt(
4976 This->parentStorage->smallBlockDepotChain,
4977 offsetOfBlockInDepot,
4978 sizeof(DWORD),
4979 &buffer,
4980 &bytesRead);
4983 * If we run out of space for the small block depot, enlarge it
4985 if (success)
4987 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
4989 if (nextBlockIndex != BLOCK_UNUSED)
4990 blockIndex++;
4992 else
4994 ULONG count =
4995 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4997 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4998 ULONG nextBlock, newsbdIndex;
4999 BYTE* smallBlockDepot;
5001 nextBlock = sbdIndex;
5002 while (nextBlock != BLOCK_END_OF_CHAIN)
5004 sbdIndex = nextBlock;
5005 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5008 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5009 if (sbdIndex != BLOCK_END_OF_CHAIN)
5010 StorageImpl_SetNextBlockInChain(
5011 This->parentStorage,
5012 sbdIndex,
5013 newsbdIndex);
5015 StorageImpl_SetNextBlockInChain(
5016 This->parentStorage,
5017 newsbdIndex,
5018 BLOCK_END_OF_CHAIN);
5021 * Initialize all the small blocks to free
5023 smallBlockDepot =
5024 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5026 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5027 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5029 if (count == 0)
5032 * We have just created the small block depot.
5034 StgProperty rootProp;
5035 ULONG sbStartIndex;
5038 * Save it in the header
5040 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5041 StorageImpl_SaveFileHeader(This->parentStorage);
5044 * And allocate the first big block that will contain small blocks
5046 sbStartIndex =
5047 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5049 StorageImpl_SetNextBlockInChain(
5050 This->parentStorage,
5051 sbStartIndex,
5052 BLOCK_END_OF_CHAIN);
5054 StorageImpl_ReadProperty(
5055 This->parentStorage,
5056 This->parentStorage->base.rootPropertySetIndex,
5057 &rootProp);
5059 rootProp.startingBlock = sbStartIndex;
5060 rootProp.size.u.HighPart = 0;
5061 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5063 StorageImpl_WriteProperty(
5064 This->parentStorage,
5065 This->parentStorage->base.rootPropertySetIndex,
5066 &rootProp);
5071 smallBlocksPerBigBlock =
5072 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5075 * Verify if we have to allocate big blocks to contain small blocks
5077 if (blockIndex % smallBlocksPerBigBlock == 0)
5079 StgProperty rootProp;
5080 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5082 StorageImpl_ReadProperty(
5083 This->parentStorage,
5084 This->parentStorage->base.rootPropertySetIndex,
5085 &rootProp);
5087 if (rootProp.size.u.LowPart <
5088 (blocksRequired * This->parentStorage->bigBlockSize))
5090 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5092 BlockChainStream_SetSize(
5093 This->parentStorage->smallBlockRootChain,
5094 rootProp.size);
5096 StorageImpl_WriteProperty(
5097 This->parentStorage,
5098 This->parentStorage->base.rootPropertySetIndex,
5099 &rootProp);
5103 return blockIndex;
5106 /******************************************************************************
5107 * SmallBlockChainStream_ReadAt
5109 * Reads a specified number of bytes from this chain at the specified offset.
5110 * bytesRead may be NULL.
5111 * Failure will be returned if the specified number of bytes has not been read.
5113 HRESULT SmallBlockChainStream_ReadAt(
5114 SmallBlockChainStream* This,
5115 ULARGE_INTEGER offset,
5116 ULONG size,
5117 void* buffer,
5118 ULONG* bytesRead)
5120 HRESULT rc = S_OK;
5121 ULARGE_INTEGER offsetInBigBlockFile;
5122 ULONG blockNoInSequence =
5123 offset.u.LowPart / This->parentStorage->smallBlockSize;
5125 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5126 ULONG bytesToReadInBuffer;
5127 ULONG blockIndex;
5128 ULONG bytesReadFromBigBlockFile;
5129 BYTE* bufferWalker;
5132 * This should never happen on a small block file.
5134 assert(offset.u.HighPart==0);
5137 * Find the first block in the stream that contains part of the buffer.
5139 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5141 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5143 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5144 if(FAILED(rc))
5145 return rc;
5146 blockNoInSequence--;
5150 * Start reading the buffer.
5152 *bytesRead = 0;
5153 bufferWalker = buffer;
5155 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5158 * Calculate how many bytes we can copy from this small block.
5160 bytesToReadInBuffer =
5161 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5164 * Calculate the offset of the small block in the small block file.
5166 offsetInBigBlockFile.u.HighPart = 0;
5167 offsetInBigBlockFile.u.LowPart =
5168 blockIndex * This->parentStorage->smallBlockSize;
5170 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5173 * Read those bytes in the buffer from the small block file.
5174 * The small block has already been identified so it shouldn't fail
5175 * unless the file is corrupt.
5177 if (!BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5178 offsetInBigBlockFile,
5179 bytesToReadInBuffer,
5180 bufferWalker,
5181 &bytesReadFromBigBlockFile))
5182 return STG_E_DOCFILECORRUPT;
5184 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5187 * Step to the next big block.
5189 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5190 if(FAILED(rc))
5191 return rc;
5193 bufferWalker += bytesToReadInBuffer;
5194 size -= bytesToReadInBuffer;
5195 *bytesRead += bytesToReadInBuffer;
5196 offsetInBlock = 0; /* There is no offset on the next block */
5199 return rc;
5202 /******************************************************************************
5203 * SmallBlockChainStream_WriteAt
5205 * Writes the specified number of bytes to this chain at the specified offset.
5206 * bytesWritten may be NULL.
5207 * Will fail if not all specified number of bytes have been written.
5209 BOOL SmallBlockChainStream_WriteAt(
5210 SmallBlockChainStream* This,
5211 ULARGE_INTEGER offset,
5212 ULONG size,
5213 const void* buffer,
5214 ULONG* bytesWritten)
5216 ULARGE_INTEGER offsetInBigBlockFile;
5217 ULONG blockNoInSequence =
5218 offset.u.LowPart / This->parentStorage->smallBlockSize;
5220 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5221 ULONG bytesToWriteInBuffer;
5222 ULONG blockIndex;
5223 ULONG bytesWrittenFromBigBlockFile;
5224 const BYTE* bufferWalker;
5227 * This should never happen on a small block file.
5229 assert(offset.u.HighPart==0);
5232 * Find the first block in the stream that contains part of the buffer.
5234 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5236 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5238 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5239 return FALSE;
5240 blockNoInSequence--;
5244 * Start writing the buffer.
5246 * Here, I'm casting away the constness on the buffer variable
5247 * This is OK since we don't intend to modify that buffer.
5249 *bytesWritten = 0;
5250 bufferWalker = (const BYTE*)buffer;
5251 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5254 * Calculate how many bytes we can copy to this small block.
5256 bytesToWriteInBuffer =
5257 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5260 * Calculate the offset of the small block in the small block file.
5262 offsetInBigBlockFile.u.HighPart = 0;
5263 offsetInBigBlockFile.u.LowPart =
5264 blockIndex * This->parentStorage->smallBlockSize;
5266 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5269 * Write those bytes in the buffer to the small block file.
5271 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5272 offsetInBigBlockFile,
5273 bytesToWriteInBuffer,
5274 bufferWalker,
5275 &bytesWrittenFromBigBlockFile);
5277 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5280 * Step to the next big block.
5282 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5283 &blockIndex)))
5284 return FALSE;
5285 bufferWalker += bytesToWriteInBuffer;
5286 size -= bytesToWriteInBuffer;
5287 *bytesWritten += bytesToWriteInBuffer;
5288 offsetInBlock = 0; /* There is no offset on the next block */
5291 return (size == 0);
5294 /******************************************************************************
5295 * SmallBlockChainStream_Shrink
5297 * Shrinks this chain in the small block depot.
5299 BOOL SmallBlockChainStream_Shrink(
5300 SmallBlockChainStream* This,
5301 ULARGE_INTEGER newSize)
5303 ULONG blockIndex, extraBlock;
5304 ULONG numBlocks;
5305 ULONG count = 0;
5307 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5309 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5310 numBlocks++;
5312 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5315 * Go to the new end of chain
5317 while (count < numBlocks)
5319 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5320 &blockIndex)))
5321 return FALSE;
5322 count++;
5326 * If the count is 0, we have a special case, the head of the chain was
5327 * just freed.
5329 if (count == 0)
5331 StgProperty chainProp;
5333 StorageImpl_ReadProperty(This->parentStorage,
5334 This->ownerPropertyIndex,
5335 &chainProp);
5337 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5339 StorageImpl_WriteProperty(This->parentStorage,
5340 This->ownerPropertyIndex,
5341 &chainProp);
5344 * We start freeing the chain at the head block.
5346 extraBlock = blockIndex;
5348 else
5350 /* Get the next block before marking the new end */
5351 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5352 &extraBlock)))
5353 return FALSE;
5355 /* Mark the new end of chain */
5356 SmallBlockChainStream_SetNextBlockInChain(
5357 This,
5358 blockIndex,
5359 BLOCK_END_OF_CHAIN);
5363 * Mark the extra blocks as free
5365 while (extraBlock != BLOCK_END_OF_CHAIN)
5367 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5368 &blockIndex)))
5369 return FALSE;
5370 SmallBlockChainStream_FreeBlock(This, extraBlock);
5371 extraBlock = blockIndex;
5374 return TRUE;
5377 /******************************************************************************
5378 * SmallBlockChainStream_Enlarge
5380 * Grows this chain in the small block depot.
5382 BOOL SmallBlockChainStream_Enlarge(
5383 SmallBlockChainStream* This,
5384 ULARGE_INTEGER newSize)
5386 ULONG blockIndex, currentBlock;
5387 ULONG newNumBlocks;
5388 ULONG oldNumBlocks = 0;
5390 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5393 * Empty chain
5395 if (blockIndex == BLOCK_END_OF_CHAIN)
5398 StgProperty chainProp;
5400 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5401 &chainProp);
5403 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5405 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5406 &chainProp);
5408 blockIndex = chainProp.startingBlock;
5409 SmallBlockChainStream_SetNextBlockInChain(
5410 This,
5411 blockIndex,
5412 BLOCK_END_OF_CHAIN);
5415 currentBlock = blockIndex;
5418 * Figure out how many blocks are needed to contain this stream
5420 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5422 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5423 newNumBlocks++;
5426 * Go to the current end of chain
5428 while (blockIndex != BLOCK_END_OF_CHAIN)
5430 oldNumBlocks++;
5431 currentBlock = blockIndex;
5432 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5433 return FALSE;
5437 * Add new blocks to the chain
5439 while (oldNumBlocks < newNumBlocks)
5441 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5442 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5444 SmallBlockChainStream_SetNextBlockInChain(
5445 This,
5446 blockIndex,
5447 BLOCK_END_OF_CHAIN);
5449 currentBlock = blockIndex;
5450 oldNumBlocks++;
5453 return TRUE;
5456 /******************************************************************************
5457 * SmallBlockChainStream_GetCount
5459 * Returns the number of blocks that comprises this chain.
5460 * This is not the size of this chain as the last block may not be full!
5462 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5464 ULONG blockIndex;
5465 ULONG count = 0;
5467 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5469 while (blockIndex != BLOCK_END_OF_CHAIN)
5471 count++;
5473 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5474 return 0;
5477 return count;
5480 /******************************************************************************
5481 * SmallBlockChainStream_SetSize
5483 * Sets the size of this stream.
5484 * The file will grow if we grow the chain.
5486 * TODO: Free the actual blocks in the file when we shrink the chain.
5487 * Currently, the blocks are still in the file. So the file size
5488 * doesn't shrink even if we shrink streams.
5490 BOOL SmallBlockChainStream_SetSize(
5491 SmallBlockChainStream* This,
5492 ULARGE_INTEGER newSize)
5494 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5496 if (newSize.u.LowPart == size.u.LowPart)
5497 return TRUE;
5499 if (newSize.u.LowPart < size.u.LowPart)
5501 SmallBlockChainStream_Shrink(This, newSize);
5503 else
5505 SmallBlockChainStream_Enlarge(This, newSize);
5508 return TRUE;
5511 /******************************************************************************
5512 * SmallBlockChainStream_GetSize
5514 * Returns the size of this chain.
5516 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5518 StgProperty chainProperty;
5520 StorageImpl_ReadProperty(
5521 This->parentStorage,
5522 This->ownerPropertyIndex,
5523 &chainProperty);
5525 return chainProperty.size;
5528 /******************************************************************************
5529 * StgCreateDocfile [OLE32.@]
5531 HRESULT WINAPI StgCreateDocfile(
5532 LPCOLESTR pwcsName,
5533 DWORD grfMode,
5534 DWORD reserved,
5535 IStorage **ppstgOpen)
5537 StorageImpl* newStorage = 0;
5538 HANDLE hFile = INVALID_HANDLE_VALUE;
5539 HRESULT hr = STG_E_INVALIDFLAG;
5540 DWORD shareMode;
5541 DWORD accessMode;
5542 DWORD creationMode;
5543 DWORD fileAttributes;
5544 WCHAR tempFileName[MAX_PATH];
5546 TRACE("(%s, %lx, %ld, %p)\n",
5547 debugstr_w(pwcsName), grfMode,
5548 reserved, ppstgOpen);
5551 * Validate the parameters
5553 if (ppstgOpen == 0)
5554 return STG_E_INVALIDPOINTER;
5555 if (reserved != 0)
5556 return STG_E_INVALIDPARAMETER;
5559 * Validate the STGM flags
5561 if ( FAILED( validateSTGM(grfMode) ))
5562 goto end;
5564 /* StgCreateDocFile always opens for write */
5565 switch(STGM_ACCESS_MODE(grfMode))
5567 case STGM_WRITE:
5568 case STGM_READWRITE:
5569 break;
5570 default:
5571 goto end;
5574 /* can't share write */
5575 switch(STGM_SHARE_MODE(grfMode))
5577 case STGM_SHARE_EXCLUSIVE:
5578 case STGM_SHARE_DENY_WRITE:
5579 break;
5580 default:
5581 goto end;
5584 /* shared reading requires transacted mode */
5585 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5586 !(grfMode&STGM_TRANSACTED) )
5587 goto end;
5590 * Generate a unique name.
5592 if (pwcsName == 0)
5594 WCHAR tempPath[MAX_PATH];
5595 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5597 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
5598 goto end;
5600 memset(tempPath, 0, sizeof(tempPath));
5601 memset(tempFileName, 0, sizeof(tempFileName));
5603 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5604 tempPath[0] = '.';
5606 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5607 pwcsName = tempFileName;
5608 else
5610 hr = STG_E_INSUFFICIENTMEMORY;
5611 goto end;
5614 creationMode = TRUNCATE_EXISTING;
5616 else
5618 creationMode = GetCreationModeFromSTGM(grfMode);
5622 * Interpret the STGM value grfMode
5624 shareMode = GetShareModeFromSTGM(grfMode);
5625 accessMode = GetAccessModeFromSTGM(grfMode);
5627 if (grfMode & STGM_DELETEONRELEASE)
5628 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5629 else
5630 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5632 if (grfMode & STGM_TRANSACTED)
5633 FIXME("Transacted mode not implemented.\n");
5636 * Initialize the "out" parameter.
5638 *ppstgOpen = 0;
5640 hFile = CreateFileW(pwcsName,
5641 accessMode,
5642 shareMode,
5643 NULL,
5644 creationMode,
5645 fileAttributes,
5648 if (hFile == INVALID_HANDLE_VALUE)
5650 if(GetLastError() == ERROR_FILE_EXISTS)
5651 hr = STG_E_FILEALREADYEXISTS;
5652 else
5653 hr = E_FAIL;
5654 goto end;
5658 * Allocate and initialize the new IStorage32object.
5660 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5662 if (newStorage == 0)
5664 hr = STG_E_INSUFFICIENTMEMORY;
5665 goto end;
5668 hr = StorageImpl_Construct(
5669 newStorage,
5670 hFile,
5671 pwcsName,
5672 NULL,
5673 grfMode,
5674 TRUE,
5675 TRUE);
5677 if (FAILED(hr))
5679 HeapFree(GetProcessHeap(), 0, newStorage);
5680 goto end;
5684 * Get an "out" pointer for the caller.
5686 hr = StorageBaseImpl_QueryInterface(
5687 (IStorage*)newStorage,
5688 (REFIID)&IID_IStorage,
5689 (void**)ppstgOpen);
5690 end:
5691 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5693 return hr;
5696 /******************************************************************************
5697 * StgCreateStorageEx [OLE32.@]
5699 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5701 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5702 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5704 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5706 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5707 return STG_E_INVALIDPARAMETER;
5710 if (stgfmt != STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5712 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5713 return STG_E_INVALIDPARAMETER;
5716 if (stgfmt == STGFMT_FILE)
5718 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5719 return STG_E_INVALIDPARAMETER;
5722 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5724 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5725 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5728 ERR("Invalid stgfmt argument\n");
5729 return STG_E_INVALIDPARAMETER;
5732 /******************************************************************************
5733 * StgCreatePropSetStg [OLE32.@]
5735 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5736 IPropertySetStorage **ppPropSetStg)
5738 HRESULT hr;
5740 TRACE("(%p, 0x%lx, %p)\n", pstg, reserved, ppPropSetStg);
5741 if (reserved)
5742 hr = STG_E_INVALIDPARAMETER;
5743 else
5744 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5745 (void**)ppPropSetStg);
5746 return hr;
5749 /******************************************************************************
5750 * StgOpenStorageEx [OLE32.@]
5752 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5754 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5755 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5757 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5759 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5760 return STG_E_INVALIDPARAMETER;
5763 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5765 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5766 return STG_E_INVALIDPARAMETER;
5769 if (stgfmt == STGFMT_FILE)
5771 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5772 return STG_E_INVALIDPARAMETER;
5775 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE || stgfmt == STGFMT_ANY)
5777 if (stgfmt == STGFMT_ANY)
5778 WARN("STGFMT_ANY assuming storage\n");
5779 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5780 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5783 ERR("Invalid stgfmt argument\n");
5784 return STG_E_INVALIDPARAMETER;
5788 /******************************************************************************
5789 * StgOpenStorage [OLE32.@]
5791 HRESULT WINAPI StgOpenStorage(
5792 const OLECHAR *pwcsName,
5793 IStorage *pstgPriority,
5794 DWORD grfMode,
5795 SNB snbExclude,
5796 DWORD reserved,
5797 IStorage **ppstgOpen)
5799 StorageImpl* newStorage = 0;
5800 HRESULT hr = S_OK;
5801 HANDLE hFile = 0;
5802 DWORD shareMode;
5803 DWORD accessMode;
5804 WCHAR fullname[MAX_PATH];
5805 DWORD length;
5807 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5808 debugstr_w(pwcsName), pstgPriority, grfMode,
5809 snbExclude, reserved, ppstgOpen);
5812 * Perform sanity checks
5814 if (pwcsName == 0)
5816 hr = STG_E_INVALIDNAME;
5817 goto end;
5820 if (ppstgOpen == 0)
5822 hr = STG_E_INVALIDPOINTER;
5823 goto end;
5826 if (reserved)
5828 hr = STG_E_INVALIDPARAMETER;
5829 goto end;
5833 * Validate the sharing mode
5835 if (!(grfMode & STGM_TRANSACTED))
5836 switch(STGM_SHARE_MODE(grfMode))
5838 case STGM_SHARE_EXCLUSIVE:
5839 case STGM_SHARE_DENY_WRITE:
5840 break;
5841 default:
5842 hr = STG_E_INVALIDFLAG;
5843 goto end;
5847 * Validate the STGM flags
5849 if ( FAILED( validateSTGM(grfMode) ) ||
5850 (grfMode&STGM_CREATE))
5852 hr = STG_E_INVALIDFLAG;
5853 goto end;
5856 /* shared reading requires transacted mode */
5857 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5858 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5859 !(grfMode&STGM_TRANSACTED) )
5861 hr = STG_E_INVALIDFLAG;
5862 goto end;
5866 * Interpret the STGM value grfMode
5868 shareMode = GetShareModeFromSTGM(grfMode);
5869 accessMode = GetAccessModeFromSTGM(grfMode);
5872 * Initialize the "out" parameter.
5874 *ppstgOpen = 0;
5876 hFile = CreateFileW( pwcsName,
5877 accessMode,
5878 shareMode,
5879 NULL,
5880 OPEN_EXISTING,
5881 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5884 if (hFile==INVALID_HANDLE_VALUE)
5886 DWORD last_error = GetLastError();
5888 hr = E_FAIL;
5890 switch (last_error)
5892 case ERROR_FILE_NOT_FOUND:
5893 hr = STG_E_FILENOTFOUND;
5894 break;
5896 case ERROR_PATH_NOT_FOUND:
5897 hr = STG_E_PATHNOTFOUND;
5898 break;
5900 case ERROR_ACCESS_DENIED:
5901 case ERROR_WRITE_PROTECT:
5902 hr = STG_E_ACCESSDENIED;
5903 break;
5905 case ERROR_SHARING_VIOLATION:
5906 hr = STG_E_SHAREVIOLATION;
5907 break;
5909 default:
5910 hr = E_FAIL;
5913 goto end;
5917 * Refuse to open the file if it's too small to be a structured storage file
5918 * FIXME: verify the file when reading instead of here
5920 length = GetFileSize(hFile, NULL);
5921 if (length < 0x100)
5923 CloseHandle(hFile);
5924 hr = STG_E_FILEALREADYEXISTS;
5925 goto end;
5929 * Allocate and initialize the new IStorage32object.
5931 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5933 if (newStorage == 0)
5935 hr = STG_E_INSUFFICIENTMEMORY;
5936 goto end;
5939 /* if the file's length was zero, initialize the storage */
5940 hr = StorageImpl_Construct(
5941 newStorage,
5942 hFile,
5943 pwcsName,
5944 NULL,
5945 grfMode,
5946 TRUE,
5947 FALSE );
5949 if (FAILED(hr))
5951 HeapFree(GetProcessHeap(), 0, newStorage);
5953 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5955 if(hr == STG_E_INVALIDHEADER)
5956 hr = STG_E_FILEALREADYEXISTS;
5957 goto end;
5960 /* prepare the file name string given in lieu of the root property name */
5961 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5962 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5963 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5966 * Get an "out" pointer for the caller.
5968 hr = StorageBaseImpl_QueryInterface(
5969 (IStorage*)newStorage,
5970 (REFIID)&IID_IStorage,
5971 (void**)ppstgOpen);
5973 end:
5974 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5975 return hr;
5978 /******************************************************************************
5979 * StgCreateDocfileOnILockBytes [OLE32.@]
5981 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5982 ILockBytes *plkbyt,
5983 DWORD grfMode,
5984 DWORD reserved,
5985 IStorage** ppstgOpen)
5987 StorageImpl* newStorage = 0;
5988 HRESULT hr = S_OK;
5991 * Validate the parameters
5993 if ((ppstgOpen == 0) || (plkbyt == 0))
5994 return STG_E_INVALIDPOINTER;
5997 * Allocate and initialize the new IStorage object.
5999 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6001 if (newStorage == 0)
6002 return STG_E_INSUFFICIENTMEMORY;
6004 hr = StorageImpl_Construct(
6005 newStorage,
6008 plkbyt,
6009 grfMode,
6010 FALSE,
6011 TRUE);
6013 if (FAILED(hr))
6015 HeapFree(GetProcessHeap(), 0, newStorage);
6016 return hr;
6020 * Get an "out" pointer for the caller.
6022 hr = StorageBaseImpl_QueryInterface(
6023 (IStorage*)newStorage,
6024 (REFIID)&IID_IStorage,
6025 (void**)ppstgOpen);
6027 return hr;
6030 /******************************************************************************
6031 * StgOpenStorageOnILockBytes [OLE32.@]
6033 HRESULT WINAPI StgOpenStorageOnILockBytes(
6034 ILockBytes *plkbyt,
6035 IStorage *pstgPriority,
6036 DWORD grfMode,
6037 SNB snbExclude,
6038 DWORD reserved,
6039 IStorage **ppstgOpen)
6041 StorageImpl* newStorage = 0;
6042 HRESULT hr = S_OK;
6045 * Perform a sanity check
6047 if ((plkbyt == 0) || (ppstgOpen == 0))
6048 return STG_E_INVALIDPOINTER;
6051 * Validate the STGM flags
6053 if ( FAILED( validateSTGM(grfMode) ))
6054 return STG_E_INVALIDFLAG;
6057 * Initialize the "out" parameter.
6059 *ppstgOpen = 0;
6062 * Allocate and initialize the new IStorage object.
6064 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6066 if (newStorage == 0)
6067 return STG_E_INSUFFICIENTMEMORY;
6069 hr = StorageImpl_Construct(
6070 newStorage,
6073 plkbyt,
6074 grfMode,
6075 FALSE,
6076 FALSE);
6078 if (FAILED(hr))
6080 HeapFree(GetProcessHeap(), 0, newStorage);
6081 return hr;
6085 * Get an "out" pointer for the caller.
6087 hr = StorageBaseImpl_QueryInterface(
6088 (IStorage*)newStorage,
6089 (REFIID)&IID_IStorage,
6090 (void**)ppstgOpen);
6092 return hr;
6095 /******************************************************************************
6096 * StgSetTimes [ole32.@]
6097 * StgSetTimes [OLE32.@]
6101 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6102 FILETIME const *patime, FILETIME const *pmtime)
6104 IStorage *stg = NULL;
6105 HRESULT r;
6107 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6109 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6110 0, 0, &stg);
6111 if( SUCCEEDED(r) )
6113 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6114 IStorage_Release(stg);
6117 return r;
6120 /******************************************************************************
6121 * StgIsStorageILockBytes [OLE32.@]
6123 * Determines if the ILockBytes contains a storage object.
6125 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6127 BYTE sig[8];
6128 ULARGE_INTEGER offset;
6130 offset.u.HighPart = 0;
6131 offset.u.LowPart = 0;
6133 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6135 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6136 return S_OK;
6138 return S_FALSE;
6141 /******************************************************************************
6142 * WriteClassStg [OLE32.@]
6144 * This method will store the specified CLSID in the specified storage object
6146 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6148 HRESULT hRes;
6150 if(!pStg)
6151 return E_INVALIDARG;
6153 hRes = IStorage_SetClass(pStg, rclsid);
6155 return hRes;
6158 /***********************************************************************
6159 * ReadClassStg (OLE32.@)
6161 * This method reads the CLSID previously written to a storage object with
6162 * the WriteClassStg.
6164 * PARAMS
6165 * pstg [I] IStorage pointer
6166 * pclsid [O] Pointer to where the CLSID is written
6168 * RETURNS
6169 * Success: S_OK.
6170 * Failure: HRESULT code.
6172 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6174 STATSTG pstatstg;
6175 HRESULT hRes;
6177 TRACE("(%p, %p)\n", pstg, pclsid);
6179 if(!pstg || !pclsid)
6180 return E_INVALIDARG;
6183 * read a STATSTG structure (contains the clsid) from the storage
6185 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6187 if(SUCCEEDED(hRes))
6188 *pclsid=pstatstg.clsid;
6190 return hRes;
6193 /***********************************************************************
6194 * OleLoadFromStream (OLE32.@)
6196 * This function loads an object from stream
6198 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6200 CLSID clsid;
6201 HRESULT res;
6202 LPPERSISTSTREAM xstm;
6204 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6206 res=ReadClassStm(pStm,&clsid);
6207 if (!SUCCEEDED(res))
6208 return res;
6209 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6210 if (!SUCCEEDED(res))
6211 return res;
6212 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6213 if (!SUCCEEDED(res)) {
6214 IUnknown_Release((IUnknown*)*ppvObj);
6215 return res;
6217 res=IPersistStream_Load(xstm,pStm);
6218 IPersistStream_Release(xstm);
6219 /* FIXME: all refcounts ok at this point? I think they should be:
6220 * pStm : unchanged
6221 * ppvObj : 1
6222 * xstm : 0 (released)
6224 return res;
6227 /***********************************************************************
6228 * OleSaveToStream (OLE32.@)
6230 * This function saves an object with the IPersistStream interface on it
6231 * to the specified stream.
6233 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6236 CLSID clsid;
6237 HRESULT res;
6239 TRACE("(%p,%p)\n",pPStm,pStm);
6241 res=IPersistStream_GetClassID(pPStm,&clsid);
6243 if (SUCCEEDED(res)){
6245 res=WriteClassStm(pStm,&clsid);
6247 if (SUCCEEDED(res))
6249 res=IPersistStream_Save(pPStm,pStm,TRUE);
6252 TRACE("Finished Save\n");
6253 return res;
6256 /****************************************************************************
6257 * This method validate a STGM parameter that can contain the values below
6259 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6260 * The stgm values contained in 0xffff0000 are bitmasks.
6262 * STGM_DIRECT 0x00000000
6263 * STGM_TRANSACTED 0x00010000
6264 * STGM_SIMPLE 0x08000000
6266 * STGM_READ 0x00000000
6267 * STGM_WRITE 0x00000001
6268 * STGM_READWRITE 0x00000002
6270 * STGM_SHARE_DENY_NONE 0x00000040
6271 * STGM_SHARE_DENY_READ 0x00000030
6272 * STGM_SHARE_DENY_WRITE 0x00000020
6273 * STGM_SHARE_EXCLUSIVE 0x00000010
6275 * STGM_PRIORITY 0x00040000
6276 * STGM_DELETEONRELEASE 0x04000000
6278 * STGM_CREATE 0x00001000
6279 * STGM_CONVERT 0x00020000
6280 * STGM_FAILIFTHERE 0x00000000
6282 * STGM_NOSCRATCH 0x00100000
6283 * STGM_NOSNAPSHOT 0x00200000
6285 static HRESULT validateSTGM(DWORD stgm)
6287 DWORD access = STGM_ACCESS_MODE(stgm);
6288 DWORD share = STGM_SHARE_MODE(stgm);
6289 DWORD create = STGM_CREATE_MODE(stgm);
6291 if (stgm&~STGM_KNOWN_FLAGS)
6293 ERR("unknown flags %08lx\n", stgm);
6294 return E_FAIL;
6297 switch (access)
6299 case STGM_READ:
6300 case STGM_WRITE:
6301 case STGM_READWRITE:
6302 break;
6303 default:
6304 return E_FAIL;
6307 switch (share)
6309 case STGM_SHARE_DENY_NONE:
6310 case STGM_SHARE_DENY_READ:
6311 case STGM_SHARE_DENY_WRITE:
6312 case STGM_SHARE_EXCLUSIVE:
6313 break;
6314 default:
6315 return E_FAIL;
6318 switch (create)
6320 case STGM_CREATE:
6321 case STGM_FAILIFTHERE:
6322 break;
6323 default:
6324 return E_FAIL;
6328 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6330 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6331 return E_FAIL;
6334 * STGM_CREATE | STGM_CONVERT
6335 * if both are false, STGM_FAILIFTHERE is set to TRUE
6337 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6338 return E_FAIL;
6341 * STGM_NOSCRATCH requires STGM_TRANSACTED
6343 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6344 return E_FAIL;
6347 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6348 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6350 if ( (stgm & STGM_NOSNAPSHOT) &&
6351 (!(stgm & STGM_TRANSACTED) ||
6352 share == STGM_SHARE_EXCLUSIVE ||
6353 share == STGM_SHARE_DENY_WRITE) )
6354 return E_FAIL;
6356 return S_OK;
6359 /****************************************************************************
6360 * GetShareModeFromSTGM
6362 * This method will return a share mode flag from a STGM value.
6363 * The STGM value is assumed valid.
6365 static DWORD GetShareModeFromSTGM(DWORD stgm)
6367 switch (STGM_SHARE_MODE(stgm))
6369 case STGM_SHARE_DENY_NONE:
6370 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6371 case STGM_SHARE_DENY_READ:
6372 return FILE_SHARE_WRITE;
6373 case STGM_SHARE_DENY_WRITE:
6374 return FILE_SHARE_READ;
6375 case STGM_SHARE_EXCLUSIVE:
6376 return 0;
6378 ERR("Invalid share mode!\n");
6379 assert(0);
6380 return 0;
6383 /****************************************************************************
6384 * GetAccessModeFromSTGM
6386 * This method will return an access mode flag from a STGM value.
6387 * The STGM value is assumed valid.
6389 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6391 switch (STGM_ACCESS_MODE(stgm))
6393 case STGM_READ:
6394 return GENERIC_READ;
6395 case STGM_WRITE:
6396 case STGM_READWRITE:
6397 return GENERIC_READ | GENERIC_WRITE;
6399 ERR("Invalid access mode!\n");
6400 assert(0);
6401 return 0;
6404 /****************************************************************************
6405 * GetCreationModeFromSTGM
6407 * This method will return a creation mode flag from a STGM value.
6408 * The STGM value is assumed valid.
6410 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6412 switch(STGM_CREATE_MODE(stgm))
6414 case STGM_CREATE:
6415 return CREATE_ALWAYS;
6416 case STGM_CONVERT:
6417 FIXME("STGM_CONVERT not implemented!\n");
6418 return CREATE_NEW;
6419 case STGM_FAILIFTHERE:
6420 return CREATE_NEW;
6422 ERR("Invalid create mode!\n");
6423 assert(0);
6424 return 0;
6428 /*************************************************************************
6429 * OLECONVERT_LoadOLE10 [Internal]
6431 * Loads the OLE10 STREAM to memory
6433 * PARAMS
6434 * pOleStream [I] The OLESTREAM
6435 * pData [I] Data Structure for the OLESTREAM Data
6437 * RETURNS
6438 * Success: S_OK
6439 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6440 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6442 * NOTES
6443 * This function is used by OleConvertOLESTREAMToIStorage only.
6445 * Memory allocated for pData must be freed by the caller
6447 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6449 DWORD dwSize;
6450 HRESULT hRes = S_OK;
6451 int nTryCnt=0;
6452 int max_try = 6;
6454 pData->pData = NULL;
6455 pData->pstrOleObjFileName = (CHAR *) NULL;
6457 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6459 /* Get the OleID */
6460 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6461 if(dwSize != sizeof(pData->dwOleID))
6463 hRes = CONVERT10_E_OLESTREAM_GET;
6465 else if(pData->dwOleID != OLESTREAM_ID)
6467 hRes = CONVERT10_E_OLESTREAM_FMT;
6469 else
6471 hRes = S_OK;
6472 break;
6476 if(hRes == S_OK)
6478 /* Get the TypeID...more info needed for this field */
6479 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6480 if(dwSize != sizeof(pData->dwTypeID))
6482 hRes = CONVERT10_E_OLESTREAM_GET;
6485 if(hRes == S_OK)
6487 if(pData->dwTypeID != 0)
6489 /* Get the length of the OleTypeName */
6490 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6491 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6493 hRes = CONVERT10_E_OLESTREAM_GET;
6496 if(hRes == S_OK)
6498 if(pData->dwOleTypeNameLength > 0)
6500 /* Get the OleTypeName */
6501 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6502 if(dwSize != pData->dwOleTypeNameLength)
6504 hRes = CONVERT10_E_OLESTREAM_GET;
6508 if(bStrem1)
6510 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6511 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6513 hRes = CONVERT10_E_OLESTREAM_GET;
6515 if(hRes == S_OK)
6517 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6518 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6519 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6520 if(pData->pstrOleObjFileName)
6522 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6523 if(dwSize != pData->dwOleObjFileNameLength)
6525 hRes = CONVERT10_E_OLESTREAM_GET;
6528 else
6529 hRes = CONVERT10_E_OLESTREAM_GET;
6532 else
6534 /* Get the Width of the Metafile */
6535 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6536 if(dwSize != sizeof(pData->dwMetaFileWidth))
6538 hRes = CONVERT10_E_OLESTREAM_GET;
6540 if(hRes == S_OK)
6542 /* Get the Height of the Metafile */
6543 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6544 if(dwSize != sizeof(pData->dwMetaFileHeight))
6546 hRes = CONVERT10_E_OLESTREAM_GET;
6550 if(hRes == S_OK)
6552 /* Get the Length of the Data */
6553 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6554 if(dwSize != sizeof(pData->dwDataLength))
6556 hRes = CONVERT10_E_OLESTREAM_GET;
6560 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6562 if(!bStrem1) /* if it is a second OLE stream data */
6564 pData->dwDataLength -= 8;
6565 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6566 if(dwSize != sizeof(pData->strUnknown))
6568 hRes = CONVERT10_E_OLESTREAM_GET;
6572 if(hRes == S_OK)
6574 if(pData->dwDataLength > 0)
6576 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6578 /* Get Data (ex. IStorage, Metafile, or BMP) */
6579 if(pData->pData)
6581 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6582 if(dwSize != pData->dwDataLength)
6584 hRes = CONVERT10_E_OLESTREAM_GET;
6587 else
6589 hRes = CONVERT10_E_OLESTREAM_GET;
6595 return hRes;
6598 /*************************************************************************
6599 * OLECONVERT_SaveOLE10 [Internal]
6601 * Saves the OLE10 STREAM From memory
6603 * PARAMS
6604 * pData [I] Data Structure for the OLESTREAM Data
6605 * pOleStream [I] The OLESTREAM to save
6607 * RETURNS
6608 * Success: S_OK
6609 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6611 * NOTES
6612 * This function is used by OleConvertIStorageToOLESTREAM only.
6615 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6617 DWORD dwSize;
6618 HRESULT hRes = S_OK;
6621 /* Set the OleID */
6622 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6623 if(dwSize != sizeof(pData->dwOleID))
6625 hRes = CONVERT10_E_OLESTREAM_PUT;
6628 if(hRes == S_OK)
6630 /* Set the TypeID */
6631 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6632 if(dwSize != sizeof(pData->dwTypeID))
6634 hRes = CONVERT10_E_OLESTREAM_PUT;
6638 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6640 /* Set the Length of the OleTypeName */
6641 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6642 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6644 hRes = CONVERT10_E_OLESTREAM_PUT;
6647 if(hRes == S_OK)
6649 if(pData->dwOleTypeNameLength > 0)
6651 /* Set the OleTypeName */
6652 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6653 if(dwSize != pData->dwOleTypeNameLength)
6655 hRes = CONVERT10_E_OLESTREAM_PUT;
6660 if(hRes == S_OK)
6662 /* Set the width of the Metafile */
6663 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6664 if(dwSize != sizeof(pData->dwMetaFileWidth))
6666 hRes = CONVERT10_E_OLESTREAM_PUT;
6670 if(hRes == S_OK)
6672 /* Set the height of the Metafile */
6673 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6674 if(dwSize != sizeof(pData->dwMetaFileHeight))
6676 hRes = CONVERT10_E_OLESTREAM_PUT;
6680 if(hRes == S_OK)
6682 /* Set the length of the Data */
6683 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6684 if(dwSize != sizeof(pData->dwDataLength))
6686 hRes = CONVERT10_E_OLESTREAM_PUT;
6690 if(hRes == S_OK)
6692 if(pData->dwDataLength > 0)
6694 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6695 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6696 if(dwSize != pData->dwDataLength)
6698 hRes = CONVERT10_E_OLESTREAM_PUT;
6703 return hRes;
6706 /*************************************************************************
6707 * OLECONVERT_GetOLE20FromOLE10[Internal]
6709 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6710 * opens it, and copies the content to the dest IStorage for
6711 * OleConvertOLESTREAMToIStorage
6714 * PARAMS
6715 * pDestStorage [I] The IStorage to copy the data to
6716 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6717 * nBufferLength [I] The size of the buffer
6719 * RETURNS
6720 * Nothing
6722 * NOTES
6726 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6728 HRESULT hRes;
6729 HANDLE hFile;
6730 IStorage *pTempStorage;
6731 DWORD dwNumOfBytesWritten;
6732 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6733 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6735 /* Create a temp File */
6736 GetTempPathW(MAX_PATH, wstrTempDir);
6737 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6738 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6740 if(hFile != INVALID_HANDLE_VALUE)
6742 /* Write IStorage Data to File */
6743 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6744 CloseHandle(hFile);
6746 /* Open and copy temp storage to the Dest Storage */
6747 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6748 if(hRes == S_OK)
6750 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6751 StorageBaseImpl_Release(pTempStorage);
6753 DeleteFileW(wstrTempFile);
6758 /*************************************************************************
6759 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6761 * Saves the OLE10 STREAM From memory
6763 * PARAMS
6764 * pStorage [I] The Src IStorage to copy
6765 * pData [I] The Dest Memory to write to.
6767 * RETURNS
6768 * The size in bytes allocated for pData
6770 * NOTES
6771 * Memory allocated for pData must be freed by the caller
6773 * Used by OleConvertIStorageToOLESTREAM only.
6776 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6778 HANDLE hFile;
6779 HRESULT hRes;
6780 DWORD nDataLength = 0;
6781 IStorage *pTempStorage;
6782 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6783 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6785 *pData = NULL;
6787 /* Create temp Storage */
6788 GetTempPathW(MAX_PATH, wstrTempDir);
6789 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6790 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6792 if(hRes == S_OK)
6794 /* Copy Src Storage to the Temp Storage */
6795 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6796 StorageBaseImpl_Release(pTempStorage);
6798 /* Open Temp Storage as a file and copy to memory */
6799 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6800 if(hFile != INVALID_HANDLE_VALUE)
6802 nDataLength = GetFileSize(hFile, NULL);
6803 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6804 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6805 CloseHandle(hFile);
6807 DeleteFileW(wstrTempFile);
6809 return nDataLength;
6812 /*************************************************************************
6813 * OLECONVERT_CreateOleStream [Internal]
6815 * Creates the "\001OLE" stream in the IStorage if necessary.
6817 * PARAMS
6818 * pStorage [I] Dest storage to create the stream in
6820 * RETURNS
6821 * Nothing
6823 * NOTES
6824 * This function is used by OleConvertOLESTREAMToIStorage only.
6826 * This stream is still unknown, MS Word seems to have extra data
6827 * but since the data is stored in the OLESTREAM there should be
6828 * no need to recreate the stream. If the stream is manually
6829 * deleted it will create it with this default data.
6832 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6834 HRESULT hRes;
6835 IStream *pStream;
6836 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6837 BYTE pOleStreamHeader [] =
6839 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6840 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6841 0x00, 0x00, 0x00, 0x00
6844 /* Create stream if not present */
6845 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6846 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6848 if(hRes == S_OK)
6850 /* Write default Data */
6851 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6852 IStream_Release(pStream);
6856 /* write a string to a stream, preceded by its length */
6857 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6859 HRESULT r;
6860 LPSTR str;
6861 DWORD len = 0;
6863 if( string )
6864 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6865 r = IStream_Write( stm, &len, sizeof(len), NULL);
6866 if( FAILED( r ) )
6867 return r;
6868 if(len == 0)
6869 return r;
6870 str = CoTaskMemAlloc( len );
6871 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6872 r = IStream_Write( stm, str, len, NULL);
6873 CoTaskMemFree( str );
6874 return r;
6877 /* read a string preceded by its length from a stream */
6878 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6880 HRESULT r;
6881 DWORD len, count = 0;
6882 LPSTR str;
6883 LPWSTR wstr;
6885 r = IStream_Read( stm, &len, sizeof(len), &count );
6886 if( FAILED( r ) )
6887 return r;
6888 if( count != sizeof(len) )
6889 return E_OUTOFMEMORY;
6891 TRACE("%ld bytes\n",len);
6893 str = CoTaskMemAlloc( len );
6894 if( !str )
6895 return E_OUTOFMEMORY;
6896 count = 0;
6897 r = IStream_Read( stm, str, len, &count );
6898 if( FAILED( r ) )
6899 return r;
6900 if( count != len )
6902 CoTaskMemFree( str );
6903 return E_OUTOFMEMORY;
6906 TRACE("Read string %s\n",debugstr_an(str,len));
6908 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6909 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6910 if( wstr )
6911 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6912 CoTaskMemFree( str );
6914 *string = wstr;
6916 return r;
6920 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6921 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6923 IStream *pstm;
6924 HRESULT r = S_OK;
6925 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6927 static const BYTE unknown1[12] =
6928 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6929 0xFF, 0xFF, 0xFF, 0xFF};
6930 static const BYTE unknown2[16] =
6931 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6934 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6935 debugstr_w(lpszUserType), debugstr_w(szClipName),
6936 debugstr_w(szProgIDName));
6938 /* Create a CompObj stream if it doesn't exist */
6939 r = IStorage_CreateStream(pstg, szwStreamName,
6940 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6941 if( FAILED (r) )
6942 return r;
6944 /* Write CompObj Structure to stream */
6945 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6947 if( SUCCEEDED( r ) )
6948 r = WriteClassStm( pstm, clsid );
6950 if( SUCCEEDED( r ) )
6951 r = STREAM_WriteString( pstm, lpszUserType );
6952 if( SUCCEEDED( r ) )
6953 r = STREAM_WriteString( pstm, szClipName );
6954 if( SUCCEEDED( r ) )
6955 r = STREAM_WriteString( pstm, szProgIDName );
6956 if( SUCCEEDED( r ) )
6957 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6959 IStream_Release( pstm );
6961 return r;
6964 /***********************************************************************
6965 * WriteFmtUserTypeStg (OLE32.@)
6967 HRESULT WINAPI WriteFmtUserTypeStg(
6968 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6970 HRESULT r;
6971 WCHAR szwClipName[0x40];
6972 CLSID clsid = CLSID_NULL;
6973 LPWSTR wstrProgID = NULL;
6974 DWORD n;
6976 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6978 /* get the clipboard format name */
6979 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6980 szwClipName[n]=0;
6982 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6984 /* FIXME: There's room to save a CLSID and its ProgID, but
6985 the CLSID is not looked up in the registry and in all the
6986 tests I wrote it was CLSID_NULL. Where does it come from?
6989 /* get the real program ID. This may fail, but that's fine */
6990 ProgIDFromCLSID(&clsid, &wstrProgID);
6992 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6994 r = STORAGE_WriteCompObj( pstg, &clsid,
6995 lpszUserType, szwClipName, wstrProgID );
6997 CoTaskMemFree(wstrProgID);
6999 return r;
7003 /******************************************************************************
7004 * ReadFmtUserTypeStg [OLE32.@]
7006 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7008 HRESULT r;
7009 IStream *stm = 0;
7010 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7011 unsigned char unknown1[12];
7012 unsigned char unknown2[16];
7013 DWORD count;
7014 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7015 CLSID clsid;
7017 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7019 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7020 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7021 if( FAILED ( r ) )
7023 WARN("Failed to open stream r = %08lx\n", r);
7024 return r;
7027 /* read the various parts of the structure */
7028 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7029 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7030 goto end;
7031 r = ReadClassStm( stm, &clsid );
7032 if( FAILED( r ) )
7033 goto end;
7035 r = STREAM_ReadString( stm, &szCLSIDName );
7036 if( FAILED( r ) )
7037 goto end;
7039 r = STREAM_ReadString( stm, &szOleTypeName );
7040 if( FAILED( r ) )
7041 goto end;
7043 r = STREAM_ReadString( stm, &szProgIDName );
7044 if( FAILED( r ) )
7045 goto end;
7047 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7048 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7049 goto end;
7051 /* ok, success... now we just need to store what we found */
7052 if( pcf )
7053 *pcf = RegisterClipboardFormatW( szOleTypeName );
7054 CoTaskMemFree( szOleTypeName );
7056 if( lplpszUserType )
7057 *lplpszUserType = szCLSIDName;
7058 CoTaskMemFree( szProgIDName );
7060 end:
7061 IStream_Release( stm );
7063 return r;
7067 /*************************************************************************
7068 * OLECONVERT_CreateCompObjStream [Internal]
7070 * Creates a "\001CompObj" is the destination IStorage if necessary.
7072 * PARAMS
7073 * pStorage [I] The dest IStorage to create the CompObj Stream
7074 * if necessary.
7075 * strOleTypeName [I] The ProgID
7077 * RETURNS
7078 * Success: S_OK
7079 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7081 * NOTES
7082 * This function is used by OleConvertOLESTREAMToIStorage only.
7084 * The stream data is stored in the OLESTREAM and there should be
7085 * no need to recreate the stream. If the stream is manually
7086 * deleted it will attempt to create it by querying the registry.
7090 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7092 IStream *pStream;
7093 HRESULT hStorageRes, hRes = S_OK;
7094 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7095 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7096 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7098 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7099 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7101 /* Initialize the CompObj structure */
7102 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7103 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7104 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7107 /* Create a CompObj stream if it doesn't exist */
7108 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7109 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7110 if(hStorageRes == S_OK)
7112 /* copy the OleTypeName to the compobj struct */
7113 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7114 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7116 /* copy the OleTypeName to the compobj struct */
7117 /* Note: in the test made, these were Identical */
7118 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7119 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7121 /* Get the CLSID */
7122 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7123 bufferW, OLESTREAM_MAX_STR_LEN );
7124 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7126 if(hRes == S_OK)
7128 HKEY hKey;
7129 LONG hErr;
7130 /* Get the CLSID Default Name from the Registry */
7131 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7132 if(hErr == ERROR_SUCCESS)
7134 char strTemp[OLESTREAM_MAX_STR_LEN];
7135 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7136 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7137 if(hErr == ERROR_SUCCESS)
7139 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7141 RegCloseKey(hKey);
7145 /* Write CompObj Structure to stream */
7146 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7148 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7150 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7151 if(IStorageCompObj.dwCLSIDNameLength > 0)
7153 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7155 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7156 if(IStorageCompObj.dwOleTypeNameLength > 0)
7158 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7160 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7161 if(IStorageCompObj.dwProgIDNameLength > 0)
7163 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7165 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7166 IStream_Release(pStream);
7168 return hRes;
7172 /*************************************************************************
7173 * OLECONVERT_CreateOlePresStream[Internal]
7175 * Creates the "\002OlePres000" Stream with the Metafile data
7177 * PARAMS
7178 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7179 * dwExtentX [I] Width of the Metafile
7180 * dwExtentY [I] Height of the Metafile
7181 * pData [I] Metafile data
7182 * dwDataLength [I] Size of the Metafile data
7184 * RETURNS
7185 * Success: S_OK
7186 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7188 * NOTES
7189 * This function is used by OleConvertOLESTREAMToIStorage only.
7192 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7194 HRESULT hRes;
7195 IStream *pStream;
7196 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7197 BYTE pOlePresStreamHeader [] =
7199 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7200 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7201 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7202 0x00, 0x00, 0x00, 0x00
7205 BYTE pOlePresStreamHeaderEmpty [] =
7207 0x00, 0x00, 0x00, 0x00,
7208 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7209 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7210 0x00, 0x00, 0x00, 0x00
7213 /* Create the OlePres000 Stream */
7214 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7215 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7217 if(hRes == S_OK)
7219 DWORD nHeaderSize;
7220 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7222 memset(&OlePres, 0, sizeof(OlePres));
7223 /* Do we have any metafile data to save */
7224 if(dwDataLength > 0)
7226 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7227 nHeaderSize = sizeof(pOlePresStreamHeader);
7229 else
7231 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7232 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7234 /* Set width and height of the metafile */
7235 OlePres.dwExtentX = dwExtentX;
7236 OlePres.dwExtentY = -dwExtentY;
7238 /* Set Data and Length */
7239 if(dwDataLength > sizeof(METAFILEPICT16))
7241 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7242 OlePres.pData = &(pData[8]);
7244 /* Save OlePres000 Data to Stream */
7245 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7246 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7247 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7248 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7249 if(OlePres.dwSize > 0)
7251 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7253 IStream_Release(pStream);
7257 /*************************************************************************
7258 * OLECONVERT_CreateOle10NativeStream [Internal]
7260 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7262 * PARAMS
7263 * pStorage [I] Dest storage to create the stream in
7264 * pData [I] Ole10 Native Data (ex. bmp)
7265 * dwDataLength [I] Size of the Ole10 Native Data
7267 * RETURNS
7268 * Nothing
7270 * NOTES
7271 * This function is used by OleConvertOLESTREAMToIStorage only.
7273 * Might need to verify the data and return appropriate error message
7276 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7278 HRESULT hRes;
7279 IStream *pStream;
7280 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7282 /* Create the Ole10Native Stream */
7283 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7284 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7286 if(hRes == S_OK)
7288 /* Write info to stream */
7289 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7290 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7291 IStream_Release(pStream);
7296 /*************************************************************************
7297 * OLECONVERT_GetOLE10ProgID [Internal]
7299 * Finds the ProgID (or OleTypeID) from the IStorage
7301 * PARAMS
7302 * pStorage [I] The Src IStorage to get the ProgID
7303 * strProgID [I] the ProgID string to get
7304 * dwSize [I] the size of the string
7306 * RETURNS
7307 * Success: S_OK
7308 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7310 * NOTES
7311 * This function is used by OleConvertIStorageToOLESTREAM only.
7315 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7317 HRESULT hRes;
7318 IStream *pStream;
7319 LARGE_INTEGER iSeekPos;
7320 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7321 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7323 /* Open the CompObj Stream */
7324 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7325 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7326 if(hRes == S_OK)
7329 /*Get the OleType from the CompObj Stream */
7330 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7331 iSeekPos.u.HighPart = 0;
7333 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7334 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7335 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7336 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7337 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7338 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7339 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7341 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7342 if(*dwSize > 0)
7344 IStream_Read(pStream, strProgID, *dwSize, NULL);
7346 IStream_Release(pStream);
7348 else
7350 STATSTG stat;
7351 LPOLESTR wstrProgID;
7353 /* Get the OleType from the registry */
7354 REFCLSID clsid = &(stat.clsid);
7355 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7356 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7357 if(hRes == S_OK)
7359 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7363 return hRes;
7366 /*************************************************************************
7367 * OLECONVERT_GetOle10PresData [Internal]
7369 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7371 * PARAMS
7372 * pStorage [I] Src IStroage
7373 * pOleStream [I] Dest OleStream Mem Struct
7375 * RETURNS
7376 * Nothing
7378 * NOTES
7379 * This function is used by OleConvertIStorageToOLESTREAM only.
7381 * Memory allocated for pData must be freed by the caller
7385 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7388 HRESULT hRes;
7389 IStream *pStream;
7390 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7392 /* Initialize Default data for OLESTREAM */
7393 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7394 pOleStreamData[0].dwTypeID = 2;
7395 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7396 pOleStreamData[1].dwTypeID = 0;
7397 pOleStreamData[0].dwMetaFileWidth = 0;
7398 pOleStreamData[0].dwMetaFileHeight = 0;
7399 pOleStreamData[0].pData = NULL;
7400 pOleStreamData[1].pData = NULL;
7402 /* Open Ole10Native Stream */
7403 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7404 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7405 if(hRes == S_OK)
7408 /* Read Size and Data */
7409 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7410 if(pOleStreamData->dwDataLength > 0)
7412 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7413 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7415 IStream_Release(pStream);
7421 /*************************************************************************
7422 * OLECONVERT_GetOle20PresData[Internal]
7424 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7426 * PARAMS
7427 * pStorage [I] Src IStroage
7428 * pOleStreamData [I] Dest OleStream Mem Struct
7430 * RETURNS
7431 * Nothing
7433 * NOTES
7434 * This function is used by OleConvertIStorageToOLESTREAM only.
7436 * Memory allocated for pData must be freed by the caller
7438 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7440 HRESULT hRes;
7441 IStream *pStream;
7442 OLECONVERT_ISTORAGE_OLEPRES olePress;
7443 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7445 /* Initialize Default data for OLESTREAM */
7446 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7447 pOleStreamData[0].dwTypeID = 2;
7448 pOleStreamData[0].dwMetaFileWidth = 0;
7449 pOleStreamData[0].dwMetaFileHeight = 0;
7450 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7451 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7452 pOleStreamData[1].dwTypeID = 0;
7453 pOleStreamData[1].dwOleTypeNameLength = 0;
7454 pOleStreamData[1].strOleTypeName[0] = 0;
7455 pOleStreamData[1].dwMetaFileWidth = 0;
7456 pOleStreamData[1].dwMetaFileHeight = 0;
7457 pOleStreamData[1].pData = NULL;
7458 pOleStreamData[1].dwDataLength = 0;
7461 /* Open OlePress000 stream */
7462 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7463 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7464 if(hRes == S_OK)
7466 LARGE_INTEGER iSeekPos;
7467 METAFILEPICT16 MetaFilePict;
7468 static const char strMetafilePictName[] = "METAFILEPICT";
7470 /* Set the TypeID for a Metafile */
7471 pOleStreamData[1].dwTypeID = 5;
7473 /* Set the OleTypeName to Metafile */
7474 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7475 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7477 iSeekPos.u.HighPart = 0;
7478 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7480 /* Get Presentation Data */
7481 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7482 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7483 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7484 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7486 /*Set width and Height */
7487 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7488 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7489 if(olePress.dwSize > 0)
7491 /* Set Length */
7492 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7494 /* Set MetaFilePict struct */
7495 MetaFilePict.mm = 8;
7496 MetaFilePict.xExt = olePress.dwExtentX;
7497 MetaFilePict.yExt = olePress.dwExtentY;
7498 MetaFilePict.hMF = 0;
7500 /* Get Metafile Data */
7501 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7502 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7503 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7505 IStream_Release(pStream);
7509 /*************************************************************************
7510 * OleConvertOLESTREAMToIStorage [OLE32.@]
7512 * Read info on MSDN
7514 * TODO
7515 * DVTARGETDEVICE paramenter is not handled
7516 * Still unsure of some mem fields for OLE 10 Stream
7517 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7518 * and "\001OLE" streams
7521 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7522 LPOLESTREAM pOleStream,
7523 LPSTORAGE pstg,
7524 const DVTARGETDEVICE* ptd)
7526 int i;
7527 HRESULT hRes=S_OK;
7528 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7530 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7532 if(ptd != NULL)
7534 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7537 if(pstg == NULL || pOleStream == NULL)
7539 hRes = E_INVALIDARG;
7542 if(hRes == S_OK)
7544 /* Load the OLESTREAM to Memory */
7545 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7548 if(hRes == S_OK)
7550 /* Load the OLESTREAM to Memory (part 2)*/
7551 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7554 if(hRes == S_OK)
7557 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7559 /* Do we have the IStorage Data in the OLESTREAM */
7560 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7562 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7563 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7565 else
7567 /* It must be an original OLE 1.0 source */
7568 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7571 else
7573 /* It must be an original OLE 1.0 source */
7574 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7577 /* Create CompObj Stream if necessary */
7578 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7579 if(hRes == S_OK)
7581 /*Create the Ole Stream if necessary */
7582 OLECONVERT_CreateOleStream(pstg);
7587 /* Free allocated memory */
7588 for(i=0; i < 2; i++)
7590 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7591 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7592 pOleStreamData[i].pstrOleObjFileName = NULL;
7594 return hRes;
7597 /*************************************************************************
7598 * OleConvertIStorageToOLESTREAM [OLE32.@]
7600 * Read info on MSDN
7602 * Read info on MSDN
7604 * TODO
7605 * Still unsure of some mem fields for OLE 10 Stream
7606 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7607 * and "\001OLE" streams.
7610 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7611 LPSTORAGE pstg,
7612 LPOLESTREAM pOleStream)
7614 int i;
7615 HRESULT hRes = S_OK;
7616 IStream *pStream;
7617 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7618 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7621 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7623 if(pstg == NULL || pOleStream == NULL)
7625 hRes = E_INVALIDARG;
7627 if(hRes == S_OK)
7629 /* Get the ProgID */
7630 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7631 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7633 if(hRes == S_OK)
7635 /* Was it originally Ole10 */
7636 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7637 if(hRes == S_OK)
7639 IStream_Release(pStream);
7640 /* Get Presentation Data for Ole10Native */
7641 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7643 else
7645 /* Get Presentation Data (OLE20) */
7646 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7649 /* Save OLESTREAM */
7650 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7651 if(hRes == S_OK)
7653 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7658 /* Free allocated memory */
7659 for(i=0; i < 2; i++)
7661 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7664 return hRes;
7667 /***********************************************************************
7668 * GetConvertStg (OLE32.@)
7670 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7671 FIXME("unimplemented stub!\n");
7672 return E_FAIL;
7675 /******************************************************************************
7676 * StgIsStorageFile [OLE32.@]
7678 HRESULT WINAPI
7679 StgIsStorageFile(LPCOLESTR fn)
7681 HANDLE hf;
7682 BYTE magic[8];
7683 DWORD bytes_read;
7685 TRACE("(\'%s\')\n", debugstr_w(fn));
7686 hf = CreateFileW(fn, GENERIC_READ,
7687 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7688 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7690 if (hf == INVALID_HANDLE_VALUE)
7691 return STG_E_FILENOTFOUND;
7693 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7695 WARN(" unable to read file\n");
7696 CloseHandle(hf);
7697 return S_FALSE;
7700 CloseHandle(hf);
7702 if (bytes_read != 8) {
7703 WARN(" too short\n");
7704 return S_FALSE;
7707 if (!memcmp(magic,STORAGE_magic,8)) {
7708 WARN(" -> YES\n");
7709 return S_OK;
7712 WARN(" -> Invalid header.\n");
7713 return S_FALSE;