ole32: Fix infinite recursion in CompositeMonikerImpl_BindToStorage.
[wine/wine64.git] / dlls / ole32 / storage32.c
bloba42841aa5855e5429265336de208d294c170ff12
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;
306 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
307 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
310 * Perform a sanity check on the parameters.
312 if ( (pwcsName==NULL) || (ppstm==0) )
314 res = E_INVALIDARG;
315 goto end;
319 * Initialize the out parameter
321 *ppstm = NULL;
324 * Validate the STGM flags
326 if ( FAILED( validateSTGM(grfMode) ))
328 res = STG_E_INVALIDFLAG;
329 goto end;
333 * As documented.
335 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
336 (grfMode & STGM_DELETEONRELEASE) ||
337 (grfMode & STGM_TRANSACTED) )
339 res = STG_E_INVALIDFUNCTION;
340 goto end;
344 * Check that we're compatible with the parent's storage mode, but
345 * only if we are not in transacted mode
347 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
348 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
350 res = STG_E_ACCESSDENIED;
351 goto end;
356 * Create a property enumeration to search the properties
358 propertyEnumeration = IEnumSTATSTGImpl_Construct(
359 This->ancestorStorage,
360 This->rootPropertySetIndex);
363 * Search the enumeration for the property with the given name
365 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
366 propertyEnumeration,
367 pwcsName,
368 &currentProperty);
371 * Delete the property enumeration since we don't need it anymore
373 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
376 * If it was found, construct the stream object and return a pointer to it.
378 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
379 (currentProperty.propertyType==PROPTYPE_STREAM) )
381 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
383 if (newStream!=0)
385 newStream->grfMode = grfMode;
386 *ppstm = (IStream*)newStream;
389 * Since we are returning a pointer to the interface, we have to
390 * nail down the reference.
392 IStream_AddRef(*ppstm);
395 * add us to the storage's list of active streams
398 StorageBaseImpl_AddStream(This,newStream);
400 res = S_OK;
401 goto end;
404 res = E_OUTOFMEMORY;
405 goto end;
408 res = STG_E_FILENOTFOUND;
410 end:
411 if (res == S_OK)
412 TRACE("<-- IStream %p\n", *ppstm);
413 TRACE("<-- %08lx\n", res);
414 return res;
417 /************************************************************************
418 * Storage32BaseImpl_OpenStorage (IStorage)
420 * This method will open a new storage object from the current storage.
422 * See Windows documentation for more details on IStorage methods.
424 HRESULT WINAPI StorageBaseImpl_OpenStorage(
425 IStorage* iface,
426 const OLECHAR* pwcsName, /* [string][unique][in] */
427 IStorage* pstgPriority, /* [unique][in] */
428 DWORD grfMode, /* [in] */
429 SNB snbExclude, /* [unique][in] */
430 DWORD reserved, /* [in] */
431 IStorage** ppstg) /* [out] */
433 StorageBaseImpl *This = (StorageBaseImpl *)iface;
434 StorageInternalImpl* newStorage;
435 IEnumSTATSTGImpl* propertyEnumeration;
436 StgProperty currentProperty;
437 ULONG foundPropertyIndex;
438 HRESULT res = STG_E_UNKNOWN;
440 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
441 iface, debugstr_w(pwcsName), pstgPriority,
442 grfMode, snbExclude, reserved, ppstg);
445 * Perform a sanity check on the parameters.
447 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
449 res = E_INVALIDARG;
450 goto end;
453 /* as documented */
454 if (snbExclude != NULL)
456 res = STG_E_INVALIDPARAMETER;
457 goto end;
461 * Validate the STGM flags
463 if ( FAILED( validateSTGM(grfMode) ))
465 res = STG_E_INVALIDFLAG;
466 goto end;
470 * As documented.
472 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
473 (grfMode & STGM_DELETEONRELEASE) ||
474 (grfMode & STGM_PRIORITY) )
476 res = STG_E_INVALIDFUNCTION;
477 goto end;
481 * Check that we're compatible with the parent's storage mode,
482 * but only if we are not transacted
484 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
485 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
487 res = STG_E_ACCESSDENIED;
488 goto end;
493 * Initialize the out parameter
495 *ppstg = NULL;
498 * Create a property enumeration to search the properties
500 propertyEnumeration = IEnumSTATSTGImpl_Construct(
501 This->ancestorStorage,
502 This->rootPropertySetIndex);
505 * Search the enumeration for the property with the given name
507 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
508 propertyEnumeration,
509 pwcsName,
510 &currentProperty);
513 * Delete the property enumeration since we don't need it anymore
515 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
518 * If it was found, construct the stream object and return a pointer to it.
520 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
521 (currentProperty.propertyType==PROPTYPE_STORAGE) )
524 * Construct a new Storage object
526 newStorage = StorageInternalImpl_Construct(
527 This->ancestorStorage,
528 grfMode,
529 foundPropertyIndex);
531 if (newStorage != 0)
533 *ppstg = (IStorage*)newStorage;
536 * Since we are returning a pointer to the interface,
537 * we have to nail down the reference.
539 StorageBaseImpl_AddRef(*ppstg);
541 res = S_OK;
542 goto end;
545 res = STG_E_INSUFFICIENTMEMORY;
546 goto end;
549 res = STG_E_FILENOTFOUND;
551 end:
552 TRACE("<-- %08lx\n", res);
553 return res;
556 /************************************************************************
557 * Storage32BaseImpl_EnumElements (IStorage)
559 * This method will create an enumerator object that can be used to
560 * retrieve informatino about all the properties in the storage object.
562 * See Windows documentation for more details on IStorage methods.
564 HRESULT WINAPI StorageBaseImpl_EnumElements(
565 IStorage* iface,
566 DWORD reserved1, /* [in] */
567 void* reserved2, /* [size_is][unique][in] */
568 DWORD reserved3, /* [in] */
569 IEnumSTATSTG** ppenum) /* [out] */
571 StorageBaseImpl *This = (StorageBaseImpl *)iface;
572 IEnumSTATSTGImpl* newEnum;
574 TRACE("(%p, %ld, %p, %ld, %p)\n",
575 iface, reserved1, reserved2, reserved3, ppenum);
578 * Perform a sanity check on the parameters.
580 if ( (This==0) || (ppenum==0))
581 return E_INVALIDARG;
584 * Construct the enumerator.
586 newEnum = IEnumSTATSTGImpl_Construct(
587 This->ancestorStorage,
588 This->rootPropertySetIndex);
590 if (newEnum!=0)
592 *ppenum = (IEnumSTATSTG*)newEnum;
595 * Don't forget to nail down a reference to the new object before
596 * returning it.
598 IEnumSTATSTG_AddRef(*ppenum);
600 return S_OK;
603 return E_OUTOFMEMORY;
606 /************************************************************************
607 * Storage32BaseImpl_Stat (IStorage)
609 * This method will retrieve information about this storage object.
611 * See Windows documentation for more details on IStorage methods.
613 HRESULT WINAPI StorageBaseImpl_Stat(
614 IStorage* iface,
615 STATSTG* pstatstg, /* [out] */
616 DWORD grfStatFlag) /* [in] */
618 StorageBaseImpl *This = (StorageBaseImpl *)iface;
619 StgProperty curProperty;
620 BOOL readSuccessful;
621 HRESULT res = STG_E_UNKNOWN;
623 TRACE("(%p, %p, %lx)\n",
624 iface, pstatstg, grfStatFlag);
627 * Perform a sanity check on the parameters.
629 if ( (This==0) || (pstatstg==0))
631 res = E_INVALIDARG;
632 goto end;
636 * Read the information from the property.
638 readSuccessful = StorageImpl_ReadProperty(
639 This->ancestorStorage,
640 This->rootPropertySetIndex,
641 &curProperty);
643 if (readSuccessful)
645 StorageUtl_CopyPropertyToSTATSTG(
646 pstatstg,
647 &curProperty,
648 grfStatFlag);
650 pstatstg->grfMode = This->openFlags;
652 res = S_OK;
653 goto end;
656 res = E_FAIL;
658 end:
659 if (res == S_OK)
661 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);
663 TRACE("<-- %08lx\n", res);
664 return res;
667 /************************************************************************
668 * Storage32BaseImpl_RenameElement (IStorage)
670 * This method will rename the specified element.
672 * See Windows documentation for more details on IStorage methods.
674 * Implementation notes: The method used to rename consists of creating a clone
675 * of the deleted StgProperty object setting it with the new name and to
676 * perform a DestroyElement of the old StgProperty.
678 HRESULT WINAPI StorageBaseImpl_RenameElement(
679 IStorage* iface,
680 const OLECHAR* pwcsOldName, /* [in] */
681 const OLECHAR* pwcsNewName) /* [in] */
683 StorageBaseImpl *This = (StorageBaseImpl *)iface;
684 IEnumSTATSTGImpl* propertyEnumeration;
685 StgProperty currentProperty;
686 ULONG foundPropertyIndex;
688 TRACE("(%p, %s, %s)\n",
689 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
692 * Create a property enumeration to search the properties
694 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
695 This->rootPropertySetIndex);
698 * Search the enumeration for the new property name
700 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
701 pwcsNewName,
702 &currentProperty);
704 if (foundPropertyIndex != PROPERTY_NULL)
707 * There is already a property with the new name
709 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
710 return STG_E_FILEALREADYEXISTS;
713 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
716 * Search the enumeration for the old property name
718 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
719 pwcsOldName,
720 &currentProperty);
723 * Delete the property enumeration since we don't need it anymore
725 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
727 if (foundPropertyIndex != PROPERTY_NULL)
729 StgProperty renamedProperty;
730 ULONG renamedPropertyIndex;
733 * Setup a new property for the renamed property
735 renamedProperty.sizeOfNameString =
736 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
738 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
739 return STG_E_INVALIDNAME;
741 strcpyW(renamedProperty.name, pwcsNewName);
743 renamedProperty.propertyType = currentProperty.propertyType;
744 renamedProperty.startingBlock = currentProperty.startingBlock;
745 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
746 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
748 renamedProperty.previousProperty = PROPERTY_NULL;
749 renamedProperty.nextProperty = PROPERTY_NULL;
752 * Bring the dirProperty link in case it is a storage and in which
753 * case the renamed storage elements don't require to be reorganized.
755 renamedProperty.dirProperty = currentProperty.dirProperty;
757 /* call CoFileTime to get the current time
758 renamedProperty.timeStampS1
759 renamedProperty.timeStampD1
760 renamedProperty.timeStampS2
761 renamedProperty.timeStampD2
762 renamedProperty.propertyUniqueID
766 * Obtain a free property in the property chain
768 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
771 * Save the new property into the new property spot
773 StorageImpl_WriteProperty(
774 This->ancestorStorage,
775 renamedPropertyIndex,
776 &renamedProperty);
779 * Find a spot in the property chain for our newly created property.
781 updatePropertyChain(
782 (StorageImpl*)This,
783 renamedPropertyIndex,
784 renamedProperty);
787 * At this point the renamed property has been inserted in the tree,
788 * now, before Destroying the old property we must zero its dirProperty
789 * otherwise the DestroyProperty below will zap it all and we do not want
790 * this to happen.
791 * Also, we fake that the old property is a storage so the DestroyProperty
792 * will not do a SetSize(0) on the stream data.
794 * This means that we need to tweak the StgProperty if it is a stream or a
795 * non empty storage.
797 StorageImpl_ReadProperty(This->ancestorStorage,
798 foundPropertyIndex,
799 &currentProperty);
801 currentProperty.dirProperty = PROPERTY_NULL;
802 currentProperty.propertyType = PROPTYPE_STORAGE;
803 StorageImpl_WriteProperty(
804 This->ancestorStorage,
805 foundPropertyIndex,
806 &currentProperty);
809 * Invoke Destroy to get rid of the ole property and automatically redo
810 * the linking of its previous and next members...
812 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
815 else
818 * There is no property with the old name
820 return STG_E_FILENOTFOUND;
823 return S_OK;
826 /************************************************************************
827 * Storage32BaseImpl_CreateStream (IStorage)
829 * This method will create a stream object within this storage
831 * See Windows documentation for more details on IStorage methods.
833 HRESULT WINAPI StorageBaseImpl_CreateStream(
834 IStorage* iface,
835 const OLECHAR* pwcsName, /* [string][in] */
836 DWORD grfMode, /* [in] */
837 DWORD reserved1, /* [in] */
838 DWORD reserved2, /* [in] */
839 IStream** ppstm) /* [out] */
841 StorageBaseImpl *This = (StorageBaseImpl *)iface;
842 IEnumSTATSTGImpl* propertyEnumeration;
843 StgStreamImpl* newStream;
844 StgProperty currentProperty, newStreamProperty;
845 ULONG foundPropertyIndex, newPropertyIndex;
847 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
848 iface, debugstr_w(pwcsName), grfMode,
849 reserved1, reserved2, ppstm);
852 * Validate parameters
854 if (ppstm == 0)
855 return STG_E_INVALIDPOINTER;
857 if (pwcsName == 0)
858 return STG_E_INVALIDNAME;
860 if (reserved1 || reserved2)
861 return STG_E_INVALIDPARAMETER;
864 * Validate the STGM flags
866 if ( FAILED( validateSTGM(grfMode) ))
867 return STG_E_INVALIDFLAG;
869 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
870 return STG_E_INVALIDFLAG;
873 * As documented.
875 if ((grfMode & STGM_DELETEONRELEASE) ||
876 (grfMode & STGM_TRANSACTED))
877 return STG_E_INVALIDFUNCTION;
880 * Check that we're compatible with the parent's storage mode
881 * if not in transacted mode
883 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
884 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
885 return STG_E_ACCESSDENIED;
889 * Initialize the out parameter
891 *ppstm = 0;
894 * Create a property enumeration to search the properties
896 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
897 This->rootPropertySetIndex);
899 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
900 pwcsName,
901 &currentProperty);
903 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
905 if (foundPropertyIndex != PROPERTY_NULL)
908 * An element with this name already exists
910 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
912 IStorage_DestroyElement(iface, pwcsName);
914 else
915 return STG_E_FILEALREADYEXISTS;
917 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
919 WARN("read-only storage\n");
920 return STG_E_ACCESSDENIED;
924 * memset the empty property
926 memset(&newStreamProperty, 0, sizeof(StgProperty));
928 newStreamProperty.sizeOfNameString =
929 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
931 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
932 return STG_E_INVALIDNAME;
934 strcpyW(newStreamProperty.name, pwcsName);
936 newStreamProperty.propertyType = PROPTYPE_STREAM;
937 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
938 newStreamProperty.size.u.LowPart = 0;
939 newStreamProperty.size.u.HighPart = 0;
941 newStreamProperty.previousProperty = PROPERTY_NULL;
942 newStreamProperty.nextProperty = PROPERTY_NULL;
943 newStreamProperty.dirProperty = PROPERTY_NULL;
945 /* call CoFileTime to get the current time
946 newStreamProperty.timeStampS1
947 newStreamProperty.timeStampD1
948 newStreamProperty.timeStampS2
949 newStreamProperty.timeStampD2
952 /* newStreamProperty.propertyUniqueID */
955 * Get a free property or create a new one
957 newPropertyIndex = getFreeProperty(This->ancestorStorage);
960 * Save the new property into the new property spot
962 StorageImpl_WriteProperty(
963 This->ancestorStorage,
964 newPropertyIndex,
965 &newStreamProperty);
968 * Find a spot in the property chain for our newly created property.
970 updatePropertyChain(
971 (StorageImpl*)This,
972 newPropertyIndex,
973 newStreamProperty);
976 * Open the stream to return it.
978 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
980 if (newStream != 0)
982 *ppstm = (IStream*)newStream;
985 * Since we are returning a pointer to the interface, we have to nail down
986 * the reference.
988 IStream_AddRef(*ppstm);
990 /* add us to the storage's list of active streams
992 StorageBaseImpl_AddStream(This,newStream);
995 else
997 return STG_E_INSUFFICIENTMEMORY;
1000 return S_OK;
1003 /************************************************************************
1004 * Storage32BaseImpl_SetClass (IStorage)
1006 * This method will write the specified CLSID in the property of this
1007 * storage.
1009 * See Windows documentation for more details on IStorage methods.
1011 HRESULT WINAPI StorageBaseImpl_SetClass(
1012 IStorage* iface,
1013 REFCLSID clsid) /* [in] */
1015 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1016 HRESULT hRes = E_FAIL;
1017 StgProperty curProperty;
1018 BOOL success;
1020 TRACE("(%p, %p)\n", iface, clsid);
1022 success = StorageImpl_ReadProperty(This->ancestorStorage,
1023 This->rootPropertySetIndex,
1024 &curProperty);
1025 if (success)
1027 curProperty.propertyUniqueID = *clsid;
1029 success = StorageImpl_WriteProperty(This->ancestorStorage,
1030 This->rootPropertySetIndex,
1031 &curProperty);
1032 if (success)
1033 hRes = S_OK;
1036 return hRes;
1039 /************************************************************************
1040 ** Storage32Impl implementation
1043 /************************************************************************
1044 * Storage32Impl_CreateStorage (IStorage)
1046 * This method will create the storage object within the provided storage.
1048 * See Windows documentation for more details on IStorage methods.
1050 HRESULT WINAPI StorageImpl_CreateStorage(
1051 IStorage* iface,
1052 const OLECHAR *pwcsName, /* [string][in] */
1053 DWORD grfMode, /* [in] */
1054 DWORD reserved1, /* [in] */
1055 DWORD reserved2, /* [in] */
1056 IStorage **ppstg) /* [out] */
1058 StorageImpl* const This=(StorageImpl*)iface;
1060 IEnumSTATSTGImpl *propertyEnumeration;
1061 StgProperty currentProperty;
1062 StgProperty newProperty;
1063 ULONG foundPropertyIndex;
1064 ULONG newPropertyIndex;
1065 HRESULT hr;
1067 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1068 iface, debugstr_w(pwcsName), grfMode,
1069 reserved1, reserved2, ppstg);
1072 * Validate parameters
1074 if (ppstg == 0)
1075 return STG_E_INVALIDPOINTER;
1077 if (pwcsName == 0)
1078 return STG_E_INVALIDNAME;
1081 * Initialize the out parameter
1083 *ppstg = NULL;
1086 * Validate the STGM flags
1088 if ( FAILED( validateSTGM(grfMode) ) ||
1089 (grfMode & STGM_DELETEONRELEASE) )
1091 WARN("bad grfMode: 0x%lx\n", grfMode);
1092 return STG_E_INVALIDFLAG;
1096 * Check that we're compatible with the parent's storage mode
1098 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1100 WARN("access denied\n");
1101 return STG_E_ACCESSDENIED;
1105 * Create a property enumeration and search the properties
1107 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1108 This->base.rootPropertySetIndex);
1110 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1111 pwcsName,
1112 &currentProperty);
1113 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1115 if (foundPropertyIndex != PROPERTY_NULL)
1118 * An element with this name already exists
1120 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1121 IStorage_DestroyElement(iface, pwcsName);
1122 else
1124 WARN("file already exists\n");
1125 return STG_E_FILEALREADYEXISTS;
1128 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1130 WARN("read-only storage\n");
1131 return STG_E_ACCESSDENIED;
1135 * memset the empty property
1137 memset(&newProperty, 0, sizeof(StgProperty));
1139 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1141 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1143 FIXME("name too long\n");
1144 return STG_E_INVALIDNAME;
1147 strcpyW(newProperty.name, pwcsName);
1149 newProperty.propertyType = PROPTYPE_STORAGE;
1150 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1151 newProperty.size.u.LowPart = 0;
1152 newProperty.size.u.HighPart = 0;
1154 newProperty.previousProperty = PROPERTY_NULL;
1155 newProperty.nextProperty = PROPERTY_NULL;
1156 newProperty.dirProperty = PROPERTY_NULL;
1158 /* call CoFileTime to get the current time
1159 newProperty.timeStampS1
1160 newProperty.timeStampD1
1161 newProperty.timeStampS2
1162 newProperty.timeStampD2
1165 /* newStorageProperty.propertyUniqueID */
1168 * Obtain a free property in the property chain
1170 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1173 * Save the new property into the new property spot
1175 StorageImpl_WriteProperty(
1176 This->base.ancestorStorage,
1177 newPropertyIndex,
1178 &newProperty);
1181 * Find a spot in the property chain for our newly created property.
1183 updatePropertyChain(
1184 This,
1185 newPropertyIndex,
1186 newProperty);
1189 * Open it to get a pointer to return.
1191 hr = IStorage_OpenStorage(
1192 iface,
1193 (const OLECHAR*)pwcsName,
1195 grfMode,
1198 ppstg);
1200 if( (hr != S_OK) || (*ppstg == NULL))
1202 return hr;
1206 return S_OK;
1210 /***************************************************************************
1212 * Internal Method
1214 * Get a free property or create a new one.
1216 static ULONG getFreeProperty(
1217 StorageImpl *storage)
1219 ULONG currentPropertyIndex = 0;
1220 ULONG newPropertyIndex = PROPERTY_NULL;
1221 BOOL readSuccessful = TRUE;
1222 StgProperty currentProperty;
1227 * Start by reading the root property
1229 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1230 currentPropertyIndex,
1231 &currentProperty);
1232 if (readSuccessful)
1234 if (currentProperty.sizeOfNameString == 0)
1237 * The property existis and is available, we found it.
1239 newPropertyIndex = currentPropertyIndex;
1242 else
1245 * We exhausted the property list, we will create more space below
1247 newPropertyIndex = currentPropertyIndex;
1249 currentPropertyIndex++;
1251 } while (newPropertyIndex == PROPERTY_NULL);
1254 * grow the property chain
1256 if (! readSuccessful)
1258 StgProperty emptyProperty;
1259 ULARGE_INTEGER newSize;
1260 ULONG propertyIndex;
1261 ULONG lastProperty = 0;
1262 ULONG blockCount = 0;
1265 * obtain the new count of property blocks
1267 blockCount = BlockChainStream_GetCount(
1268 storage->base.ancestorStorage->rootBlockChain)+1;
1271 * initialize the size used by the property stream
1273 newSize.u.HighPart = 0;
1274 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1277 * add a property block to the property chain
1279 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1282 * memset the empty property in order to initialize the unused newly
1283 * created property
1285 memset(&emptyProperty, 0, sizeof(StgProperty));
1288 * initialize them
1290 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1292 for(
1293 propertyIndex = newPropertyIndex;
1294 propertyIndex < lastProperty;
1295 propertyIndex++)
1297 StorageImpl_WriteProperty(
1298 storage->base.ancestorStorage,
1299 propertyIndex,
1300 &emptyProperty);
1304 return newPropertyIndex;
1307 /****************************************************************************
1309 * Internal Method
1311 * Case insensitive comparaison of StgProperty.name by first considering
1312 * their size.
1314 * Returns <0 when newPrpoerty < currentProperty
1315 * >0 when newPrpoerty > currentProperty
1316 * 0 when newPrpoerty == currentProperty
1318 static LONG propertyNameCmp(
1319 const OLECHAR *newProperty,
1320 const OLECHAR *currentProperty)
1322 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1324 if (diff == 0)
1327 * We compare the string themselves only when they are of the same length
1329 diff = lstrcmpiW( newProperty, currentProperty);
1332 return diff;
1335 /****************************************************************************
1337 * Internal Method
1339 * Properly link this new element in the property chain.
1341 static void updatePropertyChain(
1342 StorageImpl *storage,
1343 ULONG newPropertyIndex,
1344 StgProperty newProperty)
1346 StgProperty currentProperty;
1349 * Read the root property
1351 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1352 storage->base.rootPropertySetIndex,
1353 &currentProperty);
1355 if (currentProperty.dirProperty != PROPERTY_NULL)
1358 * The root storage contains some element, therefore, start the research
1359 * for the appropriate location.
1361 BOOL found = 0;
1362 ULONG current, next, previous, currentPropertyId;
1365 * Keep the StgProperty sequence number of the storage first property
1367 currentPropertyId = currentProperty.dirProperty;
1370 * Read
1372 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1373 currentProperty.dirProperty,
1374 &currentProperty);
1376 previous = currentProperty.previousProperty;
1377 next = currentProperty.nextProperty;
1378 current = currentPropertyId;
1380 while (found == 0)
1382 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1384 if (diff < 0)
1386 if (previous != PROPERTY_NULL)
1388 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1389 previous,
1390 &currentProperty);
1391 current = previous;
1393 else
1395 currentProperty.previousProperty = newPropertyIndex;
1396 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1397 current,
1398 &currentProperty);
1399 found = 1;
1402 else if (diff > 0)
1404 if (next != PROPERTY_NULL)
1406 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1407 next,
1408 &currentProperty);
1409 current = next;
1411 else
1413 currentProperty.nextProperty = newPropertyIndex;
1414 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1415 current,
1416 &currentProperty);
1417 found = 1;
1420 else
1423 * Trying to insert an item with the same name in the
1424 * subtree structure.
1426 assert(FALSE);
1429 previous = currentProperty.previousProperty;
1430 next = currentProperty.nextProperty;
1433 else
1436 * The root storage is empty, link the new property to its dir property
1438 currentProperty.dirProperty = newPropertyIndex;
1439 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1440 storage->base.rootPropertySetIndex,
1441 &currentProperty);
1446 /*************************************************************************
1447 * CopyTo (IStorage)
1449 HRESULT WINAPI StorageImpl_CopyTo(
1450 IStorage* iface,
1451 DWORD ciidExclude, /* [in] */
1452 const IID* rgiidExclude, /* [size_is][unique][in] */
1453 SNB snbExclude, /* [unique][in] */
1454 IStorage* pstgDest) /* [unique][in] */
1456 IEnumSTATSTG *elements = 0;
1457 STATSTG curElement, strStat;
1458 HRESULT hr;
1459 IStorage *pstgTmp, *pstgChild;
1460 IStream *pstrTmp, *pstrChild;
1462 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1463 FIXME("Exclude option not implemented\n");
1465 TRACE("(%p, %ld, %p, %p, %p)\n",
1466 iface, ciidExclude, rgiidExclude,
1467 snbExclude, pstgDest);
1470 * Perform a sanity check
1472 if ( pstgDest == 0 )
1473 return STG_E_INVALIDPOINTER;
1476 * Enumerate the elements
1478 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1480 if ( hr != S_OK )
1481 return hr;
1484 * set the class ID
1486 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1487 IStorage_SetClass( pstgDest, &curElement.clsid );
1492 * Obtain the next element
1494 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1496 if ( hr == S_FALSE )
1498 hr = S_OK; /* done, every element has been copied */
1499 break;
1502 if (curElement.type == STGTY_STORAGE)
1505 * open child source storage
1507 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1508 STGM_READ|STGM_SHARE_EXCLUSIVE,
1509 NULL, 0, &pstgChild );
1511 if (hr != S_OK)
1512 break;
1515 * Check if destination storage is not a child of the source
1516 * storage, which will cause an infinite loop
1518 if (pstgChild == pstgDest)
1520 IEnumSTATSTG_Release(elements);
1522 return STG_E_ACCESSDENIED;
1526 * create a new storage in destination storage
1528 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1529 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1530 0, 0,
1531 &pstgTmp );
1533 * if it already exist, don't create a new one use this one
1535 if (hr == STG_E_FILEALREADYEXISTS)
1537 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1538 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1539 NULL, 0, &pstgTmp );
1542 if (hr != S_OK)
1543 break;
1547 * do the copy recursively
1549 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1550 snbExclude, pstgTmp );
1552 IStorage_Release( pstgTmp );
1553 IStorage_Release( pstgChild );
1555 else if (curElement.type == STGTY_STREAM)
1558 * create a new stream in destination storage. If the stream already
1559 * exist, it will be deleted and a new one will be created.
1561 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1562 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1563 0, 0, &pstrTmp );
1565 if (hr != S_OK)
1566 break;
1569 * open child stream storage
1571 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1572 STGM_READ|STGM_SHARE_EXCLUSIVE,
1573 0, &pstrChild );
1575 if (hr != S_OK)
1576 break;
1579 * Get the size of the source stream
1581 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1584 * Set the size of the destination stream.
1586 IStream_SetSize(pstrTmp, strStat.cbSize);
1589 * do the copy
1591 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1592 NULL, NULL );
1594 IStream_Release( pstrTmp );
1595 IStream_Release( pstrChild );
1597 else
1599 WARN("unknown element type: %ld\n", curElement.type);
1602 } while (hr == S_OK);
1605 * Clean-up
1607 IEnumSTATSTG_Release(elements);
1609 return hr;
1612 /*************************************************************************
1613 * MoveElementTo (IStorage)
1615 HRESULT WINAPI StorageImpl_MoveElementTo(
1616 IStorage* iface,
1617 const OLECHAR *pwcsName, /* [string][in] */
1618 IStorage *pstgDest, /* [unique][in] */
1619 const OLECHAR *pwcsNewName,/* [string][in] */
1620 DWORD grfFlags) /* [in] */
1622 FIXME("not implemented!\n");
1623 return E_NOTIMPL;
1626 /*************************************************************************
1627 * Commit (IStorage)
1629 * Ensures that any changes made to a storage object open in transacted mode
1630 * are reflected in the parent storage
1632 * NOTES
1633 * Wine doesn't implement transacted mode, which seems to be a basic
1634 * optimization, so we can ignore this stub for now.
1636 HRESULT WINAPI StorageImpl_Commit(
1637 IStorage* iface,
1638 DWORD grfCommitFlags)/* [in] */
1640 FIXME("(%ld): stub!\n", grfCommitFlags);
1641 return S_OK;
1644 /*************************************************************************
1645 * Revert (IStorage)
1647 * Discard all changes that have been made since the last commit operation
1649 HRESULT WINAPI StorageImpl_Revert(
1650 IStorage* iface)
1652 FIXME("not implemented!\n");
1653 return E_NOTIMPL;
1656 /*************************************************************************
1657 * DestroyElement (IStorage)
1659 * Strategy: This implementation is built this way for simplicity not for speed.
1660 * I always delete the topmost element of the enumeration and adjust
1661 * the deleted element pointer all the time. This takes longer to
1662 * do but allow to reinvoke DestroyElement whenever we encounter a
1663 * storage object. The optimisation resides in the usage of another
1664 * enumeration strategy that would give all the leaves of a storage
1665 * first. (postfix order)
1667 HRESULT WINAPI StorageImpl_DestroyElement(
1668 IStorage* iface,
1669 const OLECHAR *pwcsName)/* [string][in] */
1671 StorageImpl* const This=(StorageImpl*)iface;
1673 IEnumSTATSTGImpl* propertyEnumeration;
1674 HRESULT hr = S_OK;
1675 BOOL res;
1676 StgProperty propertyToDelete;
1677 StgProperty parentProperty;
1678 ULONG foundPropertyIndexToDelete;
1679 ULONG typeOfRelation;
1680 ULONG parentPropertyId;
1682 TRACE("(%p, %s)\n",
1683 iface, debugstr_w(pwcsName));
1686 * Perform a sanity check on the parameters.
1688 if (pwcsName==NULL)
1689 return STG_E_INVALIDPOINTER;
1692 * Create a property enumeration to search the property with the given name
1694 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1695 This->base.ancestorStorage,
1696 This->base.rootPropertySetIndex);
1698 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1699 propertyEnumeration,
1700 pwcsName,
1701 &propertyToDelete);
1703 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1705 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1707 return STG_E_FILENOTFOUND;
1711 * Find the parent property of the property to delete (the one that
1712 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1713 * the parent is This. Otherwise, the parent is one of its sibling...
1717 * First, read This's StgProperty..
1719 res = StorageImpl_ReadProperty(
1720 This->base.ancestorStorage,
1721 This->base.rootPropertySetIndex,
1722 &parentProperty);
1724 assert(res);
1727 * Second, check to see if by any chance the actual storage (This) is not
1728 * the parent of the property to delete... We never know...
1730 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1733 * Set data as it would have been done in the else part...
1735 typeOfRelation = PROPERTY_RELATION_DIR;
1736 parentPropertyId = This->base.rootPropertySetIndex;
1738 else
1741 * Create a property enumeration to search the parent properties, and
1742 * delete it once done.
1744 IEnumSTATSTGImpl* propertyEnumeration2;
1746 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1747 This->base.ancestorStorage,
1748 This->base.rootPropertySetIndex);
1750 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1751 propertyEnumeration2,
1752 foundPropertyIndexToDelete,
1753 &parentProperty,
1754 &parentPropertyId);
1756 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1759 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1761 hr = deleteStorageProperty(
1762 This,
1763 foundPropertyIndexToDelete,
1764 propertyToDelete);
1766 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1768 hr = deleteStreamProperty(
1769 This,
1770 foundPropertyIndexToDelete,
1771 propertyToDelete);
1774 if (hr!=S_OK)
1775 return hr;
1778 * Adjust the property chain
1780 hr = adjustPropertyChain(
1781 This,
1782 propertyToDelete,
1783 parentProperty,
1784 parentPropertyId,
1785 typeOfRelation);
1787 return hr;
1791 /************************************************************************
1792 * StorageImpl_Stat (IStorage)
1794 * This method will retrieve information about this storage object.
1796 * See Windows documentation for more details on IStorage methods.
1798 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1799 STATSTG* pstatstg, /* [out] */
1800 DWORD grfStatFlag) /* [in] */
1802 StorageImpl* const This = (StorageImpl*)iface;
1803 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1805 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1807 CoTaskMemFree(pstatstg->pwcsName);
1808 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1809 strcpyW(pstatstg->pwcsName, This->pwcsName);
1812 return result;
1815 /******************************************************************************
1816 * Internal stream list handlers
1819 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1821 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1822 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1825 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1827 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1828 list_remove(&(strm->StrmListEntry));
1831 void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1833 struct list *cur, *cur2;
1834 StgStreamImpl *strm=NULL;
1836 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1837 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1838 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1839 strm->parentStorage = NULL;
1840 list_remove(cur);
1845 /*********************************************************************
1847 * Internal Method
1849 * Perform the deletion of a complete storage node
1852 static HRESULT deleteStorageProperty(
1853 StorageImpl *parentStorage,
1854 ULONG indexOfPropertyToDelete,
1855 StgProperty propertyToDelete)
1857 IEnumSTATSTG *elements = 0;
1858 IStorage *childStorage = 0;
1859 STATSTG currentElement;
1860 HRESULT hr;
1861 HRESULT destroyHr = S_OK;
1864 * Open the storage and enumerate it
1866 hr = StorageBaseImpl_OpenStorage(
1867 (IStorage*)parentStorage,
1868 propertyToDelete.name,
1870 STGM_SHARE_EXCLUSIVE,
1873 &childStorage);
1875 if (hr != S_OK)
1877 return hr;
1881 * Enumerate the elements
1883 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1888 * Obtain the next element
1890 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1891 if (hr==S_OK)
1893 destroyHr = StorageImpl_DestroyElement(
1894 (IStorage*)childStorage,
1895 (OLECHAR*)currentElement.pwcsName);
1897 CoTaskMemFree(currentElement.pwcsName);
1901 * We need to Reset the enumeration every time because we delete elements
1902 * and the enumeration could be invalid
1904 IEnumSTATSTG_Reset(elements);
1906 } while ((hr == S_OK) && (destroyHr == S_OK));
1909 * Invalidate the property by zeroing its name member.
1911 propertyToDelete.sizeOfNameString = 0;
1913 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1914 indexOfPropertyToDelete,
1915 &propertyToDelete);
1917 IStorage_Release(childStorage);
1918 IEnumSTATSTG_Release(elements);
1920 return destroyHr;
1923 /*********************************************************************
1925 * Internal Method
1927 * Perform the deletion of a stream node
1930 static HRESULT deleteStreamProperty(
1931 StorageImpl *parentStorage,
1932 ULONG indexOfPropertyToDelete,
1933 StgProperty propertyToDelete)
1935 IStream *pis;
1936 HRESULT hr;
1937 ULARGE_INTEGER size;
1939 size.u.HighPart = 0;
1940 size.u.LowPart = 0;
1942 hr = StorageBaseImpl_OpenStream(
1943 (IStorage*)parentStorage,
1944 (OLECHAR*)propertyToDelete.name,
1945 NULL,
1946 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1948 &pis);
1950 if (hr!=S_OK)
1952 return(hr);
1956 * Zap the stream
1958 hr = IStream_SetSize(pis, size);
1960 if(hr != S_OK)
1962 return hr;
1966 * Release the stream object.
1968 IStream_Release(pis);
1971 * Invalidate the property by zeroing its name member.
1973 propertyToDelete.sizeOfNameString = 0;
1976 * Here we should re-read the property so we get the updated pointer
1977 * but since we are here to zap it, I don't do it...
1979 StorageImpl_WriteProperty(
1980 parentStorage->base.ancestorStorage,
1981 indexOfPropertyToDelete,
1982 &propertyToDelete);
1984 return S_OK;
1987 /*********************************************************************
1989 * Internal Method
1991 * Finds a placeholder for the StgProperty within the Storage
1994 static HRESULT findPlaceholder(
1995 StorageImpl *storage,
1996 ULONG propertyIndexToStore,
1997 ULONG storePropertyIndex,
1998 INT typeOfRelation)
2000 StgProperty storeProperty;
2001 HRESULT hr = S_OK;
2002 BOOL res = TRUE;
2005 * Read the storage property
2007 res = StorageImpl_ReadProperty(
2008 storage->base.ancestorStorage,
2009 storePropertyIndex,
2010 &storeProperty);
2012 if(! res)
2014 return E_FAIL;
2017 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2019 if (storeProperty.previousProperty != PROPERTY_NULL)
2021 return findPlaceholder(
2022 storage,
2023 propertyIndexToStore,
2024 storeProperty.previousProperty,
2025 typeOfRelation);
2027 else
2029 storeProperty.previousProperty = propertyIndexToStore;
2032 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2034 if (storeProperty.nextProperty != PROPERTY_NULL)
2036 return findPlaceholder(
2037 storage,
2038 propertyIndexToStore,
2039 storeProperty.nextProperty,
2040 typeOfRelation);
2042 else
2044 storeProperty.nextProperty = propertyIndexToStore;
2047 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2049 if (storeProperty.dirProperty != PROPERTY_NULL)
2051 return findPlaceholder(
2052 storage,
2053 propertyIndexToStore,
2054 storeProperty.dirProperty,
2055 typeOfRelation);
2057 else
2059 storeProperty.dirProperty = propertyIndexToStore;
2063 hr = StorageImpl_WriteProperty(
2064 storage->base.ancestorStorage,
2065 storePropertyIndex,
2066 &storeProperty);
2068 if(! hr)
2070 return E_FAIL;
2073 return S_OK;
2076 /*************************************************************************
2078 * Internal Method
2080 * This method takes the previous and the next property link of a property
2081 * to be deleted and find them a place in the Storage.
2083 static HRESULT adjustPropertyChain(
2084 StorageImpl *This,
2085 StgProperty propertyToDelete,
2086 StgProperty parentProperty,
2087 ULONG parentPropertyId,
2088 INT typeOfRelation)
2090 ULONG newLinkProperty = PROPERTY_NULL;
2091 BOOL needToFindAPlaceholder = FALSE;
2092 ULONG storeNode = PROPERTY_NULL;
2093 ULONG toStoreNode = PROPERTY_NULL;
2094 INT relationType = 0;
2095 HRESULT hr = S_OK;
2096 BOOL res = TRUE;
2098 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2100 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2103 * Set the parent previous to the property to delete previous
2105 newLinkProperty = propertyToDelete.previousProperty;
2107 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2110 * We also need to find a storage for the other link, setup variables
2111 * to do this at the end...
2113 needToFindAPlaceholder = TRUE;
2114 storeNode = propertyToDelete.previousProperty;
2115 toStoreNode = propertyToDelete.nextProperty;
2116 relationType = PROPERTY_RELATION_NEXT;
2119 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2122 * Set the parent previous to the property to delete next
2124 newLinkProperty = propertyToDelete.nextProperty;
2128 * Link it for real...
2130 parentProperty.previousProperty = newLinkProperty;
2133 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2135 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2138 * Set the parent next to the property to delete next previous
2140 newLinkProperty = propertyToDelete.previousProperty;
2142 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2145 * We also need to find a storage for the other link, setup variables
2146 * to do this at the end...
2148 needToFindAPlaceholder = TRUE;
2149 storeNode = propertyToDelete.previousProperty;
2150 toStoreNode = propertyToDelete.nextProperty;
2151 relationType = PROPERTY_RELATION_NEXT;
2154 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2157 * Set the parent next to the property to delete next
2159 newLinkProperty = propertyToDelete.nextProperty;
2163 * Link it for real...
2165 parentProperty.nextProperty = newLinkProperty;
2167 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2169 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2172 * Set the parent dir to the property to delete previous
2174 newLinkProperty = propertyToDelete.previousProperty;
2176 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2179 * We also need to find a storage for the other link, setup variables
2180 * to do this at the end...
2182 needToFindAPlaceholder = TRUE;
2183 storeNode = propertyToDelete.previousProperty;
2184 toStoreNode = propertyToDelete.nextProperty;
2185 relationType = PROPERTY_RELATION_NEXT;
2188 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2191 * Set the parent dir to the property to delete next
2193 newLinkProperty = propertyToDelete.nextProperty;
2197 * Link it for real...
2199 parentProperty.dirProperty = newLinkProperty;
2203 * Write back the parent property
2205 res = StorageImpl_WriteProperty(
2206 This->base.ancestorStorage,
2207 parentPropertyId,
2208 &parentProperty);
2209 if(! res)
2211 return E_FAIL;
2215 * If a placeholder is required for the other link, then, find one and
2216 * get out of here...
2218 if (needToFindAPlaceholder)
2220 hr = findPlaceholder(
2221 This,
2222 toStoreNode,
2223 storeNode,
2224 relationType);
2227 return hr;
2231 /******************************************************************************
2232 * SetElementTimes (IStorage)
2234 HRESULT WINAPI StorageImpl_SetElementTimes(
2235 IStorage* iface,
2236 const OLECHAR *pwcsName,/* [string][in] */
2237 const FILETIME *pctime, /* [in] */
2238 const FILETIME *patime, /* [in] */
2239 const FILETIME *pmtime) /* [in] */
2241 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2242 return S_OK;
2245 /******************************************************************************
2246 * SetStateBits (IStorage)
2248 HRESULT WINAPI StorageImpl_SetStateBits(
2249 IStorage* iface,
2250 DWORD grfStateBits,/* [in] */
2251 DWORD grfMask) /* [in] */
2253 FIXME("not implemented!\n");
2254 return E_NOTIMPL;
2258 * Virtual function table for the IStorage32Impl class.
2260 static const IStorageVtbl Storage32Impl_Vtbl =
2262 StorageBaseImpl_QueryInterface,
2263 StorageBaseImpl_AddRef,
2264 StorageBaseImpl_Release,
2265 StorageBaseImpl_CreateStream,
2266 StorageBaseImpl_OpenStream,
2267 StorageImpl_CreateStorage,
2268 StorageBaseImpl_OpenStorage,
2269 StorageImpl_CopyTo,
2270 StorageImpl_MoveElementTo,
2271 StorageImpl_Commit,
2272 StorageImpl_Revert,
2273 StorageBaseImpl_EnumElements,
2274 StorageImpl_DestroyElement,
2275 StorageBaseImpl_RenameElement,
2276 StorageImpl_SetElementTimes,
2277 StorageBaseImpl_SetClass,
2278 StorageImpl_SetStateBits,
2279 StorageImpl_Stat
2282 HRESULT StorageImpl_Construct(
2283 StorageImpl* This,
2284 HANDLE hFile,
2285 LPCOLESTR pwcsName,
2286 ILockBytes* pLkbyt,
2287 DWORD openFlags,
2288 BOOL fileBased,
2289 BOOL fileCreate)
2291 HRESULT hr = S_OK;
2292 StgProperty currentProperty;
2293 BOOL readSuccessful;
2294 ULONG currentPropertyIndex;
2296 if ( FAILED( validateSTGM(openFlags) ))
2297 return STG_E_INVALIDFLAG;
2299 memset(This, 0, sizeof(StorageImpl));
2302 * Initialize stream list
2305 list_init(&This->base.strmHead);
2308 * Initialize the virtual function table.
2310 This->base.lpVtbl = &Storage32Impl_Vtbl;
2311 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2312 This->base.v_destructor = &StorageImpl_Destroy;
2313 This->base.openFlags = (openFlags & ~STGM_CREATE);
2316 * This is the top-level storage so initialize the ancestor pointer
2317 * to this.
2319 This->base.ancestorStorage = This;
2322 * Initialize the physical support of the storage.
2324 This->hFile = hFile;
2327 * Store copy of file path.
2329 if(pwcsName) {
2330 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2331 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2332 if (!This->pwcsName)
2333 return STG_E_INSUFFICIENTMEMORY;
2334 strcpyW(This->pwcsName, pwcsName);
2338 * Initialize the big block cache.
2340 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2341 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2342 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2343 pLkbyt,
2344 openFlags,
2345 This->bigBlockSize,
2346 fileBased);
2348 if (This->bigBlockFile == 0)
2349 return E_FAIL;
2351 if (fileCreate)
2353 ULARGE_INTEGER size;
2354 BYTE* bigBlockBuffer;
2357 * Initialize all header variables:
2358 * - The big block depot consists of one block and it is at block 0
2359 * - The properties start at block 1
2360 * - There is no small block depot
2362 memset( This->bigBlockDepotStart,
2363 BLOCK_UNUSED,
2364 sizeof(This->bigBlockDepotStart));
2366 This->bigBlockDepotCount = 1;
2367 This->bigBlockDepotStart[0] = 0;
2368 This->rootStartBlock = 1;
2369 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2370 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2371 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2372 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2373 This->extBigBlockDepotCount = 0;
2375 StorageImpl_SaveFileHeader(This);
2378 * Add one block for the big block depot and one block for the properties
2380 size.u.HighPart = 0;
2381 size.u.LowPart = This->bigBlockSize * 3;
2382 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2385 * Initialize the big block depot
2387 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2388 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2389 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2390 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2391 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2393 else
2396 * Load the header for the file.
2398 hr = StorageImpl_LoadFileHeader(This);
2400 if (FAILED(hr))
2402 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2404 return hr;
2409 * There is no block depot cached yet.
2411 This->indexBlockDepotCached = 0xFFFFFFFF;
2414 * Start searching for free blocks with block 0.
2416 This->prevFreeBlock = 0;
2419 * Create the block chain abstractions.
2421 if(!(This->rootBlockChain =
2422 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2423 return STG_E_READFAULT;
2425 if(!(This->smallBlockDepotChain =
2426 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2427 PROPERTY_NULL)))
2428 return STG_E_READFAULT;
2431 * Write the root property (memory only)
2433 if (fileCreate)
2435 StgProperty rootProp;
2437 * Initialize the property chain
2439 memset(&rootProp, 0, sizeof(rootProp));
2440 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2441 sizeof(rootProp.name)/sizeof(WCHAR) );
2442 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2443 rootProp.propertyType = PROPTYPE_ROOT;
2444 rootProp.previousProperty = PROPERTY_NULL;
2445 rootProp.nextProperty = PROPERTY_NULL;
2446 rootProp.dirProperty = PROPERTY_NULL;
2447 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2448 rootProp.size.u.HighPart = 0;
2449 rootProp.size.u.LowPart = 0;
2451 StorageImpl_WriteProperty(This, 0, &rootProp);
2455 * Find the ID of the root in the property sets.
2457 currentPropertyIndex = 0;
2461 readSuccessful = StorageImpl_ReadProperty(
2462 This,
2463 currentPropertyIndex,
2464 &currentProperty);
2466 if (readSuccessful)
2468 if ( (currentProperty.sizeOfNameString != 0 ) &&
2469 (currentProperty.propertyType == PROPTYPE_ROOT) )
2471 This->base.rootPropertySetIndex = currentPropertyIndex;
2475 currentPropertyIndex++;
2477 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2479 if (!readSuccessful)
2481 /* TODO CLEANUP */
2482 return STG_E_READFAULT;
2486 * Create the block chain abstraction for the small block root chain.
2488 if(!(This->smallBlockRootChain =
2489 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2490 return STG_E_READFAULT;
2492 return hr;
2495 void StorageImpl_Destroy(StorageBaseImpl* iface)
2497 StorageImpl *This = (StorageImpl*) iface;
2498 TRACE("(%p)\n", This);
2500 StorageBaseImpl_DeleteAll(&This->base);
2502 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2504 BlockChainStream_Destroy(This->smallBlockRootChain);
2505 BlockChainStream_Destroy(This->rootBlockChain);
2506 BlockChainStream_Destroy(This->smallBlockDepotChain);
2508 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2509 HeapFree(GetProcessHeap(), 0, This);
2512 /******************************************************************************
2513 * Storage32Impl_GetNextFreeBigBlock
2515 * Returns the index of the next free big block.
2516 * If the big block depot is filled, this method will enlarge it.
2519 ULONG StorageImpl_GetNextFreeBigBlock(
2520 StorageImpl* This)
2522 ULONG depotBlockIndexPos;
2523 void *depotBuffer;
2524 ULONG depotBlockOffset;
2525 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2526 ULONG nextBlockIndex = BLOCK_SPECIAL;
2527 int depotIndex = 0;
2528 ULONG freeBlock = BLOCK_UNUSED;
2530 depotIndex = This->prevFreeBlock / blocksPerDepot;
2531 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2534 * Scan the entire big block depot until we find a block marked free
2536 while (nextBlockIndex != BLOCK_UNUSED)
2538 if (depotIndex < COUNT_BBDEPOTINHEADER)
2540 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2543 * Grow the primary depot.
2545 if (depotBlockIndexPos == BLOCK_UNUSED)
2547 depotBlockIndexPos = depotIndex*blocksPerDepot;
2550 * Add a block depot.
2552 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2553 This->bigBlockDepotCount++;
2554 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2557 * Flag it as a block depot.
2559 StorageImpl_SetNextBlockInChain(This,
2560 depotBlockIndexPos,
2561 BLOCK_SPECIAL);
2563 /* Save new header information.
2565 StorageImpl_SaveFileHeader(This);
2568 else
2570 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2572 if (depotBlockIndexPos == BLOCK_UNUSED)
2575 * Grow the extended depot.
2577 ULONG extIndex = BLOCK_UNUSED;
2578 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2579 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2581 if (extBlockOffset == 0)
2583 /* We need an extended block.
2585 extIndex = Storage32Impl_AddExtBlockDepot(This);
2586 This->extBigBlockDepotCount++;
2587 depotBlockIndexPos = extIndex + 1;
2589 else
2590 depotBlockIndexPos = depotIndex * blocksPerDepot;
2593 * Add a block depot and mark it in the extended block.
2595 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2596 This->bigBlockDepotCount++;
2597 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2599 /* Flag the block depot.
2601 StorageImpl_SetNextBlockInChain(This,
2602 depotBlockIndexPos,
2603 BLOCK_SPECIAL);
2605 /* If necessary, flag the extended depot block.
2607 if (extIndex != BLOCK_UNUSED)
2608 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2610 /* Save header information.
2612 StorageImpl_SaveFileHeader(This);
2616 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2618 if (depotBuffer != 0)
2620 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2621 ( nextBlockIndex != BLOCK_UNUSED))
2623 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2625 if (nextBlockIndex == BLOCK_UNUSED)
2627 freeBlock = (depotIndex * blocksPerDepot) +
2628 (depotBlockOffset/sizeof(ULONG));
2631 depotBlockOffset += sizeof(ULONG);
2634 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2637 depotIndex++;
2638 depotBlockOffset = 0;
2641 This->prevFreeBlock = freeBlock;
2643 return freeBlock;
2646 /******************************************************************************
2647 * Storage32Impl_AddBlockDepot
2649 * This will create a depot block, essentially it is a block initialized
2650 * to BLOCK_UNUSEDs.
2652 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2654 BYTE* blockBuffer;
2656 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2659 * Initialize blocks as free
2661 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2663 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2666 /******************************************************************************
2667 * Storage32Impl_GetExtDepotBlock
2669 * Returns the index of the block that corresponds to the specified depot
2670 * index. This method is only for depot indexes equal or greater than
2671 * COUNT_BBDEPOTINHEADER.
2673 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2675 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2676 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2677 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2678 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2679 ULONG blockIndex = BLOCK_UNUSED;
2680 ULONG extBlockIndex = This->extBigBlockDepotStart;
2682 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2684 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2685 return BLOCK_UNUSED;
2687 while (extBlockCount > 0)
2689 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2690 extBlockCount--;
2693 if (extBlockIndex != BLOCK_UNUSED)
2695 BYTE* depotBuffer;
2697 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2699 if (depotBuffer != 0)
2701 StorageUtl_ReadDWord(depotBuffer,
2702 extBlockOffset * sizeof(ULONG),
2703 &blockIndex);
2705 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2709 return blockIndex;
2712 /******************************************************************************
2713 * Storage32Impl_SetExtDepotBlock
2715 * Associates the specified block index to the specified depot index.
2716 * This method is only for depot indexes equal or greater than
2717 * COUNT_BBDEPOTINHEADER.
2719 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2720 ULONG depotIndex,
2721 ULONG blockIndex)
2723 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2724 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2725 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2726 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2727 ULONG extBlockIndex = This->extBigBlockDepotStart;
2729 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2731 while (extBlockCount > 0)
2733 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2734 extBlockCount--;
2737 if (extBlockIndex != BLOCK_UNUSED)
2739 BYTE* depotBuffer;
2741 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2743 if (depotBuffer != 0)
2745 StorageUtl_WriteDWord(depotBuffer,
2746 extBlockOffset * sizeof(ULONG),
2747 blockIndex);
2749 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2754 /******************************************************************************
2755 * Storage32Impl_AddExtBlockDepot
2757 * Creates an extended depot block.
2759 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2761 ULONG numExtBlocks = This->extBigBlockDepotCount;
2762 ULONG nextExtBlock = This->extBigBlockDepotStart;
2763 BYTE* depotBuffer = NULL;
2764 ULONG index = BLOCK_UNUSED;
2765 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2766 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2767 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2769 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2770 blocksPerDepotBlock;
2772 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2775 * The first extended block.
2777 This->extBigBlockDepotStart = index;
2779 else
2781 unsigned int i;
2783 * Follow the chain to the last one.
2785 for (i = 0; i < (numExtBlocks - 1); i++)
2787 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2791 * Add the new extended block to the chain.
2793 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2794 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2795 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2799 * Initialize this block.
2801 depotBuffer = StorageImpl_GetBigBlock(This, index);
2802 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2803 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2805 return index;
2808 /******************************************************************************
2809 * Storage32Impl_FreeBigBlock
2811 * This method will flag the specified block as free in the big block depot.
2813 void StorageImpl_FreeBigBlock(
2814 StorageImpl* This,
2815 ULONG blockIndex)
2817 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2819 if (blockIndex < This->prevFreeBlock)
2820 This->prevFreeBlock = blockIndex;
2823 /************************************************************************
2824 * Storage32Impl_GetNextBlockInChain
2826 * This method will retrieve the block index of the next big block in
2827 * in the chain.
2829 * Params: This - Pointer to the Storage object.
2830 * blockIndex - Index of the block to retrieve the chain
2831 * for.
2832 * nextBlockIndex - receives the return value.
2834 * Returns: This method returns the index of the next block in the chain.
2835 * It will return the constants:
2836 * BLOCK_SPECIAL - If the block given was not part of a
2837 * chain.
2838 * BLOCK_END_OF_CHAIN - If the block given was the last in
2839 * a chain.
2840 * BLOCK_UNUSED - If the block given was not past of a chain
2841 * and is available.
2842 * BLOCK_EXTBBDEPOT - This block is part of the extended
2843 * big block depot.
2845 * See Windows documentation for more details on IStorage methods.
2847 HRESULT StorageImpl_GetNextBlockInChain(
2848 StorageImpl* This,
2849 ULONG blockIndex,
2850 ULONG* nextBlockIndex)
2852 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2853 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2854 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2855 void* depotBuffer;
2856 ULONG depotBlockIndexPos;
2857 int index;
2859 *nextBlockIndex = BLOCK_SPECIAL;
2861 if(depotBlockCount >= This->bigBlockDepotCount)
2863 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2864 This->bigBlockDepotCount);
2865 return STG_E_READFAULT;
2869 * Cache the currently accessed depot block.
2871 if (depotBlockCount != This->indexBlockDepotCached)
2873 This->indexBlockDepotCached = depotBlockCount;
2875 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2877 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2879 else
2882 * We have to look in the extended depot.
2884 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2887 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2889 if (!depotBuffer)
2890 return STG_E_READFAULT;
2892 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2894 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2895 This->blockDepotCached[index] = *nextBlockIndex;
2897 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2900 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2902 return S_OK;
2905 /******************************************************************************
2906 * Storage32Impl_GetNextExtendedBlock
2908 * Given an extended block this method will return the next extended block.
2910 * NOTES:
2911 * The last ULONG of an extended block is the block index of the next
2912 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2913 * depot.
2915 * Return values:
2916 * - The index of the next extended block
2917 * - BLOCK_UNUSED: there is no next extended block.
2918 * - Any other return values denotes failure.
2920 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2922 ULONG nextBlockIndex = BLOCK_SPECIAL;
2923 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2924 void* depotBuffer;
2926 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2928 if (depotBuffer!=0)
2930 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2932 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2935 return nextBlockIndex;
2938 /******************************************************************************
2939 * Storage32Impl_SetNextBlockInChain
2941 * This method will write the index of the specified block's next block
2942 * in the big block depot.
2944 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2945 * do the following
2947 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2948 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2949 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2952 void StorageImpl_SetNextBlockInChain(
2953 StorageImpl* This,
2954 ULONG blockIndex,
2955 ULONG nextBlock)
2957 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2958 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2959 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2960 ULONG depotBlockIndexPos;
2961 void* depotBuffer;
2963 assert(depotBlockCount < This->bigBlockDepotCount);
2964 assert(blockIndex != nextBlock);
2966 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2968 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2970 else
2973 * We have to look in the extended depot.
2975 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2978 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2980 if (depotBuffer!=0)
2982 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2983 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2987 * Update the cached block depot, if necessary.
2989 if (depotBlockCount == This->indexBlockDepotCached)
2991 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2995 /******************************************************************************
2996 * Storage32Impl_LoadFileHeader
2998 * This method will read in the file header, i.e. big block index -1.
3000 HRESULT StorageImpl_LoadFileHeader(
3001 StorageImpl* This)
3003 HRESULT hr = STG_E_FILENOTFOUND;
3004 void* headerBigBlock = NULL;
3005 int index;
3007 TRACE("\n");
3009 * Get a pointer to the big block of data containing the header.
3011 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
3014 * Extract the information from the header.
3016 if (headerBigBlock!=0)
3019 * Check for the "magic number" signature and return an error if it is not
3020 * found.
3022 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3024 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3025 return STG_E_OLDFORMAT;
3028 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3030 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3031 return STG_E_INVALIDHEADER;
3034 StorageUtl_ReadWord(
3035 headerBigBlock,
3036 OFFSET_BIGBLOCKSIZEBITS,
3037 &This->bigBlockSizeBits);
3039 StorageUtl_ReadWord(
3040 headerBigBlock,
3041 OFFSET_SMALLBLOCKSIZEBITS,
3042 &This->smallBlockSizeBits);
3044 StorageUtl_ReadDWord(
3045 headerBigBlock,
3046 OFFSET_BBDEPOTCOUNT,
3047 &This->bigBlockDepotCount);
3049 StorageUtl_ReadDWord(
3050 headerBigBlock,
3051 OFFSET_ROOTSTARTBLOCK,
3052 &This->rootStartBlock);
3054 StorageUtl_ReadDWord(
3055 headerBigBlock,
3056 OFFSET_SBDEPOTSTART,
3057 &This->smallBlockDepotStart);
3059 StorageUtl_ReadDWord(
3060 headerBigBlock,
3061 OFFSET_EXTBBDEPOTSTART,
3062 &This->extBigBlockDepotStart);
3064 StorageUtl_ReadDWord(
3065 headerBigBlock,
3066 OFFSET_EXTBBDEPOTCOUNT,
3067 &This->extBigBlockDepotCount);
3069 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3071 StorageUtl_ReadDWord(
3072 headerBigBlock,
3073 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3074 &(This->bigBlockDepotStart[index]));
3078 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3080 if ((1 << 2) == 4)
3082 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3083 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3085 else
3087 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3088 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3092 * Right now, the code is making some assumptions about the size of the
3093 * blocks, just make sure they are what we're expecting.
3095 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3096 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3098 WARN("Broken OLE storage file\n");
3099 hr = STG_E_INVALIDHEADER;
3101 else
3102 hr = S_OK;
3105 * Release the block.
3107 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3110 return hr;
3113 /******************************************************************************
3114 * Storage32Impl_SaveFileHeader
3116 * This method will save to the file the header, i.e. big block -1.
3118 void StorageImpl_SaveFileHeader(
3119 StorageImpl* This)
3121 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3122 int index;
3123 BOOL success;
3126 * Get a pointer to the big block of data containing the header.
3128 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3131 * If the block read failed, the file is probably new.
3133 if (!success)
3136 * Initialize for all unknown fields.
3138 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3141 * Initialize the magic number.
3143 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3146 * And a bunch of things we don't know what they mean
3148 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3149 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3150 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3151 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3155 * Write the information to the header.
3157 StorageUtl_WriteWord(
3158 headerBigBlock,
3159 OFFSET_BIGBLOCKSIZEBITS,
3160 This->bigBlockSizeBits);
3162 StorageUtl_WriteWord(
3163 headerBigBlock,
3164 OFFSET_SMALLBLOCKSIZEBITS,
3165 This->smallBlockSizeBits);
3167 StorageUtl_WriteDWord(
3168 headerBigBlock,
3169 OFFSET_BBDEPOTCOUNT,
3170 This->bigBlockDepotCount);
3172 StorageUtl_WriteDWord(
3173 headerBigBlock,
3174 OFFSET_ROOTSTARTBLOCK,
3175 This->rootStartBlock);
3177 StorageUtl_WriteDWord(
3178 headerBigBlock,
3179 OFFSET_SBDEPOTSTART,
3180 This->smallBlockDepotStart);
3182 StorageUtl_WriteDWord(
3183 headerBigBlock,
3184 OFFSET_SBDEPOTCOUNT,
3185 This->smallBlockDepotChain ?
3186 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3188 StorageUtl_WriteDWord(
3189 headerBigBlock,
3190 OFFSET_EXTBBDEPOTSTART,
3191 This->extBigBlockDepotStart);
3193 StorageUtl_WriteDWord(
3194 headerBigBlock,
3195 OFFSET_EXTBBDEPOTCOUNT,
3196 This->extBigBlockDepotCount);
3198 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3200 StorageUtl_WriteDWord(
3201 headerBigBlock,
3202 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3203 (This->bigBlockDepotStart[index]));
3207 * Write the big block back to the file.
3209 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3212 /******************************************************************************
3213 * Storage32Impl_ReadProperty
3215 * This method will read the specified property from the property chain.
3217 BOOL StorageImpl_ReadProperty(
3218 StorageImpl* This,
3219 ULONG index,
3220 StgProperty* buffer)
3222 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3223 ULARGE_INTEGER offsetInPropSet;
3224 BOOL readSuccessful;
3225 ULONG bytesRead;
3227 offsetInPropSet.u.HighPart = 0;
3228 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3230 readSuccessful = BlockChainStream_ReadAt(
3231 This->rootBlockChain,
3232 offsetInPropSet,
3233 PROPSET_BLOCK_SIZE,
3234 currentProperty,
3235 &bytesRead);
3237 if (readSuccessful)
3239 /* replace the name of root entry (often "Root Entry") by the file name */
3240 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3241 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3243 memset(buffer->name, 0, sizeof(buffer->name));
3244 memcpy(
3245 buffer->name,
3246 propName,
3247 PROPERTY_NAME_BUFFER_LEN );
3248 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3250 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3252 StorageUtl_ReadWord(
3253 currentProperty,
3254 OFFSET_PS_NAMELENGTH,
3255 &buffer->sizeOfNameString);
3257 StorageUtl_ReadDWord(
3258 currentProperty,
3259 OFFSET_PS_PREVIOUSPROP,
3260 &buffer->previousProperty);
3262 StorageUtl_ReadDWord(
3263 currentProperty,
3264 OFFSET_PS_NEXTPROP,
3265 &buffer->nextProperty);
3267 StorageUtl_ReadDWord(
3268 currentProperty,
3269 OFFSET_PS_DIRPROP,
3270 &buffer->dirProperty);
3272 StorageUtl_ReadGUID(
3273 currentProperty,
3274 OFFSET_PS_GUID,
3275 &buffer->propertyUniqueID);
3277 StorageUtl_ReadDWord(
3278 currentProperty,
3279 OFFSET_PS_TSS1,
3280 &buffer->timeStampS1);
3282 StorageUtl_ReadDWord(
3283 currentProperty,
3284 OFFSET_PS_TSD1,
3285 &buffer->timeStampD1);
3287 StorageUtl_ReadDWord(
3288 currentProperty,
3289 OFFSET_PS_TSS2,
3290 &buffer->timeStampS2);
3292 StorageUtl_ReadDWord(
3293 currentProperty,
3294 OFFSET_PS_TSD2,
3295 &buffer->timeStampD2);
3297 StorageUtl_ReadDWord(
3298 currentProperty,
3299 OFFSET_PS_STARTBLOCK,
3300 &buffer->startingBlock);
3302 StorageUtl_ReadDWord(
3303 currentProperty,
3304 OFFSET_PS_SIZE,
3305 &buffer->size.u.LowPart);
3307 buffer->size.u.HighPart = 0;
3310 return readSuccessful;
3313 /*********************************************************************
3314 * Write the specified property into the property chain
3316 BOOL StorageImpl_WriteProperty(
3317 StorageImpl* This,
3318 ULONG index,
3319 StgProperty* buffer)
3321 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3322 ULARGE_INTEGER offsetInPropSet;
3323 BOOL writeSuccessful;
3324 ULONG bytesWritten;
3326 offsetInPropSet.u.HighPart = 0;
3327 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3329 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3331 memcpy(
3332 currentProperty + OFFSET_PS_NAME,
3333 buffer->name,
3334 PROPERTY_NAME_BUFFER_LEN );
3336 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3338 StorageUtl_WriteWord(
3339 currentProperty,
3340 OFFSET_PS_NAMELENGTH,
3341 buffer->sizeOfNameString);
3343 StorageUtl_WriteDWord(
3344 currentProperty,
3345 OFFSET_PS_PREVIOUSPROP,
3346 buffer->previousProperty);
3348 StorageUtl_WriteDWord(
3349 currentProperty,
3350 OFFSET_PS_NEXTPROP,
3351 buffer->nextProperty);
3353 StorageUtl_WriteDWord(
3354 currentProperty,
3355 OFFSET_PS_DIRPROP,
3356 buffer->dirProperty);
3358 StorageUtl_WriteGUID(
3359 currentProperty,
3360 OFFSET_PS_GUID,
3361 &buffer->propertyUniqueID);
3363 StorageUtl_WriteDWord(
3364 currentProperty,
3365 OFFSET_PS_TSS1,
3366 buffer->timeStampS1);
3368 StorageUtl_WriteDWord(
3369 currentProperty,
3370 OFFSET_PS_TSD1,
3371 buffer->timeStampD1);
3373 StorageUtl_WriteDWord(
3374 currentProperty,
3375 OFFSET_PS_TSS2,
3376 buffer->timeStampS2);
3378 StorageUtl_WriteDWord(
3379 currentProperty,
3380 OFFSET_PS_TSD2,
3381 buffer->timeStampD2);
3383 StorageUtl_WriteDWord(
3384 currentProperty,
3385 OFFSET_PS_STARTBLOCK,
3386 buffer->startingBlock);
3388 StorageUtl_WriteDWord(
3389 currentProperty,
3390 OFFSET_PS_SIZE,
3391 buffer->size.u.LowPart);
3393 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3394 offsetInPropSet,
3395 PROPSET_BLOCK_SIZE,
3396 currentProperty,
3397 &bytesWritten);
3398 return writeSuccessful;
3401 BOOL StorageImpl_ReadBigBlock(
3402 StorageImpl* This,
3403 ULONG blockIndex,
3404 void* buffer)
3406 void* bigBlockBuffer;
3408 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3410 if (bigBlockBuffer!=0)
3412 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3414 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3416 return TRUE;
3419 return FALSE;
3422 BOOL StorageImpl_WriteBigBlock(
3423 StorageImpl* This,
3424 ULONG blockIndex,
3425 void* buffer)
3427 void* bigBlockBuffer;
3429 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3431 if (bigBlockBuffer!=0)
3433 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3435 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3437 return TRUE;
3440 return FALSE;
3443 void* StorageImpl_GetROBigBlock(
3444 StorageImpl* This,
3445 ULONG blockIndex)
3447 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3450 void* StorageImpl_GetBigBlock(
3451 StorageImpl* This,
3452 ULONG blockIndex)
3454 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3457 void StorageImpl_ReleaseBigBlock(
3458 StorageImpl* This,
3459 void* pBigBlock)
3461 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3464 /******************************************************************************
3465 * Storage32Impl_SmallBlocksToBigBlocks
3467 * This method will convert a small block chain to a big block chain.
3468 * The small block chain will be destroyed.
3470 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3471 StorageImpl* This,
3472 SmallBlockChainStream** ppsbChain)
3474 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3475 ULARGE_INTEGER size, offset;
3476 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3477 ULONG propertyIndex;
3478 BOOL successWrite;
3479 HRESULT successRead;
3480 StgProperty chainProperty;
3481 BYTE *buffer;
3482 BlockChainStream *bbTempChain = NULL;
3483 BlockChainStream *bigBlockChain = NULL;
3486 * Create a temporary big block chain that doesn't have
3487 * an associated property. This temporary chain will be
3488 * used to copy data from small blocks to big blocks.
3490 bbTempChain = BlockChainStream_Construct(This,
3491 &bbHeadOfChain,
3492 PROPERTY_NULL);
3493 if(!bbTempChain) return NULL;
3495 * Grow the big block chain.
3497 size = SmallBlockChainStream_GetSize(*ppsbChain);
3498 BlockChainStream_SetSize(bbTempChain, size);
3501 * Copy the contents of the small block chain to the big block chain
3502 * by small block size increments.
3504 offset.u.LowPart = 0;
3505 offset.u.HighPart = 0;
3506 cbTotalRead = 0;
3507 cbTotalWritten = 0;
3509 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3512 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3513 offset,
3514 DEF_SMALL_BLOCK_SIZE,
3515 buffer,
3516 &cbRead);
3517 if (FAILED(successRead))
3518 break;
3520 if (cbRead > 0)
3522 cbTotalRead += cbRead;
3524 successWrite = BlockChainStream_WriteAt(bbTempChain,
3525 offset,
3526 cbRead,
3527 buffer,
3528 &cbWritten);
3530 if (!successWrite)
3531 break;
3533 cbTotalWritten += cbWritten;
3534 offset.u.LowPart += This->smallBlockSize;
3536 } while (cbRead > 0);
3537 HeapFree(GetProcessHeap(),0,buffer);
3539 assert(cbTotalRead == cbTotalWritten);
3542 * Destroy the small block chain.
3544 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3545 size.u.HighPart = 0;
3546 size.u.LowPart = 0;
3547 SmallBlockChainStream_SetSize(*ppsbChain, size);
3548 SmallBlockChainStream_Destroy(*ppsbChain);
3549 *ppsbChain = 0;
3552 * Change the property information. This chain is now a big block chain
3553 * and it doesn't reside in the small blocks chain anymore.
3555 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3557 chainProperty.startingBlock = bbHeadOfChain;
3559 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3562 * Destroy the temporary propertyless big block chain.
3563 * Create a new big block chain associated with this property.
3565 BlockChainStream_Destroy(bbTempChain);
3566 bigBlockChain = BlockChainStream_Construct(This,
3567 NULL,
3568 propertyIndex);
3570 return bigBlockChain;
3573 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3575 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3577 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3578 HeapFree(GetProcessHeap(), 0, This);
3581 /******************************************************************************
3583 ** Storage32InternalImpl_Commit
3585 ** The non-root storages cannot be opened in transacted mode thus this function
3586 ** does nothing.
3588 HRESULT WINAPI StorageInternalImpl_Commit(
3589 IStorage* iface,
3590 DWORD grfCommitFlags) /* [in] */
3592 return S_OK;
3595 /******************************************************************************
3597 ** Storage32InternalImpl_Revert
3599 ** The non-root storages cannot be opened in transacted mode thus this function
3600 ** does nothing.
3602 HRESULT WINAPI StorageInternalImpl_Revert(
3603 IStorage* iface)
3605 return S_OK;
3608 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3610 IStorage_Release((IStorage*)This->parentStorage);
3611 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3612 HeapFree(GetProcessHeap(), 0, This);
3615 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3616 IEnumSTATSTG* iface,
3617 REFIID riid,
3618 void** ppvObject)
3620 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3623 * Perform a sanity check on the parameters.
3625 if (ppvObject==0)
3626 return E_INVALIDARG;
3629 * Initialize the return parameter.
3631 *ppvObject = 0;
3634 * Compare the riid with the interface IDs implemented by this object.
3636 if (IsEqualGUID(&IID_IUnknown, riid) ||
3637 IsEqualGUID(&IID_IStorage, riid))
3639 *ppvObject = (IEnumSTATSTG*)This;
3640 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3641 return S_OK;
3644 return E_NOINTERFACE;
3647 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3648 IEnumSTATSTG* iface)
3650 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3651 return InterlockedIncrement(&This->ref);
3654 ULONG WINAPI IEnumSTATSTGImpl_Release(
3655 IEnumSTATSTG* iface)
3657 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3659 ULONG newRef;
3661 newRef = InterlockedDecrement(&This->ref);
3664 * If the reference count goes down to 0, perform suicide.
3666 if (newRef==0)
3668 IEnumSTATSTGImpl_Destroy(This);
3671 return newRef;
3674 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3675 IEnumSTATSTG* iface,
3676 ULONG celt,
3677 STATSTG* rgelt,
3678 ULONG* pceltFetched)
3680 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3682 StgProperty currentProperty;
3683 STATSTG* currentReturnStruct = rgelt;
3684 ULONG objectFetched = 0;
3685 ULONG currentSearchNode;
3688 * Perform a sanity check on the parameters.
3690 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3691 return E_INVALIDARG;
3694 * To avoid the special case, get another pointer to a ULONG value if
3695 * the caller didn't supply one.
3697 if (pceltFetched==0)
3698 pceltFetched = &objectFetched;
3701 * Start the iteration, we will iterate until we hit the end of the
3702 * linked list or until we hit the number of items to iterate through
3704 *pceltFetched = 0;
3707 * Start with the node at the top of the stack.
3709 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3711 while ( ( *pceltFetched < 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 * Copy the information to the return buffer.
3729 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3730 &currentProperty,
3731 STATFLAG_DEFAULT);
3734 * Step to the next item in the iteration
3736 (*pceltFetched)++;
3737 currentReturnStruct++;
3740 * Push the next search node in the search stack.
3742 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3745 * continue the iteration.
3747 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3750 if (*pceltFetched == celt)
3751 return S_OK;
3753 return S_FALSE;
3757 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3758 IEnumSTATSTG* iface,
3759 ULONG celt)
3761 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3763 StgProperty currentProperty;
3764 ULONG objectFetched = 0;
3765 ULONG currentSearchNode;
3768 * Start with the node at the top of the stack.
3770 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3772 while ( (objectFetched < celt) &&
3773 (currentSearchNode!=PROPERTY_NULL) )
3776 * Remove the top node from the stack
3778 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3781 * Read the property from the storage.
3783 StorageImpl_ReadProperty(This->parentStorage,
3784 currentSearchNode,
3785 &currentProperty);
3788 * Step to the next item in the iteration
3790 objectFetched++;
3793 * Push the next search node in the search stack.
3795 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3798 * continue the iteration.
3800 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3803 if (objectFetched == celt)
3804 return S_OK;
3806 return S_FALSE;
3809 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3810 IEnumSTATSTG* iface)
3812 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3814 StgProperty rootProperty;
3815 BOOL readSuccessful;
3818 * Re-initialize the search stack to an empty stack
3820 This->stackSize = 0;
3823 * Read the root property from the storage.
3825 readSuccessful = StorageImpl_ReadProperty(
3826 This->parentStorage,
3827 This->firstPropertyNode,
3828 &rootProperty);
3830 if (readSuccessful)
3832 assert(rootProperty.sizeOfNameString!=0);
3835 * Push the search node in the search stack.
3837 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3840 return S_OK;
3843 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3844 IEnumSTATSTG* iface,
3845 IEnumSTATSTG** ppenum)
3847 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3849 IEnumSTATSTGImpl* newClone;
3852 * Perform a sanity check on the parameters.
3854 if (ppenum==0)
3855 return E_INVALIDARG;
3857 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3858 This->firstPropertyNode);
3862 * The new clone enumeration must point to the same current node as
3863 * the ole one.
3865 newClone->stackSize = This->stackSize ;
3866 newClone->stackMaxSize = This->stackMaxSize ;
3867 newClone->stackToVisit =
3868 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3870 memcpy(
3871 newClone->stackToVisit,
3872 This->stackToVisit,
3873 sizeof(ULONG) * newClone->stackSize);
3875 *ppenum = (IEnumSTATSTG*)newClone;
3878 * Don't forget to nail down a reference to the clone before
3879 * returning it.
3881 IEnumSTATSTGImpl_AddRef(*ppenum);
3883 return S_OK;
3886 INT IEnumSTATSTGImpl_FindParentProperty(
3887 IEnumSTATSTGImpl *This,
3888 ULONG childProperty,
3889 StgProperty *currentProperty,
3890 ULONG *thisNodeId)
3892 ULONG currentSearchNode;
3893 ULONG foundNode;
3896 * To avoid the special case, get another pointer to a ULONG value if
3897 * the caller didn't supply one.
3899 if (thisNodeId==0)
3900 thisNodeId = &foundNode;
3903 * Start with the node at the top of the stack.
3905 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3908 while (currentSearchNode!=PROPERTY_NULL)
3911 * Store the current node in the returned parameters
3913 *thisNodeId = currentSearchNode;
3916 * Remove the top node from the stack
3918 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3921 * Read the property from the storage.
3923 StorageImpl_ReadProperty(
3924 This->parentStorage,
3925 currentSearchNode,
3926 currentProperty);
3928 if (currentProperty->previousProperty == childProperty)
3929 return PROPERTY_RELATION_PREVIOUS;
3931 else if (currentProperty->nextProperty == childProperty)
3932 return PROPERTY_RELATION_NEXT;
3934 else if (currentProperty->dirProperty == childProperty)
3935 return PROPERTY_RELATION_DIR;
3938 * Push the next search node in the search stack.
3940 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3943 * continue the iteration.
3945 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3948 return PROPERTY_NULL;
3951 ULONG IEnumSTATSTGImpl_FindProperty(
3952 IEnumSTATSTGImpl* This,
3953 const OLECHAR* lpszPropName,
3954 StgProperty* currentProperty)
3956 ULONG currentSearchNode;
3959 * Start with the node at the top of the stack.
3961 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3963 while (currentSearchNode!=PROPERTY_NULL)
3966 * Remove the top node from the stack
3968 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3971 * Read the property from the storage.
3973 StorageImpl_ReadProperty(This->parentStorage,
3974 currentSearchNode,
3975 currentProperty);
3977 if ( propertyNameCmp(
3978 (const OLECHAR*)currentProperty->name,
3979 (const OLECHAR*)lpszPropName) == 0)
3980 return currentSearchNode;
3983 * Push the next search node in the search stack.
3985 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3988 * continue the iteration.
3990 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3993 return PROPERTY_NULL;
3996 void IEnumSTATSTGImpl_PushSearchNode(
3997 IEnumSTATSTGImpl* This,
3998 ULONG nodeToPush)
4000 StgProperty rootProperty;
4001 BOOL readSuccessful;
4004 * First, make sure we're not trying to push an unexisting node.
4006 if (nodeToPush==PROPERTY_NULL)
4007 return;
4010 * First push the node to the stack
4012 if (This->stackSize == This->stackMaxSize)
4014 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4016 This->stackToVisit = HeapReAlloc(
4017 GetProcessHeap(),
4019 This->stackToVisit,
4020 sizeof(ULONG) * This->stackMaxSize);
4023 This->stackToVisit[This->stackSize] = nodeToPush;
4024 This->stackSize++;
4027 * Read the root property from the storage.
4029 readSuccessful = StorageImpl_ReadProperty(
4030 This->parentStorage,
4031 nodeToPush,
4032 &rootProperty);
4034 if (readSuccessful)
4036 assert(rootProperty.sizeOfNameString!=0);
4039 * Push the previous search node in the search stack.
4041 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4045 ULONG IEnumSTATSTGImpl_PopSearchNode(
4046 IEnumSTATSTGImpl* This,
4047 BOOL remove)
4049 ULONG topNode;
4051 if (This->stackSize == 0)
4052 return PROPERTY_NULL;
4054 topNode = This->stackToVisit[This->stackSize-1];
4056 if (remove)
4057 This->stackSize--;
4059 return topNode;
4063 * Virtual function table for the IEnumSTATSTGImpl class.
4065 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4067 IEnumSTATSTGImpl_QueryInterface,
4068 IEnumSTATSTGImpl_AddRef,
4069 IEnumSTATSTGImpl_Release,
4070 IEnumSTATSTGImpl_Next,
4071 IEnumSTATSTGImpl_Skip,
4072 IEnumSTATSTGImpl_Reset,
4073 IEnumSTATSTGImpl_Clone
4076 /******************************************************************************
4077 ** IEnumSTATSTGImpl implementation
4080 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4081 StorageImpl* parentStorage,
4082 ULONG firstPropertyNode)
4084 IEnumSTATSTGImpl* newEnumeration;
4086 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4088 if (newEnumeration!=0)
4091 * Set-up the virtual function table and reference count.
4093 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4094 newEnumeration->ref = 0;
4097 * We want to nail-down the reference to the storage in case the
4098 * enumeration out-lives the storage in the client application.
4100 newEnumeration->parentStorage = parentStorage;
4101 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4103 newEnumeration->firstPropertyNode = firstPropertyNode;
4106 * Initialize the search stack
4108 newEnumeration->stackSize = 0;
4109 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4110 newEnumeration->stackToVisit =
4111 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4114 * Make sure the current node of the iterator is the first one.
4116 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4119 return newEnumeration;
4123 * Virtual function table for the Storage32InternalImpl class.
4125 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4127 StorageBaseImpl_QueryInterface,
4128 StorageBaseImpl_AddRef,
4129 StorageBaseImpl_Release,
4130 StorageBaseImpl_CreateStream,
4131 StorageBaseImpl_OpenStream,
4132 StorageImpl_CreateStorage,
4133 StorageBaseImpl_OpenStorage,
4134 StorageImpl_CopyTo,
4135 StorageImpl_MoveElementTo,
4136 StorageInternalImpl_Commit,
4137 StorageInternalImpl_Revert,
4138 StorageBaseImpl_EnumElements,
4139 StorageImpl_DestroyElement,
4140 StorageBaseImpl_RenameElement,
4141 StorageImpl_SetElementTimes,
4142 StorageBaseImpl_SetClass,
4143 StorageImpl_SetStateBits,
4144 StorageBaseImpl_Stat
4147 /******************************************************************************
4148 ** Storage32InternalImpl implementation
4151 StorageInternalImpl* StorageInternalImpl_Construct(
4152 StorageImpl* ancestorStorage,
4153 DWORD openFlags,
4154 ULONG rootPropertyIndex)
4156 StorageInternalImpl* newStorage;
4159 * Allocate space for the new storage object
4161 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4163 if (newStorage!=0)
4165 memset(newStorage, 0, sizeof(StorageInternalImpl));
4168 * Initialize the stream list
4171 list_init(&newStorage->base.strmHead);
4174 * Initialize the virtual function table.
4176 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4177 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4178 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4181 * Keep the ancestor storage pointer and nail a reference to it.
4183 newStorage->base.ancestorStorage = ancestorStorage;
4184 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4187 * Keep the index of the root property set for this storage,
4189 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4191 return newStorage;
4194 return 0;
4197 /******************************************************************************
4198 ** StorageUtl implementation
4201 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4203 WORD tmp;
4205 memcpy(&tmp, buffer+offset, sizeof(WORD));
4206 *value = le16toh(tmp);
4209 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4211 value = htole16(value);
4212 memcpy(buffer+offset, &value, sizeof(WORD));
4215 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4217 DWORD tmp;
4219 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4220 *value = le32toh(tmp);
4223 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4225 value = htole32(value);
4226 memcpy(buffer+offset, &value, sizeof(DWORD));
4229 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4230 ULARGE_INTEGER* value)
4232 #ifdef WORDS_BIGENDIAN
4233 ULARGE_INTEGER tmp;
4235 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4236 value->u.LowPart = htole32(tmp.u.HighPart);
4237 value->u.HighPart = htole32(tmp.u.LowPart);
4238 #else
4239 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4240 #endif
4243 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4244 const ULARGE_INTEGER *value)
4246 #ifdef WORDS_BIGENDIAN
4247 ULARGE_INTEGER tmp;
4249 tmp.u.LowPart = htole32(value->u.HighPart);
4250 tmp.u.HighPart = htole32(value->u.LowPart);
4251 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4252 #else
4253 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4254 #endif
4257 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4259 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4260 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4261 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4263 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4266 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4268 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4269 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4270 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4272 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4275 void StorageUtl_CopyPropertyToSTATSTG(
4276 STATSTG* destination,
4277 StgProperty* source,
4278 int statFlags)
4281 * The copy of the string occurs only when the flag is not set
4283 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4284 (source->name == NULL) ||
4285 (source->name[0] == 0) )
4287 destination->pwcsName = 0;
4289 else
4291 destination->pwcsName =
4292 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4294 strcpyW((LPWSTR)destination->pwcsName, source->name);
4297 switch (source->propertyType)
4299 case PROPTYPE_STORAGE:
4300 case PROPTYPE_ROOT:
4301 destination->type = STGTY_STORAGE;
4302 break;
4303 case PROPTYPE_STREAM:
4304 destination->type = STGTY_STREAM;
4305 break;
4306 default:
4307 destination->type = STGTY_STREAM;
4308 break;
4311 destination->cbSize = source->size;
4313 currentReturnStruct->mtime = {0}; TODO
4314 currentReturnStruct->ctime = {0};
4315 currentReturnStruct->atime = {0};
4317 destination->grfMode = 0;
4318 destination->grfLocksSupported = 0;
4319 destination->clsid = source->propertyUniqueID;
4320 destination->grfStateBits = 0;
4321 destination->reserved = 0;
4324 /******************************************************************************
4325 ** BlockChainStream implementation
4328 BlockChainStream* BlockChainStream_Construct(
4329 StorageImpl* parentStorage,
4330 ULONG* headOfStreamPlaceHolder,
4331 ULONG propertyIndex)
4333 BlockChainStream* newStream;
4334 ULONG blockIndex;
4336 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4338 newStream->parentStorage = parentStorage;
4339 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4340 newStream->ownerPropertyIndex = propertyIndex;
4341 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4342 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4343 newStream->numBlocks = 0;
4345 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4347 while (blockIndex != BLOCK_END_OF_CHAIN)
4349 newStream->numBlocks++;
4350 newStream->tailIndex = blockIndex;
4352 if(FAILED(StorageImpl_GetNextBlockInChain(
4353 parentStorage,
4354 blockIndex,
4355 &blockIndex)))
4357 HeapFree(GetProcessHeap(), 0, newStream);
4358 return NULL;
4362 return newStream;
4365 void BlockChainStream_Destroy(BlockChainStream* This)
4367 HeapFree(GetProcessHeap(), 0, This);
4370 /******************************************************************************
4371 * BlockChainStream_GetHeadOfChain
4373 * Returns the head of this stream chain.
4374 * Some special chains don't have properties, their heads are kept in
4375 * This->headOfStreamPlaceHolder.
4378 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4380 StgProperty chainProperty;
4381 BOOL readSuccessful;
4383 if (This->headOfStreamPlaceHolder != 0)
4384 return *(This->headOfStreamPlaceHolder);
4386 if (This->ownerPropertyIndex != PROPERTY_NULL)
4388 readSuccessful = StorageImpl_ReadProperty(
4389 This->parentStorage,
4390 This->ownerPropertyIndex,
4391 &chainProperty);
4393 if (readSuccessful)
4395 return chainProperty.startingBlock;
4399 return BLOCK_END_OF_CHAIN;
4402 /******************************************************************************
4403 * BlockChainStream_GetCount
4405 * Returns the number of blocks that comprises this chain.
4406 * This is not the size of the stream as the last block may not be full!
4409 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4411 ULONG blockIndex;
4412 ULONG count = 0;
4414 blockIndex = BlockChainStream_GetHeadOfChain(This);
4416 while (blockIndex != BLOCK_END_OF_CHAIN)
4418 count++;
4420 if(FAILED(StorageImpl_GetNextBlockInChain(
4421 This->parentStorage,
4422 blockIndex,
4423 &blockIndex)))
4424 return 0;
4427 return count;
4430 /******************************************************************************
4431 * BlockChainStream_ReadAt
4433 * Reads a specified number of bytes from this chain at the specified offset.
4434 * bytesRead may be NULL.
4435 * Failure will be returned if the specified number of bytes has not been read.
4437 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4438 ULARGE_INTEGER offset,
4439 ULONG size,
4440 void* buffer,
4441 ULONG* bytesRead)
4443 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4444 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4445 ULONG bytesToReadInBuffer;
4446 ULONG blockIndex;
4447 BYTE* bufferWalker;
4448 BYTE* bigBlockBuffer;
4451 * Find the first block in the stream that contains part of the buffer.
4453 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4454 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4455 (blockNoInSequence < This->lastBlockNoInSequence) )
4457 blockIndex = BlockChainStream_GetHeadOfChain(This);
4458 This->lastBlockNoInSequence = blockNoInSequence;
4460 else
4462 ULONG temp = blockNoInSequence;
4464 blockIndex = This->lastBlockNoInSequenceIndex;
4465 blockNoInSequence -= This->lastBlockNoInSequence;
4466 This->lastBlockNoInSequence = temp;
4469 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4471 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4472 return FALSE;
4473 blockNoInSequence--;
4476 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4477 return FALSE; /* We failed to find the starting block */
4479 This->lastBlockNoInSequenceIndex = blockIndex;
4482 * Start reading the buffer.
4484 *bytesRead = 0;
4485 bufferWalker = buffer;
4487 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4490 * Calculate how many bytes we can copy from this big block.
4492 bytesToReadInBuffer =
4493 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4496 * Copy those bytes to the buffer
4498 bigBlockBuffer =
4499 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4500 if (!bigBlockBuffer)
4501 return FALSE;
4503 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4505 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4508 * Step to the next big block.
4510 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4511 return FALSE;
4513 bufferWalker += bytesToReadInBuffer;
4514 size -= bytesToReadInBuffer;
4515 *bytesRead += bytesToReadInBuffer;
4516 offsetInBlock = 0; /* There is no offset on the next block */
4520 return (size == 0);
4523 /******************************************************************************
4524 * BlockChainStream_WriteAt
4526 * Writes the specified number of bytes to this chain at the specified offset.
4527 * bytesWritten may be NULL.
4528 * Will fail if not all specified number of bytes have been written.
4530 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4531 ULARGE_INTEGER offset,
4532 ULONG size,
4533 const void* buffer,
4534 ULONG* bytesWritten)
4536 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4537 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4538 ULONG bytesToWrite;
4539 ULONG blockIndex;
4540 const BYTE* bufferWalker;
4541 BYTE* bigBlockBuffer;
4544 * Find the first block in the stream that contains part of the buffer.
4546 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4547 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4548 (blockNoInSequence < This->lastBlockNoInSequence) )
4550 blockIndex = BlockChainStream_GetHeadOfChain(This);
4551 This->lastBlockNoInSequence = blockNoInSequence;
4553 else
4555 ULONG temp = blockNoInSequence;
4557 blockIndex = This->lastBlockNoInSequenceIndex;
4558 blockNoInSequence -= This->lastBlockNoInSequence;
4559 This->lastBlockNoInSequence = temp;
4562 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4564 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4565 &blockIndex)))
4566 return FALSE;
4567 blockNoInSequence--;
4570 This->lastBlockNoInSequenceIndex = blockIndex;
4573 * Here, I'm casting away the constness on the buffer variable
4574 * This is OK since we don't intend to modify that buffer.
4576 *bytesWritten = 0;
4577 bufferWalker = (const BYTE*)buffer;
4579 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4582 * Calculate how many bytes we can copy from this big block.
4584 bytesToWrite =
4585 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4588 * Copy those bytes to the buffer
4590 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4592 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4594 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4597 * Step to the next big block.
4599 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4600 &blockIndex)))
4601 return FALSE;
4602 bufferWalker += bytesToWrite;
4603 size -= bytesToWrite;
4604 *bytesWritten += bytesToWrite;
4605 offsetInBlock = 0; /* There is no offset on the next block */
4608 return (size == 0);
4611 /******************************************************************************
4612 * BlockChainStream_Shrink
4614 * Shrinks this chain in the big block depot.
4616 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4617 ULARGE_INTEGER newSize)
4619 ULONG blockIndex, extraBlock;
4620 ULONG numBlocks;
4621 ULONG count = 1;
4624 * Reset the last accessed block cache.
4626 This->lastBlockNoInSequence = 0xFFFFFFFF;
4627 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4630 * Figure out how many blocks are needed to contain the new size
4632 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4634 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4635 numBlocks++;
4637 blockIndex = BlockChainStream_GetHeadOfChain(This);
4640 * Go to the new end of chain
4642 while (count < numBlocks)
4644 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4645 &blockIndex)))
4646 return FALSE;
4647 count++;
4650 /* Get the next block before marking the new end */
4651 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4652 &extraBlock)))
4653 return FALSE;
4655 /* Mark the new end of chain */
4656 StorageImpl_SetNextBlockInChain(
4657 This->parentStorage,
4658 blockIndex,
4659 BLOCK_END_OF_CHAIN);
4661 This->tailIndex = blockIndex;
4662 This->numBlocks = numBlocks;
4665 * Mark the extra blocks as free
4667 while (extraBlock != BLOCK_END_OF_CHAIN)
4669 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4670 &blockIndex)))
4671 return FALSE;
4672 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4673 extraBlock = blockIndex;
4676 return TRUE;
4679 /******************************************************************************
4680 * BlockChainStream_Enlarge
4682 * Grows this chain in the big block depot.
4684 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4685 ULARGE_INTEGER newSize)
4687 ULONG blockIndex, currentBlock;
4688 ULONG newNumBlocks;
4689 ULONG oldNumBlocks = 0;
4691 blockIndex = BlockChainStream_GetHeadOfChain(This);
4694 * Empty chain. Create the head.
4696 if (blockIndex == BLOCK_END_OF_CHAIN)
4698 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4699 StorageImpl_SetNextBlockInChain(This->parentStorage,
4700 blockIndex,
4701 BLOCK_END_OF_CHAIN);
4703 if (This->headOfStreamPlaceHolder != 0)
4705 *(This->headOfStreamPlaceHolder) = blockIndex;
4707 else
4709 StgProperty chainProp;
4710 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4712 StorageImpl_ReadProperty(
4713 This->parentStorage,
4714 This->ownerPropertyIndex,
4715 &chainProp);
4717 chainProp.startingBlock = blockIndex;
4719 StorageImpl_WriteProperty(
4720 This->parentStorage,
4721 This->ownerPropertyIndex,
4722 &chainProp);
4725 This->tailIndex = blockIndex;
4726 This->numBlocks = 1;
4730 * Figure out how many blocks are needed to contain this stream
4732 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4734 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4735 newNumBlocks++;
4738 * Go to the current end of chain
4740 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4742 currentBlock = blockIndex;
4744 while (blockIndex != BLOCK_END_OF_CHAIN)
4746 This->numBlocks++;
4747 currentBlock = blockIndex;
4749 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4750 &blockIndex)))
4751 return FALSE;
4754 This->tailIndex = currentBlock;
4757 currentBlock = This->tailIndex;
4758 oldNumBlocks = This->numBlocks;
4761 * Add new blocks to the chain
4763 if (oldNumBlocks < newNumBlocks)
4765 while (oldNumBlocks < newNumBlocks)
4767 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4769 StorageImpl_SetNextBlockInChain(
4770 This->parentStorage,
4771 currentBlock,
4772 blockIndex);
4774 StorageImpl_SetNextBlockInChain(
4775 This->parentStorage,
4776 blockIndex,
4777 BLOCK_END_OF_CHAIN);
4779 currentBlock = blockIndex;
4780 oldNumBlocks++;
4783 This->tailIndex = blockIndex;
4784 This->numBlocks = newNumBlocks;
4787 return TRUE;
4790 /******************************************************************************
4791 * BlockChainStream_SetSize
4793 * Sets the size of this stream. The big block depot will be updated.
4794 * The file will grow if we grow the chain.
4796 * TODO: Free the actual blocks in the file when we shrink the chain.
4797 * Currently, the blocks are still in the file. So the file size
4798 * doesn't shrink even if we shrink streams.
4800 BOOL BlockChainStream_SetSize(
4801 BlockChainStream* This,
4802 ULARGE_INTEGER newSize)
4804 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4806 if (newSize.u.LowPart == size.u.LowPart)
4807 return TRUE;
4809 if (newSize.u.LowPart < size.u.LowPart)
4811 BlockChainStream_Shrink(This, newSize);
4813 else
4815 ULARGE_INTEGER fileSize =
4816 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4818 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4821 * Make sure the file stays a multiple of blocksize
4823 if ((diff % This->parentStorage->bigBlockSize) != 0)
4824 diff += (This->parentStorage->bigBlockSize -
4825 (diff % This->parentStorage->bigBlockSize) );
4827 fileSize.u.LowPart += diff;
4828 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4830 BlockChainStream_Enlarge(This, newSize);
4833 return TRUE;
4836 /******************************************************************************
4837 * BlockChainStream_GetSize
4839 * Returns the size of this chain.
4840 * Will return the block count if this chain doesn't have a property.
4842 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4844 StgProperty chainProperty;
4846 if(This->headOfStreamPlaceHolder == NULL)
4849 * This chain is a data stream read the property and return
4850 * the appropriate size
4852 StorageImpl_ReadProperty(
4853 This->parentStorage,
4854 This->ownerPropertyIndex,
4855 &chainProperty);
4857 return chainProperty.size;
4859 else
4862 * this chain is a chain that does not have a property, figure out the
4863 * size by making the product number of used blocks times the
4864 * size of them
4866 ULARGE_INTEGER result;
4867 result.u.HighPart = 0;
4869 result.u.LowPart =
4870 BlockChainStream_GetCount(This) *
4871 This->parentStorage->bigBlockSize;
4873 return result;
4877 /******************************************************************************
4878 ** SmallBlockChainStream implementation
4881 SmallBlockChainStream* SmallBlockChainStream_Construct(
4882 StorageImpl* parentStorage,
4883 ULONG propertyIndex)
4885 SmallBlockChainStream* newStream;
4887 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4889 newStream->parentStorage = parentStorage;
4890 newStream->ownerPropertyIndex = propertyIndex;
4892 return newStream;
4895 void SmallBlockChainStream_Destroy(
4896 SmallBlockChainStream* This)
4898 HeapFree(GetProcessHeap(), 0, This);
4901 /******************************************************************************
4902 * SmallBlockChainStream_GetHeadOfChain
4904 * Returns the head of this chain of small blocks.
4906 ULONG SmallBlockChainStream_GetHeadOfChain(
4907 SmallBlockChainStream* This)
4909 StgProperty chainProperty;
4910 BOOL readSuccessful;
4912 if (This->ownerPropertyIndex)
4914 readSuccessful = StorageImpl_ReadProperty(
4915 This->parentStorage,
4916 This->ownerPropertyIndex,
4917 &chainProperty);
4919 if (readSuccessful)
4921 return chainProperty.startingBlock;
4926 return BLOCK_END_OF_CHAIN;
4929 /******************************************************************************
4930 * SmallBlockChainStream_GetNextBlockInChain
4932 * Returns the index of the next small block in this chain.
4934 * Return Values:
4935 * - BLOCK_END_OF_CHAIN: end of this chain
4936 * - BLOCK_UNUSED: small block 'blockIndex' is free
4938 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4939 SmallBlockChainStream* This,
4940 ULONG blockIndex,
4941 ULONG* nextBlockInChain)
4943 ULARGE_INTEGER offsetOfBlockInDepot;
4944 DWORD buffer;
4945 ULONG bytesRead;
4946 BOOL success;
4948 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4950 offsetOfBlockInDepot.u.HighPart = 0;
4951 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4954 * Read those bytes in the buffer from the small block file.
4956 success = BlockChainStream_ReadAt(
4957 This->parentStorage->smallBlockDepotChain,
4958 offsetOfBlockInDepot,
4959 sizeof(DWORD),
4960 &buffer,
4961 &bytesRead);
4963 if (success)
4965 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4966 return S_OK;
4969 return STG_E_READFAULT;
4972 /******************************************************************************
4973 * SmallBlockChainStream_SetNextBlockInChain
4975 * Writes the index of the next block of the specified block in the small
4976 * block depot.
4977 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4978 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4980 void SmallBlockChainStream_SetNextBlockInChain(
4981 SmallBlockChainStream* This,
4982 ULONG blockIndex,
4983 ULONG nextBlock)
4985 ULARGE_INTEGER offsetOfBlockInDepot;
4986 DWORD buffer;
4987 ULONG bytesWritten;
4989 offsetOfBlockInDepot.u.HighPart = 0;
4990 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4992 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4995 * Read those bytes in the buffer from the small block file.
4997 BlockChainStream_WriteAt(
4998 This->parentStorage->smallBlockDepotChain,
4999 offsetOfBlockInDepot,
5000 sizeof(DWORD),
5001 &buffer,
5002 &bytesWritten);
5005 /******************************************************************************
5006 * SmallBlockChainStream_FreeBlock
5008 * Flag small block 'blockIndex' as free in the small block depot.
5010 void SmallBlockChainStream_FreeBlock(
5011 SmallBlockChainStream* This,
5012 ULONG blockIndex)
5014 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5017 /******************************************************************************
5018 * SmallBlockChainStream_GetNextFreeBlock
5020 * Returns the index of a free small block. The small block depot will be
5021 * enlarged if necessary. The small block chain will also be enlarged if
5022 * necessary.
5024 ULONG SmallBlockChainStream_GetNextFreeBlock(
5025 SmallBlockChainStream* This)
5027 ULARGE_INTEGER offsetOfBlockInDepot;
5028 DWORD buffer;
5029 ULONG bytesRead;
5030 ULONG blockIndex = 0;
5031 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5032 BOOL success = TRUE;
5033 ULONG smallBlocksPerBigBlock;
5035 offsetOfBlockInDepot.u.HighPart = 0;
5038 * Scan the small block depot for a free block
5040 while (nextBlockIndex != BLOCK_UNUSED)
5042 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5044 success = BlockChainStream_ReadAt(
5045 This->parentStorage->smallBlockDepotChain,
5046 offsetOfBlockInDepot,
5047 sizeof(DWORD),
5048 &buffer,
5049 &bytesRead);
5052 * If we run out of space for the small block depot, enlarge it
5054 if (success)
5056 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5058 if (nextBlockIndex != BLOCK_UNUSED)
5059 blockIndex++;
5061 else
5063 ULONG count =
5064 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5066 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5067 ULONG nextBlock, newsbdIndex;
5068 BYTE* smallBlockDepot;
5070 nextBlock = sbdIndex;
5071 while (nextBlock != BLOCK_END_OF_CHAIN)
5073 sbdIndex = nextBlock;
5074 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5077 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5078 if (sbdIndex != BLOCK_END_OF_CHAIN)
5079 StorageImpl_SetNextBlockInChain(
5080 This->parentStorage,
5081 sbdIndex,
5082 newsbdIndex);
5084 StorageImpl_SetNextBlockInChain(
5085 This->parentStorage,
5086 newsbdIndex,
5087 BLOCK_END_OF_CHAIN);
5090 * Initialize all the small blocks to free
5092 smallBlockDepot =
5093 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5095 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5096 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5098 if (count == 0)
5101 * We have just created the small block depot.
5103 StgProperty rootProp;
5104 ULONG sbStartIndex;
5107 * Save it in the header
5109 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5110 StorageImpl_SaveFileHeader(This->parentStorage);
5113 * And allocate the first big block that will contain small blocks
5115 sbStartIndex =
5116 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5118 StorageImpl_SetNextBlockInChain(
5119 This->parentStorage,
5120 sbStartIndex,
5121 BLOCK_END_OF_CHAIN);
5123 StorageImpl_ReadProperty(
5124 This->parentStorage,
5125 This->parentStorage->base.rootPropertySetIndex,
5126 &rootProp);
5128 rootProp.startingBlock = sbStartIndex;
5129 rootProp.size.u.HighPart = 0;
5130 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5132 StorageImpl_WriteProperty(
5133 This->parentStorage,
5134 This->parentStorage->base.rootPropertySetIndex,
5135 &rootProp);
5140 smallBlocksPerBigBlock =
5141 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5144 * Verify if we have to allocate big blocks to contain small blocks
5146 if (blockIndex % smallBlocksPerBigBlock == 0)
5148 StgProperty rootProp;
5149 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5151 StorageImpl_ReadProperty(
5152 This->parentStorage,
5153 This->parentStorage->base.rootPropertySetIndex,
5154 &rootProp);
5156 if (rootProp.size.u.LowPart <
5157 (blocksRequired * This->parentStorage->bigBlockSize))
5159 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5161 BlockChainStream_SetSize(
5162 This->parentStorage->smallBlockRootChain,
5163 rootProp.size);
5165 StorageImpl_WriteProperty(
5166 This->parentStorage,
5167 This->parentStorage->base.rootPropertySetIndex,
5168 &rootProp);
5172 return blockIndex;
5175 /******************************************************************************
5176 * SmallBlockChainStream_ReadAt
5178 * Reads a specified number of bytes from this chain at the specified offset.
5179 * bytesRead may be NULL.
5180 * Failure will be returned if the specified number of bytes has not been read.
5182 HRESULT SmallBlockChainStream_ReadAt(
5183 SmallBlockChainStream* This,
5184 ULARGE_INTEGER offset,
5185 ULONG size,
5186 void* buffer,
5187 ULONG* bytesRead)
5189 HRESULT rc = S_OK;
5190 ULARGE_INTEGER offsetInBigBlockFile;
5191 ULONG blockNoInSequence =
5192 offset.u.LowPart / This->parentStorage->smallBlockSize;
5194 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5195 ULONG bytesToReadInBuffer;
5196 ULONG blockIndex;
5197 ULONG bytesReadFromBigBlockFile;
5198 BYTE* bufferWalker;
5201 * This should never happen on a small block file.
5203 assert(offset.u.HighPart==0);
5206 * Find the first block in the stream that contains part of the buffer.
5208 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5210 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5212 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5213 if(FAILED(rc))
5214 return rc;
5215 blockNoInSequence--;
5219 * Start reading the buffer.
5221 *bytesRead = 0;
5222 bufferWalker = buffer;
5224 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5227 * Calculate how many bytes we can copy from this small block.
5229 bytesToReadInBuffer =
5230 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5233 * Calculate the offset of the small block in the small block file.
5235 offsetInBigBlockFile.u.HighPart = 0;
5236 offsetInBigBlockFile.u.LowPart =
5237 blockIndex * This->parentStorage->smallBlockSize;
5239 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5242 * Read those bytes in the buffer from the small block file.
5243 * The small block has already been identified so it shouldn't fail
5244 * unless the file is corrupt.
5246 if (!BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5247 offsetInBigBlockFile,
5248 bytesToReadInBuffer,
5249 bufferWalker,
5250 &bytesReadFromBigBlockFile))
5251 return STG_E_DOCFILECORRUPT;
5253 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5256 * Step to the next big block.
5258 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5259 if(FAILED(rc))
5260 return rc;
5262 bufferWalker += bytesToReadInBuffer;
5263 size -= bytesToReadInBuffer;
5264 *bytesRead += bytesToReadInBuffer;
5265 offsetInBlock = 0; /* There is no offset on the next block */
5268 return rc;
5271 /******************************************************************************
5272 * SmallBlockChainStream_WriteAt
5274 * Writes the specified number of bytes to this chain at the specified offset.
5275 * bytesWritten may be NULL.
5276 * Will fail if not all specified number of bytes have been written.
5278 BOOL SmallBlockChainStream_WriteAt(
5279 SmallBlockChainStream* This,
5280 ULARGE_INTEGER offset,
5281 ULONG size,
5282 const void* buffer,
5283 ULONG* bytesWritten)
5285 ULARGE_INTEGER offsetInBigBlockFile;
5286 ULONG blockNoInSequence =
5287 offset.u.LowPart / This->parentStorage->smallBlockSize;
5289 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5290 ULONG bytesToWriteInBuffer;
5291 ULONG blockIndex;
5292 ULONG bytesWrittenFromBigBlockFile;
5293 const BYTE* bufferWalker;
5296 * This should never happen on a small block file.
5298 assert(offset.u.HighPart==0);
5301 * Find the first block in the stream that contains part of the buffer.
5303 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5305 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5307 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5308 return FALSE;
5309 blockNoInSequence--;
5313 * Start writing the buffer.
5315 * Here, I'm casting away the constness on the buffer variable
5316 * This is OK since we don't intend to modify that buffer.
5318 *bytesWritten = 0;
5319 bufferWalker = (const BYTE*)buffer;
5320 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5323 * Calculate how many bytes we can copy to this small block.
5325 bytesToWriteInBuffer =
5326 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5329 * Calculate the offset of the small block in the small block file.
5331 offsetInBigBlockFile.u.HighPart = 0;
5332 offsetInBigBlockFile.u.LowPart =
5333 blockIndex * This->parentStorage->smallBlockSize;
5335 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5338 * Write those bytes in the buffer to the small block file.
5340 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5341 offsetInBigBlockFile,
5342 bytesToWriteInBuffer,
5343 bufferWalker,
5344 &bytesWrittenFromBigBlockFile);
5346 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5349 * Step to the next big block.
5351 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5352 &blockIndex)))
5353 return FALSE;
5354 bufferWalker += bytesToWriteInBuffer;
5355 size -= bytesToWriteInBuffer;
5356 *bytesWritten += bytesToWriteInBuffer;
5357 offsetInBlock = 0; /* There is no offset on the next block */
5360 return (size == 0);
5363 /******************************************************************************
5364 * SmallBlockChainStream_Shrink
5366 * Shrinks this chain in the small block depot.
5368 BOOL SmallBlockChainStream_Shrink(
5369 SmallBlockChainStream* This,
5370 ULARGE_INTEGER newSize)
5372 ULONG blockIndex, extraBlock;
5373 ULONG numBlocks;
5374 ULONG count = 0;
5376 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5378 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5379 numBlocks++;
5381 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5384 * Go to the new end of chain
5386 while (count < numBlocks)
5388 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5389 &blockIndex)))
5390 return FALSE;
5391 count++;
5395 * If the count is 0, we have a special case, the head of the chain was
5396 * just freed.
5398 if (count == 0)
5400 StgProperty chainProp;
5402 StorageImpl_ReadProperty(This->parentStorage,
5403 This->ownerPropertyIndex,
5404 &chainProp);
5406 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5408 StorageImpl_WriteProperty(This->parentStorage,
5409 This->ownerPropertyIndex,
5410 &chainProp);
5413 * We start freeing the chain at the head block.
5415 extraBlock = blockIndex;
5417 else
5419 /* Get the next block before marking the new end */
5420 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5421 &extraBlock)))
5422 return FALSE;
5424 /* Mark the new end of chain */
5425 SmallBlockChainStream_SetNextBlockInChain(
5426 This,
5427 blockIndex,
5428 BLOCK_END_OF_CHAIN);
5432 * Mark the extra blocks as free
5434 while (extraBlock != BLOCK_END_OF_CHAIN)
5436 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5437 &blockIndex)))
5438 return FALSE;
5439 SmallBlockChainStream_FreeBlock(This, extraBlock);
5440 extraBlock = blockIndex;
5443 return TRUE;
5446 /******************************************************************************
5447 * SmallBlockChainStream_Enlarge
5449 * Grows this chain in the small block depot.
5451 BOOL SmallBlockChainStream_Enlarge(
5452 SmallBlockChainStream* This,
5453 ULARGE_INTEGER newSize)
5455 ULONG blockIndex, currentBlock;
5456 ULONG newNumBlocks;
5457 ULONG oldNumBlocks = 0;
5459 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5462 * Empty chain
5464 if (blockIndex == BLOCK_END_OF_CHAIN)
5467 StgProperty chainProp;
5469 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5470 &chainProp);
5472 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5474 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5475 &chainProp);
5477 blockIndex = chainProp.startingBlock;
5478 SmallBlockChainStream_SetNextBlockInChain(
5479 This,
5480 blockIndex,
5481 BLOCK_END_OF_CHAIN);
5484 currentBlock = blockIndex;
5487 * Figure out how many blocks are needed to contain this stream
5489 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5491 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5492 newNumBlocks++;
5495 * Go to the current end of chain
5497 while (blockIndex != BLOCK_END_OF_CHAIN)
5499 oldNumBlocks++;
5500 currentBlock = blockIndex;
5501 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5502 return FALSE;
5506 * Add new blocks to the chain
5508 while (oldNumBlocks < newNumBlocks)
5510 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5511 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5513 SmallBlockChainStream_SetNextBlockInChain(
5514 This,
5515 blockIndex,
5516 BLOCK_END_OF_CHAIN);
5518 currentBlock = blockIndex;
5519 oldNumBlocks++;
5522 return TRUE;
5525 /******************************************************************************
5526 * SmallBlockChainStream_GetCount
5528 * Returns the number of blocks that comprises this chain.
5529 * This is not the size of this chain as the last block may not be full!
5531 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5533 ULONG blockIndex;
5534 ULONG count = 0;
5536 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5538 while (blockIndex != BLOCK_END_OF_CHAIN)
5540 count++;
5542 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5543 return 0;
5546 return count;
5549 /******************************************************************************
5550 * SmallBlockChainStream_SetSize
5552 * Sets the size of this stream.
5553 * The file will grow if we grow the chain.
5555 * TODO: Free the actual blocks in the file when we shrink the chain.
5556 * Currently, the blocks are still in the file. So the file size
5557 * doesn't shrink even if we shrink streams.
5559 BOOL SmallBlockChainStream_SetSize(
5560 SmallBlockChainStream* This,
5561 ULARGE_INTEGER newSize)
5563 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5565 if (newSize.u.LowPart == size.u.LowPart)
5566 return TRUE;
5568 if (newSize.u.LowPart < size.u.LowPart)
5570 SmallBlockChainStream_Shrink(This, newSize);
5572 else
5574 SmallBlockChainStream_Enlarge(This, newSize);
5577 return TRUE;
5580 /******************************************************************************
5581 * SmallBlockChainStream_GetSize
5583 * Returns the size of this chain.
5585 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5587 StgProperty chainProperty;
5589 StorageImpl_ReadProperty(
5590 This->parentStorage,
5591 This->ownerPropertyIndex,
5592 &chainProperty);
5594 return chainProperty.size;
5597 /******************************************************************************
5598 * StgCreateDocfile [OLE32.@]
5599 * Creates a new compound file storage object
5601 * PARAMS
5602 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5603 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5604 * reserved [ ?] unused?, usually 0
5605 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5607 * RETURNS
5608 * S_OK if the file was successfully created
5609 * some STG_E_ value if error
5610 * NOTES
5611 * if pwcsName is NULL, create file with new unique name
5612 * the function can returns
5613 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5614 * (unrealized now)
5616 HRESULT WINAPI StgCreateDocfile(
5617 LPCOLESTR pwcsName,
5618 DWORD grfMode,
5619 DWORD reserved,
5620 IStorage **ppstgOpen)
5622 StorageImpl* newStorage = 0;
5623 HANDLE hFile = INVALID_HANDLE_VALUE;
5624 HRESULT hr = STG_E_INVALIDFLAG;
5625 DWORD shareMode;
5626 DWORD accessMode;
5627 DWORD creationMode;
5628 DWORD fileAttributes;
5629 WCHAR tempFileName[MAX_PATH];
5631 TRACE("(%s, %lx, %ld, %p)\n",
5632 debugstr_w(pwcsName), grfMode,
5633 reserved, ppstgOpen);
5636 * Validate the parameters
5638 if (ppstgOpen == 0)
5639 return STG_E_INVALIDPOINTER;
5640 if (reserved != 0)
5641 return STG_E_INVALIDPARAMETER;
5644 * Validate the STGM flags
5646 if ( FAILED( validateSTGM(grfMode) ))
5647 goto end;
5649 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5650 switch(STGM_ACCESS_MODE(grfMode))
5652 case STGM_WRITE:
5653 case STGM_READWRITE:
5654 break;
5655 default:
5656 goto end;
5659 /* if no share mode given then DENY_NONE is the default */
5660 if (STGM_SHARE_MODE(grfMode) == 0)
5661 grfMode |= STGM_SHARE_DENY_NONE;
5663 /* must have at least one access mode */
5664 if (STGM_ACCESS_MODE(grfMode) == 0)
5665 goto end;
5667 /* in direct mode, can only use SHARE_EXCLUSIVE */
5668 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5669 goto end;
5671 /* but in transacted mode, any share mode is valid */
5674 * Generate a unique name.
5676 if (pwcsName == 0)
5678 WCHAR tempPath[MAX_PATH];
5679 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5681 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
5682 goto end;
5684 memset(tempPath, 0, sizeof(tempPath));
5685 memset(tempFileName, 0, sizeof(tempFileName));
5687 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5688 tempPath[0] = '.';
5690 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5691 pwcsName = tempFileName;
5692 else
5694 hr = STG_E_INSUFFICIENTMEMORY;
5695 goto end;
5698 creationMode = TRUNCATE_EXISTING;
5700 else
5702 creationMode = GetCreationModeFromSTGM(grfMode);
5706 * Interpret the STGM value grfMode
5708 shareMode = GetShareModeFromSTGM(grfMode);
5709 accessMode = GetAccessModeFromSTGM(grfMode);
5711 if (grfMode & STGM_DELETEONRELEASE)
5712 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5713 else
5714 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5716 if (grfMode & STGM_TRANSACTED)
5717 FIXME("Transacted mode not implemented.\n");
5720 * Initialize the "out" parameter.
5722 *ppstgOpen = 0;
5724 hFile = CreateFileW(pwcsName,
5725 accessMode,
5726 shareMode,
5727 NULL,
5728 creationMode,
5729 fileAttributes,
5732 if (hFile == INVALID_HANDLE_VALUE)
5734 if(GetLastError() == ERROR_FILE_EXISTS)
5735 hr = STG_E_FILEALREADYEXISTS;
5736 else
5737 hr = E_FAIL;
5738 goto end;
5742 * Allocate and initialize the new IStorage32object.
5744 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5746 if (newStorage == 0)
5748 hr = STG_E_INSUFFICIENTMEMORY;
5749 goto end;
5752 hr = StorageImpl_Construct(
5753 newStorage,
5754 hFile,
5755 pwcsName,
5756 NULL,
5757 grfMode,
5758 TRUE,
5759 TRUE);
5761 if (FAILED(hr))
5763 HeapFree(GetProcessHeap(), 0, newStorage);
5764 goto end;
5768 * Get an "out" pointer for the caller.
5770 hr = StorageBaseImpl_QueryInterface(
5771 (IStorage*)newStorage,
5772 (REFIID)&IID_IStorage,
5773 (void**)ppstgOpen);
5774 end:
5775 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5777 return hr;
5780 /******************************************************************************
5781 * StgCreateStorageEx [OLE32.@]
5783 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5785 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5786 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5788 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5790 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5791 return STG_E_INVALIDPARAMETER;
5794 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5796 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5797 return STG_E_INVALIDPARAMETER;
5800 if (stgfmt == STGFMT_FILE)
5802 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5803 return STG_E_INVALIDPARAMETER;
5806 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5808 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5809 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5812 ERR("Invalid stgfmt argument\n");
5813 return STG_E_INVALIDPARAMETER;
5816 /******************************************************************************
5817 * StgCreatePropSetStg [OLE32.@]
5819 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5820 IPropertySetStorage **ppPropSetStg)
5822 HRESULT hr;
5824 TRACE("(%p, 0x%lx, %p)\n", pstg, reserved, ppPropSetStg);
5825 if (reserved)
5826 hr = STG_E_INVALIDPARAMETER;
5827 else
5828 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5829 (void**)ppPropSetStg);
5830 return hr;
5833 /******************************************************************************
5834 * StgOpenStorageEx [OLE32.@]
5836 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5838 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5839 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5841 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5843 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5844 return STG_E_INVALIDPARAMETER;
5847 if (stgfmt == STGFMT_DOCFILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5849 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5850 return STG_E_INVALIDPARAMETER;
5853 if (stgfmt == STGFMT_FILE)
5855 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5856 return STG_E_INVALIDPARAMETER;
5859 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE || stgfmt == STGFMT_ANY)
5861 if (stgfmt == STGFMT_ANY)
5862 WARN("STGFMT_ANY assuming storage\n");
5863 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5864 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5867 ERR("Invalid stgfmt argument\n");
5868 return STG_E_INVALIDPARAMETER;
5872 /******************************************************************************
5873 * StgOpenStorage [OLE32.@]
5875 HRESULT WINAPI StgOpenStorage(
5876 const OLECHAR *pwcsName,
5877 IStorage *pstgPriority,
5878 DWORD grfMode,
5879 SNB snbExclude,
5880 DWORD reserved,
5881 IStorage **ppstgOpen)
5883 StorageImpl* newStorage = 0;
5884 HRESULT hr = S_OK;
5885 HANDLE hFile = 0;
5886 DWORD shareMode;
5887 DWORD accessMode;
5888 WCHAR fullname[MAX_PATH];
5889 DWORD length;
5891 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5892 debugstr_w(pwcsName), pstgPriority, grfMode,
5893 snbExclude, reserved, ppstgOpen);
5896 * Perform sanity checks
5898 if (pwcsName == 0)
5900 hr = STG_E_INVALIDNAME;
5901 goto end;
5904 if (ppstgOpen == 0)
5906 hr = STG_E_INVALIDPOINTER;
5907 goto end;
5910 if (reserved)
5912 hr = STG_E_INVALIDPARAMETER;
5913 goto end;
5916 if (grfMode & STGM_PRIORITY)
5918 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5919 return STG_E_INVALIDFLAG;
5920 if (grfMode & STGM_DELETEONRELEASE)
5921 return STG_E_INVALIDFUNCTION;
5922 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5923 return STG_E_INVALIDFLAG;
5924 grfMode &= ~0xf0; /* remove the existing sharing mode */
5925 grfMode |= STGM_SHARE_DENY_NONE;
5927 /* STGM_PRIORITY stops other IStorage objects on the same file from
5928 * committing until the STGM_PRIORITY IStorage is closed. it also
5929 * stops non-transacted mode StgOpenStorage calls with write access from
5930 * succeeding. obviously, both of these cannot be achieved through just
5931 * file share flags */
5932 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5936 * Validate the sharing mode
5938 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5939 switch(STGM_SHARE_MODE(grfMode))
5941 case STGM_SHARE_EXCLUSIVE:
5942 case STGM_SHARE_DENY_WRITE:
5943 break;
5944 default:
5945 hr = STG_E_INVALIDFLAG;
5946 goto end;
5950 * Validate the STGM flags
5952 if ( FAILED( validateSTGM(grfMode) ) ||
5953 (grfMode&STGM_CREATE))
5955 hr = STG_E_INVALIDFLAG;
5956 goto end;
5959 /* shared reading requires transacted mode */
5960 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5961 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5962 !(grfMode&STGM_TRANSACTED) )
5964 hr = STG_E_INVALIDFLAG;
5965 goto end;
5969 * Interpret the STGM value grfMode
5971 shareMode = GetShareModeFromSTGM(grfMode);
5972 accessMode = GetAccessModeFromSTGM(grfMode);
5975 * Initialize the "out" parameter.
5977 *ppstgOpen = 0;
5979 hFile = CreateFileW( pwcsName,
5980 accessMode,
5981 shareMode,
5982 NULL,
5983 OPEN_EXISTING,
5984 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5987 if (hFile==INVALID_HANDLE_VALUE)
5989 DWORD last_error = GetLastError();
5991 hr = E_FAIL;
5993 switch (last_error)
5995 case ERROR_FILE_NOT_FOUND:
5996 hr = STG_E_FILENOTFOUND;
5997 break;
5999 case ERROR_PATH_NOT_FOUND:
6000 hr = STG_E_PATHNOTFOUND;
6001 break;
6003 case ERROR_ACCESS_DENIED:
6004 case ERROR_WRITE_PROTECT:
6005 hr = STG_E_ACCESSDENIED;
6006 break;
6008 case ERROR_SHARING_VIOLATION:
6009 hr = STG_E_SHAREVIOLATION;
6010 break;
6012 default:
6013 hr = E_FAIL;
6016 goto end;
6020 * Refuse to open the file if it's too small to be a structured storage file
6021 * FIXME: verify the file when reading instead of here
6023 length = GetFileSize(hFile, NULL);
6024 if (length < 0x100)
6026 CloseHandle(hFile);
6027 hr = STG_E_FILEALREADYEXISTS;
6028 goto end;
6032 * Allocate and initialize the new IStorage32object.
6034 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6036 if (newStorage == 0)
6038 hr = STG_E_INSUFFICIENTMEMORY;
6039 goto end;
6042 /* if the file's length was zero, initialize the storage */
6043 hr = StorageImpl_Construct(
6044 newStorage,
6045 hFile,
6046 pwcsName,
6047 NULL,
6048 grfMode,
6049 TRUE,
6050 FALSE );
6052 if (FAILED(hr))
6054 HeapFree(GetProcessHeap(), 0, newStorage);
6056 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6058 if(hr == STG_E_INVALIDHEADER)
6059 hr = STG_E_FILEALREADYEXISTS;
6060 goto end;
6063 /* prepare the file name string given in lieu of the root property name */
6064 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6065 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6066 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6069 * Get an "out" pointer for the caller.
6071 hr = StorageBaseImpl_QueryInterface(
6072 (IStorage*)newStorage,
6073 (REFIID)&IID_IStorage,
6074 (void**)ppstgOpen);
6076 end:
6077 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6078 return hr;
6081 /******************************************************************************
6082 * StgCreateDocfileOnILockBytes [OLE32.@]
6084 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6085 ILockBytes *plkbyt,
6086 DWORD grfMode,
6087 DWORD reserved,
6088 IStorage** ppstgOpen)
6090 StorageImpl* newStorage = 0;
6091 HRESULT hr = S_OK;
6094 * Validate the parameters
6096 if ((ppstgOpen == 0) || (plkbyt == 0))
6097 return STG_E_INVALIDPOINTER;
6100 * Allocate and initialize the new IStorage object.
6102 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6104 if (newStorage == 0)
6105 return STG_E_INSUFFICIENTMEMORY;
6107 hr = StorageImpl_Construct(
6108 newStorage,
6111 plkbyt,
6112 grfMode,
6113 FALSE,
6114 TRUE);
6116 if (FAILED(hr))
6118 HeapFree(GetProcessHeap(), 0, newStorage);
6119 return hr;
6123 * Get an "out" pointer for the caller.
6125 hr = StorageBaseImpl_QueryInterface(
6126 (IStorage*)newStorage,
6127 (REFIID)&IID_IStorage,
6128 (void**)ppstgOpen);
6130 return hr;
6133 /******************************************************************************
6134 * StgOpenStorageOnILockBytes [OLE32.@]
6136 HRESULT WINAPI StgOpenStorageOnILockBytes(
6137 ILockBytes *plkbyt,
6138 IStorage *pstgPriority,
6139 DWORD grfMode,
6140 SNB snbExclude,
6141 DWORD reserved,
6142 IStorage **ppstgOpen)
6144 StorageImpl* newStorage = 0;
6145 HRESULT hr = S_OK;
6148 * Perform a sanity check
6150 if ((plkbyt == 0) || (ppstgOpen == 0))
6151 return STG_E_INVALIDPOINTER;
6154 * Validate the STGM flags
6156 if ( FAILED( validateSTGM(grfMode) ))
6157 return STG_E_INVALIDFLAG;
6160 * Initialize the "out" parameter.
6162 *ppstgOpen = 0;
6165 * Allocate and initialize the new IStorage object.
6167 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6169 if (newStorage == 0)
6170 return STG_E_INSUFFICIENTMEMORY;
6172 hr = StorageImpl_Construct(
6173 newStorage,
6176 plkbyt,
6177 grfMode,
6178 FALSE,
6179 FALSE);
6181 if (FAILED(hr))
6183 HeapFree(GetProcessHeap(), 0, newStorage);
6184 return hr;
6188 * Get an "out" pointer for the caller.
6190 hr = StorageBaseImpl_QueryInterface(
6191 (IStorage*)newStorage,
6192 (REFIID)&IID_IStorage,
6193 (void**)ppstgOpen);
6195 return hr;
6198 /******************************************************************************
6199 * StgSetTimes [ole32.@]
6200 * StgSetTimes [OLE32.@]
6204 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6205 FILETIME const *patime, FILETIME const *pmtime)
6207 IStorage *stg = NULL;
6208 HRESULT r;
6210 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6212 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6213 0, 0, &stg);
6214 if( SUCCEEDED(r) )
6216 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6217 IStorage_Release(stg);
6220 return r;
6223 /******************************************************************************
6224 * StgIsStorageILockBytes [OLE32.@]
6226 * Determines if the ILockBytes contains a storage object.
6228 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6230 BYTE sig[8];
6231 ULARGE_INTEGER offset;
6233 offset.u.HighPart = 0;
6234 offset.u.LowPart = 0;
6236 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6238 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6239 return S_OK;
6241 return S_FALSE;
6244 /******************************************************************************
6245 * WriteClassStg [OLE32.@]
6247 * This method will store the specified CLSID in the specified storage object
6249 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6251 HRESULT hRes;
6253 if(!pStg)
6254 return E_INVALIDARG;
6256 hRes = IStorage_SetClass(pStg, rclsid);
6258 return hRes;
6261 /***********************************************************************
6262 * ReadClassStg (OLE32.@)
6264 * This method reads the CLSID previously written to a storage object with
6265 * the WriteClassStg.
6267 * PARAMS
6268 * pstg [I] IStorage pointer
6269 * pclsid [O] Pointer to where the CLSID is written
6271 * RETURNS
6272 * Success: S_OK.
6273 * Failure: HRESULT code.
6275 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6277 STATSTG pstatstg;
6278 HRESULT hRes;
6280 TRACE("(%p, %p)\n", pstg, pclsid);
6282 if(!pstg || !pclsid)
6283 return E_INVALIDARG;
6286 * read a STATSTG structure (contains the clsid) from the storage
6288 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6290 if(SUCCEEDED(hRes))
6291 *pclsid=pstatstg.clsid;
6293 return hRes;
6296 /***********************************************************************
6297 * OleLoadFromStream (OLE32.@)
6299 * This function loads an object from stream
6301 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6303 CLSID clsid;
6304 HRESULT res;
6305 LPPERSISTSTREAM xstm;
6307 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6309 res=ReadClassStm(pStm,&clsid);
6310 if (!SUCCEEDED(res))
6311 return res;
6312 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6313 if (!SUCCEEDED(res))
6314 return res;
6315 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6316 if (!SUCCEEDED(res)) {
6317 IUnknown_Release((IUnknown*)*ppvObj);
6318 return res;
6320 res=IPersistStream_Load(xstm,pStm);
6321 IPersistStream_Release(xstm);
6322 /* FIXME: all refcounts ok at this point? I think they should be:
6323 * pStm : unchanged
6324 * ppvObj : 1
6325 * xstm : 0 (released)
6327 return res;
6330 /***********************************************************************
6331 * OleSaveToStream (OLE32.@)
6333 * This function saves an object with the IPersistStream interface on it
6334 * to the specified stream.
6336 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6339 CLSID clsid;
6340 HRESULT res;
6342 TRACE("(%p,%p)\n",pPStm,pStm);
6344 res=IPersistStream_GetClassID(pPStm,&clsid);
6346 if (SUCCEEDED(res)){
6348 res=WriteClassStm(pStm,&clsid);
6350 if (SUCCEEDED(res))
6352 res=IPersistStream_Save(pPStm,pStm,TRUE);
6355 TRACE("Finished Save\n");
6356 return res;
6359 /****************************************************************************
6360 * This method validate a STGM parameter that can contain the values below
6362 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6363 * The stgm values contained in 0xffff0000 are bitmasks.
6365 * STGM_DIRECT 0x00000000
6366 * STGM_TRANSACTED 0x00010000
6367 * STGM_SIMPLE 0x08000000
6369 * STGM_READ 0x00000000
6370 * STGM_WRITE 0x00000001
6371 * STGM_READWRITE 0x00000002
6373 * STGM_SHARE_DENY_NONE 0x00000040
6374 * STGM_SHARE_DENY_READ 0x00000030
6375 * STGM_SHARE_DENY_WRITE 0x00000020
6376 * STGM_SHARE_EXCLUSIVE 0x00000010
6378 * STGM_PRIORITY 0x00040000
6379 * STGM_DELETEONRELEASE 0x04000000
6381 * STGM_CREATE 0x00001000
6382 * STGM_CONVERT 0x00020000
6383 * STGM_FAILIFTHERE 0x00000000
6385 * STGM_NOSCRATCH 0x00100000
6386 * STGM_NOSNAPSHOT 0x00200000
6388 static HRESULT validateSTGM(DWORD stgm)
6390 DWORD access = STGM_ACCESS_MODE(stgm);
6391 DWORD share = STGM_SHARE_MODE(stgm);
6392 DWORD create = STGM_CREATE_MODE(stgm);
6394 if (stgm&~STGM_KNOWN_FLAGS)
6396 ERR("unknown flags %08lx\n", stgm);
6397 return E_FAIL;
6400 switch (access)
6402 case STGM_READ:
6403 case STGM_WRITE:
6404 case STGM_READWRITE:
6405 break;
6406 default:
6407 return E_FAIL;
6410 switch (share)
6412 case STGM_SHARE_DENY_NONE:
6413 case STGM_SHARE_DENY_READ:
6414 case STGM_SHARE_DENY_WRITE:
6415 case STGM_SHARE_EXCLUSIVE:
6416 break;
6417 default:
6418 return E_FAIL;
6421 switch (create)
6423 case STGM_CREATE:
6424 case STGM_FAILIFTHERE:
6425 break;
6426 default:
6427 return E_FAIL;
6431 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6433 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6434 return E_FAIL;
6437 * STGM_CREATE | STGM_CONVERT
6438 * if both are false, STGM_FAILIFTHERE is set to TRUE
6440 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6441 return E_FAIL;
6444 * STGM_NOSCRATCH requires STGM_TRANSACTED
6446 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6447 return E_FAIL;
6450 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6451 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6453 if ( (stgm & STGM_NOSNAPSHOT) &&
6454 (!(stgm & STGM_TRANSACTED) ||
6455 share == STGM_SHARE_EXCLUSIVE ||
6456 share == STGM_SHARE_DENY_WRITE) )
6457 return E_FAIL;
6459 return S_OK;
6462 /****************************************************************************
6463 * GetShareModeFromSTGM
6465 * This method will return a share mode flag from a STGM value.
6466 * The STGM value is assumed valid.
6468 static DWORD GetShareModeFromSTGM(DWORD stgm)
6470 switch (STGM_SHARE_MODE(stgm))
6472 case STGM_SHARE_DENY_NONE:
6473 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6474 case STGM_SHARE_DENY_READ:
6475 return FILE_SHARE_WRITE;
6476 case STGM_SHARE_DENY_WRITE:
6477 return FILE_SHARE_READ;
6478 case STGM_SHARE_EXCLUSIVE:
6479 return 0;
6481 ERR("Invalid share mode!\n");
6482 assert(0);
6483 return 0;
6486 /****************************************************************************
6487 * GetAccessModeFromSTGM
6489 * This method will return an access mode flag from a STGM value.
6490 * The STGM value is assumed valid.
6492 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6494 switch (STGM_ACCESS_MODE(stgm))
6496 case STGM_READ:
6497 return GENERIC_READ;
6498 case STGM_WRITE:
6499 case STGM_READWRITE:
6500 return GENERIC_READ | GENERIC_WRITE;
6502 ERR("Invalid access mode!\n");
6503 assert(0);
6504 return 0;
6507 /****************************************************************************
6508 * GetCreationModeFromSTGM
6510 * This method will return a creation mode flag from a STGM value.
6511 * The STGM value is assumed valid.
6513 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6515 switch(STGM_CREATE_MODE(stgm))
6517 case STGM_CREATE:
6518 return CREATE_ALWAYS;
6519 case STGM_CONVERT:
6520 FIXME("STGM_CONVERT not implemented!\n");
6521 return CREATE_NEW;
6522 case STGM_FAILIFTHERE:
6523 return CREATE_NEW;
6525 ERR("Invalid create mode!\n");
6526 assert(0);
6527 return 0;
6531 /*************************************************************************
6532 * OLECONVERT_LoadOLE10 [Internal]
6534 * Loads the OLE10 STREAM to memory
6536 * PARAMS
6537 * pOleStream [I] The OLESTREAM
6538 * pData [I] Data Structure for the OLESTREAM Data
6540 * RETURNS
6541 * Success: S_OK
6542 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6543 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6545 * NOTES
6546 * This function is used by OleConvertOLESTREAMToIStorage only.
6548 * Memory allocated for pData must be freed by the caller
6550 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6552 DWORD dwSize;
6553 HRESULT hRes = S_OK;
6554 int nTryCnt=0;
6555 int max_try = 6;
6557 pData->pData = NULL;
6558 pData->pstrOleObjFileName = (CHAR *) NULL;
6560 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6562 /* Get the OleID */
6563 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6564 if(dwSize != sizeof(pData->dwOleID))
6566 hRes = CONVERT10_E_OLESTREAM_GET;
6568 else if(pData->dwOleID != OLESTREAM_ID)
6570 hRes = CONVERT10_E_OLESTREAM_FMT;
6572 else
6574 hRes = S_OK;
6575 break;
6579 if(hRes == S_OK)
6581 /* Get the TypeID...more info needed for this field */
6582 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6583 if(dwSize != sizeof(pData->dwTypeID))
6585 hRes = CONVERT10_E_OLESTREAM_GET;
6588 if(hRes == S_OK)
6590 if(pData->dwTypeID != 0)
6592 /* Get the length of the OleTypeName */
6593 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6594 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6596 hRes = CONVERT10_E_OLESTREAM_GET;
6599 if(hRes == S_OK)
6601 if(pData->dwOleTypeNameLength > 0)
6603 /* Get the OleTypeName */
6604 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6605 if(dwSize != pData->dwOleTypeNameLength)
6607 hRes = CONVERT10_E_OLESTREAM_GET;
6611 if(bStrem1)
6613 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6614 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6616 hRes = CONVERT10_E_OLESTREAM_GET;
6618 if(hRes == S_OK)
6620 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6621 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6622 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6623 if(pData->pstrOleObjFileName)
6625 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6626 if(dwSize != pData->dwOleObjFileNameLength)
6628 hRes = CONVERT10_E_OLESTREAM_GET;
6631 else
6632 hRes = CONVERT10_E_OLESTREAM_GET;
6635 else
6637 /* Get the Width of the Metafile */
6638 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6639 if(dwSize != sizeof(pData->dwMetaFileWidth))
6641 hRes = CONVERT10_E_OLESTREAM_GET;
6643 if(hRes == S_OK)
6645 /* Get the Height of the Metafile */
6646 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6647 if(dwSize != sizeof(pData->dwMetaFileHeight))
6649 hRes = CONVERT10_E_OLESTREAM_GET;
6653 if(hRes == S_OK)
6655 /* Get the Length of the Data */
6656 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6657 if(dwSize != sizeof(pData->dwDataLength))
6659 hRes = CONVERT10_E_OLESTREAM_GET;
6663 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6665 if(!bStrem1) /* if it is a second OLE stream data */
6667 pData->dwDataLength -= 8;
6668 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6669 if(dwSize != sizeof(pData->strUnknown))
6671 hRes = CONVERT10_E_OLESTREAM_GET;
6675 if(hRes == S_OK)
6677 if(pData->dwDataLength > 0)
6679 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6681 /* Get Data (ex. IStorage, Metafile, or BMP) */
6682 if(pData->pData)
6684 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6685 if(dwSize != pData->dwDataLength)
6687 hRes = CONVERT10_E_OLESTREAM_GET;
6690 else
6692 hRes = CONVERT10_E_OLESTREAM_GET;
6698 return hRes;
6701 /*************************************************************************
6702 * OLECONVERT_SaveOLE10 [Internal]
6704 * Saves the OLE10 STREAM From memory
6706 * PARAMS
6707 * pData [I] Data Structure for the OLESTREAM Data
6708 * pOleStream [I] The OLESTREAM to save
6710 * RETURNS
6711 * Success: S_OK
6712 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6714 * NOTES
6715 * This function is used by OleConvertIStorageToOLESTREAM only.
6718 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6720 DWORD dwSize;
6721 HRESULT hRes = S_OK;
6724 /* Set the OleID */
6725 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6726 if(dwSize != sizeof(pData->dwOleID))
6728 hRes = CONVERT10_E_OLESTREAM_PUT;
6731 if(hRes == S_OK)
6733 /* Set the TypeID */
6734 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6735 if(dwSize != sizeof(pData->dwTypeID))
6737 hRes = CONVERT10_E_OLESTREAM_PUT;
6741 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6743 /* Set the Length of the OleTypeName */
6744 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6745 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6747 hRes = CONVERT10_E_OLESTREAM_PUT;
6750 if(hRes == S_OK)
6752 if(pData->dwOleTypeNameLength > 0)
6754 /* Set the OleTypeName */
6755 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6756 if(dwSize != pData->dwOleTypeNameLength)
6758 hRes = CONVERT10_E_OLESTREAM_PUT;
6763 if(hRes == S_OK)
6765 /* Set the width of the Metafile */
6766 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6767 if(dwSize != sizeof(pData->dwMetaFileWidth))
6769 hRes = CONVERT10_E_OLESTREAM_PUT;
6773 if(hRes == S_OK)
6775 /* Set the height of the Metafile */
6776 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6777 if(dwSize != sizeof(pData->dwMetaFileHeight))
6779 hRes = CONVERT10_E_OLESTREAM_PUT;
6783 if(hRes == S_OK)
6785 /* Set the length of the Data */
6786 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6787 if(dwSize != sizeof(pData->dwDataLength))
6789 hRes = CONVERT10_E_OLESTREAM_PUT;
6793 if(hRes == S_OK)
6795 if(pData->dwDataLength > 0)
6797 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6798 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6799 if(dwSize != pData->dwDataLength)
6801 hRes = CONVERT10_E_OLESTREAM_PUT;
6806 return hRes;
6809 /*************************************************************************
6810 * OLECONVERT_GetOLE20FromOLE10[Internal]
6812 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6813 * opens it, and copies the content to the dest IStorage for
6814 * OleConvertOLESTREAMToIStorage
6817 * PARAMS
6818 * pDestStorage [I] The IStorage to copy the data to
6819 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6820 * nBufferLength [I] The size of the buffer
6822 * RETURNS
6823 * Nothing
6825 * NOTES
6829 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6831 HRESULT hRes;
6832 HANDLE hFile;
6833 IStorage *pTempStorage;
6834 DWORD dwNumOfBytesWritten;
6835 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6836 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6838 /* Create a temp File */
6839 GetTempPathW(MAX_PATH, wstrTempDir);
6840 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6841 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6843 if(hFile != INVALID_HANDLE_VALUE)
6845 /* Write IStorage Data to File */
6846 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6847 CloseHandle(hFile);
6849 /* Open and copy temp storage to the Dest Storage */
6850 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6851 if(hRes == S_OK)
6853 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6854 StorageBaseImpl_Release(pTempStorage);
6856 DeleteFileW(wstrTempFile);
6861 /*************************************************************************
6862 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6864 * Saves the OLE10 STREAM From memory
6866 * PARAMS
6867 * pStorage [I] The Src IStorage to copy
6868 * pData [I] The Dest Memory to write to.
6870 * RETURNS
6871 * The size in bytes allocated for pData
6873 * NOTES
6874 * Memory allocated for pData must be freed by the caller
6876 * Used by OleConvertIStorageToOLESTREAM only.
6879 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6881 HANDLE hFile;
6882 HRESULT hRes;
6883 DWORD nDataLength = 0;
6884 IStorage *pTempStorage;
6885 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6886 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6888 *pData = NULL;
6890 /* Create temp Storage */
6891 GetTempPathW(MAX_PATH, wstrTempDir);
6892 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6893 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6895 if(hRes == S_OK)
6897 /* Copy Src Storage to the Temp Storage */
6898 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6899 StorageBaseImpl_Release(pTempStorage);
6901 /* Open Temp Storage as a file and copy to memory */
6902 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6903 if(hFile != INVALID_HANDLE_VALUE)
6905 nDataLength = GetFileSize(hFile, NULL);
6906 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6907 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6908 CloseHandle(hFile);
6910 DeleteFileW(wstrTempFile);
6912 return nDataLength;
6915 /*************************************************************************
6916 * OLECONVERT_CreateOleStream [Internal]
6918 * Creates the "\001OLE" stream in the IStorage if necessary.
6920 * PARAMS
6921 * pStorage [I] Dest storage to create the stream in
6923 * RETURNS
6924 * Nothing
6926 * NOTES
6927 * This function is used by OleConvertOLESTREAMToIStorage only.
6929 * This stream is still unknown, MS Word seems to have extra data
6930 * but since the data is stored in the OLESTREAM there should be
6931 * no need to recreate the stream. If the stream is manually
6932 * deleted it will create it with this default data.
6935 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6937 HRESULT hRes;
6938 IStream *pStream;
6939 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6940 BYTE pOleStreamHeader [] =
6942 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6943 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6944 0x00, 0x00, 0x00, 0x00
6947 /* Create stream if not present */
6948 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6949 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6951 if(hRes == S_OK)
6953 /* Write default Data */
6954 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6955 IStream_Release(pStream);
6959 /* write a string to a stream, preceded by its length */
6960 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6962 HRESULT r;
6963 LPSTR str;
6964 DWORD len = 0;
6966 if( string )
6967 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6968 r = IStream_Write( stm, &len, sizeof(len), NULL);
6969 if( FAILED( r ) )
6970 return r;
6971 if(len == 0)
6972 return r;
6973 str = CoTaskMemAlloc( len );
6974 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6975 r = IStream_Write( stm, str, len, NULL);
6976 CoTaskMemFree( str );
6977 return r;
6980 /* read a string preceded by its length from a stream */
6981 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6983 HRESULT r;
6984 DWORD len, count = 0;
6985 LPSTR str;
6986 LPWSTR wstr;
6988 r = IStream_Read( stm, &len, sizeof(len), &count );
6989 if( FAILED( r ) )
6990 return r;
6991 if( count != sizeof(len) )
6992 return E_OUTOFMEMORY;
6994 TRACE("%ld bytes\n",len);
6996 str = CoTaskMemAlloc( len );
6997 if( !str )
6998 return E_OUTOFMEMORY;
6999 count = 0;
7000 r = IStream_Read( stm, str, len, &count );
7001 if( FAILED( r ) )
7002 return r;
7003 if( count != len )
7005 CoTaskMemFree( str );
7006 return E_OUTOFMEMORY;
7009 TRACE("Read string %s\n",debugstr_an(str,len));
7011 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7012 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7013 if( wstr )
7014 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7015 CoTaskMemFree( str );
7017 *string = wstr;
7019 return r;
7023 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7024 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7026 IStream *pstm;
7027 HRESULT r = S_OK;
7028 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7030 static const BYTE unknown1[12] =
7031 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7032 0xFF, 0xFF, 0xFF, 0xFF};
7033 static const BYTE unknown2[16] =
7034 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7035 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7037 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7038 debugstr_w(lpszUserType), debugstr_w(szClipName),
7039 debugstr_w(szProgIDName));
7041 /* Create a CompObj stream if it doesn't exist */
7042 r = IStorage_CreateStream(pstg, szwStreamName,
7043 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7044 if( FAILED (r) )
7045 return r;
7047 /* Write CompObj Structure to stream */
7048 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7050 if( SUCCEEDED( r ) )
7051 r = WriteClassStm( pstm, clsid );
7053 if( SUCCEEDED( r ) )
7054 r = STREAM_WriteString( pstm, lpszUserType );
7055 if( SUCCEEDED( r ) )
7056 r = STREAM_WriteString( pstm, szClipName );
7057 if( SUCCEEDED( r ) )
7058 r = STREAM_WriteString( pstm, szProgIDName );
7059 if( SUCCEEDED( r ) )
7060 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7062 IStream_Release( pstm );
7064 return r;
7067 /***********************************************************************
7068 * WriteFmtUserTypeStg (OLE32.@)
7070 HRESULT WINAPI WriteFmtUserTypeStg(
7071 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7073 HRESULT r;
7074 WCHAR szwClipName[0x40];
7075 CLSID clsid = CLSID_NULL;
7076 LPWSTR wstrProgID = NULL;
7077 DWORD n;
7079 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7081 /* get the clipboard format name */
7082 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7083 szwClipName[n]=0;
7085 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7087 /* FIXME: There's room to save a CLSID and its ProgID, but
7088 the CLSID is not looked up in the registry and in all the
7089 tests I wrote it was CLSID_NULL. Where does it come from?
7092 /* get the real program ID. This may fail, but that's fine */
7093 ProgIDFromCLSID(&clsid, &wstrProgID);
7095 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7097 r = STORAGE_WriteCompObj( pstg, &clsid,
7098 lpszUserType, szwClipName, wstrProgID );
7100 CoTaskMemFree(wstrProgID);
7102 return r;
7106 /******************************************************************************
7107 * ReadFmtUserTypeStg [OLE32.@]
7109 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7111 HRESULT r;
7112 IStream *stm = 0;
7113 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7114 unsigned char unknown1[12];
7115 unsigned char unknown2[16];
7116 DWORD count;
7117 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7118 CLSID clsid;
7120 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7122 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7123 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7124 if( FAILED ( r ) )
7126 WARN("Failed to open stream r = %08lx\n", r);
7127 return r;
7130 /* read the various parts of the structure */
7131 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7132 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7133 goto end;
7134 r = ReadClassStm( stm, &clsid );
7135 if( FAILED( r ) )
7136 goto end;
7138 r = STREAM_ReadString( stm, &szCLSIDName );
7139 if( FAILED( r ) )
7140 goto end;
7142 r = STREAM_ReadString( stm, &szOleTypeName );
7143 if( FAILED( r ) )
7144 goto end;
7146 r = STREAM_ReadString( stm, &szProgIDName );
7147 if( FAILED( r ) )
7148 goto end;
7150 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7151 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7152 goto end;
7154 /* ok, success... now we just need to store what we found */
7155 if( pcf )
7156 *pcf = RegisterClipboardFormatW( szOleTypeName );
7157 CoTaskMemFree( szOleTypeName );
7159 if( lplpszUserType )
7160 *lplpszUserType = szCLSIDName;
7161 CoTaskMemFree( szProgIDName );
7163 end:
7164 IStream_Release( stm );
7166 return r;
7170 /*************************************************************************
7171 * OLECONVERT_CreateCompObjStream [Internal]
7173 * Creates a "\001CompObj" is the destination IStorage if necessary.
7175 * PARAMS
7176 * pStorage [I] The dest IStorage to create the CompObj Stream
7177 * if necessary.
7178 * strOleTypeName [I] The ProgID
7180 * RETURNS
7181 * Success: S_OK
7182 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7184 * NOTES
7185 * This function is used by OleConvertOLESTREAMToIStorage only.
7187 * The stream data is stored in the OLESTREAM and there should be
7188 * no need to recreate the stream. If the stream is manually
7189 * deleted it will attempt to create it by querying the registry.
7193 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7195 IStream *pStream;
7196 HRESULT hStorageRes, hRes = S_OK;
7197 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7198 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7199 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7201 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7202 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7204 /* Initialize the CompObj structure */
7205 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7206 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7207 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7210 /* Create a CompObj stream if it doesn't exist */
7211 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7212 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7213 if(hStorageRes == S_OK)
7215 /* copy the OleTypeName to the compobj struct */
7216 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7217 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7219 /* copy the OleTypeName to the compobj struct */
7220 /* Note: in the test made, these were Identical */
7221 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7222 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7224 /* Get the CLSID */
7225 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7226 bufferW, OLESTREAM_MAX_STR_LEN );
7227 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7229 if(hRes == S_OK)
7231 HKEY hKey;
7232 LONG hErr;
7233 /* Get the CLSID Default Name from the Registry */
7234 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7235 if(hErr == ERROR_SUCCESS)
7237 char strTemp[OLESTREAM_MAX_STR_LEN];
7238 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7239 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7240 if(hErr == ERROR_SUCCESS)
7242 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7244 RegCloseKey(hKey);
7248 /* Write CompObj Structure to stream */
7249 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7251 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7253 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7254 if(IStorageCompObj.dwCLSIDNameLength > 0)
7256 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7258 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7259 if(IStorageCompObj.dwOleTypeNameLength > 0)
7261 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7263 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7264 if(IStorageCompObj.dwProgIDNameLength > 0)
7266 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7268 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7269 IStream_Release(pStream);
7271 return hRes;
7275 /*************************************************************************
7276 * OLECONVERT_CreateOlePresStream[Internal]
7278 * Creates the "\002OlePres000" Stream with the Metafile data
7280 * PARAMS
7281 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7282 * dwExtentX [I] Width of the Metafile
7283 * dwExtentY [I] Height of the Metafile
7284 * pData [I] Metafile data
7285 * dwDataLength [I] Size of the Metafile data
7287 * RETURNS
7288 * Success: S_OK
7289 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7291 * NOTES
7292 * This function is used by OleConvertOLESTREAMToIStorage only.
7295 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7297 HRESULT hRes;
7298 IStream *pStream;
7299 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7300 BYTE pOlePresStreamHeader [] =
7302 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7303 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7304 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7305 0x00, 0x00, 0x00, 0x00
7308 BYTE pOlePresStreamHeaderEmpty [] =
7310 0x00, 0x00, 0x00, 0x00,
7311 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7312 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7313 0x00, 0x00, 0x00, 0x00
7316 /* Create the OlePres000 Stream */
7317 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7318 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7320 if(hRes == S_OK)
7322 DWORD nHeaderSize;
7323 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7325 memset(&OlePres, 0, sizeof(OlePres));
7326 /* Do we have any metafile data to save */
7327 if(dwDataLength > 0)
7329 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7330 nHeaderSize = sizeof(pOlePresStreamHeader);
7332 else
7334 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7335 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7337 /* Set width and height of the metafile */
7338 OlePres.dwExtentX = dwExtentX;
7339 OlePres.dwExtentY = -dwExtentY;
7341 /* Set Data and Length */
7342 if(dwDataLength > sizeof(METAFILEPICT16))
7344 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7345 OlePres.pData = &(pData[8]);
7347 /* Save OlePres000 Data to Stream */
7348 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7349 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7350 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7351 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7352 if(OlePres.dwSize > 0)
7354 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7356 IStream_Release(pStream);
7360 /*************************************************************************
7361 * OLECONVERT_CreateOle10NativeStream [Internal]
7363 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7365 * PARAMS
7366 * pStorage [I] Dest storage to create the stream in
7367 * pData [I] Ole10 Native Data (ex. bmp)
7368 * dwDataLength [I] Size of the Ole10 Native Data
7370 * RETURNS
7371 * Nothing
7373 * NOTES
7374 * This function is used by OleConvertOLESTREAMToIStorage only.
7376 * Might need to verify the data and return appropriate error message
7379 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7381 HRESULT hRes;
7382 IStream *pStream;
7383 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7385 /* Create the Ole10Native Stream */
7386 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7387 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7389 if(hRes == S_OK)
7391 /* Write info to stream */
7392 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7393 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7394 IStream_Release(pStream);
7399 /*************************************************************************
7400 * OLECONVERT_GetOLE10ProgID [Internal]
7402 * Finds the ProgID (or OleTypeID) from the IStorage
7404 * PARAMS
7405 * pStorage [I] The Src IStorage to get the ProgID
7406 * strProgID [I] the ProgID string to get
7407 * dwSize [I] the size of the string
7409 * RETURNS
7410 * Success: S_OK
7411 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7413 * NOTES
7414 * This function is used by OleConvertIStorageToOLESTREAM only.
7418 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7420 HRESULT hRes;
7421 IStream *pStream;
7422 LARGE_INTEGER iSeekPos;
7423 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7424 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7426 /* Open the CompObj Stream */
7427 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7428 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7429 if(hRes == S_OK)
7432 /*Get the OleType from the CompObj Stream */
7433 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7434 iSeekPos.u.HighPart = 0;
7436 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7437 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7438 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7439 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7440 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7441 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7442 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7444 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7445 if(*dwSize > 0)
7447 IStream_Read(pStream, strProgID, *dwSize, NULL);
7449 IStream_Release(pStream);
7451 else
7453 STATSTG stat;
7454 LPOLESTR wstrProgID;
7456 /* Get the OleType from the registry */
7457 REFCLSID clsid = &(stat.clsid);
7458 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7459 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7460 if(hRes == S_OK)
7462 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7466 return hRes;
7469 /*************************************************************************
7470 * OLECONVERT_GetOle10PresData [Internal]
7472 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7474 * PARAMS
7475 * pStorage [I] Src IStroage
7476 * pOleStream [I] Dest OleStream Mem Struct
7478 * RETURNS
7479 * Nothing
7481 * NOTES
7482 * This function is used by OleConvertIStorageToOLESTREAM only.
7484 * Memory allocated for pData must be freed by the caller
7488 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7491 HRESULT hRes;
7492 IStream *pStream;
7493 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7495 /* Initialize Default data for OLESTREAM */
7496 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7497 pOleStreamData[0].dwTypeID = 2;
7498 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7499 pOleStreamData[1].dwTypeID = 0;
7500 pOleStreamData[0].dwMetaFileWidth = 0;
7501 pOleStreamData[0].dwMetaFileHeight = 0;
7502 pOleStreamData[0].pData = NULL;
7503 pOleStreamData[1].pData = NULL;
7505 /* Open Ole10Native Stream */
7506 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7507 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7508 if(hRes == S_OK)
7511 /* Read Size and Data */
7512 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7513 if(pOleStreamData->dwDataLength > 0)
7515 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7516 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7518 IStream_Release(pStream);
7524 /*************************************************************************
7525 * OLECONVERT_GetOle20PresData[Internal]
7527 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7529 * PARAMS
7530 * pStorage [I] Src IStroage
7531 * pOleStreamData [I] Dest OleStream Mem Struct
7533 * RETURNS
7534 * Nothing
7536 * NOTES
7537 * This function is used by OleConvertIStorageToOLESTREAM only.
7539 * Memory allocated for pData must be freed by the caller
7541 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7543 HRESULT hRes;
7544 IStream *pStream;
7545 OLECONVERT_ISTORAGE_OLEPRES olePress;
7546 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7548 /* Initialize Default data for OLESTREAM */
7549 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7550 pOleStreamData[0].dwTypeID = 2;
7551 pOleStreamData[0].dwMetaFileWidth = 0;
7552 pOleStreamData[0].dwMetaFileHeight = 0;
7553 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7554 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7555 pOleStreamData[1].dwTypeID = 0;
7556 pOleStreamData[1].dwOleTypeNameLength = 0;
7557 pOleStreamData[1].strOleTypeName[0] = 0;
7558 pOleStreamData[1].dwMetaFileWidth = 0;
7559 pOleStreamData[1].dwMetaFileHeight = 0;
7560 pOleStreamData[1].pData = NULL;
7561 pOleStreamData[1].dwDataLength = 0;
7564 /* Open OlePress000 stream */
7565 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7566 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7567 if(hRes == S_OK)
7569 LARGE_INTEGER iSeekPos;
7570 METAFILEPICT16 MetaFilePict;
7571 static const char strMetafilePictName[] = "METAFILEPICT";
7573 /* Set the TypeID for a Metafile */
7574 pOleStreamData[1].dwTypeID = 5;
7576 /* Set the OleTypeName to Metafile */
7577 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7578 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7580 iSeekPos.u.HighPart = 0;
7581 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7583 /* Get Presentation Data */
7584 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7585 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7586 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7587 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7589 /*Set width and Height */
7590 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7591 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7592 if(olePress.dwSize > 0)
7594 /* Set Length */
7595 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7597 /* Set MetaFilePict struct */
7598 MetaFilePict.mm = 8;
7599 MetaFilePict.xExt = olePress.dwExtentX;
7600 MetaFilePict.yExt = olePress.dwExtentY;
7601 MetaFilePict.hMF = 0;
7603 /* Get Metafile Data */
7604 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7605 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7606 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7608 IStream_Release(pStream);
7612 /*************************************************************************
7613 * OleConvertOLESTREAMToIStorage [OLE32.@]
7615 * Read info on MSDN
7617 * TODO
7618 * DVTARGETDEVICE paramenter is not handled
7619 * Still unsure of some mem fields for OLE 10 Stream
7620 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7621 * and "\001OLE" streams
7624 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7625 LPOLESTREAM pOleStream,
7626 LPSTORAGE pstg,
7627 const DVTARGETDEVICE* ptd)
7629 int i;
7630 HRESULT hRes=S_OK;
7631 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7633 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7635 if(ptd != NULL)
7637 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7640 if(pstg == NULL || pOleStream == NULL)
7642 hRes = E_INVALIDARG;
7645 if(hRes == S_OK)
7647 /* Load the OLESTREAM to Memory */
7648 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7651 if(hRes == S_OK)
7653 /* Load the OLESTREAM to Memory (part 2)*/
7654 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7657 if(hRes == S_OK)
7660 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7662 /* Do we have the IStorage Data in the OLESTREAM */
7663 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7665 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7666 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7668 else
7670 /* It must be an original OLE 1.0 source */
7671 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7674 else
7676 /* It must be an original OLE 1.0 source */
7677 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7680 /* Create CompObj Stream if necessary */
7681 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7682 if(hRes == S_OK)
7684 /*Create the Ole Stream if necessary */
7685 OLECONVERT_CreateOleStream(pstg);
7690 /* Free allocated memory */
7691 for(i=0; i < 2; i++)
7693 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7694 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7695 pOleStreamData[i].pstrOleObjFileName = NULL;
7697 return hRes;
7700 /*************************************************************************
7701 * OleConvertIStorageToOLESTREAM [OLE32.@]
7703 * Read info on MSDN
7705 * Read info on MSDN
7707 * TODO
7708 * Still unsure of some mem fields for OLE 10 Stream
7709 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7710 * and "\001OLE" streams.
7713 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7714 LPSTORAGE pstg,
7715 LPOLESTREAM pOleStream)
7717 int i;
7718 HRESULT hRes = S_OK;
7719 IStream *pStream;
7720 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7721 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7724 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7726 if(pstg == NULL || pOleStream == NULL)
7728 hRes = E_INVALIDARG;
7730 if(hRes == S_OK)
7732 /* Get the ProgID */
7733 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7734 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7736 if(hRes == S_OK)
7738 /* Was it originally Ole10 */
7739 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7740 if(hRes == S_OK)
7742 IStream_Release(pStream);
7743 /* Get Presentation Data for Ole10Native */
7744 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7746 else
7748 /* Get Presentation Data (OLE20) */
7749 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7752 /* Save OLESTREAM */
7753 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7754 if(hRes == S_OK)
7756 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7761 /* Free allocated memory */
7762 for(i=0; i < 2; i++)
7764 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7767 return hRes;
7770 /***********************************************************************
7771 * GetConvertStg (OLE32.@)
7773 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7774 FIXME("unimplemented stub!\n");
7775 return E_FAIL;
7778 /******************************************************************************
7779 * StgIsStorageFile [OLE32.@]
7780 * Verify if the file contains a storage object
7782 * PARAMS
7783 * fn [ I] Filename
7785 * RETURNS
7786 * S_OK if file has magic bytes as a storage object
7787 * S_FALSE if file is not storage
7789 HRESULT WINAPI
7790 StgIsStorageFile(LPCOLESTR fn)
7792 HANDLE hf;
7793 BYTE magic[8];
7794 DWORD bytes_read;
7796 TRACE("(\'%s\')\n", debugstr_w(fn));
7797 hf = CreateFileW(fn, GENERIC_READ,
7798 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7799 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7801 if (hf == INVALID_HANDLE_VALUE)
7802 return STG_E_FILENOTFOUND;
7804 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7806 WARN(" unable to read file\n");
7807 CloseHandle(hf);
7808 return S_FALSE;
7811 CloseHandle(hf);
7813 if (bytes_read != 8) {
7814 WARN(" too short\n");
7815 return S_FALSE;
7818 if (!memcmp(magic,STORAGE_magic,8)) {
7819 WARN(" -> YES\n");
7820 return S_OK;
7823 WARN(" -> Invalid header.\n");
7824 return S_FALSE;