-Wpointer-sign fixes for gcc 4.0.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blobd6e1fedcfbd77e61aad1b5f59d885dfc42bab335
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
28 #include <assert.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
34 #define COBJMACROS
35 #define NONAMELESSUNION
36 #define NONAMELESSSTRUCT
38 #include "windef.h"
39 #include "winbase.h"
40 #include "winnls.h"
41 #include "winuser.h"
42 #include "wine/unicode.h"
43 #include "wine/debug.h"
45 #include "storage32.h"
46 #include "ole2.h" /* For Write/ReadClassStm */
48 #include "winreg.h"
49 #include "wine/wingdi16.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(storage);
53 #define FILE_BEGIN 0
55 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
56 #define OLESTREAM_ID 0x501
57 #define OLESTREAM_MAX_STR_LEN 255
59 static const char rootPropertyName[] = "Root Entry";
62 /* OLESTREAM memory structure to use for Get and Put Routines */
63 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
64 typedef struct
66 DWORD dwOleID;
67 DWORD dwTypeID;
68 DWORD dwOleTypeNameLength;
69 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
70 CHAR *pstrOleObjFileName;
71 DWORD dwOleObjFileNameLength;
72 DWORD dwMetaFileWidth;
73 DWORD dwMetaFileHeight;
74 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
75 DWORD dwDataLength;
76 BYTE *pData;
77 }OLECONVERT_OLESTREAM_DATA;
79 /* CompObj Stream structure */
80 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
81 typedef struct
83 BYTE byUnknown1[12];
84 CLSID clsid;
85 DWORD dwCLSIDNameLength;
86 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
87 DWORD dwOleTypeNameLength;
88 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
89 DWORD dwProgIDNameLength;
90 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
91 BYTE byUnknown2[16];
92 }OLECONVERT_ISTORAGE_COMPOBJ;
95 /* Ole Presention Stream structure */
96 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
97 typedef struct
99 BYTE byUnknown1[28];
100 DWORD dwExtentX;
101 DWORD dwExtentY;
102 DWORD dwSize;
103 BYTE *pData;
104 }OLECONVERT_ISTORAGE_OLEPRES;
108 /***********************************************************************
109 * Forward declaration of internal functions used by the method DestroyElement
111 static HRESULT deleteStorageProperty(
112 StorageImpl *parentStorage,
113 ULONG foundPropertyIndexToDelete,
114 StgProperty propertyToDelete);
116 static HRESULT deleteStreamProperty(
117 StorageImpl *parentStorage,
118 ULONG foundPropertyIndexToDelete,
119 StgProperty propertyToDelete);
121 static HRESULT findPlaceholder(
122 StorageImpl *storage,
123 ULONG propertyIndexToStore,
124 ULONG storagePropertyIndex,
125 INT typeOfRelation);
127 static HRESULT adjustPropertyChain(
128 StorageImpl *This,
129 StgProperty propertyToDelete,
130 StgProperty parentProperty,
131 ULONG parentPropertyId,
132 INT typeOfRelation);
134 /***********************************************************************
135 * Declaration of the functions used to manipulate StgProperty
138 static ULONG getFreeProperty(
139 StorageImpl *storage);
141 static void updatePropertyChain(
142 StorageImpl *storage,
143 ULONG newPropertyIndex,
144 StgProperty newProperty);
146 static LONG propertyNameCmp(
147 const OLECHAR *newProperty,
148 const OLECHAR *currentProperty);
151 /***********************************************************************
152 * Declaration of miscellaneous functions...
154 static HRESULT validateSTGM(DWORD stgmValue);
156 static DWORD GetShareModeFromSTGM(DWORD stgm);
157 static DWORD GetAccessModeFromSTGM(DWORD stgm);
158 static DWORD GetCreationModeFromSTGM(DWORD stgm);
160 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
164 /************************************************************************
165 ** Storage32BaseImpl implementatiion
168 /************************************************************************
169 * Storage32BaseImpl_QueryInterface (IUnknown)
171 * This method implements the common QueryInterface for all IStorage32
172 * implementations contained in this file.
174 * See Windows documentation for more details on IUnknown methods.
176 HRESULT WINAPI StorageBaseImpl_QueryInterface(
177 IStorage* iface,
178 REFIID riid,
179 void** ppvObject)
181 StorageBaseImpl *This = (StorageBaseImpl *)iface;
183 * Perform a sanity check on the parameters.
185 if ( (This==0) || (ppvObject==0) )
186 return E_INVALIDARG;
189 * Initialize the return parameter.
191 *ppvObject = 0;
194 * Compare the riid with the interface IDs implemented by this object.
196 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
198 *ppvObject = (IStorage*)This;
200 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
202 *ppvObject = (IStorage*)This;
204 else if (memcmp(&IID_IPropertySetStorage, riid, sizeof(IID_IPropertySetStorage)) == 0)
206 *ppvObject = (IStorage*)&This->pssVtbl;
210 * Check that we obtained an interface.
212 if ((*ppvObject)==0)
213 return E_NOINTERFACE;
216 * Query Interface always increases the reference count by one when it is
217 * successful
219 IStorage_AddRef(iface);
221 return S_OK;
224 /************************************************************************
225 * Storage32BaseImpl_AddRef (IUnknown)
227 * This method implements the common AddRef for all IStorage32
228 * implementations contained in this file.
230 * See Windows documentation for more details on IUnknown methods.
232 ULONG WINAPI StorageBaseImpl_AddRef(
233 IStorage* iface)
235 StorageBaseImpl *This = (StorageBaseImpl *)iface;
236 ULONG ref = InterlockedIncrement(&This->ref);
238 TRACE("(%p) AddRef to %ld\n", This, ref);
240 return ref;
243 /************************************************************************
244 * Storage32BaseImpl_Release (IUnknown)
246 * This method implements the common Release for all IStorage32
247 * implementations contained in this file.
249 * See Windows documentation for more details on IUnknown methods.
251 ULONG WINAPI StorageBaseImpl_Release(
252 IStorage* iface)
254 StorageBaseImpl *This = (StorageBaseImpl *)iface;
256 * Decrease the reference count on this object.
258 ULONG ref = InterlockedDecrement(&This->ref);
260 TRACE("(%p) ReleaseRef to %ld\n", This, ref);
263 * If the reference count goes down to 0, perform suicide.
265 if (ref == 0)
268 * Since we are using a system of base-classes, we want to call the
269 * destructor of the appropriate derived class. To do this, we are
270 * using virtual functions to implement the destructor.
272 This->v_destructor(This);
275 return ref;
278 /************************************************************************
279 * Storage32BaseImpl_OpenStream (IStorage)
281 * This method will open the specified stream object from the current storage.
283 * See Windows documentation for more details on IStorage methods.
285 HRESULT WINAPI StorageBaseImpl_OpenStream(
286 IStorage* iface,
287 const OLECHAR* pwcsName, /* [string][in] */
288 void* reserved1, /* [unique][in] */
289 DWORD grfMode, /* [in] */
290 DWORD reserved2, /* [in] */
291 IStream** ppstm) /* [out] */
293 StorageBaseImpl *This = (StorageBaseImpl *)iface;
294 IEnumSTATSTGImpl* propertyEnumeration;
295 StgStreamImpl* newStream;
296 StgProperty currentProperty;
297 ULONG foundPropertyIndex;
298 HRESULT res = STG_E_UNKNOWN;
299 DWORD parent_grfMode;
301 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
302 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
305 * Perform a sanity check on the parameters.
307 if ( (pwcsName==NULL) || (ppstm==0) )
309 res = E_INVALIDARG;
310 goto end;
314 * Initialize the out parameter
316 *ppstm = NULL;
319 * Validate the STGM flags
321 if ( FAILED( validateSTGM(grfMode) ))
323 res = STG_E_INVALIDFLAG;
324 goto end;
328 * As documented.
330 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
331 (grfMode & STGM_DELETEONRELEASE) ||
332 (grfMode & STGM_TRANSACTED) )
334 res = STG_E_INVALIDFUNCTION;
335 goto end;
339 * Check that we're compatible with the parent's storage mode
341 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
342 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
344 res = STG_E_ACCESSDENIED;
345 goto end;
349 * Create a property enumeration to search the properties
351 propertyEnumeration = IEnumSTATSTGImpl_Construct(
352 This->ancestorStorage,
353 This->rootPropertySetIndex);
356 * Search the enumeration for the property with the given name
358 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
359 propertyEnumeration,
360 pwcsName,
361 &currentProperty);
364 * Delete the property enumeration since we don't need it anymore
366 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
369 * If it was found, construct the stream object and return a pointer to it.
371 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
372 (currentProperty.propertyType==PROPTYPE_STREAM) )
374 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
376 if (newStream!=0)
378 newStream->grfMode = grfMode;
379 *ppstm = (IStream*)newStream;
382 * Since we are returning a pointer to the interface, we have to
383 * nail down the reference.
385 IStream_AddRef(*ppstm);
387 res = S_OK;
388 goto end;
391 res = E_OUTOFMEMORY;
392 goto end;
395 res = STG_E_FILENOTFOUND;
397 end:
398 if (res == S_OK)
399 TRACE("<-- IStream %p\n", *ppstm);
400 TRACE("<-- %08lx\n", res);
401 return res;
404 /************************************************************************
405 * Storage32BaseImpl_OpenStorage (IStorage)
407 * This method will open a new storage object from the current storage.
409 * See Windows documentation for more details on IStorage methods.
411 HRESULT WINAPI StorageBaseImpl_OpenStorage(
412 IStorage* iface,
413 const OLECHAR* pwcsName, /* [string][unique][in] */
414 IStorage* pstgPriority, /* [unique][in] */
415 DWORD grfMode, /* [in] */
416 SNB snbExclude, /* [unique][in] */
417 DWORD reserved, /* [in] */
418 IStorage** ppstg) /* [out] */
420 StorageBaseImpl *This = (StorageBaseImpl *)iface;
421 StorageInternalImpl* newStorage;
422 IEnumSTATSTGImpl* propertyEnumeration;
423 StgProperty currentProperty;
424 ULONG foundPropertyIndex;
425 HRESULT res = STG_E_UNKNOWN;
426 DWORD parent_grfMode;
428 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
429 iface, debugstr_w(pwcsName), pstgPriority,
430 grfMode, snbExclude, reserved, ppstg);
433 * Perform a sanity check on the parameters.
435 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
437 res = E_INVALIDARG;
438 goto end;
441 /* as documented */
442 if (snbExclude != NULL)
444 res = STG_E_INVALIDPARAMETER;
445 goto end;
449 * Validate the STGM flags
451 if ( FAILED( validateSTGM(grfMode) ))
453 res = STG_E_INVALIDFLAG;
454 goto end;
458 * As documented.
460 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
461 (grfMode & STGM_DELETEONRELEASE) ||
462 (grfMode & STGM_PRIORITY) )
464 res = STG_E_INVALIDFUNCTION;
465 goto end;
469 * Check that we're compatible with the parent's storage mode
471 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
472 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
474 res = STG_E_ACCESSDENIED;
475 goto end;
479 * Initialize the out parameter
481 *ppstg = NULL;
484 * Create a property enumeration to search the properties
486 propertyEnumeration = IEnumSTATSTGImpl_Construct(
487 This->ancestorStorage,
488 This->rootPropertySetIndex);
491 * Search the enumeration for the property with the given name
493 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
494 propertyEnumeration,
495 pwcsName,
496 &currentProperty);
499 * Delete the property enumeration since we don't need it anymore
501 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
504 * If it was found, construct the stream object and return a pointer to it.
506 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
507 (currentProperty.propertyType==PROPTYPE_STORAGE) )
510 * Construct a new Storage object
512 newStorage = StorageInternalImpl_Construct(
513 This->ancestorStorage,
514 grfMode,
515 foundPropertyIndex);
517 if (newStorage != 0)
519 *ppstg = (IStorage*)newStorage;
522 * Since we are returning a pointer to the interface,
523 * we have to nail down the reference.
525 StorageBaseImpl_AddRef(*ppstg);
527 res = S_OK;
528 goto end;
531 res = STG_E_INSUFFICIENTMEMORY;
532 goto end;
535 res = STG_E_FILENOTFOUND;
537 end:
538 TRACE("<-- %08lx\n", res);
539 return res;
542 /************************************************************************
543 * Storage32BaseImpl_EnumElements (IStorage)
545 * This method will create an enumerator object that can be used to
546 * retrieve informatino about all the properties in the storage object.
548 * See Windows documentation for more details on IStorage methods.
550 HRESULT WINAPI StorageBaseImpl_EnumElements(
551 IStorage* iface,
552 DWORD reserved1, /* [in] */
553 void* reserved2, /* [size_is][unique][in] */
554 DWORD reserved3, /* [in] */
555 IEnumSTATSTG** ppenum) /* [out] */
557 StorageBaseImpl *This = (StorageBaseImpl *)iface;
558 IEnumSTATSTGImpl* newEnum;
560 TRACE("(%p, %ld, %p, %ld, %p)\n",
561 iface, reserved1, reserved2, reserved3, ppenum);
564 * Perform a sanity check on the parameters.
566 if ( (This==0) || (ppenum==0))
567 return E_INVALIDARG;
570 * Construct the enumerator.
572 newEnum = IEnumSTATSTGImpl_Construct(
573 This->ancestorStorage,
574 This->rootPropertySetIndex);
576 if (newEnum!=0)
578 *ppenum = (IEnumSTATSTG*)newEnum;
581 * Don't forget to nail down a reference to the new object before
582 * returning it.
584 IEnumSTATSTG_AddRef(*ppenum);
586 return S_OK;
589 return E_OUTOFMEMORY;
592 /************************************************************************
593 * Storage32BaseImpl_Stat (IStorage)
595 * This method will retrieve information about this storage object.
597 * See Windows documentation for more details on IStorage methods.
599 HRESULT WINAPI StorageBaseImpl_Stat(
600 IStorage* iface,
601 STATSTG* pstatstg, /* [out] */
602 DWORD grfStatFlag) /* [in] */
604 StorageBaseImpl *This = (StorageBaseImpl *)iface;
605 StgProperty curProperty;
606 BOOL readSuccessful;
607 HRESULT res = STG_E_UNKNOWN;
609 TRACE("(%p, %p, %lx)\n",
610 iface, pstatstg, grfStatFlag);
613 * Perform a sanity check on the parameters.
615 if ( (This==0) || (pstatstg==0))
617 res = E_INVALIDARG;
618 goto end;
622 * Read the information from the property.
624 readSuccessful = StorageImpl_ReadProperty(
625 This->ancestorStorage,
626 This->rootPropertySetIndex,
627 &curProperty);
629 if (readSuccessful)
631 StorageUtl_CopyPropertyToSTATSTG(
632 pstatstg,
633 &curProperty,
634 grfStatFlag);
636 res = S_OK;
637 goto end;
640 res = E_FAIL;
642 end:
643 if (res == S_OK)
645 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);
647 TRACE("<-- %08lx\n", res);
648 return res;
651 /************************************************************************
652 * Storage32BaseImpl_RenameElement (IStorage)
654 * This method will rename the specified element.
656 * See Windows documentation for more details on IStorage methods.
658 * Implementation notes: The method used to rename consists of creating a clone
659 * of the deleted StgProperty object setting it with the new name and to
660 * perform a DestroyElement of the old StgProperty.
662 HRESULT WINAPI StorageBaseImpl_RenameElement(
663 IStorage* iface,
664 const OLECHAR* pwcsOldName, /* [in] */
665 const OLECHAR* pwcsNewName) /* [in] */
667 StorageBaseImpl *This = (StorageBaseImpl *)iface;
668 IEnumSTATSTGImpl* propertyEnumeration;
669 StgProperty currentProperty;
670 ULONG foundPropertyIndex;
672 TRACE("(%p, %s, %s)\n",
673 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
676 * Create a property enumeration to search the properties
678 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
679 This->rootPropertySetIndex);
682 * Search the enumeration for the new property name
684 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
685 pwcsNewName,
686 &currentProperty);
688 if (foundPropertyIndex != PROPERTY_NULL)
691 * There is already a property with the new name
693 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
694 return STG_E_FILEALREADYEXISTS;
697 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
700 * Search the enumeration for the old property name
702 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
703 pwcsOldName,
704 &currentProperty);
707 * Delete the property enumeration since we don't need it anymore
709 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
711 if (foundPropertyIndex != PROPERTY_NULL)
713 StgProperty renamedProperty;
714 ULONG renamedPropertyIndex;
717 * Setup a new property for the renamed property
719 renamedProperty.sizeOfNameString =
720 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
722 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
723 return STG_E_INVALIDNAME;
725 strcpyW(renamedProperty.name, pwcsNewName);
727 renamedProperty.propertyType = currentProperty.propertyType;
728 renamedProperty.startingBlock = currentProperty.startingBlock;
729 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
730 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
732 renamedProperty.previousProperty = PROPERTY_NULL;
733 renamedProperty.nextProperty = PROPERTY_NULL;
736 * Bring the dirProperty link in case it is a storage and in which
737 * case the renamed storage elements don't require to be reorganized.
739 renamedProperty.dirProperty = currentProperty.dirProperty;
741 /* call CoFileTime to get the current time
742 renamedProperty.timeStampS1
743 renamedProperty.timeStampD1
744 renamedProperty.timeStampS2
745 renamedProperty.timeStampD2
746 renamedProperty.propertyUniqueID
750 * Obtain a free property in the property chain
752 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
755 * Save the new property into the new property spot
757 StorageImpl_WriteProperty(
758 This->ancestorStorage,
759 renamedPropertyIndex,
760 &renamedProperty);
763 * Find a spot in the property chain for our newly created property.
765 updatePropertyChain(
766 (StorageImpl*)This,
767 renamedPropertyIndex,
768 renamedProperty);
771 * At this point the renamed property has been inserted in the tree,
772 * now, before to Destroy the old property we must zeroed it's dirProperty
773 * otherwise the DestroyProperty below will zap it all and we do not want
774 * this to happen.
775 * Also, we fake that the old property is a storage so the DestroyProperty
776 * will not do a SetSize(0) on the stream data.
778 * This means that we need to tweek the StgProperty if it is a stream or a
779 * non empty storage.
781 StorageImpl_ReadProperty(This->ancestorStorage,
782 foundPropertyIndex,
783 &currentProperty);
785 currentProperty.dirProperty = PROPERTY_NULL;
786 currentProperty.propertyType = PROPTYPE_STORAGE;
787 StorageImpl_WriteProperty(
788 This->ancestorStorage,
789 foundPropertyIndex,
790 &currentProperty);
793 * Invoke Destroy to get rid of the ole property and automatically redo
794 * the linking of it's previous and next members...
796 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
799 else
802 * There is no property with the old name
804 return STG_E_FILENOTFOUND;
807 return S_OK;
810 /************************************************************************
811 * Storage32BaseImpl_CreateStream (IStorage)
813 * This method will create a stream object within this storage
815 * See Windows documentation for more details on IStorage methods.
817 HRESULT WINAPI StorageBaseImpl_CreateStream(
818 IStorage* iface,
819 const OLECHAR* pwcsName, /* [string][in] */
820 DWORD grfMode, /* [in] */
821 DWORD reserved1, /* [in] */
822 DWORD reserved2, /* [in] */
823 IStream** ppstm) /* [out] */
825 StorageBaseImpl *This = (StorageBaseImpl *)iface;
826 IEnumSTATSTGImpl* propertyEnumeration;
827 StgStreamImpl* newStream;
828 StgProperty currentProperty, newStreamProperty;
829 ULONG foundPropertyIndex, newPropertyIndex;
830 DWORD parent_grfMode;
832 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
833 iface, debugstr_w(pwcsName), grfMode,
834 reserved1, reserved2, ppstm);
837 * Validate parameters
839 if (ppstm == 0)
840 return STG_E_INVALIDPOINTER;
842 if (pwcsName == 0)
843 return STG_E_INVALIDNAME;
845 if (reserved1 || reserved2)
846 return STG_E_INVALIDPARAMETER;
849 * Validate the STGM flags
851 if ( FAILED( validateSTGM(grfMode) ))
852 return STG_E_INVALIDFLAG;
854 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
855 return STG_E_INVALIDFLAG;
858 * As documented.
860 if ((grfMode & STGM_DELETEONRELEASE) ||
861 (grfMode & STGM_TRANSACTED))
862 return STG_E_INVALIDFUNCTION;
865 * Check that we're compatible with the parent's storage mode
867 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
868 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
869 return STG_E_ACCESSDENIED;
872 * Initialize the out parameter
874 *ppstm = 0;
877 * Create a property enumeration to search the properties
879 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
880 This->rootPropertySetIndex);
882 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
883 pwcsName,
884 &currentProperty);
886 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
888 if (foundPropertyIndex != PROPERTY_NULL)
891 * An element with this name already exists
893 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
895 IStorage_DestroyElement(iface, pwcsName);
897 else
898 return STG_E_FILEALREADYEXISTS;
902 * memset the empty property
904 memset(&newStreamProperty, 0, sizeof(StgProperty));
906 newStreamProperty.sizeOfNameString =
907 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
909 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
910 return STG_E_INVALIDNAME;
912 strcpyW(newStreamProperty.name, pwcsName);
914 newStreamProperty.propertyType = PROPTYPE_STREAM;
915 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
916 newStreamProperty.size.u.LowPart = 0;
917 newStreamProperty.size.u.HighPart = 0;
919 newStreamProperty.previousProperty = PROPERTY_NULL;
920 newStreamProperty.nextProperty = PROPERTY_NULL;
921 newStreamProperty.dirProperty = PROPERTY_NULL;
923 /* call CoFileTime to get the current time
924 newStreamProperty.timeStampS1
925 newStreamProperty.timeStampD1
926 newStreamProperty.timeStampS2
927 newStreamProperty.timeStampD2
930 /* newStreamProperty.propertyUniqueID */
933 * Get a free property or create a new one
935 newPropertyIndex = getFreeProperty(This->ancestorStorage);
938 * Save the new property into the new property spot
940 StorageImpl_WriteProperty(
941 This->ancestorStorage,
942 newPropertyIndex,
943 &newStreamProperty);
946 * Find a spot in the property chain for our newly created property.
948 updatePropertyChain(
949 (StorageImpl*)This,
950 newPropertyIndex,
951 newStreamProperty);
954 * Open the stream to return it.
956 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
958 if (newStream != 0)
960 *ppstm = (IStream*)newStream;
963 * Since we are returning a pointer to the interface, we have to nail down
964 * the reference.
966 IStream_AddRef(*ppstm);
968 else
970 return STG_E_INSUFFICIENTMEMORY;
973 return S_OK;
976 /************************************************************************
977 * Storage32BaseImpl_SetClass (IStorage)
979 * This method will write the specified CLSID in the property of this
980 * storage.
982 * See Windows documentation for more details on IStorage methods.
984 HRESULT WINAPI StorageBaseImpl_SetClass(
985 IStorage* iface,
986 REFCLSID clsid) /* [in] */
988 StorageBaseImpl *This = (StorageBaseImpl *)iface;
989 HRESULT hRes = E_FAIL;
990 StgProperty curProperty;
991 BOOL success;
993 TRACE("(%p, %p)\n", iface, clsid);
995 success = StorageImpl_ReadProperty(This->ancestorStorage,
996 This->rootPropertySetIndex,
997 &curProperty);
998 if (success)
1000 curProperty.propertyUniqueID = *clsid;
1002 success = StorageImpl_WriteProperty(This->ancestorStorage,
1003 This->rootPropertySetIndex,
1004 &curProperty);
1005 if (success)
1006 hRes = S_OK;
1009 return hRes;
1012 /************************************************************************
1013 ** Storage32Impl implementation
1016 /************************************************************************
1017 * Storage32Impl_CreateStorage (IStorage)
1019 * This method will create the storage object within the provided storage.
1021 * See Windows documentation for more details on IStorage methods.
1023 HRESULT WINAPI StorageImpl_CreateStorage(
1024 IStorage* iface,
1025 const OLECHAR *pwcsName, /* [string][in] */
1026 DWORD grfMode, /* [in] */
1027 DWORD reserved1, /* [in] */
1028 DWORD reserved2, /* [in] */
1029 IStorage **ppstg) /* [out] */
1031 StorageImpl* const This=(StorageImpl*)iface;
1033 IEnumSTATSTGImpl *propertyEnumeration;
1034 StgProperty currentProperty;
1035 StgProperty newProperty;
1036 ULONG foundPropertyIndex;
1037 ULONG newPropertyIndex;
1038 HRESULT hr;
1039 DWORD parent_grfMode;
1041 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1042 iface, debugstr_w(pwcsName), grfMode,
1043 reserved1, reserved2, ppstg);
1046 * Validate parameters
1048 if (ppstg == 0)
1049 return STG_E_INVALIDPOINTER;
1051 if (pwcsName == 0)
1052 return STG_E_INVALIDNAME;
1055 * Validate the STGM flags
1057 if ( FAILED( validateSTGM(grfMode) ) ||
1058 (grfMode & STGM_DELETEONRELEASE) )
1059 return STG_E_INVALIDFLAG;
1062 * Check that we're compatible with the parent's storage mode
1064 parent_grfMode = STGM_ACCESS_MODE( This->base.ancestorStorage->base.openFlags );
1065 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
1066 return STG_E_ACCESSDENIED;
1069 * Initialize the out parameter
1071 *ppstg = 0;
1074 * Create a property enumeration and search the properties
1076 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1077 This->base.rootPropertySetIndex);
1079 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1080 pwcsName,
1081 &currentProperty);
1082 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1084 if (foundPropertyIndex != PROPERTY_NULL)
1087 * An element with this name already exists
1089 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1090 IStorage_DestroyElement(iface, pwcsName);
1091 else
1092 return STG_E_FILEALREADYEXISTS;
1096 * memset the empty property
1098 memset(&newProperty, 0, sizeof(StgProperty));
1100 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1102 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1103 return STG_E_INVALIDNAME;
1105 strcpyW(newProperty.name, pwcsName);
1107 newProperty.propertyType = PROPTYPE_STORAGE;
1108 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1109 newProperty.size.u.LowPart = 0;
1110 newProperty.size.u.HighPart = 0;
1112 newProperty.previousProperty = PROPERTY_NULL;
1113 newProperty.nextProperty = PROPERTY_NULL;
1114 newProperty.dirProperty = PROPERTY_NULL;
1116 /* call CoFileTime to get the current time
1117 newProperty.timeStampS1
1118 newProperty.timeStampD1
1119 newProperty.timeStampS2
1120 newProperty.timeStampD2
1123 /* newStorageProperty.propertyUniqueID */
1126 * Obtain a free property in the property chain
1128 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1131 * Save the new property into the new property spot
1133 StorageImpl_WriteProperty(
1134 This->base.ancestorStorage,
1135 newPropertyIndex,
1136 &newProperty);
1139 * Find a spot in the property chain for our newly created property.
1141 updatePropertyChain(
1142 This,
1143 newPropertyIndex,
1144 newProperty);
1147 * Open it to get a pointer to return.
1149 hr = IStorage_OpenStorage(
1150 iface,
1151 (const OLECHAR*)pwcsName,
1153 grfMode,
1156 ppstg);
1158 if( (hr != S_OK) || (*ppstg == NULL))
1160 return hr;
1164 return S_OK;
1168 /***************************************************************************
1170 * Internal Method
1172 * Get a free property or create a new one.
1174 static ULONG getFreeProperty(
1175 StorageImpl *storage)
1177 ULONG currentPropertyIndex = 0;
1178 ULONG newPropertyIndex = PROPERTY_NULL;
1179 BOOL readSuccessful = TRUE;
1180 StgProperty currentProperty;
1185 * Start by reading the root property
1187 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1188 currentPropertyIndex,
1189 &currentProperty);
1190 if (readSuccessful)
1192 if (currentProperty.sizeOfNameString == 0)
1195 * The property existis and is available, we found it.
1197 newPropertyIndex = currentPropertyIndex;
1200 else
1203 * We exhausted the property list, we will create more space below
1205 newPropertyIndex = currentPropertyIndex;
1207 currentPropertyIndex++;
1209 } while (newPropertyIndex == PROPERTY_NULL);
1212 * grow the property chain
1214 if (! readSuccessful)
1216 StgProperty emptyProperty;
1217 ULARGE_INTEGER newSize;
1218 ULONG propertyIndex;
1219 ULONG lastProperty = 0;
1220 ULONG blockCount = 0;
1223 * obtain the new count of property blocks
1225 blockCount = BlockChainStream_GetCount(
1226 storage->base.ancestorStorage->rootBlockChain)+1;
1229 * initialize the size used by the property stream
1231 newSize.u.HighPart = 0;
1232 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1235 * add a property block to the property chain
1237 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1240 * memset the empty property in order to initialize the unused newly
1241 * created property
1243 memset(&emptyProperty, 0, sizeof(StgProperty));
1246 * initialize them
1248 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1250 for(
1251 propertyIndex = newPropertyIndex;
1252 propertyIndex < lastProperty;
1253 propertyIndex++)
1255 StorageImpl_WriteProperty(
1256 storage->base.ancestorStorage,
1257 propertyIndex,
1258 &emptyProperty);
1262 return newPropertyIndex;
1265 /****************************************************************************
1267 * Internal Method
1269 * Case insensitive comparaison of StgProperty.name by first considering
1270 * their size.
1272 * Returns <0 when newPrpoerty < currentProperty
1273 * >0 when newPrpoerty > currentProperty
1274 * 0 when newPrpoerty == currentProperty
1276 static LONG propertyNameCmp(
1277 const OLECHAR *newProperty,
1278 const OLECHAR *currentProperty)
1280 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1282 if (diff == 0)
1285 * We compare the string themselves only when they are of the same length
1287 diff = lstrcmpiW( newProperty, currentProperty);
1290 return diff;
1293 /****************************************************************************
1295 * Internal Method
1297 * Properly link this new element in the property chain.
1299 static void updatePropertyChain(
1300 StorageImpl *storage,
1301 ULONG newPropertyIndex,
1302 StgProperty newProperty)
1304 StgProperty currentProperty;
1307 * Read the root property
1309 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1310 storage->base.rootPropertySetIndex,
1311 &currentProperty);
1313 if (currentProperty.dirProperty != PROPERTY_NULL)
1316 * The root storage contains some element, therefore, start the research
1317 * for the appropriate location.
1319 BOOL found = 0;
1320 ULONG current, next, previous, currentPropertyId;
1323 * Keep the StgProperty sequence number of the storage first property
1325 currentPropertyId = currentProperty.dirProperty;
1328 * Read
1330 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1331 currentProperty.dirProperty,
1332 &currentProperty);
1334 previous = currentProperty.previousProperty;
1335 next = currentProperty.nextProperty;
1336 current = currentPropertyId;
1338 while (found == 0)
1340 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1342 if (diff < 0)
1344 if (previous != PROPERTY_NULL)
1346 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1347 previous,
1348 &currentProperty);
1349 current = previous;
1351 else
1353 currentProperty.previousProperty = newPropertyIndex;
1354 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1355 current,
1356 &currentProperty);
1357 found = 1;
1360 else if (diff > 0)
1362 if (next != PROPERTY_NULL)
1364 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1365 next,
1366 &currentProperty);
1367 current = next;
1369 else
1371 currentProperty.nextProperty = newPropertyIndex;
1372 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1373 current,
1374 &currentProperty);
1375 found = 1;
1378 else
1381 * Trying to insert an item with the same name in the
1382 * subtree structure.
1384 assert(FALSE);
1387 previous = currentProperty.previousProperty;
1388 next = currentProperty.nextProperty;
1391 else
1394 * The root storage is empty, link the new property to it's dir property
1396 currentProperty.dirProperty = newPropertyIndex;
1397 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1398 storage->base.rootPropertySetIndex,
1399 &currentProperty);
1404 /*************************************************************************
1405 * CopyTo (IStorage)
1407 HRESULT WINAPI StorageImpl_CopyTo(
1408 IStorage* iface,
1409 DWORD ciidExclude, /* [in] */
1410 const IID* rgiidExclude, /* [size_is][unique][in] */
1411 SNB snbExclude, /* [unique][in] */
1412 IStorage* pstgDest) /* [unique][in] */
1414 IEnumSTATSTG *elements = 0;
1415 STATSTG curElement, strStat;
1416 HRESULT hr;
1417 IStorage *pstgTmp, *pstgChild;
1418 IStream *pstrTmp, *pstrChild;
1420 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1421 FIXME("Exclude option not implemented\n");
1423 TRACE("(%p, %ld, %p, %p, %p)\n",
1424 iface, ciidExclude, rgiidExclude,
1425 snbExclude, pstgDest);
1428 * Perform a sanity check
1430 if ( pstgDest == 0 )
1431 return STG_E_INVALIDPOINTER;
1434 * Enumerate the elements
1436 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1438 if ( hr != S_OK )
1439 return hr;
1442 * set the class ID
1444 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1445 IStorage_SetClass( pstgDest, &curElement.clsid );
1450 * Obtain the next element
1452 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1454 if ( hr == S_FALSE )
1456 hr = S_OK; /* done, every element has been copied */
1457 break;
1460 if (curElement.type == STGTY_STORAGE)
1463 * open child source storage
1465 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1466 STGM_READ|STGM_SHARE_EXCLUSIVE,
1467 NULL, 0, &pstgChild );
1469 if (hr != S_OK)
1470 break;
1473 * Check if destination storage is not a child of the source
1474 * storage, which will cause an infinite loop
1476 if (pstgChild == pstgDest)
1478 IEnumSTATSTG_Release(elements);
1480 return STG_E_ACCESSDENIED;
1484 * create a new storage in destination storage
1486 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1487 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1488 0, 0,
1489 &pstgTmp );
1491 * if it already exist, don't create a new one use this one
1493 if (hr == STG_E_FILEALREADYEXISTS)
1495 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1496 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1497 NULL, 0, &pstgTmp );
1500 if (hr != S_OK)
1501 break;
1505 * do the copy recursively
1507 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1508 snbExclude, pstgTmp );
1510 IStorage_Release( pstgTmp );
1511 IStorage_Release( pstgChild );
1513 else if (curElement.type == STGTY_STREAM)
1516 * create a new stream in destination storage. If the stream already
1517 * exist, it will be deleted and a new one will be created.
1519 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1520 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1521 0, 0, &pstrTmp );
1523 if (hr != S_OK)
1524 break;
1527 * open child stream storage
1529 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1530 STGM_READ|STGM_SHARE_EXCLUSIVE,
1531 0, &pstrChild );
1533 if (hr != S_OK)
1534 break;
1537 * Get the size of the source stream
1539 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1542 * Set the size of the destination stream.
1544 IStream_SetSize(pstrTmp, strStat.cbSize);
1547 * do the copy
1549 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1550 NULL, NULL );
1552 IStream_Release( pstrTmp );
1553 IStream_Release( pstrChild );
1555 else
1557 WARN("unknown element type: %ld\n", curElement.type);
1560 } while (hr == S_OK);
1563 * Clean-up
1565 IEnumSTATSTG_Release(elements);
1567 return hr;
1570 /*************************************************************************
1571 * MoveElementTo (IStorage)
1573 HRESULT WINAPI StorageImpl_MoveElementTo(
1574 IStorage* iface,
1575 const OLECHAR *pwcsName, /* [string][in] */
1576 IStorage *pstgDest, /* [unique][in] */
1577 const OLECHAR *pwcsNewName,/* [string][in] */
1578 DWORD grfFlags) /* [in] */
1580 FIXME("not implemented!\n");
1581 return E_NOTIMPL;
1584 /*************************************************************************
1585 * Commit (IStorage)
1587 HRESULT WINAPI StorageImpl_Commit(
1588 IStorage* iface,
1589 DWORD grfCommitFlags)/* [in] */
1591 FIXME("(%ld): stub!\n", grfCommitFlags);
1592 return S_OK;
1595 /*************************************************************************
1596 * Revert (IStorage)
1598 HRESULT WINAPI StorageImpl_Revert(
1599 IStorage* iface)
1601 FIXME("not implemented!\n");
1602 return E_NOTIMPL;
1605 /*************************************************************************
1606 * DestroyElement (IStorage)
1608 * Stategy: This implementation is build this way for simplicity not for speed.
1609 * I always delete the top most element of the enumeration and adjust
1610 * the deleted element pointer all the time. This takes longer to
1611 * do but allow to reinvoke DestroyElement whenever we encounter a
1612 * storage object. The optimisation reside in the usage of another
1613 * enumeration stategy that would give all the leaves of a storage
1614 * first. (postfix order)
1616 HRESULT WINAPI StorageImpl_DestroyElement(
1617 IStorage* iface,
1618 const OLECHAR *pwcsName)/* [string][in] */
1620 StorageImpl* const This=(StorageImpl*)iface;
1622 IEnumSTATSTGImpl* propertyEnumeration;
1623 HRESULT hr = S_OK;
1624 BOOL res;
1625 StgProperty propertyToDelete;
1626 StgProperty parentProperty;
1627 ULONG foundPropertyIndexToDelete;
1628 ULONG typeOfRelation;
1629 ULONG parentPropertyId;
1631 TRACE("(%p, %s)\n",
1632 iface, debugstr_w(pwcsName));
1635 * Perform a sanity check on the parameters.
1637 if (pwcsName==NULL)
1638 return STG_E_INVALIDPOINTER;
1641 * Create a property enumeration to search the property with the given name
1643 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1644 This->base.ancestorStorage,
1645 This->base.rootPropertySetIndex);
1647 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1648 propertyEnumeration,
1649 pwcsName,
1650 &propertyToDelete);
1652 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1654 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1656 return STG_E_FILENOTFOUND;
1660 * Find the parent property of the property to delete (the one that
1661 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1662 * the parent is This. Otherwise, the parent is one of it's sibling...
1666 * First, read This's StgProperty..
1668 res = StorageImpl_ReadProperty(
1669 This->base.ancestorStorage,
1670 This->base.rootPropertySetIndex,
1671 &parentProperty);
1673 assert(res);
1676 * Second, check to see if by any chance the actual storage (This) is not
1677 * the parent of the property to delete... We never know...
1679 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1682 * Set data as it would have been done in the else part...
1684 typeOfRelation = PROPERTY_RELATION_DIR;
1685 parentPropertyId = This->base.rootPropertySetIndex;
1687 else
1690 * Create a property enumeration to search the parent properties, and
1691 * delete it once done.
1693 IEnumSTATSTGImpl* propertyEnumeration2;
1695 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1696 This->base.ancestorStorage,
1697 This->base.rootPropertySetIndex);
1699 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1700 propertyEnumeration2,
1701 foundPropertyIndexToDelete,
1702 &parentProperty,
1703 &parentPropertyId);
1705 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1708 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1710 hr = deleteStorageProperty(
1711 This,
1712 foundPropertyIndexToDelete,
1713 propertyToDelete);
1715 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1717 hr = deleteStreamProperty(
1718 This,
1719 foundPropertyIndexToDelete,
1720 propertyToDelete);
1723 if (hr!=S_OK)
1724 return hr;
1727 * Adjust the property chain
1729 hr = adjustPropertyChain(
1730 This,
1731 propertyToDelete,
1732 parentProperty,
1733 parentPropertyId,
1734 typeOfRelation);
1736 return hr;
1740 /************************************************************************
1741 * StorageImpl_Stat (IStorage)
1743 * This method will retrieve information about this storage object.
1745 * See Windows documentation for more details on IStorage methods.
1747 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1748 STATSTG* pstatstg, /* [out] */
1749 DWORD grfStatFlag) /* [in] */
1751 StorageImpl* const This = (StorageImpl*)iface;
1752 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1754 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1756 CoTaskMemFree(pstatstg->pwcsName);
1757 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1758 strcpyW(pstatstg->pwcsName, This->pwcsName);
1761 return result;
1766 /*********************************************************************
1768 * Internal Method
1770 * Perform the deletion of a complete storage node
1773 static HRESULT deleteStorageProperty(
1774 StorageImpl *parentStorage,
1775 ULONG indexOfPropertyToDelete,
1776 StgProperty propertyToDelete)
1778 IEnumSTATSTG *elements = 0;
1779 IStorage *childStorage = 0;
1780 STATSTG currentElement;
1781 HRESULT hr;
1782 HRESULT destroyHr = S_OK;
1785 * Open the storage and enumerate it
1787 hr = StorageBaseImpl_OpenStorage(
1788 (IStorage*)parentStorage,
1789 propertyToDelete.name,
1791 STGM_SHARE_EXCLUSIVE,
1794 &childStorage);
1796 if (hr != S_OK)
1798 return hr;
1802 * Enumerate the elements
1804 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1809 * Obtain the next element
1811 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1812 if (hr==S_OK)
1814 destroyHr = StorageImpl_DestroyElement(
1815 (IStorage*)childStorage,
1816 (OLECHAR*)currentElement.pwcsName);
1818 CoTaskMemFree(currentElement.pwcsName);
1822 * We need to Reset the enumeration every time because we delete elements
1823 * and the enumeration could be invalid
1825 IEnumSTATSTG_Reset(elements);
1827 } while ((hr == S_OK) && (destroyHr == S_OK));
1830 * Invalidate the property by zeroing it's name member.
1832 propertyToDelete.sizeOfNameString = 0;
1834 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1835 indexOfPropertyToDelete,
1836 &propertyToDelete);
1838 IStorage_Release(childStorage);
1839 IEnumSTATSTG_Release(elements);
1841 return destroyHr;
1844 /*********************************************************************
1846 * Internal Method
1848 * Perform the deletion of a stream node
1851 static HRESULT deleteStreamProperty(
1852 StorageImpl *parentStorage,
1853 ULONG indexOfPropertyToDelete,
1854 StgProperty propertyToDelete)
1856 IStream *pis;
1857 HRESULT hr;
1858 ULARGE_INTEGER size;
1860 size.u.HighPart = 0;
1861 size.u.LowPart = 0;
1863 hr = StorageBaseImpl_OpenStream(
1864 (IStorage*)parentStorage,
1865 (OLECHAR*)propertyToDelete.name,
1866 NULL,
1867 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1869 &pis);
1871 if (hr!=S_OK)
1873 return(hr);
1877 * Zap the stream
1879 hr = IStream_SetSize(pis, size);
1881 if(hr != S_OK)
1883 return hr;
1887 * Release the stream object.
1889 IStream_Release(pis);
1892 * Invalidate the property by zeroing it's name member.
1894 propertyToDelete.sizeOfNameString = 0;
1897 * Here we should re-read the property so we get the updated pointer
1898 * but since we are here to zap it, I don't do it...
1900 StorageImpl_WriteProperty(
1901 parentStorage->base.ancestorStorage,
1902 indexOfPropertyToDelete,
1903 &propertyToDelete);
1905 return S_OK;
1908 /*********************************************************************
1910 * Internal Method
1912 * Finds a placeholder for the StgProperty within the Storage
1915 static HRESULT findPlaceholder(
1916 StorageImpl *storage,
1917 ULONG propertyIndexToStore,
1918 ULONG storePropertyIndex,
1919 INT typeOfRelation)
1921 StgProperty storeProperty;
1922 HRESULT hr = S_OK;
1923 BOOL res = TRUE;
1926 * Read the storage property
1928 res = StorageImpl_ReadProperty(
1929 storage->base.ancestorStorage,
1930 storePropertyIndex,
1931 &storeProperty);
1933 if(! res)
1935 return E_FAIL;
1938 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1940 if (storeProperty.previousProperty != PROPERTY_NULL)
1942 return findPlaceholder(
1943 storage,
1944 propertyIndexToStore,
1945 storeProperty.previousProperty,
1946 typeOfRelation);
1948 else
1950 storeProperty.previousProperty = propertyIndexToStore;
1953 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1955 if (storeProperty.nextProperty != PROPERTY_NULL)
1957 return findPlaceholder(
1958 storage,
1959 propertyIndexToStore,
1960 storeProperty.nextProperty,
1961 typeOfRelation);
1963 else
1965 storeProperty.nextProperty = propertyIndexToStore;
1968 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1970 if (storeProperty.dirProperty != PROPERTY_NULL)
1972 return findPlaceholder(
1973 storage,
1974 propertyIndexToStore,
1975 storeProperty.dirProperty,
1976 typeOfRelation);
1978 else
1980 storeProperty.dirProperty = propertyIndexToStore;
1984 hr = StorageImpl_WriteProperty(
1985 storage->base.ancestorStorage,
1986 storePropertyIndex,
1987 &storeProperty);
1989 if(! hr)
1991 return E_FAIL;
1994 return S_OK;
1997 /*************************************************************************
1999 * Internal Method
2001 * This method takes the previous and the next property link of a property
2002 * to be deleted and find them a place in the Storage.
2004 static HRESULT adjustPropertyChain(
2005 StorageImpl *This,
2006 StgProperty propertyToDelete,
2007 StgProperty parentProperty,
2008 ULONG parentPropertyId,
2009 INT typeOfRelation)
2011 ULONG newLinkProperty = PROPERTY_NULL;
2012 BOOL needToFindAPlaceholder = FALSE;
2013 ULONG storeNode = PROPERTY_NULL;
2014 ULONG toStoreNode = PROPERTY_NULL;
2015 INT relationType = 0;
2016 HRESULT hr = S_OK;
2017 BOOL res = TRUE;
2019 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2021 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2024 * Set the parent previous to the property to delete previous
2026 newLinkProperty = propertyToDelete.previousProperty;
2028 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2031 * We also need to find a storage for the other link, setup variables
2032 * to do this at the end...
2034 needToFindAPlaceholder = TRUE;
2035 storeNode = propertyToDelete.previousProperty;
2036 toStoreNode = propertyToDelete.nextProperty;
2037 relationType = PROPERTY_RELATION_NEXT;
2040 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2043 * Set the parent previous to the property to delete next
2045 newLinkProperty = propertyToDelete.nextProperty;
2049 * Link it for real...
2051 parentProperty.previousProperty = newLinkProperty;
2054 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2056 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2059 * Set the parent next to the property to delete next previous
2061 newLinkProperty = propertyToDelete.previousProperty;
2063 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2066 * We also need to find a storage for the other link, setup variables
2067 * to do this at the end...
2069 needToFindAPlaceholder = TRUE;
2070 storeNode = propertyToDelete.previousProperty;
2071 toStoreNode = propertyToDelete.nextProperty;
2072 relationType = PROPERTY_RELATION_NEXT;
2075 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2078 * Set the parent next to the property to delete next
2080 newLinkProperty = propertyToDelete.nextProperty;
2084 * Link it for real...
2086 parentProperty.nextProperty = newLinkProperty;
2088 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2090 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2093 * Set the parent dir to the property to delete previous
2095 newLinkProperty = propertyToDelete.previousProperty;
2097 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2100 * We also need to find a storage for the other link, setup variables
2101 * to do this at the end...
2103 needToFindAPlaceholder = TRUE;
2104 storeNode = propertyToDelete.previousProperty;
2105 toStoreNode = propertyToDelete.nextProperty;
2106 relationType = PROPERTY_RELATION_NEXT;
2109 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2112 * Set the parent dir to the property to delete next
2114 newLinkProperty = propertyToDelete.nextProperty;
2118 * Link it for real...
2120 parentProperty.dirProperty = newLinkProperty;
2124 * Write back the parent property
2126 res = StorageImpl_WriteProperty(
2127 This->base.ancestorStorage,
2128 parentPropertyId,
2129 &parentProperty);
2130 if(! res)
2132 return E_FAIL;
2136 * If a placeholder is required for the other link, then, find one and
2137 * get out of here...
2139 if (needToFindAPlaceholder)
2141 hr = findPlaceholder(
2142 This,
2143 toStoreNode,
2144 storeNode,
2145 relationType);
2148 return hr;
2152 /******************************************************************************
2153 * SetElementTimes (IStorage)
2155 HRESULT WINAPI StorageImpl_SetElementTimes(
2156 IStorage* iface,
2157 const OLECHAR *pwcsName,/* [string][in] */
2158 const FILETIME *pctime, /* [in] */
2159 const FILETIME *patime, /* [in] */
2160 const FILETIME *pmtime) /* [in] */
2162 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2163 return S_OK;
2166 /******************************************************************************
2167 * SetStateBits (IStorage)
2169 HRESULT WINAPI StorageImpl_SetStateBits(
2170 IStorage* iface,
2171 DWORD grfStateBits,/* [in] */
2172 DWORD grfMask) /* [in] */
2174 FIXME("not implemented!\n");
2175 return E_NOTIMPL;
2179 * Virtual function table for the IStorage32Impl class.
2181 static const IStorageVtbl Storage32Impl_Vtbl =
2183 StorageBaseImpl_QueryInterface,
2184 StorageBaseImpl_AddRef,
2185 StorageBaseImpl_Release,
2186 StorageBaseImpl_CreateStream,
2187 StorageBaseImpl_OpenStream,
2188 StorageImpl_CreateStorage,
2189 StorageBaseImpl_OpenStorage,
2190 StorageImpl_CopyTo,
2191 StorageImpl_MoveElementTo,
2192 StorageImpl_Commit,
2193 StorageImpl_Revert,
2194 StorageBaseImpl_EnumElements,
2195 StorageImpl_DestroyElement,
2196 StorageBaseImpl_RenameElement,
2197 StorageImpl_SetElementTimes,
2198 StorageBaseImpl_SetClass,
2199 StorageImpl_SetStateBits,
2200 StorageImpl_Stat
2203 HRESULT StorageImpl_Construct(
2204 StorageImpl* This,
2205 HANDLE hFile,
2206 LPCOLESTR pwcsName,
2207 ILockBytes* pLkbyt,
2208 DWORD openFlags,
2209 BOOL fileBased,
2210 BOOL fileCreate)
2212 HRESULT hr = S_OK;
2213 StgProperty currentProperty;
2214 BOOL readSuccessful;
2215 ULONG currentPropertyIndex;
2217 if ( FAILED( validateSTGM(openFlags) ))
2218 return STG_E_INVALIDFLAG;
2220 memset(This, 0, sizeof(StorageImpl));
2223 * Initialize the virtual function table.
2225 This->base.lpVtbl = &Storage32Impl_Vtbl;
2226 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2227 This->base.v_destructor = &StorageImpl_Destroy;
2228 This->base.openFlags = openFlags;
2231 * This is the top-level storage so initialize the ancestor pointer
2232 * to this.
2234 This->base.ancestorStorage = This;
2237 * Initialize the physical support of the storage.
2239 This->hFile = hFile;
2242 * Store copy of file path.
2244 if(pwcsName) {
2245 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2246 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2247 if (!This->pwcsName)
2248 return STG_E_INSUFFICIENTMEMORY;
2249 strcpyW(This->pwcsName, pwcsName);
2253 * Initialize the big block cache.
2255 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2256 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2257 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2258 pLkbyt,
2259 openFlags,
2260 This->bigBlockSize,
2261 fileBased);
2263 if (This->bigBlockFile == 0)
2264 return E_FAIL;
2266 if (fileCreate)
2268 ULARGE_INTEGER size;
2269 BYTE* bigBlockBuffer;
2272 * Initialize all header variables:
2273 * - The big block depot consists of one block and it is at block 0
2274 * - The properties start at block 1
2275 * - There is no small block depot
2277 memset( This->bigBlockDepotStart,
2278 BLOCK_UNUSED,
2279 sizeof(This->bigBlockDepotStart));
2281 This->bigBlockDepotCount = 1;
2282 This->bigBlockDepotStart[0] = 0;
2283 This->rootStartBlock = 1;
2284 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2285 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2286 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2287 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2288 This->extBigBlockDepotCount = 0;
2290 StorageImpl_SaveFileHeader(This);
2293 * Add one block for the big block depot and one block for the properties
2295 size.u.HighPart = 0;
2296 size.u.LowPart = This->bigBlockSize * 3;
2297 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2300 * Initialize the big block depot
2302 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2303 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2304 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2305 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2306 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2308 else
2311 * Load the header for the file.
2313 hr = StorageImpl_LoadFileHeader(This);
2315 if (FAILED(hr))
2317 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2319 return hr;
2324 * There is no block depot cached yet.
2326 This->indexBlockDepotCached = 0xFFFFFFFF;
2329 * Start searching for free blocks with block 0.
2331 This->prevFreeBlock = 0;
2334 * Create the block chain abstractions.
2336 if(!(This->rootBlockChain =
2337 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2338 return STG_E_READFAULT;
2340 if(!(This->smallBlockDepotChain =
2341 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2342 PROPERTY_NULL)))
2343 return STG_E_READFAULT;
2346 * Write the root property
2348 if (fileCreate)
2350 StgProperty rootProp;
2352 * Initialize the property chain
2354 memset(&rootProp, 0, sizeof(rootProp));
2355 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2356 sizeof(rootProp.name)/sizeof(WCHAR) );
2357 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2358 rootProp.propertyType = PROPTYPE_ROOT;
2359 rootProp.previousProperty = PROPERTY_NULL;
2360 rootProp.nextProperty = PROPERTY_NULL;
2361 rootProp.dirProperty = PROPERTY_NULL;
2362 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2363 rootProp.size.u.HighPart = 0;
2364 rootProp.size.u.LowPart = 0;
2366 StorageImpl_WriteProperty(This, 0, &rootProp);
2370 * Find the ID of the root in the property sets.
2372 currentPropertyIndex = 0;
2376 readSuccessful = StorageImpl_ReadProperty(
2377 This,
2378 currentPropertyIndex,
2379 &currentProperty);
2381 if (readSuccessful)
2383 if ( (currentProperty.sizeOfNameString != 0 ) &&
2384 (currentProperty.propertyType == PROPTYPE_ROOT) )
2386 This->base.rootPropertySetIndex = currentPropertyIndex;
2390 currentPropertyIndex++;
2392 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2394 if (!readSuccessful)
2396 /* TODO CLEANUP */
2397 return STG_E_READFAULT;
2401 * Create the block chain abstraction for the small block root chain.
2403 if(!(This->smallBlockRootChain =
2404 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2405 return STG_E_READFAULT;
2407 return hr;
2410 void StorageImpl_Destroy(StorageBaseImpl* iface)
2412 StorageImpl *This = (StorageImpl*) iface;
2413 TRACE("(%p)\n", This);
2415 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2417 BlockChainStream_Destroy(This->smallBlockRootChain);
2418 BlockChainStream_Destroy(This->rootBlockChain);
2419 BlockChainStream_Destroy(This->smallBlockDepotChain);
2421 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2422 HeapFree(GetProcessHeap(), 0, This);
2425 /******************************************************************************
2426 * Storage32Impl_GetNextFreeBigBlock
2428 * Returns the index of the next free big block.
2429 * If the big block depot is filled, this method will enlarge it.
2432 ULONG StorageImpl_GetNextFreeBigBlock(
2433 StorageImpl* This)
2435 ULONG depotBlockIndexPos;
2436 void *depotBuffer;
2437 ULONG depotBlockOffset;
2438 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2439 ULONG nextBlockIndex = BLOCK_SPECIAL;
2440 int depotIndex = 0;
2441 ULONG freeBlock = BLOCK_UNUSED;
2443 depotIndex = This->prevFreeBlock / blocksPerDepot;
2444 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2447 * Scan the entire big block depot until we find a block marked free
2449 while (nextBlockIndex != BLOCK_UNUSED)
2451 if (depotIndex < COUNT_BBDEPOTINHEADER)
2453 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2456 * Grow the primary depot.
2458 if (depotBlockIndexPos == BLOCK_UNUSED)
2460 depotBlockIndexPos = depotIndex*blocksPerDepot;
2463 * Add a block depot.
2465 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2466 This->bigBlockDepotCount++;
2467 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2470 * Flag it as a block depot.
2472 StorageImpl_SetNextBlockInChain(This,
2473 depotBlockIndexPos,
2474 BLOCK_SPECIAL);
2476 /* Save new header information.
2478 StorageImpl_SaveFileHeader(This);
2481 else
2483 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2485 if (depotBlockIndexPos == BLOCK_UNUSED)
2488 * Grow the extended depot.
2490 ULONG extIndex = BLOCK_UNUSED;
2491 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2492 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2494 if (extBlockOffset == 0)
2496 /* We need an extended block.
2498 extIndex = Storage32Impl_AddExtBlockDepot(This);
2499 This->extBigBlockDepotCount++;
2500 depotBlockIndexPos = extIndex + 1;
2502 else
2503 depotBlockIndexPos = depotIndex * blocksPerDepot;
2506 * Add a block depot and mark it in the extended block.
2508 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2509 This->bigBlockDepotCount++;
2510 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2512 /* Flag the block depot.
2514 StorageImpl_SetNextBlockInChain(This,
2515 depotBlockIndexPos,
2516 BLOCK_SPECIAL);
2518 /* If necessary, flag the extended depot block.
2520 if (extIndex != BLOCK_UNUSED)
2521 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2523 /* Save header information.
2525 StorageImpl_SaveFileHeader(This);
2529 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2531 if (depotBuffer != 0)
2533 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2534 ( nextBlockIndex != BLOCK_UNUSED))
2536 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2538 if (nextBlockIndex == BLOCK_UNUSED)
2540 freeBlock = (depotIndex * blocksPerDepot) +
2541 (depotBlockOffset/sizeof(ULONG));
2544 depotBlockOffset += sizeof(ULONG);
2547 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2550 depotIndex++;
2551 depotBlockOffset = 0;
2554 This->prevFreeBlock = freeBlock;
2556 return freeBlock;
2559 /******************************************************************************
2560 * Storage32Impl_AddBlockDepot
2562 * This will create a depot block, essentially it is a block initialized
2563 * to BLOCK_UNUSEDs.
2565 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2567 BYTE* blockBuffer;
2569 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2572 * Initialize blocks as free
2574 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2576 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2579 /******************************************************************************
2580 * Storage32Impl_GetExtDepotBlock
2582 * Returns the index of the block that corresponds to the specified depot
2583 * index. This method is only for depot indexes equal or greater than
2584 * COUNT_BBDEPOTINHEADER.
2586 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2588 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2589 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2590 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2591 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2592 ULONG blockIndex = BLOCK_UNUSED;
2593 ULONG extBlockIndex = This->extBigBlockDepotStart;
2595 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2597 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2598 return BLOCK_UNUSED;
2600 while (extBlockCount > 0)
2602 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2603 extBlockCount--;
2606 if (extBlockIndex != BLOCK_UNUSED)
2608 BYTE* depotBuffer;
2610 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2612 if (depotBuffer != 0)
2614 StorageUtl_ReadDWord(depotBuffer,
2615 extBlockOffset * sizeof(ULONG),
2616 &blockIndex);
2618 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2622 return blockIndex;
2625 /******************************************************************************
2626 * Storage32Impl_SetExtDepotBlock
2628 * Associates the specified block index to the specified depot index.
2629 * This method is only for depot indexes equal or greater than
2630 * COUNT_BBDEPOTINHEADER.
2632 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2633 ULONG depotIndex,
2634 ULONG blockIndex)
2636 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2637 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2638 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2639 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2640 ULONG extBlockIndex = This->extBigBlockDepotStart;
2642 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2644 while (extBlockCount > 0)
2646 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2647 extBlockCount--;
2650 if (extBlockIndex != BLOCK_UNUSED)
2652 BYTE* depotBuffer;
2654 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2656 if (depotBuffer != 0)
2658 StorageUtl_WriteDWord(depotBuffer,
2659 extBlockOffset * sizeof(ULONG),
2660 blockIndex);
2662 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2667 /******************************************************************************
2668 * Storage32Impl_AddExtBlockDepot
2670 * Creates an extended depot block.
2672 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2674 ULONG numExtBlocks = This->extBigBlockDepotCount;
2675 ULONG nextExtBlock = This->extBigBlockDepotStart;
2676 BYTE* depotBuffer = NULL;
2677 ULONG index = BLOCK_UNUSED;
2678 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2679 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2680 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2682 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2683 blocksPerDepotBlock;
2685 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2688 * The first extended block.
2690 This->extBigBlockDepotStart = index;
2692 else
2694 unsigned int i;
2696 * Follow the chain to the last one.
2698 for (i = 0; i < (numExtBlocks - 1); i++)
2700 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2704 * Add the new extended block to the chain.
2706 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2707 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2708 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2712 * Initialize this block.
2714 depotBuffer = StorageImpl_GetBigBlock(This, index);
2715 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2716 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2718 return index;
2721 /******************************************************************************
2722 * Storage32Impl_FreeBigBlock
2724 * This method will flag the specified block as free in the big block depot.
2726 void StorageImpl_FreeBigBlock(
2727 StorageImpl* This,
2728 ULONG blockIndex)
2730 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2732 if (blockIndex < This->prevFreeBlock)
2733 This->prevFreeBlock = blockIndex;
2736 /************************************************************************
2737 * Storage32Impl_GetNextBlockInChain
2739 * This method will retrieve the block index of the next big block in
2740 * in the chain.
2742 * Params: This - Pointer to the Storage object.
2743 * blockIndex - Index of the block to retrieve the chain
2744 * for.
2745 * nextBlockIndex - receives the return value.
2747 * Returns: This method returns the index of the next block in the chain.
2748 * It will return the constants:
2749 * BLOCK_SPECIAL - If the block given was not part of a
2750 * chain.
2751 * BLOCK_END_OF_CHAIN - If the block given was the last in
2752 * a chain.
2753 * BLOCK_UNUSED - If the block given was not past of a chain
2754 * and is available.
2755 * BLOCK_EXTBBDEPOT - This block is part of the extended
2756 * big block depot.
2758 * See Windows documentation for more details on IStorage methods.
2760 HRESULT StorageImpl_GetNextBlockInChain(
2761 StorageImpl* This,
2762 ULONG blockIndex,
2763 ULONG* nextBlockIndex)
2765 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2766 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2767 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2768 void* depotBuffer;
2769 ULONG depotBlockIndexPos;
2770 int index;
2772 *nextBlockIndex = BLOCK_SPECIAL;
2774 if(depotBlockCount >= This->bigBlockDepotCount)
2776 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2777 This->bigBlockDepotCount);
2778 return STG_E_READFAULT;
2782 * Cache the currently accessed depot block.
2784 if (depotBlockCount != This->indexBlockDepotCached)
2786 This->indexBlockDepotCached = depotBlockCount;
2788 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2790 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2792 else
2795 * We have to look in the extended depot.
2797 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2800 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2802 if (!depotBuffer)
2803 return STG_E_READFAULT;
2805 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2807 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2808 This->blockDepotCached[index] = *nextBlockIndex;
2810 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2813 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2815 return S_OK;
2818 /******************************************************************************
2819 * Storage32Impl_GetNextExtendedBlock
2821 * Given an extended block this method will return the next extended block.
2823 * NOTES:
2824 * The last ULONG of an extended block is the block index of the next
2825 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2826 * depot.
2828 * Return values:
2829 * - The index of the next extended block
2830 * - BLOCK_UNUSED: there is no next extended block.
2831 * - Any other return values denotes failure.
2833 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2835 ULONG nextBlockIndex = BLOCK_SPECIAL;
2836 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2837 void* depotBuffer;
2839 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2841 if (depotBuffer!=0)
2843 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2845 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2848 return nextBlockIndex;
2851 /******************************************************************************
2852 * Storage32Impl_SetNextBlockInChain
2854 * This method will write the index of the specified block's next block
2855 * in the big block depot.
2857 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2858 * do the following
2860 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2861 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2862 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2865 void StorageImpl_SetNextBlockInChain(
2866 StorageImpl* This,
2867 ULONG blockIndex,
2868 ULONG nextBlock)
2870 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2871 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2872 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2873 ULONG depotBlockIndexPos;
2874 void* depotBuffer;
2876 assert(depotBlockCount < This->bigBlockDepotCount);
2877 assert(blockIndex != nextBlock);
2879 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2881 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2883 else
2886 * We have to look in the extended depot.
2888 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2891 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2893 if (depotBuffer!=0)
2895 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2896 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2900 * Update the cached block depot, if necessary.
2902 if (depotBlockCount == This->indexBlockDepotCached)
2904 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2908 /******************************************************************************
2909 * Storage32Impl_LoadFileHeader
2911 * This method will read in the file header, i.e. big block index -1.
2913 HRESULT StorageImpl_LoadFileHeader(
2914 StorageImpl* This)
2916 HRESULT hr = STG_E_FILENOTFOUND;
2917 void* headerBigBlock = NULL;
2918 int index;
2921 * Get a pointer to the big block of data containing the header.
2923 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2926 * Extract the information from the header.
2928 if (headerBigBlock!=0)
2931 * Check for the "magic number" signature and return an error if it is not
2932 * found.
2934 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2936 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2937 return STG_E_OLDFORMAT;
2940 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2942 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2943 return STG_E_INVALIDHEADER;
2946 StorageUtl_ReadWord(
2947 headerBigBlock,
2948 OFFSET_BIGBLOCKSIZEBITS,
2949 &This->bigBlockSizeBits);
2951 StorageUtl_ReadWord(
2952 headerBigBlock,
2953 OFFSET_SMALLBLOCKSIZEBITS,
2954 &This->smallBlockSizeBits);
2956 StorageUtl_ReadDWord(
2957 headerBigBlock,
2958 OFFSET_BBDEPOTCOUNT,
2959 &This->bigBlockDepotCount);
2961 StorageUtl_ReadDWord(
2962 headerBigBlock,
2963 OFFSET_ROOTSTARTBLOCK,
2964 &This->rootStartBlock);
2966 StorageUtl_ReadDWord(
2967 headerBigBlock,
2968 OFFSET_SBDEPOTSTART,
2969 &This->smallBlockDepotStart);
2971 StorageUtl_ReadDWord(
2972 headerBigBlock,
2973 OFFSET_EXTBBDEPOTSTART,
2974 &This->extBigBlockDepotStart);
2976 StorageUtl_ReadDWord(
2977 headerBigBlock,
2978 OFFSET_EXTBBDEPOTCOUNT,
2979 &This->extBigBlockDepotCount);
2981 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2983 StorageUtl_ReadDWord(
2984 headerBigBlock,
2985 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2986 &(This->bigBlockDepotStart[index]));
2990 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2992 if ((1 << 2) == 4)
2994 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2995 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2997 else
2999 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3000 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3004 * Right now, the code is making some assumptions about the size of the
3005 * blocks, just make sure they are what we're expecting.
3007 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3008 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3010 WARN("Broken OLE storage file\n");
3011 hr = STG_E_INVALIDHEADER;
3013 else
3014 hr = S_OK;
3017 * Release the block.
3019 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3022 return hr;
3025 /******************************************************************************
3026 * Storage32Impl_SaveFileHeader
3028 * This method will save to the file the header, i.e. big block -1.
3030 void StorageImpl_SaveFileHeader(
3031 StorageImpl* This)
3033 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3034 int index;
3035 BOOL success;
3038 * Get a pointer to the big block of data containing the header.
3040 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3043 * If the block read failed, the file is probably new.
3045 if (!success)
3048 * Initialize for all unknown fields.
3050 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3053 * Initialize the magic number.
3055 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3058 * And a bunch of things we don't know what they mean
3060 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3061 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3062 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3063 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3067 * Write the information to the header.
3069 StorageUtl_WriteWord(
3070 headerBigBlock,
3071 OFFSET_BIGBLOCKSIZEBITS,
3072 This->bigBlockSizeBits);
3074 StorageUtl_WriteWord(
3075 headerBigBlock,
3076 OFFSET_SMALLBLOCKSIZEBITS,
3077 This->smallBlockSizeBits);
3079 StorageUtl_WriteDWord(
3080 headerBigBlock,
3081 OFFSET_BBDEPOTCOUNT,
3082 This->bigBlockDepotCount);
3084 StorageUtl_WriteDWord(
3085 headerBigBlock,
3086 OFFSET_ROOTSTARTBLOCK,
3087 This->rootStartBlock);
3089 StorageUtl_WriteDWord(
3090 headerBigBlock,
3091 OFFSET_SBDEPOTSTART,
3092 This->smallBlockDepotStart);
3094 StorageUtl_WriteDWord(
3095 headerBigBlock,
3096 OFFSET_SBDEPOTCOUNT,
3097 This->smallBlockDepotChain ?
3098 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3100 StorageUtl_WriteDWord(
3101 headerBigBlock,
3102 OFFSET_EXTBBDEPOTSTART,
3103 This->extBigBlockDepotStart);
3105 StorageUtl_WriteDWord(
3106 headerBigBlock,
3107 OFFSET_EXTBBDEPOTCOUNT,
3108 This->extBigBlockDepotCount);
3110 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3112 StorageUtl_WriteDWord(
3113 headerBigBlock,
3114 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3115 (This->bigBlockDepotStart[index]));
3119 * Write the big block back to the file.
3121 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3124 /******************************************************************************
3125 * Storage32Impl_ReadProperty
3127 * This method will read the specified property from the property chain.
3129 BOOL StorageImpl_ReadProperty(
3130 StorageImpl* This,
3131 ULONG index,
3132 StgProperty* buffer)
3134 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3135 ULARGE_INTEGER offsetInPropSet;
3136 BOOL readSuccessful;
3137 ULONG bytesRead;
3139 offsetInPropSet.u.HighPart = 0;
3140 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3142 readSuccessful = BlockChainStream_ReadAt(
3143 This->rootBlockChain,
3144 offsetInPropSet,
3145 PROPSET_BLOCK_SIZE,
3146 currentProperty,
3147 &bytesRead);
3149 if (readSuccessful)
3151 /* replace the name of root entry (often "Root Entry") by the file name */
3152 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3153 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3155 memset(buffer->name, 0, sizeof(buffer->name));
3156 memcpy(
3157 buffer->name,
3158 propName,
3159 PROPERTY_NAME_BUFFER_LEN );
3160 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3162 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3164 StorageUtl_ReadWord(
3165 currentProperty,
3166 OFFSET_PS_NAMELENGTH,
3167 &buffer->sizeOfNameString);
3169 StorageUtl_ReadDWord(
3170 currentProperty,
3171 OFFSET_PS_PREVIOUSPROP,
3172 &buffer->previousProperty);
3174 StorageUtl_ReadDWord(
3175 currentProperty,
3176 OFFSET_PS_NEXTPROP,
3177 &buffer->nextProperty);
3179 StorageUtl_ReadDWord(
3180 currentProperty,
3181 OFFSET_PS_DIRPROP,
3182 &buffer->dirProperty);
3184 StorageUtl_ReadGUID(
3185 currentProperty,
3186 OFFSET_PS_GUID,
3187 &buffer->propertyUniqueID);
3189 StorageUtl_ReadDWord(
3190 currentProperty,
3191 OFFSET_PS_TSS1,
3192 &buffer->timeStampS1);
3194 StorageUtl_ReadDWord(
3195 currentProperty,
3196 OFFSET_PS_TSD1,
3197 &buffer->timeStampD1);
3199 StorageUtl_ReadDWord(
3200 currentProperty,
3201 OFFSET_PS_TSS2,
3202 &buffer->timeStampS2);
3204 StorageUtl_ReadDWord(
3205 currentProperty,
3206 OFFSET_PS_TSD2,
3207 &buffer->timeStampD2);
3209 StorageUtl_ReadDWord(
3210 currentProperty,
3211 OFFSET_PS_STARTBLOCK,
3212 &buffer->startingBlock);
3214 StorageUtl_ReadDWord(
3215 currentProperty,
3216 OFFSET_PS_SIZE,
3217 &buffer->size.u.LowPart);
3219 buffer->size.u.HighPart = 0;
3222 return readSuccessful;
3225 /*********************************************************************
3226 * Write the specified property into the property chain
3228 BOOL StorageImpl_WriteProperty(
3229 StorageImpl* This,
3230 ULONG index,
3231 StgProperty* buffer)
3233 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3234 ULARGE_INTEGER offsetInPropSet;
3235 BOOL writeSuccessful;
3236 ULONG bytesWritten;
3238 offsetInPropSet.u.HighPart = 0;
3239 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3241 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3243 memcpy(
3244 currentProperty + OFFSET_PS_NAME,
3245 buffer->name,
3246 PROPERTY_NAME_BUFFER_LEN );
3248 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3250 StorageUtl_WriteWord(
3251 currentProperty,
3252 OFFSET_PS_NAMELENGTH,
3253 buffer->sizeOfNameString);
3255 StorageUtl_WriteDWord(
3256 currentProperty,
3257 OFFSET_PS_PREVIOUSPROP,
3258 buffer->previousProperty);
3260 StorageUtl_WriteDWord(
3261 currentProperty,
3262 OFFSET_PS_NEXTPROP,
3263 buffer->nextProperty);
3265 StorageUtl_WriteDWord(
3266 currentProperty,
3267 OFFSET_PS_DIRPROP,
3268 buffer->dirProperty);
3270 StorageUtl_WriteGUID(
3271 currentProperty,
3272 OFFSET_PS_GUID,
3273 &buffer->propertyUniqueID);
3275 StorageUtl_WriteDWord(
3276 currentProperty,
3277 OFFSET_PS_TSS1,
3278 buffer->timeStampS1);
3280 StorageUtl_WriteDWord(
3281 currentProperty,
3282 OFFSET_PS_TSD1,
3283 buffer->timeStampD1);
3285 StorageUtl_WriteDWord(
3286 currentProperty,
3287 OFFSET_PS_TSS2,
3288 buffer->timeStampS2);
3290 StorageUtl_WriteDWord(
3291 currentProperty,
3292 OFFSET_PS_TSD2,
3293 buffer->timeStampD2);
3295 StorageUtl_WriteDWord(
3296 currentProperty,
3297 OFFSET_PS_STARTBLOCK,
3298 buffer->startingBlock);
3300 StorageUtl_WriteDWord(
3301 currentProperty,
3302 OFFSET_PS_SIZE,
3303 buffer->size.u.LowPart);
3305 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3306 offsetInPropSet,
3307 PROPSET_BLOCK_SIZE,
3308 currentProperty,
3309 &bytesWritten);
3310 return writeSuccessful;
3313 BOOL StorageImpl_ReadBigBlock(
3314 StorageImpl* This,
3315 ULONG blockIndex,
3316 void* buffer)
3318 void* bigBlockBuffer;
3320 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3322 if (bigBlockBuffer!=0)
3324 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3326 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3328 return TRUE;
3331 return FALSE;
3334 BOOL StorageImpl_WriteBigBlock(
3335 StorageImpl* This,
3336 ULONG blockIndex,
3337 void* buffer)
3339 void* bigBlockBuffer;
3341 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3343 if (bigBlockBuffer!=0)
3345 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3347 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3349 return TRUE;
3352 return FALSE;
3355 void* StorageImpl_GetROBigBlock(
3356 StorageImpl* This,
3357 ULONG blockIndex)
3359 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3362 void* StorageImpl_GetBigBlock(
3363 StorageImpl* This,
3364 ULONG blockIndex)
3366 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3369 void StorageImpl_ReleaseBigBlock(
3370 StorageImpl* This,
3371 void* pBigBlock)
3373 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3376 /******************************************************************************
3377 * Storage32Impl_SmallBlocksToBigBlocks
3379 * This method will convert a small block chain to a big block chain.
3380 * The small block chain will be destroyed.
3382 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3383 StorageImpl* This,
3384 SmallBlockChainStream** ppsbChain)
3386 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3387 ULARGE_INTEGER size, offset;
3388 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3389 ULONG propertyIndex;
3390 BOOL successRead, successWrite;
3391 StgProperty chainProperty;
3392 BYTE *buffer;
3393 BlockChainStream *bbTempChain = NULL;
3394 BlockChainStream *bigBlockChain = NULL;
3397 * Create a temporary big block chain that doesn't have
3398 * an associated property. This temporary chain will be
3399 * used to copy data from small blocks to big blocks.
3401 bbTempChain = BlockChainStream_Construct(This,
3402 &bbHeadOfChain,
3403 PROPERTY_NULL);
3404 if(!bbTempChain) return NULL;
3406 * Grow the big block chain.
3408 size = SmallBlockChainStream_GetSize(*ppsbChain);
3409 BlockChainStream_SetSize(bbTempChain, size);
3412 * Copy the contents of the small block chain to the big block chain
3413 * by small block size increments.
3415 offset.u.LowPart = 0;
3416 offset.u.HighPart = 0;
3417 cbTotalRead = 0;
3418 cbTotalWritten = 0;
3420 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3423 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3424 offset,
3425 DEF_SMALL_BLOCK_SIZE,
3426 buffer,
3427 &cbRead);
3428 cbTotalRead += cbRead;
3430 successWrite = BlockChainStream_WriteAt(bbTempChain,
3431 offset,
3432 cbRead,
3433 buffer,
3434 &cbWritten);
3435 cbTotalWritten += cbWritten;
3437 offset.u.LowPart += This->smallBlockSize;
3439 } while (successRead && successWrite);
3440 HeapFree(GetProcessHeap(),0,buffer);
3442 assert(cbTotalRead == cbTotalWritten);
3445 * Destroy the small block chain.
3447 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3448 size.u.HighPart = 0;
3449 size.u.LowPart = 0;
3450 SmallBlockChainStream_SetSize(*ppsbChain, size);
3451 SmallBlockChainStream_Destroy(*ppsbChain);
3452 *ppsbChain = 0;
3455 * Change the property information. This chain is now a big block chain
3456 * and it doesn't reside in the small blocks chain anymore.
3458 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3460 chainProperty.startingBlock = bbHeadOfChain;
3462 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3465 * Destroy the temporary propertyless big block chain.
3466 * Create a new big block chain associated with this property.
3468 BlockChainStream_Destroy(bbTempChain);
3469 bigBlockChain = BlockChainStream_Construct(This,
3470 NULL,
3471 propertyIndex);
3473 return bigBlockChain;
3476 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3478 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3480 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3481 HeapFree(GetProcessHeap(), 0, This);
3484 /******************************************************************************
3486 ** Storage32InternalImpl_Commit
3488 ** The non-root storages cannot be opened in transacted mode thus this function
3489 ** does nothing.
3491 HRESULT WINAPI StorageInternalImpl_Commit(
3492 IStorage* iface,
3493 DWORD grfCommitFlags) /* [in] */
3495 return S_OK;
3498 /******************************************************************************
3500 ** Storage32InternalImpl_Revert
3502 ** The non-root storages cannot be opened in transacted mode thus this function
3503 ** does nothing.
3505 HRESULT WINAPI StorageInternalImpl_Revert(
3506 IStorage* iface)
3508 return S_OK;
3511 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3513 IStorage_Release((IStorage*)This->parentStorage);
3514 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3515 HeapFree(GetProcessHeap(), 0, This);
3518 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3519 IEnumSTATSTG* iface,
3520 REFIID riid,
3521 void** ppvObject)
3523 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3526 * Perform a sanity check on the parameters.
3528 if (ppvObject==0)
3529 return E_INVALIDARG;
3532 * Initialize the return parameter.
3534 *ppvObject = 0;
3537 * Compare the riid with the interface IDs implemented by this object.
3539 if (IsEqualGUID(&IID_IUnknown, riid) ||
3540 IsEqualGUID(&IID_IStorage, riid))
3542 *ppvObject = (IEnumSTATSTG*)This;
3543 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3544 return S_OK;
3547 return E_NOINTERFACE;
3550 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3551 IEnumSTATSTG* iface)
3553 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3554 return InterlockedIncrement(&This->ref);
3557 ULONG WINAPI IEnumSTATSTGImpl_Release(
3558 IEnumSTATSTG* iface)
3560 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3562 ULONG newRef;
3564 newRef = InterlockedDecrement(&This->ref);
3567 * If the reference count goes down to 0, perform suicide.
3569 if (newRef==0)
3571 IEnumSTATSTGImpl_Destroy(This);
3574 return newRef;
3577 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3578 IEnumSTATSTG* iface,
3579 ULONG celt,
3580 STATSTG* rgelt,
3581 ULONG* pceltFetched)
3583 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3585 StgProperty currentProperty;
3586 STATSTG* currentReturnStruct = rgelt;
3587 ULONG objectFetched = 0;
3588 ULONG currentSearchNode;
3591 * Perform a sanity check on the parameters.
3593 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3594 return E_INVALIDARG;
3597 * To avoid the special case, get another pointer to a ULONG value if
3598 * the caller didn't supply one.
3600 if (pceltFetched==0)
3601 pceltFetched = &objectFetched;
3604 * Start the iteration, we will iterate until we hit the end of the
3605 * linked list or until we hit the number of items to iterate through
3607 *pceltFetched = 0;
3610 * Start with the node at the top of the stack.
3612 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3614 while ( ( *pceltFetched < celt) &&
3615 ( currentSearchNode!=PROPERTY_NULL) )
3618 * Remove the top node from the stack
3620 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3623 * Read the property from the storage.
3625 StorageImpl_ReadProperty(This->parentStorage,
3626 currentSearchNode,
3627 &currentProperty);
3630 * Copy the information to the return buffer.
3632 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3633 &currentProperty,
3634 STATFLAG_DEFAULT);
3637 * Step to the next item in the iteration
3639 (*pceltFetched)++;
3640 currentReturnStruct++;
3643 * Push the next search node in the search stack.
3645 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3648 * continue the iteration.
3650 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3653 if (*pceltFetched == celt)
3654 return S_OK;
3656 return S_FALSE;
3660 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3661 IEnumSTATSTG* iface,
3662 ULONG celt)
3664 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3666 StgProperty currentProperty;
3667 ULONG objectFetched = 0;
3668 ULONG currentSearchNode;
3671 * Start with the node at the top of the stack.
3673 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3675 while ( (objectFetched < celt) &&
3676 (currentSearchNode!=PROPERTY_NULL) )
3679 * Remove the top node from the stack
3681 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3684 * Read the property from the storage.
3686 StorageImpl_ReadProperty(This->parentStorage,
3687 currentSearchNode,
3688 &currentProperty);
3691 * Step to the next item in the iteration
3693 objectFetched++;
3696 * Push the next search node in the search stack.
3698 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3701 * continue the iteration.
3703 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3706 if (objectFetched == celt)
3707 return S_OK;
3709 return S_FALSE;
3712 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3713 IEnumSTATSTG* iface)
3715 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3717 StgProperty rootProperty;
3718 BOOL readSuccessful;
3721 * Re-initialize the search stack to an empty stack
3723 This->stackSize = 0;
3726 * Read the root property from the storage.
3728 readSuccessful = StorageImpl_ReadProperty(
3729 This->parentStorage,
3730 This->firstPropertyNode,
3731 &rootProperty);
3733 if (readSuccessful)
3735 assert(rootProperty.sizeOfNameString!=0);
3738 * Push the search node in the search stack.
3740 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3743 return S_OK;
3746 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3747 IEnumSTATSTG* iface,
3748 IEnumSTATSTG** ppenum)
3750 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3752 IEnumSTATSTGImpl* newClone;
3755 * Perform a sanity check on the parameters.
3757 if (ppenum==0)
3758 return E_INVALIDARG;
3760 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3761 This->firstPropertyNode);
3765 * The new clone enumeration must point to the same current node as
3766 * the ole one.
3768 newClone->stackSize = This->stackSize ;
3769 newClone->stackMaxSize = This->stackMaxSize ;
3770 newClone->stackToVisit =
3771 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3773 memcpy(
3774 newClone->stackToVisit,
3775 This->stackToVisit,
3776 sizeof(ULONG) * newClone->stackSize);
3778 *ppenum = (IEnumSTATSTG*)newClone;
3781 * Don't forget to nail down a reference to the clone before
3782 * returning it.
3784 IEnumSTATSTGImpl_AddRef(*ppenum);
3786 return S_OK;
3789 INT IEnumSTATSTGImpl_FindParentProperty(
3790 IEnumSTATSTGImpl *This,
3791 ULONG childProperty,
3792 StgProperty *currentProperty,
3793 ULONG *thisNodeId)
3795 ULONG currentSearchNode;
3796 ULONG foundNode;
3799 * To avoid the special case, get another pointer to a ULONG value if
3800 * the caller didn't supply one.
3802 if (thisNodeId==0)
3803 thisNodeId = &foundNode;
3806 * Start with the node at the top of the stack.
3808 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3811 while (currentSearchNode!=PROPERTY_NULL)
3814 * Store the current node in the returned parameters
3816 *thisNodeId = currentSearchNode;
3819 * Remove the top node from the stack
3821 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3824 * Read the property from the storage.
3826 StorageImpl_ReadProperty(
3827 This->parentStorage,
3828 currentSearchNode,
3829 currentProperty);
3831 if (currentProperty->previousProperty == childProperty)
3832 return PROPERTY_RELATION_PREVIOUS;
3834 else if (currentProperty->nextProperty == childProperty)
3835 return PROPERTY_RELATION_NEXT;
3837 else if (currentProperty->dirProperty == childProperty)
3838 return PROPERTY_RELATION_DIR;
3841 * Push the next search node in the search stack.
3843 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3846 * continue the iteration.
3848 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3851 return PROPERTY_NULL;
3854 ULONG IEnumSTATSTGImpl_FindProperty(
3855 IEnumSTATSTGImpl* This,
3856 const OLECHAR* lpszPropName,
3857 StgProperty* currentProperty)
3859 ULONG currentSearchNode;
3862 * Start with the node at the top of the stack.
3864 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3866 while (currentSearchNode!=PROPERTY_NULL)
3869 * Remove the top node from the stack
3871 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3874 * Read the property from the storage.
3876 StorageImpl_ReadProperty(This->parentStorage,
3877 currentSearchNode,
3878 currentProperty);
3880 if ( propertyNameCmp(
3881 (const OLECHAR*)currentProperty->name,
3882 (const OLECHAR*)lpszPropName) == 0)
3883 return currentSearchNode;
3886 * Push the next search node in the search stack.
3888 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3891 * continue the iteration.
3893 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3896 return PROPERTY_NULL;
3899 void IEnumSTATSTGImpl_PushSearchNode(
3900 IEnumSTATSTGImpl* This,
3901 ULONG nodeToPush)
3903 StgProperty rootProperty;
3904 BOOL readSuccessful;
3907 * First, make sure we're not trying to push an unexisting node.
3909 if (nodeToPush==PROPERTY_NULL)
3910 return;
3913 * First push the node to the stack
3915 if (This->stackSize == This->stackMaxSize)
3917 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3919 This->stackToVisit = HeapReAlloc(
3920 GetProcessHeap(),
3922 This->stackToVisit,
3923 sizeof(ULONG) * This->stackMaxSize);
3926 This->stackToVisit[This->stackSize] = nodeToPush;
3927 This->stackSize++;
3930 * Read the root property from the storage.
3932 readSuccessful = StorageImpl_ReadProperty(
3933 This->parentStorage,
3934 nodeToPush,
3935 &rootProperty);
3937 if (readSuccessful)
3939 assert(rootProperty.sizeOfNameString!=0);
3942 * Push the previous search node in the search stack.
3944 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3948 ULONG IEnumSTATSTGImpl_PopSearchNode(
3949 IEnumSTATSTGImpl* This,
3950 BOOL remove)
3952 ULONG topNode;
3954 if (This->stackSize == 0)
3955 return PROPERTY_NULL;
3957 topNode = This->stackToVisit[This->stackSize-1];
3959 if (remove)
3960 This->stackSize--;
3962 return topNode;
3966 * Virtual function table for the IEnumSTATSTGImpl class.
3968 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
3970 IEnumSTATSTGImpl_QueryInterface,
3971 IEnumSTATSTGImpl_AddRef,
3972 IEnumSTATSTGImpl_Release,
3973 IEnumSTATSTGImpl_Next,
3974 IEnumSTATSTGImpl_Skip,
3975 IEnumSTATSTGImpl_Reset,
3976 IEnumSTATSTGImpl_Clone
3979 /******************************************************************************
3980 ** IEnumSTATSTGImpl implementation
3983 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3984 StorageImpl* parentStorage,
3985 ULONG firstPropertyNode)
3987 IEnumSTATSTGImpl* newEnumeration;
3989 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3991 if (newEnumeration!=0)
3994 * Set-up the virtual function table and reference count.
3996 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3997 newEnumeration->ref = 0;
4000 * We want to nail-down the reference to the storage in case the
4001 * enumeration out-lives the storage in the client application.
4003 newEnumeration->parentStorage = parentStorage;
4004 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4006 newEnumeration->firstPropertyNode = firstPropertyNode;
4009 * Initialize the search stack
4011 newEnumeration->stackSize = 0;
4012 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4013 newEnumeration->stackToVisit =
4014 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4017 * Make sure the current node of the iterator is the first one.
4019 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4022 return newEnumeration;
4026 * Virtual function table for the Storage32InternalImpl class.
4028 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4030 StorageBaseImpl_QueryInterface,
4031 StorageBaseImpl_AddRef,
4032 StorageBaseImpl_Release,
4033 StorageBaseImpl_CreateStream,
4034 StorageBaseImpl_OpenStream,
4035 StorageImpl_CreateStorage,
4036 StorageBaseImpl_OpenStorage,
4037 StorageImpl_CopyTo,
4038 StorageImpl_MoveElementTo,
4039 StorageInternalImpl_Commit,
4040 StorageInternalImpl_Revert,
4041 StorageBaseImpl_EnumElements,
4042 StorageImpl_DestroyElement,
4043 StorageBaseImpl_RenameElement,
4044 StorageImpl_SetElementTimes,
4045 StorageBaseImpl_SetClass,
4046 StorageImpl_SetStateBits,
4047 StorageBaseImpl_Stat
4050 /******************************************************************************
4051 ** Storage32InternalImpl implementation
4054 StorageInternalImpl* StorageInternalImpl_Construct(
4055 StorageImpl* ancestorStorage,
4056 DWORD openFlags,
4057 ULONG rootPropertyIndex)
4059 StorageInternalImpl* newStorage;
4062 * Allocate space for the new storage object
4064 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4066 if (newStorage!=0)
4068 memset(newStorage, 0, sizeof(StorageInternalImpl));
4071 * Initialize the virtual function table.
4073 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4074 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4075 newStorage->base.openFlags = openFlags;
4078 * Keep the ancestor storage pointer and nail a reference to it.
4080 newStorage->base.ancestorStorage = ancestorStorage;
4081 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4084 * Keep the index of the root property set for this storage,
4086 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4088 return newStorage;
4091 return 0;
4094 /******************************************************************************
4095 ** StorageUtl implementation
4098 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4100 WORD tmp;
4102 memcpy(&tmp, buffer+offset, sizeof(WORD));
4103 *value = le16toh(tmp);
4106 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4108 value = htole16(value);
4109 memcpy(buffer+offset, &value, sizeof(WORD));
4112 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4114 DWORD tmp;
4116 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4117 *value = le32toh(tmp);
4120 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4122 value = htole32(value);
4123 memcpy(buffer+offset, &value, sizeof(DWORD));
4126 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4127 ULARGE_INTEGER* value)
4129 #ifdef WORDS_BIGENDIAN
4130 ULARGE_INTEGER tmp;
4132 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4133 value->u.LowPart = htole32(tmp.u.HighPart);
4134 value->u.HighPart = htole32(tmp.u.LowPart);
4135 #else
4136 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4137 #endif
4140 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4141 const ULARGE_INTEGER *value)
4143 #ifdef WORDS_BIGENDIAN
4144 ULARGE_INTEGER tmp;
4146 tmp.u.LowPart = htole32(value->u.HighPart);
4147 tmp.u.HighPart = htole32(value->u.LowPart);
4148 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4149 #else
4150 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4151 #endif
4154 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4156 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4157 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4158 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4160 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4163 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4165 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4166 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4167 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4169 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4172 void StorageUtl_CopyPropertyToSTATSTG(
4173 STATSTG* destination,
4174 StgProperty* source,
4175 int statFlags)
4178 * The copy of the string occurs only when the flag is not set
4180 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4181 (source->name == NULL) ||
4182 (source->name[0] == 0) )
4184 destination->pwcsName = 0;
4186 else
4188 destination->pwcsName =
4189 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4191 strcpyW((LPWSTR)destination->pwcsName, source->name);
4194 switch (source->propertyType)
4196 case PROPTYPE_STORAGE:
4197 case PROPTYPE_ROOT:
4198 destination->type = STGTY_STORAGE;
4199 break;
4200 case PROPTYPE_STREAM:
4201 destination->type = STGTY_STREAM;
4202 break;
4203 default:
4204 destination->type = STGTY_STREAM;
4205 break;
4208 destination->cbSize = source->size;
4210 currentReturnStruct->mtime = {0}; TODO
4211 currentReturnStruct->ctime = {0};
4212 currentReturnStruct->atime = {0};
4214 destination->grfMode = 0;
4215 destination->grfLocksSupported = 0;
4216 destination->clsid = source->propertyUniqueID;
4217 destination->grfStateBits = 0;
4218 destination->reserved = 0;
4221 /******************************************************************************
4222 ** BlockChainStream implementation
4225 BlockChainStream* BlockChainStream_Construct(
4226 StorageImpl* parentStorage,
4227 ULONG* headOfStreamPlaceHolder,
4228 ULONG propertyIndex)
4230 BlockChainStream* newStream;
4231 ULONG blockIndex;
4233 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4235 newStream->parentStorage = parentStorage;
4236 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4237 newStream->ownerPropertyIndex = propertyIndex;
4238 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4239 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4240 newStream->numBlocks = 0;
4242 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4244 while (blockIndex != BLOCK_END_OF_CHAIN)
4246 newStream->numBlocks++;
4247 newStream->tailIndex = blockIndex;
4249 if(FAILED(StorageImpl_GetNextBlockInChain(
4250 parentStorage,
4251 blockIndex,
4252 &blockIndex)))
4254 HeapFree(GetProcessHeap(), 0, newStream);
4255 return NULL;
4259 return newStream;
4262 void BlockChainStream_Destroy(BlockChainStream* This)
4264 HeapFree(GetProcessHeap(), 0, This);
4267 /******************************************************************************
4268 * BlockChainStream_GetHeadOfChain
4270 * Returns the head of this stream chain.
4271 * Some special chains don't have properties, their heads are kept in
4272 * This->headOfStreamPlaceHolder.
4275 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4277 StgProperty chainProperty;
4278 BOOL readSuccessful;
4280 if (This->headOfStreamPlaceHolder != 0)
4281 return *(This->headOfStreamPlaceHolder);
4283 if (This->ownerPropertyIndex != PROPERTY_NULL)
4285 readSuccessful = StorageImpl_ReadProperty(
4286 This->parentStorage,
4287 This->ownerPropertyIndex,
4288 &chainProperty);
4290 if (readSuccessful)
4292 return chainProperty.startingBlock;
4296 return BLOCK_END_OF_CHAIN;
4299 /******************************************************************************
4300 * BlockChainStream_GetCount
4302 * Returns the number of blocks that comprises this chain.
4303 * This is not the size of the stream as the last block may not be full!
4306 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4308 ULONG blockIndex;
4309 ULONG count = 0;
4311 blockIndex = BlockChainStream_GetHeadOfChain(This);
4313 while (blockIndex != BLOCK_END_OF_CHAIN)
4315 count++;
4317 if(FAILED(StorageImpl_GetNextBlockInChain(
4318 This->parentStorage,
4319 blockIndex,
4320 &blockIndex)))
4321 return 0;
4324 return count;
4327 /******************************************************************************
4328 * BlockChainStream_ReadAt
4330 * Reads a specified number of bytes from this chain at the specified offset.
4331 * bytesRead may be NULL.
4332 * Failure will be returned if the specified number of bytes has not been read.
4334 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4335 ULARGE_INTEGER offset,
4336 ULONG size,
4337 void* buffer,
4338 ULONG* bytesRead)
4340 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4341 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4342 ULONG bytesToReadInBuffer;
4343 ULONG blockIndex;
4344 BYTE* bufferWalker;
4345 BYTE* bigBlockBuffer;
4348 * Find the first block in the stream that contains part of the buffer.
4350 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4351 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4352 (blockNoInSequence < This->lastBlockNoInSequence) )
4354 blockIndex = BlockChainStream_GetHeadOfChain(This);
4355 This->lastBlockNoInSequence = blockNoInSequence;
4357 else
4359 ULONG temp = blockNoInSequence;
4361 blockIndex = This->lastBlockNoInSequenceIndex;
4362 blockNoInSequence -= This->lastBlockNoInSequence;
4363 This->lastBlockNoInSequence = temp;
4366 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4368 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4369 return FALSE;
4370 blockNoInSequence--;
4373 This->lastBlockNoInSequenceIndex = blockIndex;
4376 * Start reading the buffer.
4378 *bytesRead = 0;
4379 bufferWalker = buffer;
4381 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4384 * Calculate how many bytes we can copy from this big block.
4386 bytesToReadInBuffer =
4387 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4390 * Copy those bytes to the buffer
4392 bigBlockBuffer =
4393 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4395 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4397 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4400 * Step to the next big block.
4402 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4403 return FALSE;
4405 bufferWalker += bytesToReadInBuffer;
4406 size -= bytesToReadInBuffer;
4407 *bytesRead += bytesToReadInBuffer;
4408 offsetInBlock = 0; /* There is no offset on the next block */
4412 return (size == 0);
4415 /******************************************************************************
4416 * BlockChainStream_WriteAt
4418 * Writes the specified number of bytes to this chain at the specified offset.
4419 * bytesWritten may be NULL.
4420 * Will fail if not all specified number of bytes have been written.
4422 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4423 ULARGE_INTEGER offset,
4424 ULONG size,
4425 const void* buffer,
4426 ULONG* bytesWritten)
4428 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4429 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4430 ULONG bytesToWrite;
4431 ULONG blockIndex;
4432 const BYTE* bufferWalker;
4433 BYTE* bigBlockBuffer;
4436 * Find the first block in the stream that contains part of the buffer.
4438 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4439 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4440 (blockNoInSequence < This->lastBlockNoInSequence) )
4442 blockIndex = BlockChainStream_GetHeadOfChain(This);
4443 This->lastBlockNoInSequence = blockNoInSequence;
4445 else
4447 ULONG temp = blockNoInSequence;
4449 blockIndex = This->lastBlockNoInSequenceIndex;
4450 blockNoInSequence -= This->lastBlockNoInSequence;
4451 This->lastBlockNoInSequence = temp;
4454 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4456 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4457 &blockIndex)))
4458 return FALSE;
4459 blockNoInSequence--;
4462 This->lastBlockNoInSequenceIndex = blockIndex;
4465 * Here, I'm casting away the constness on the buffer variable
4466 * This is OK since we don't intend to modify that buffer.
4468 *bytesWritten = 0;
4469 bufferWalker = (const BYTE*)buffer;
4471 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4474 * Calculate how many bytes we can copy from this big block.
4476 bytesToWrite =
4477 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4480 * Copy those bytes to the buffer
4482 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4484 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4486 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4489 * Step to the next big block.
4491 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4492 &blockIndex)))
4493 return FALSE;
4494 bufferWalker += bytesToWrite;
4495 size -= bytesToWrite;
4496 *bytesWritten += bytesToWrite;
4497 offsetInBlock = 0; /* There is no offset on the next block */
4500 return (size == 0);
4503 /******************************************************************************
4504 * BlockChainStream_Shrink
4506 * Shrinks this chain in the big block depot.
4508 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4509 ULARGE_INTEGER newSize)
4511 ULONG blockIndex, extraBlock;
4512 ULONG numBlocks;
4513 ULONG count = 1;
4516 * Reset the last accessed block cache.
4518 This->lastBlockNoInSequence = 0xFFFFFFFF;
4519 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4522 * Figure out how many blocks are needed to contain the new size
4524 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4526 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4527 numBlocks++;
4529 blockIndex = BlockChainStream_GetHeadOfChain(This);
4532 * Go to the new end of chain
4534 while (count < numBlocks)
4536 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4537 &blockIndex)))
4538 return FALSE;
4539 count++;
4542 /* Get the next block before marking the new end */
4543 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4544 &extraBlock)))
4545 return FALSE;
4547 /* Mark the new end of chain */
4548 StorageImpl_SetNextBlockInChain(
4549 This->parentStorage,
4550 blockIndex,
4551 BLOCK_END_OF_CHAIN);
4553 This->tailIndex = blockIndex;
4554 This->numBlocks = numBlocks;
4557 * Mark the extra blocks as free
4559 while (extraBlock != BLOCK_END_OF_CHAIN)
4561 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4562 &blockIndex)))
4563 return FALSE;
4564 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4565 extraBlock = blockIndex;
4568 return TRUE;
4571 /******************************************************************************
4572 * BlockChainStream_Enlarge
4574 * Grows this chain in the big block depot.
4576 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4577 ULARGE_INTEGER newSize)
4579 ULONG blockIndex, currentBlock;
4580 ULONG newNumBlocks;
4581 ULONG oldNumBlocks = 0;
4583 blockIndex = BlockChainStream_GetHeadOfChain(This);
4586 * Empty chain. Create the head.
4588 if (blockIndex == BLOCK_END_OF_CHAIN)
4590 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4591 StorageImpl_SetNextBlockInChain(This->parentStorage,
4592 blockIndex,
4593 BLOCK_END_OF_CHAIN);
4595 if (This->headOfStreamPlaceHolder != 0)
4597 *(This->headOfStreamPlaceHolder) = blockIndex;
4599 else
4601 StgProperty chainProp;
4602 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4604 StorageImpl_ReadProperty(
4605 This->parentStorage,
4606 This->ownerPropertyIndex,
4607 &chainProp);
4609 chainProp.startingBlock = blockIndex;
4611 StorageImpl_WriteProperty(
4612 This->parentStorage,
4613 This->ownerPropertyIndex,
4614 &chainProp);
4617 This->tailIndex = blockIndex;
4618 This->numBlocks = 1;
4622 * Figure out how many blocks are needed to contain this stream
4624 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4626 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4627 newNumBlocks++;
4630 * Go to the current end of chain
4632 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4634 currentBlock = blockIndex;
4636 while (blockIndex != BLOCK_END_OF_CHAIN)
4638 This->numBlocks++;
4639 currentBlock = blockIndex;
4641 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4642 &blockIndex)))
4643 return FALSE;
4646 This->tailIndex = currentBlock;
4649 currentBlock = This->tailIndex;
4650 oldNumBlocks = This->numBlocks;
4653 * Add new blocks to the chain
4655 if (oldNumBlocks < newNumBlocks)
4657 while (oldNumBlocks < newNumBlocks)
4659 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4661 StorageImpl_SetNextBlockInChain(
4662 This->parentStorage,
4663 currentBlock,
4664 blockIndex);
4666 StorageImpl_SetNextBlockInChain(
4667 This->parentStorage,
4668 blockIndex,
4669 BLOCK_END_OF_CHAIN);
4671 currentBlock = blockIndex;
4672 oldNumBlocks++;
4675 This->tailIndex = blockIndex;
4676 This->numBlocks = newNumBlocks;
4679 return TRUE;
4682 /******************************************************************************
4683 * BlockChainStream_SetSize
4685 * Sets the size of this stream. The big block depot will be updated.
4686 * The file will grow if we grow the chain.
4688 * TODO: Free the actual blocks in the file when we shrink the chain.
4689 * Currently, the blocks are still in the file. So the file size
4690 * doesn't shrink even if we shrink streams.
4692 BOOL BlockChainStream_SetSize(
4693 BlockChainStream* This,
4694 ULARGE_INTEGER newSize)
4696 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4698 if (newSize.u.LowPart == size.u.LowPart)
4699 return TRUE;
4701 if (newSize.u.LowPart < size.u.LowPart)
4703 BlockChainStream_Shrink(This, newSize);
4705 else
4707 ULARGE_INTEGER fileSize =
4708 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4710 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4713 * Make sure the file stays a multiple of blocksize
4715 if ((diff % This->parentStorage->bigBlockSize) != 0)
4716 diff += (This->parentStorage->bigBlockSize -
4717 (diff % This->parentStorage->bigBlockSize) );
4719 fileSize.u.LowPart += diff;
4720 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4722 BlockChainStream_Enlarge(This, newSize);
4725 return TRUE;
4728 /******************************************************************************
4729 * BlockChainStream_GetSize
4731 * Returns the size of this chain.
4732 * Will return the block count if this chain doesn't have a property.
4734 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4736 StgProperty chainProperty;
4738 if(This->headOfStreamPlaceHolder == NULL)
4741 * This chain is a data stream read the property and return
4742 * the appropriate size
4744 StorageImpl_ReadProperty(
4745 This->parentStorage,
4746 This->ownerPropertyIndex,
4747 &chainProperty);
4749 return chainProperty.size;
4751 else
4754 * this chain is a chain that does not have a property, figure out the
4755 * size by making the product number of used blocks times the
4756 * size of them
4758 ULARGE_INTEGER result;
4759 result.u.HighPart = 0;
4761 result.u.LowPart =
4762 BlockChainStream_GetCount(This) *
4763 This->parentStorage->bigBlockSize;
4765 return result;
4769 /******************************************************************************
4770 ** SmallBlockChainStream implementation
4773 SmallBlockChainStream* SmallBlockChainStream_Construct(
4774 StorageImpl* parentStorage,
4775 ULONG propertyIndex)
4777 SmallBlockChainStream* newStream;
4779 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4781 newStream->parentStorage = parentStorage;
4782 newStream->ownerPropertyIndex = propertyIndex;
4784 return newStream;
4787 void SmallBlockChainStream_Destroy(
4788 SmallBlockChainStream* This)
4790 HeapFree(GetProcessHeap(), 0, This);
4793 /******************************************************************************
4794 * SmallBlockChainStream_GetHeadOfChain
4796 * Returns the head of this chain of small blocks.
4798 ULONG SmallBlockChainStream_GetHeadOfChain(
4799 SmallBlockChainStream* This)
4801 StgProperty chainProperty;
4802 BOOL readSuccessful;
4804 if (This->ownerPropertyIndex)
4806 readSuccessful = StorageImpl_ReadProperty(
4807 This->parentStorage,
4808 This->ownerPropertyIndex,
4809 &chainProperty);
4811 if (readSuccessful)
4813 return chainProperty.startingBlock;
4818 return BLOCK_END_OF_CHAIN;
4821 /******************************************************************************
4822 * SmallBlockChainStream_GetNextBlockInChain
4824 * Returns the index of the next small block in this chain.
4826 * Return Values:
4827 * - BLOCK_END_OF_CHAIN: end of this chain
4828 * - BLOCK_UNUSED: small block 'blockIndex' is free
4830 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4831 SmallBlockChainStream* This,
4832 ULONG blockIndex,
4833 ULONG* nextBlockInChain)
4835 ULARGE_INTEGER offsetOfBlockInDepot;
4836 DWORD buffer;
4837 ULONG bytesRead;
4838 BOOL success;
4840 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4842 offsetOfBlockInDepot.u.HighPart = 0;
4843 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4846 * Read those bytes in the buffer from the small block file.
4848 success = BlockChainStream_ReadAt(
4849 This->parentStorage->smallBlockDepotChain,
4850 offsetOfBlockInDepot,
4851 sizeof(DWORD),
4852 &buffer,
4853 &bytesRead);
4855 if (success)
4857 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4858 return S_OK;
4861 return STG_E_READFAULT;
4864 /******************************************************************************
4865 * SmallBlockChainStream_SetNextBlockInChain
4867 * Writes the index of the next block of the specified block in the small
4868 * block depot.
4869 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4870 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4872 void SmallBlockChainStream_SetNextBlockInChain(
4873 SmallBlockChainStream* This,
4874 ULONG blockIndex,
4875 ULONG nextBlock)
4877 ULARGE_INTEGER offsetOfBlockInDepot;
4878 DWORD buffer;
4879 ULONG bytesWritten;
4881 offsetOfBlockInDepot.u.HighPart = 0;
4882 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4884 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4887 * Read those bytes in the buffer from the small block file.
4889 BlockChainStream_WriteAt(
4890 This->parentStorage->smallBlockDepotChain,
4891 offsetOfBlockInDepot,
4892 sizeof(DWORD),
4893 &buffer,
4894 &bytesWritten);
4897 /******************************************************************************
4898 * SmallBlockChainStream_FreeBlock
4900 * Flag small block 'blockIndex' as free in the small block depot.
4902 void SmallBlockChainStream_FreeBlock(
4903 SmallBlockChainStream* This,
4904 ULONG blockIndex)
4906 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4909 /******************************************************************************
4910 * SmallBlockChainStream_GetNextFreeBlock
4912 * Returns the index of a free small block. The small block depot will be
4913 * enlarged if necessary. The small block chain will also be enlarged if
4914 * necessary.
4916 ULONG SmallBlockChainStream_GetNextFreeBlock(
4917 SmallBlockChainStream* This)
4919 ULARGE_INTEGER offsetOfBlockInDepot;
4920 DWORD buffer;
4921 ULONG bytesRead;
4922 ULONG blockIndex = 0;
4923 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4924 BOOL success = TRUE;
4925 ULONG smallBlocksPerBigBlock;
4927 offsetOfBlockInDepot.u.HighPart = 0;
4930 * Scan the small block depot for a free block
4932 while (nextBlockIndex != BLOCK_UNUSED)
4934 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4936 success = BlockChainStream_ReadAt(
4937 This->parentStorage->smallBlockDepotChain,
4938 offsetOfBlockInDepot,
4939 sizeof(DWORD),
4940 &buffer,
4941 &bytesRead);
4944 * If we run out of space for the small block depot, enlarge it
4946 if (success)
4948 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
4950 if (nextBlockIndex != BLOCK_UNUSED)
4951 blockIndex++;
4953 else
4955 ULONG count =
4956 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4958 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4959 ULONG nextBlock, newsbdIndex;
4960 BYTE* smallBlockDepot;
4962 nextBlock = sbdIndex;
4963 while (nextBlock != BLOCK_END_OF_CHAIN)
4965 sbdIndex = nextBlock;
4966 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4969 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4970 if (sbdIndex != BLOCK_END_OF_CHAIN)
4971 StorageImpl_SetNextBlockInChain(
4972 This->parentStorage,
4973 sbdIndex,
4974 newsbdIndex);
4976 StorageImpl_SetNextBlockInChain(
4977 This->parentStorage,
4978 newsbdIndex,
4979 BLOCK_END_OF_CHAIN);
4982 * Initialize all the small blocks to free
4984 smallBlockDepot =
4985 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4987 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4988 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4990 if (count == 0)
4993 * We have just created the small block depot.
4995 StgProperty rootProp;
4996 ULONG sbStartIndex;
4999 * Save it in the header
5001 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5002 StorageImpl_SaveFileHeader(This->parentStorage);
5005 * And allocate the first big block that will contain small blocks
5007 sbStartIndex =
5008 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5010 StorageImpl_SetNextBlockInChain(
5011 This->parentStorage,
5012 sbStartIndex,
5013 BLOCK_END_OF_CHAIN);
5015 StorageImpl_ReadProperty(
5016 This->parentStorage,
5017 This->parentStorage->base.rootPropertySetIndex,
5018 &rootProp);
5020 rootProp.startingBlock = sbStartIndex;
5021 rootProp.size.u.HighPart = 0;
5022 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5024 StorageImpl_WriteProperty(
5025 This->parentStorage,
5026 This->parentStorage->base.rootPropertySetIndex,
5027 &rootProp);
5032 smallBlocksPerBigBlock =
5033 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5036 * Verify if we have to allocate big blocks to contain small blocks
5038 if (blockIndex % smallBlocksPerBigBlock == 0)
5040 StgProperty rootProp;
5041 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5043 StorageImpl_ReadProperty(
5044 This->parentStorage,
5045 This->parentStorage->base.rootPropertySetIndex,
5046 &rootProp);
5048 if (rootProp.size.u.LowPart <
5049 (blocksRequired * This->parentStorage->bigBlockSize))
5051 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5053 BlockChainStream_SetSize(
5054 This->parentStorage->smallBlockRootChain,
5055 rootProp.size);
5057 StorageImpl_WriteProperty(
5058 This->parentStorage,
5059 This->parentStorage->base.rootPropertySetIndex,
5060 &rootProp);
5064 return blockIndex;
5067 /******************************************************************************
5068 * SmallBlockChainStream_ReadAt
5070 * Reads a specified number of bytes from this chain at the specified offset.
5071 * bytesRead may be NULL.
5072 * Failure will be returned if the specified number of bytes has not been read.
5074 BOOL SmallBlockChainStream_ReadAt(
5075 SmallBlockChainStream* This,
5076 ULARGE_INTEGER offset,
5077 ULONG size,
5078 void* buffer,
5079 ULONG* bytesRead)
5081 ULARGE_INTEGER offsetInBigBlockFile;
5082 ULONG blockNoInSequence =
5083 offset.u.LowPart / This->parentStorage->smallBlockSize;
5085 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5086 ULONG bytesToReadInBuffer;
5087 ULONG blockIndex;
5088 ULONG bytesReadFromBigBlockFile;
5089 BYTE* bufferWalker;
5092 * This should never happen on a small block file.
5094 assert(offset.u.HighPart==0);
5097 * Find the first block in the stream that contains part of the buffer.
5099 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5101 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5103 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5104 &blockIndex)))
5105 return FALSE;
5106 blockNoInSequence--;
5110 * Start reading the buffer.
5112 *bytesRead = 0;
5113 bufferWalker = buffer;
5115 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5118 * Calculate how many bytes we can copy from this small block.
5120 bytesToReadInBuffer =
5121 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5124 * Calculate the offset of the small block in the small block file.
5126 offsetInBigBlockFile.u.HighPart = 0;
5127 offsetInBigBlockFile.u.LowPart =
5128 blockIndex * This->parentStorage->smallBlockSize;
5130 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5133 * Read those bytes in the buffer from the small block file.
5135 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5136 offsetInBigBlockFile,
5137 bytesToReadInBuffer,
5138 bufferWalker,
5139 &bytesReadFromBigBlockFile);
5141 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5144 * Step to the next big block.
5146 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5147 return FALSE;
5148 bufferWalker += bytesToReadInBuffer;
5149 size -= bytesToReadInBuffer;
5150 *bytesRead += bytesToReadInBuffer;
5151 offsetInBlock = 0; /* There is no offset on the next block */
5154 return (size == 0);
5157 /******************************************************************************
5158 * SmallBlockChainStream_WriteAt
5160 * Writes the specified number of bytes to this chain at the specified offset.
5161 * bytesWritten may be NULL.
5162 * Will fail if not all specified number of bytes have been written.
5164 BOOL SmallBlockChainStream_WriteAt(
5165 SmallBlockChainStream* This,
5166 ULARGE_INTEGER offset,
5167 ULONG size,
5168 const void* buffer,
5169 ULONG* bytesWritten)
5171 ULARGE_INTEGER offsetInBigBlockFile;
5172 ULONG blockNoInSequence =
5173 offset.u.LowPart / This->parentStorage->smallBlockSize;
5175 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5176 ULONG bytesToWriteInBuffer;
5177 ULONG blockIndex;
5178 ULONG bytesWrittenFromBigBlockFile;
5179 const BYTE* bufferWalker;
5182 * This should never happen on a small block file.
5184 assert(offset.u.HighPart==0);
5187 * Find the first block in the stream that contains part of the buffer.
5189 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5191 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5193 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5194 return FALSE;
5195 blockNoInSequence--;
5199 * Start writing the buffer.
5201 * Here, I'm casting away the constness on the buffer variable
5202 * This is OK since we don't intend to modify that buffer.
5204 *bytesWritten = 0;
5205 bufferWalker = (const BYTE*)buffer;
5206 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5209 * Calculate how many bytes we can copy to this small block.
5211 bytesToWriteInBuffer =
5212 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5215 * Calculate the offset of the small block in the small block file.
5217 offsetInBigBlockFile.u.HighPart = 0;
5218 offsetInBigBlockFile.u.LowPart =
5219 blockIndex * This->parentStorage->smallBlockSize;
5221 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5224 * Write those bytes in the buffer to the small block file.
5226 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5227 offsetInBigBlockFile,
5228 bytesToWriteInBuffer,
5229 bufferWalker,
5230 &bytesWrittenFromBigBlockFile);
5232 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5235 * Step to the next big block.
5237 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5238 &blockIndex)))
5239 return FALSE;
5240 bufferWalker += bytesToWriteInBuffer;
5241 size -= bytesToWriteInBuffer;
5242 *bytesWritten += bytesToWriteInBuffer;
5243 offsetInBlock = 0; /* There is no offset on the next block */
5246 return (size == 0);
5249 /******************************************************************************
5250 * SmallBlockChainStream_Shrink
5252 * Shrinks this chain in the small block depot.
5254 BOOL SmallBlockChainStream_Shrink(
5255 SmallBlockChainStream* This,
5256 ULARGE_INTEGER newSize)
5258 ULONG blockIndex, extraBlock;
5259 ULONG numBlocks;
5260 ULONG count = 0;
5262 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5264 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5265 numBlocks++;
5267 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5270 * Go to the new end of chain
5272 while (count < numBlocks)
5274 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5275 &blockIndex)))
5276 return FALSE;
5277 count++;
5281 * If the count is 0, we have a special case, the head of the chain was
5282 * just freed.
5284 if (count == 0)
5286 StgProperty chainProp;
5288 StorageImpl_ReadProperty(This->parentStorage,
5289 This->ownerPropertyIndex,
5290 &chainProp);
5292 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5294 StorageImpl_WriteProperty(This->parentStorage,
5295 This->ownerPropertyIndex,
5296 &chainProp);
5299 * We start freeing the chain at the head block.
5301 extraBlock = blockIndex;
5303 else
5305 /* Get the next block before marking the new end */
5306 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5307 &extraBlock)))
5308 return FALSE;
5310 /* Mark the new end of chain */
5311 SmallBlockChainStream_SetNextBlockInChain(
5312 This,
5313 blockIndex,
5314 BLOCK_END_OF_CHAIN);
5318 * Mark the extra blocks as free
5320 while (extraBlock != BLOCK_END_OF_CHAIN)
5322 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5323 &blockIndex)))
5324 return FALSE;
5325 SmallBlockChainStream_FreeBlock(This, extraBlock);
5326 extraBlock = blockIndex;
5329 return TRUE;
5332 /******************************************************************************
5333 * SmallBlockChainStream_Enlarge
5335 * Grows this chain in the small block depot.
5337 BOOL SmallBlockChainStream_Enlarge(
5338 SmallBlockChainStream* This,
5339 ULARGE_INTEGER newSize)
5341 ULONG blockIndex, currentBlock;
5342 ULONG newNumBlocks;
5343 ULONG oldNumBlocks = 0;
5345 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5348 * Empty chain
5350 if (blockIndex == BLOCK_END_OF_CHAIN)
5353 StgProperty chainProp;
5355 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5356 &chainProp);
5358 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5360 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5361 &chainProp);
5363 blockIndex = chainProp.startingBlock;
5364 SmallBlockChainStream_SetNextBlockInChain(
5365 This,
5366 blockIndex,
5367 BLOCK_END_OF_CHAIN);
5370 currentBlock = blockIndex;
5373 * Figure out how many blocks are needed to contain this stream
5375 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5377 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5378 newNumBlocks++;
5381 * Go to the current end of chain
5383 while (blockIndex != BLOCK_END_OF_CHAIN)
5385 oldNumBlocks++;
5386 currentBlock = blockIndex;
5387 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5388 return FALSE;
5392 * Add new blocks to the chain
5394 while (oldNumBlocks < newNumBlocks)
5396 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5397 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5399 SmallBlockChainStream_SetNextBlockInChain(
5400 This,
5401 blockIndex,
5402 BLOCK_END_OF_CHAIN);
5404 currentBlock = blockIndex;
5405 oldNumBlocks++;
5408 return TRUE;
5411 /******************************************************************************
5412 * SmallBlockChainStream_GetCount
5414 * Returns the number of blocks that comprises this chain.
5415 * This is not the size of this chain as the last block may not be full!
5417 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5419 ULONG blockIndex;
5420 ULONG count = 0;
5422 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5424 while (blockIndex != BLOCK_END_OF_CHAIN)
5426 count++;
5428 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5429 return 0;
5432 return count;
5435 /******************************************************************************
5436 * SmallBlockChainStream_SetSize
5438 * Sets the size of this stream.
5439 * The file will grow if we grow the chain.
5441 * TODO: Free the actual blocks in the file when we shrink the chain.
5442 * Currently, the blocks are still in the file. So the file size
5443 * doesn't shrink even if we shrink streams.
5445 BOOL SmallBlockChainStream_SetSize(
5446 SmallBlockChainStream* This,
5447 ULARGE_INTEGER newSize)
5449 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5451 if (newSize.u.LowPart == size.u.LowPart)
5452 return TRUE;
5454 if (newSize.u.LowPart < size.u.LowPart)
5456 SmallBlockChainStream_Shrink(This, newSize);
5458 else
5460 SmallBlockChainStream_Enlarge(This, newSize);
5463 return TRUE;
5466 /******************************************************************************
5467 * SmallBlockChainStream_GetSize
5469 * Returns the size of this chain.
5471 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5473 StgProperty chainProperty;
5475 StorageImpl_ReadProperty(
5476 This->parentStorage,
5477 This->ownerPropertyIndex,
5478 &chainProperty);
5480 return chainProperty.size;
5483 /******************************************************************************
5484 * StgCreateDocfile [OLE32.@]
5486 HRESULT WINAPI StgCreateDocfile(
5487 LPCOLESTR pwcsName,
5488 DWORD grfMode,
5489 DWORD reserved,
5490 IStorage **ppstgOpen)
5492 StorageImpl* newStorage = 0;
5493 HANDLE hFile = INVALID_HANDLE_VALUE;
5494 HRESULT hr = STG_E_INVALIDFLAG;
5495 DWORD shareMode;
5496 DWORD accessMode;
5497 DWORD creationMode;
5498 DWORD fileAttributes;
5499 WCHAR tempFileName[MAX_PATH];
5501 TRACE("(%s, %lx, %ld, %p)\n",
5502 debugstr_w(pwcsName), grfMode,
5503 reserved, ppstgOpen);
5506 * Validate the parameters
5508 if (ppstgOpen == 0)
5509 return STG_E_INVALIDPOINTER;
5510 if (reserved != 0)
5511 return STG_E_INVALIDPARAMETER;
5514 * Validate the STGM flags
5516 if ( FAILED( validateSTGM(grfMode) ))
5517 goto end;
5519 /* StgCreateDocFile always opens for write */
5520 switch(STGM_ACCESS_MODE(grfMode))
5522 case STGM_WRITE:
5523 case STGM_READWRITE:
5524 break;
5525 default:
5526 goto end;
5529 /* can't share write */
5530 switch(STGM_SHARE_MODE(grfMode))
5532 case STGM_SHARE_EXCLUSIVE:
5533 case STGM_SHARE_DENY_WRITE:
5534 break;
5535 default:
5536 goto end;
5539 /* shared reading requires transacted mode */
5540 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5541 !(grfMode&STGM_TRANSACTED) )
5542 goto end;
5545 * Generate a unique name.
5547 if (pwcsName == 0)
5549 WCHAR tempPath[MAX_PATH];
5550 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5552 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
5553 goto end;
5555 memset(tempPath, 0, sizeof(tempPath));
5556 memset(tempFileName, 0, sizeof(tempFileName));
5558 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5559 tempPath[0] = '.';
5561 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5562 pwcsName = tempFileName;
5563 else
5565 hr = STG_E_INSUFFICIENTMEMORY;
5566 goto end;
5569 creationMode = TRUNCATE_EXISTING;
5571 else
5573 creationMode = GetCreationModeFromSTGM(grfMode);
5577 * Interpret the STGM value grfMode
5579 shareMode = GetShareModeFromSTGM(grfMode);
5580 accessMode = GetAccessModeFromSTGM(grfMode);
5582 if (grfMode & STGM_DELETEONRELEASE)
5583 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5584 else
5585 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5587 if (grfMode & STGM_TRANSACTED)
5588 FIXME("Transacted mode not implemented.\n");
5591 * Initialize the "out" parameter.
5593 *ppstgOpen = 0;
5595 hFile = CreateFileW(pwcsName,
5596 accessMode,
5597 shareMode,
5598 NULL,
5599 creationMode,
5600 fileAttributes,
5603 if (hFile == INVALID_HANDLE_VALUE)
5605 if(GetLastError() == ERROR_FILE_EXISTS)
5606 hr = STG_E_FILEALREADYEXISTS;
5607 else
5608 hr = E_FAIL;
5609 goto end;
5613 * Allocate and initialize the new IStorage32object.
5615 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5617 if (newStorage == 0)
5619 hr = STG_E_INSUFFICIENTMEMORY;
5620 goto end;
5623 hr = StorageImpl_Construct(
5624 newStorage,
5625 hFile,
5626 pwcsName,
5627 NULL,
5628 grfMode,
5629 TRUE,
5630 TRUE);
5632 if (FAILED(hr))
5634 HeapFree(GetProcessHeap(), 0, newStorage);
5635 goto end;
5639 * Get an "out" pointer for the caller.
5641 hr = StorageBaseImpl_QueryInterface(
5642 (IStorage*)newStorage,
5643 (REFIID)&IID_IStorage,
5644 (void**)ppstgOpen);
5645 end:
5646 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5648 return hr;
5651 /******************************************************************************
5652 * StgCreateStorageEx [OLE32.@]
5654 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5656 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5657 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5659 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5661 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5662 return STG_E_INVALIDPARAMETER;
5665 if (stgfmt != STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5667 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5668 return STG_E_INVALIDPARAMETER;
5671 if (stgfmt == STGFMT_FILE)
5673 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5674 return STG_E_INVALIDPARAMETER;
5677 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5679 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5680 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5683 ERR("Invalid stgfmt argument\n");
5684 return STG_E_INVALIDPARAMETER;
5687 /******************************************************************************
5688 * StgCreatePropSetStg [OLE32.@]
5690 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5691 IPropertySetStorage **ppPropSetStg)
5693 HRESULT hr;
5695 TRACE("(%p, 0x%lx, %p): stub\n", pstg, reserved, ppPropSetStg);
5696 if (reserved)
5697 hr = STG_E_INVALIDPARAMETER;
5698 else
5699 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5700 (void**)ppPropSetStg);
5701 return hr;
5704 /******************************************************************************
5705 * StgOpenStorageEx [OLE32.@]
5707 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5709 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5710 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5712 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5714 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5715 return STG_E_INVALIDPARAMETER;
5718 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5720 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5721 return STG_E_INVALIDPARAMETER;
5724 if (stgfmt == STGFMT_FILE)
5726 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5727 return STG_E_INVALIDPARAMETER;
5730 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE || stgfmt == STGFMT_ANY)
5732 if (stgfmt == STGFMT_ANY)
5733 WARN("STGFMT_ANY assuming storage\n");
5734 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5735 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5738 ERR("Invalid stgfmt argument\n");
5739 return STG_E_INVALIDPARAMETER;
5743 /******************************************************************************
5744 * StgOpenStorage [OLE32.@]
5746 HRESULT WINAPI StgOpenStorage(
5747 const OLECHAR *pwcsName,
5748 IStorage *pstgPriority,
5749 DWORD grfMode,
5750 SNB snbExclude,
5751 DWORD reserved,
5752 IStorage **ppstgOpen)
5754 StorageImpl* newStorage = 0;
5755 HRESULT hr = S_OK;
5756 HANDLE hFile = 0;
5757 DWORD shareMode;
5758 DWORD accessMode;
5759 WCHAR fullname[MAX_PATH];
5760 DWORD length;
5762 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5763 debugstr_w(pwcsName), pstgPriority, grfMode,
5764 snbExclude, reserved, ppstgOpen);
5767 * Perform sanity checks
5769 if (pwcsName == 0)
5771 hr = STG_E_INVALIDNAME;
5772 goto end;
5775 if (ppstgOpen == 0)
5777 hr = STG_E_INVALIDPOINTER;
5778 goto end;
5781 if (reserved)
5783 hr = STG_E_INVALIDPARAMETER;
5784 goto end;
5788 * Validate the sharing mode
5790 switch(STGM_SHARE_MODE(grfMode))
5792 case STGM_SHARE_EXCLUSIVE:
5793 case STGM_SHARE_DENY_WRITE:
5794 break;
5795 default:
5796 hr = STG_E_INVALIDFLAG;
5797 goto end;
5801 * Validate the STGM flags
5803 if ( FAILED( validateSTGM(grfMode) ) ||
5804 (grfMode&STGM_CREATE))
5806 hr = STG_E_INVALIDFLAG;
5807 goto end;
5810 /* shared reading requires transacted mode */
5811 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5812 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5813 !(grfMode&STGM_TRANSACTED) )
5815 hr = STG_E_INVALIDFLAG;
5816 goto end;
5820 * Interpret the STGM value grfMode
5822 shareMode = GetShareModeFromSTGM(grfMode);
5823 accessMode = GetAccessModeFromSTGM(grfMode);
5826 * Initialize the "out" parameter.
5828 *ppstgOpen = 0;
5830 hFile = CreateFileW( pwcsName,
5831 accessMode,
5832 shareMode,
5833 NULL,
5834 OPEN_EXISTING,
5835 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5838 if (hFile==INVALID_HANDLE_VALUE)
5840 DWORD last_error = GetLastError();
5842 hr = E_FAIL;
5844 switch (last_error)
5846 case ERROR_FILE_NOT_FOUND:
5847 hr = STG_E_FILENOTFOUND;
5848 break;
5850 case ERROR_PATH_NOT_FOUND:
5851 hr = STG_E_PATHNOTFOUND;
5852 break;
5854 case ERROR_ACCESS_DENIED:
5855 case ERROR_WRITE_PROTECT:
5856 hr = STG_E_ACCESSDENIED;
5857 break;
5859 case ERROR_SHARING_VIOLATION:
5860 hr = STG_E_SHAREVIOLATION;
5861 break;
5863 default:
5864 hr = E_FAIL;
5867 goto end;
5871 * Refuse to open the file if it's too small to be a structured storage file
5872 * FIXME: verify the file when reading instead of here
5874 length = GetFileSize(hFile, NULL);
5875 if (length < 0x100)
5877 CloseHandle(hFile);
5878 hr = STG_E_FILEALREADYEXISTS;
5879 goto end;
5883 * Allocate and initialize the new IStorage32object.
5885 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5887 if (newStorage == 0)
5889 hr = STG_E_INSUFFICIENTMEMORY;
5890 goto end;
5893 /* if the file's length was zero, initialize the storage */
5894 hr = StorageImpl_Construct(
5895 newStorage,
5896 hFile,
5897 pwcsName,
5898 NULL,
5899 grfMode,
5900 TRUE,
5901 FALSE );
5903 if (FAILED(hr))
5905 HeapFree(GetProcessHeap(), 0, newStorage);
5907 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5909 if(hr == STG_E_INVALIDHEADER)
5910 hr = STG_E_FILEALREADYEXISTS;
5911 goto end;
5914 /* prepare the file name string given in lieu of the root property name */
5915 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5916 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5917 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5920 * Get an "out" pointer for the caller.
5922 hr = StorageBaseImpl_QueryInterface(
5923 (IStorage*)newStorage,
5924 (REFIID)&IID_IStorage,
5925 (void**)ppstgOpen);
5927 end:
5928 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5929 return hr;
5932 /******************************************************************************
5933 * StgCreateDocfileOnILockBytes [OLE32.@]
5935 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5936 ILockBytes *plkbyt,
5937 DWORD grfMode,
5938 DWORD reserved,
5939 IStorage** ppstgOpen)
5941 StorageImpl* newStorage = 0;
5942 HRESULT hr = S_OK;
5945 * Validate the parameters
5947 if ((ppstgOpen == 0) || (plkbyt == 0))
5948 return STG_E_INVALIDPOINTER;
5951 * Allocate and initialize the new IStorage object.
5953 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5955 if (newStorage == 0)
5956 return STG_E_INSUFFICIENTMEMORY;
5958 hr = StorageImpl_Construct(
5959 newStorage,
5962 plkbyt,
5963 grfMode,
5964 FALSE,
5965 TRUE);
5967 if (FAILED(hr))
5969 HeapFree(GetProcessHeap(), 0, newStorage);
5970 return hr;
5974 * Get an "out" pointer for the caller.
5976 hr = StorageBaseImpl_QueryInterface(
5977 (IStorage*)newStorage,
5978 (REFIID)&IID_IStorage,
5979 (void**)ppstgOpen);
5981 return hr;
5984 /******************************************************************************
5985 * StgOpenStorageOnILockBytes [OLE32.@]
5987 HRESULT WINAPI StgOpenStorageOnILockBytes(
5988 ILockBytes *plkbyt,
5989 IStorage *pstgPriority,
5990 DWORD grfMode,
5991 SNB snbExclude,
5992 DWORD reserved,
5993 IStorage **ppstgOpen)
5995 StorageImpl* newStorage = 0;
5996 HRESULT hr = S_OK;
5999 * Perform a sanity check
6001 if ((plkbyt == 0) || (ppstgOpen == 0))
6002 return STG_E_INVALIDPOINTER;
6005 * Validate the STGM flags
6007 if ( FAILED( validateSTGM(grfMode) ))
6008 return STG_E_INVALIDFLAG;
6011 * Initialize the "out" parameter.
6013 *ppstgOpen = 0;
6016 * Allocate and initialize the new IStorage object.
6018 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6020 if (newStorage == 0)
6021 return STG_E_INSUFFICIENTMEMORY;
6023 hr = StorageImpl_Construct(
6024 newStorage,
6027 plkbyt,
6028 grfMode,
6029 FALSE,
6030 FALSE);
6032 if (FAILED(hr))
6034 HeapFree(GetProcessHeap(), 0, newStorage);
6035 return hr;
6039 * Get an "out" pointer for the caller.
6041 hr = StorageBaseImpl_QueryInterface(
6042 (IStorage*)newStorage,
6043 (REFIID)&IID_IStorage,
6044 (void**)ppstgOpen);
6046 return hr;
6049 /******************************************************************************
6050 * StgSetTimes [ole32.@]
6051 * StgSetTimes [OLE32.@]
6055 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6056 FILETIME const *patime, FILETIME const *pmtime)
6058 IStorage *stg = NULL;
6059 HRESULT r;
6061 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6063 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6064 0, 0, &stg);
6065 if( SUCCEEDED(r) )
6067 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6068 IStorage_Release(stg);
6071 return r;
6074 /******************************************************************************
6075 * StgIsStorageILockBytes [OLE32.@]
6077 * Determines if the ILockBytes contains a storage object.
6079 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6081 BYTE sig[8];
6082 ULARGE_INTEGER offset;
6084 offset.u.HighPart = 0;
6085 offset.u.LowPart = 0;
6087 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6089 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6090 return S_OK;
6092 return S_FALSE;
6095 /******************************************************************************
6096 * WriteClassStg [OLE32.@]
6098 * This method will store the specified CLSID in the specified storage object
6100 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6102 HRESULT hRes;
6104 assert(pStg != 0);
6106 hRes = IStorage_SetClass(pStg, rclsid);
6108 return hRes;
6111 /***********************************************************************
6112 * ReadClassStg (OLE32.@)
6114 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
6116 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6118 STATSTG pstatstg;
6119 HRESULT hRes;
6121 TRACE("()\n");
6123 if(pclsid==NULL)
6124 return E_POINTER;
6126 * read a STATSTG structure (contains the clsid) from the storage
6128 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6130 if(SUCCEEDED(hRes))
6131 *pclsid=pstatstg.clsid;
6133 return hRes;
6136 /***********************************************************************
6137 * OleLoadFromStream (OLE32.@)
6139 * This function loads an object from stream
6141 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6143 CLSID clsid;
6144 HRESULT res;
6145 LPPERSISTSTREAM xstm;
6147 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6149 res=ReadClassStm(pStm,&clsid);
6150 if (!SUCCEEDED(res))
6151 return res;
6152 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6153 if (!SUCCEEDED(res))
6154 return res;
6155 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6156 if (!SUCCEEDED(res)) {
6157 IUnknown_Release((IUnknown*)*ppvObj);
6158 return res;
6160 res=IPersistStream_Load(xstm,pStm);
6161 IPersistStream_Release(xstm);
6162 /* FIXME: all refcounts ok at this point? I think they should be:
6163 * pStm : unchanged
6164 * ppvObj : 1
6165 * xstm : 0 (released)
6167 return res;
6170 /***********************************************************************
6171 * OleSaveToStream (OLE32.@)
6173 * This function saves an object with the IPersistStream interface on it
6174 * to the specified stream.
6176 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6179 CLSID clsid;
6180 HRESULT res;
6182 TRACE("(%p,%p)\n",pPStm,pStm);
6184 res=IPersistStream_GetClassID(pPStm,&clsid);
6186 if (SUCCEEDED(res)){
6188 res=WriteClassStm(pStm,&clsid);
6190 if (SUCCEEDED(res))
6192 res=IPersistStream_Save(pPStm,pStm,TRUE);
6195 TRACE("Finished Save\n");
6196 return res;
6199 /****************************************************************************
6200 * This method validate a STGM parameter that can contain the values below
6202 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6203 * The stgm values contained in 0xffff0000 are bitmasks.
6205 * STGM_DIRECT 0x00000000
6206 * STGM_TRANSACTED 0x00010000
6207 * STGM_SIMPLE 0x08000000
6209 * STGM_READ 0x00000000
6210 * STGM_WRITE 0x00000001
6211 * STGM_READWRITE 0x00000002
6213 * STGM_SHARE_DENY_NONE 0x00000040
6214 * STGM_SHARE_DENY_READ 0x00000030
6215 * STGM_SHARE_DENY_WRITE 0x00000020
6216 * STGM_SHARE_EXCLUSIVE 0x00000010
6218 * STGM_PRIORITY 0x00040000
6219 * STGM_DELETEONRELEASE 0x04000000
6221 * STGM_CREATE 0x00001000
6222 * STGM_CONVERT 0x00020000
6223 * STGM_FAILIFTHERE 0x00000000
6225 * STGM_NOSCRATCH 0x00100000
6226 * STGM_NOSNAPSHOT 0x00200000
6228 static HRESULT validateSTGM(DWORD stgm)
6230 DWORD access = STGM_ACCESS_MODE(stgm);
6231 DWORD share = STGM_SHARE_MODE(stgm);
6232 DWORD create = STGM_CREATE_MODE(stgm);
6234 if (stgm&~STGM_KNOWN_FLAGS)
6236 ERR("unknown flags %08lx\n", stgm);
6237 return E_FAIL;
6240 switch (access)
6242 case STGM_READ:
6243 case STGM_WRITE:
6244 case STGM_READWRITE:
6245 break;
6246 default:
6247 return E_FAIL;
6250 switch (share)
6252 case STGM_SHARE_DENY_NONE:
6253 case STGM_SHARE_DENY_READ:
6254 case STGM_SHARE_DENY_WRITE:
6255 case STGM_SHARE_EXCLUSIVE:
6256 break;
6257 default:
6258 return E_FAIL;
6261 switch (create)
6263 case STGM_CREATE:
6264 case STGM_FAILIFTHERE:
6265 break;
6266 default:
6267 return E_FAIL;
6271 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6273 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6274 return E_FAIL;
6277 * STGM_CREATE | STGM_CONVERT
6278 * if both are false, STGM_FAILIFTHERE is set to TRUE
6280 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6281 return E_FAIL;
6284 * STGM_NOSCRATCH requires STGM_TRANSACTED
6286 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6287 return E_FAIL;
6290 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6291 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6293 if ( (stgm & STGM_NOSNAPSHOT) &&
6294 (!(stgm & STGM_TRANSACTED) ||
6295 share == STGM_SHARE_EXCLUSIVE ||
6296 share == STGM_SHARE_DENY_WRITE) )
6297 return E_FAIL;
6299 return S_OK;
6302 /****************************************************************************
6303 * GetShareModeFromSTGM
6305 * This method will return a share mode flag from a STGM value.
6306 * The STGM value is assumed valid.
6308 static DWORD GetShareModeFromSTGM(DWORD stgm)
6310 switch (STGM_SHARE_MODE(stgm))
6312 case STGM_SHARE_DENY_NONE:
6313 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6314 case STGM_SHARE_DENY_READ:
6315 return FILE_SHARE_WRITE;
6316 case STGM_SHARE_DENY_WRITE:
6317 return FILE_SHARE_READ;
6318 case STGM_SHARE_EXCLUSIVE:
6319 return 0;
6321 ERR("Invalid share mode!\n");
6322 assert(0);
6323 return 0;
6326 /****************************************************************************
6327 * GetAccessModeFromSTGM
6329 * This method will return an access mode flag from a STGM value.
6330 * The STGM value is assumed valid.
6332 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6334 switch (STGM_ACCESS_MODE(stgm))
6336 case STGM_READ:
6337 return GENERIC_READ;
6338 case STGM_WRITE:
6339 case STGM_READWRITE:
6340 return GENERIC_READ | GENERIC_WRITE;
6342 ERR("Invalid access mode!\n");
6343 assert(0);
6344 return 0;
6347 /****************************************************************************
6348 * GetCreationModeFromSTGM
6350 * This method will return a creation mode flag from a STGM value.
6351 * The STGM value is assumed valid.
6353 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6355 switch(STGM_CREATE_MODE(stgm))
6357 case STGM_CREATE:
6358 return CREATE_ALWAYS;
6359 case STGM_CONVERT:
6360 FIXME("STGM_CONVERT not implemented!\n");
6361 return CREATE_NEW;
6362 case STGM_FAILIFTHERE:
6363 return CREATE_NEW;
6365 ERR("Invalid create mode!\n");
6366 assert(0);
6367 return 0;
6371 /*************************************************************************
6372 * OLECONVERT_LoadOLE10 [Internal]
6374 * Loads the OLE10 STREAM to memory
6376 * PARAMS
6377 * pOleStream [I] The OLESTREAM
6378 * pData [I] Data Structure for the OLESTREAM Data
6380 * RETURNS
6381 * Success: S_OK
6382 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6383 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6385 * NOTES
6386 * This function is used by OleConvertOLESTREAMToIStorage only.
6388 * Memory allocated for pData must be freed by the caller
6390 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6392 DWORD dwSize;
6393 HRESULT hRes = S_OK;
6394 int nTryCnt=0;
6395 int max_try = 6;
6397 pData->pData = NULL;
6398 pData->pstrOleObjFileName = (CHAR *) NULL;
6400 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6402 /* Get the OleID */
6403 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6404 if(dwSize != sizeof(pData->dwOleID))
6406 hRes = CONVERT10_E_OLESTREAM_GET;
6408 else if(pData->dwOleID != OLESTREAM_ID)
6410 hRes = CONVERT10_E_OLESTREAM_FMT;
6412 else
6414 hRes = S_OK;
6415 break;
6419 if(hRes == S_OK)
6421 /* Get the TypeID...more info needed for this field */
6422 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6423 if(dwSize != sizeof(pData->dwTypeID))
6425 hRes = CONVERT10_E_OLESTREAM_GET;
6428 if(hRes == S_OK)
6430 if(pData->dwTypeID != 0)
6432 /* Get the length of the OleTypeName */
6433 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6434 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6436 hRes = CONVERT10_E_OLESTREAM_GET;
6439 if(hRes == S_OK)
6441 if(pData->dwOleTypeNameLength > 0)
6443 /* Get the OleTypeName */
6444 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6445 if(dwSize != pData->dwOleTypeNameLength)
6447 hRes = CONVERT10_E_OLESTREAM_GET;
6451 if(bStrem1)
6453 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6454 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6456 hRes = CONVERT10_E_OLESTREAM_GET;
6458 if(hRes == S_OK)
6460 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6461 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6462 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6463 if(pData->pstrOleObjFileName)
6465 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6466 if(dwSize != pData->dwOleObjFileNameLength)
6468 hRes = CONVERT10_E_OLESTREAM_GET;
6471 else
6472 hRes = CONVERT10_E_OLESTREAM_GET;
6475 else
6477 /* Get the Width of the Metafile */
6478 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6479 if(dwSize != sizeof(pData->dwMetaFileWidth))
6481 hRes = CONVERT10_E_OLESTREAM_GET;
6483 if(hRes == S_OK)
6485 /* Get the Height of the Metafile */
6486 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6487 if(dwSize != sizeof(pData->dwMetaFileHeight))
6489 hRes = CONVERT10_E_OLESTREAM_GET;
6493 if(hRes == S_OK)
6495 /* Get the Length of the Data */
6496 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6497 if(dwSize != sizeof(pData->dwDataLength))
6499 hRes = CONVERT10_E_OLESTREAM_GET;
6503 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6505 if(!bStrem1) /* if it is a second OLE stream data */
6507 pData->dwDataLength -= 8;
6508 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6509 if(dwSize != sizeof(pData->strUnknown))
6511 hRes = CONVERT10_E_OLESTREAM_GET;
6515 if(hRes == S_OK)
6517 if(pData->dwDataLength > 0)
6519 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6521 /* Get Data (ex. IStorage, Metafile, or BMP) */
6522 if(pData->pData)
6524 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6525 if(dwSize != pData->dwDataLength)
6527 hRes = CONVERT10_E_OLESTREAM_GET;
6530 else
6532 hRes = CONVERT10_E_OLESTREAM_GET;
6538 return hRes;
6541 /*************************************************************************
6542 * OLECONVERT_SaveOLE10 [Internal]
6544 * Saves the OLE10 STREAM From memory
6546 * PARAMS
6547 * pData [I] Data Structure for the OLESTREAM Data
6548 * pOleStream [I] The OLESTREAM to save
6550 * RETURNS
6551 * Success: S_OK
6552 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6554 * NOTES
6555 * This function is used by OleConvertIStorageToOLESTREAM only.
6558 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6560 DWORD dwSize;
6561 HRESULT hRes = S_OK;
6564 /* Set the OleID */
6565 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6566 if(dwSize != sizeof(pData->dwOleID))
6568 hRes = CONVERT10_E_OLESTREAM_PUT;
6571 if(hRes == S_OK)
6573 /* Set the TypeID */
6574 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6575 if(dwSize != sizeof(pData->dwTypeID))
6577 hRes = CONVERT10_E_OLESTREAM_PUT;
6581 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6583 /* Set the Length of the OleTypeName */
6584 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6585 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6587 hRes = CONVERT10_E_OLESTREAM_PUT;
6590 if(hRes == S_OK)
6592 if(pData->dwOleTypeNameLength > 0)
6594 /* Set the OleTypeName */
6595 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6596 if(dwSize != pData->dwOleTypeNameLength)
6598 hRes = CONVERT10_E_OLESTREAM_PUT;
6603 if(hRes == S_OK)
6605 /* Set the width of the Metafile */
6606 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6607 if(dwSize != sizeof(pData->dwMetaFileWidth))
6609 hRes = CONVERT10_E_OLESTREAM_PUT;
6613 if(hRes == S_OK)
6615 /* Set the height of the Metafile */
6616 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6617 if(dwSize != sizeof(pData->dwMetaFileHeight))
6619 hRes = CONVERT10_E_OLESTREAM_PUT;
6623 if(hRes == S_OK)
6625 /* Set the length of the Data */
6626 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6627 if(dwSize != sizeof(pData->dwDataLength))
6629 hRes = CONVERT10_E_OLESTREAM_PUT;
6633 if(hRes == S_OK)
6635 if(pData->dwDataLength > 0)
6637 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6638 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6639 if(dwSize != pData->dwDataLength)
6641 hRes = CONVERT10_E_OLESTREAM_PUT;
6646 return hRes;
6649 /*************************************************************************
6650 * OLECONVERT_GetOLE20FromOLE10[Internal]
6652 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6653 * opens it, and copies the content to the dest IStorage for
6654 * OleConvertOLESTREAMToIStorage
6657 * PARAMS
6658 * pDestStorage [I] The IStorage to copy the data to
6659 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6660 * nBufferLength [I] The size of the buffer
6662 * RETURNS
6663 * Nothing
6665 * NOTES
6669 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6671 HRESULT hRes;
6672 HANDLE hFile;
6673 IStorage *pTempStorage;
6674 DWORD dwNumOfBytesWritten;
6675 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6676 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6678 /* Create a temp File */
6679 GetTempPathW(MAX_PATH, wstrTempDir);
6680 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6681 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6683 if(hFile != INVALID_HANDLE_VALUE)
6685 /* Write IStorage Data to File */
6686 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6687 CloseHandle(hFile);
6689 /* Open and copy temp storage to the Dest Storage */
6690 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6691 if(hRes == S_OK)
6693 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6694 StorageBaseImpl_Release(pTempStorage);
6696 DeleteFileW(wstrTempFile);
6701 /*************************************************************************
6702 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6704 * Saves the OLE10 STREAM From memory
6706 * PARAMS
6707 * pStorage [I] The Src IStorage to copy
6708 * pData [I] The Dest Memory to write to.
6710 * RETURNS
6711 * The size in bytes allocated for pData
6713 * NOTES
6714 * Memory allocated for pData must be freed by the caller
6716 * Used by OleConvertIStorageToOLESTREAM only.
6719 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6721 HANDLE hFile;
6722 HRESULT hRes;
6723 DWORD nDataLength = 0;
6724 IStorage *pTempStorage;
6725 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6726 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6728 *pData = NULL;
6730 /* Create temp Storage */
6731 GetTempPathW(MAX_PATH, wstrTempDir);
6732 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6733 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6735 if(hRes == S_OK)
6737 /* Copy Src Storage to the Temp Storage */
6738 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6739 StorageBaseImpl_Release(pTempStorage);
6741 /* Open Temp Storage as a file and copy to memory */
6742 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6743 if(hFile != INVALID_HANDLE_VALUE)
6745 nDataLength = GetFileSize(hFile, NULL);
6746 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6747 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6748 CloseHandle(hFile);
6750 DeleteFileW(wstrTempFile);
6752 return nDataLength;
6755 /*************************************************************************
6756 * OLECONVERT_CreateOleStream [Internal]
6758 * Creates the "\001OLE" stream in the IStorage if necessary.
6760 * PARAMS
6761 * pStorage [I] Dest storage to create the stream in
6763 * RETURNS
6764 * Nothing
6766 * NOTES
6767 * This function is used by OleConvertOLESTREAMToIStorage only.
6769 * This stream is still unknown, MS Word seems to have extra data
6770 * but since the data is stored in the OLESTREAM there should be
6771 * no need to recreate the stream. If the stream is manually
6772 * deleted it will create it with this default data.
6775 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6777 HRESULT hRes;
6778 IStream *pStream;
6779 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6780 BYTE pOleStreamHeader [] =
6782 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6783 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6784 0x00, 0x00, 0x00, 0x00
6787 /* Create stream if not present */
6788 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6789 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6791 if(hRes == S_OK)
6793 /* Write default Data */
6794 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6795 IStream_Release(pStream);
6799 /* write a string to a stream, preceded by its length */
6800 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6802 HRESULT r;
6803 LPSTR str;
6804 DWORD len = 0;
6806 if( string )
6807 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6808 r = IStream_Write( stm, &len, sizeof(len), NULL);
6809 if( FAILED( r ) )
6810 return r;
6811 if(len == 0)
6812 return r;
6813 str = CoTaskMemAlloc( len );
6814 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6815 r = IStream_Write( stm, str, len, NULL);
6816 CoTaskMemFree( str );
6817 return r;
6820 /* read a string preceded by its length from a stream */
6821 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6823 HRESULT r;
6824 DWORD len, count = 0;
6825 LPSTR str;
6826 LPWSTR wstr;
6828 r = IStream_Read( stm, &len, sizeof(len), &count );
6829 if( FAILED( r ) )
6830 return r;
6831 if( count != sizeof(len) )
6832 return E_OUTOFMEMORY;
6834 TRACE("%ld bytes\n",len);
6836 str = CoTaskMemAlloc( len );
6837 if( !str )
6838 return E_OUTOFMEMORY;
6839 count = 0;
6840 r = IStream_Read( stm, str, len, &count );
6841 if( FAILED( r ) )
6842 return r;
6843 if( count != len )
6845 CoTaskMemFree( str );
6846 return E_OUTOFMEMORY;
6849 TRACE("Read string %s\n",debugstr_an(str,len));
6851 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6852 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6853 if( wstr )
6854 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6855 CoTaskMemFree( str );
6857 *string = wstr;
6859 return r;
6863 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6864 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6866 IStream *pstm;
6867 HRESULT r = S_OK;
6868 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6870 static const BYTE unknown1[12] =
6871 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6872 0xFF, 0xFF, 0xFF, 0xFF};
6873 static const BYTE unknown2[16] =
6874 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6875 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6877 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6878 debugstr_w(lpszUserType), debugstr_w(szClipName),
6879 debugstr_w(szProgIDName));
6881 /* Create a CompObj stream if it doesn't exist */
6882 r = IStorage_CreateStream(pstg, szwStreamName,
6883 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6884 if( FAILED (r) )
6885 return r;
6887 /* Write CompObj Structure to stream */
6888 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6890 if( SUCCEEDED( r ) )
6891 r = WriteClassStm( pstm, clsid );
6893 if( SUCCEEDED( r ) )
6894 r = STREAM_WriteString( pstm, lpszUserType );
6895 if( SUCCEEDED( r ) )
6896 r = STREAM_WriteString( pstm, szClipName );
6897 if( SUCCEEDED( r ) )
6898 r = STREAM_WriteString( pstm, szProgIDName );
6899 if( SUCCEEDED( r ) )
6900 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6902 IStream_Release( pstm );
6904 return r;
6907 /***********************************************************************
6908 * WriteFmtUserTypeStg (OLE32.@)
6910 HRESULT WINAPI WriteFmtUserTypeStg(
6911 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6913 HRESULT r;
6914 WCHAR szwClipName[0x40];
6915 CLSID clsid = CLSID_NULL;
6916 LPWSTR wstrProgID = NULL;
6917 DWORD n;
6919 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6921 /* get the clipboard format name */
6922 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6923 szwClipName[n]=0;
6925 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6927 /* FIXME: There's room to save a CLSID and its ProgID, but
6928 the CLSID is not looked up in the registry and in all the
6929 tests I wrote it was CLSID_NULL. Where does it come from?
6932 /* get the real program ID. This may fail, but that's fine */
6933 ProgIDFromCLSID(&clsid, &wstrProgID);
6935 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6937 r = STORAGE_WriteCompObj( pstg, &clsid,
6938 lpszUserType, szwClipName, wstrProgID );
6940 CoTaskMemFree(wstrProgID);
6942 return r;
6946 /******************************************************************************
6947 * ReadFmtUserTypeStg [OLE32.@]
6949 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6951 HRESULT r;
6952 IStream *stm = 0;
6953 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6954 unsigned char unknown1[12];
6955 unsigned char unknown2[16];
6956 DWORD count;
6957 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6958 CLSID clsid;
6960 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6962 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6963 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6964 if( FAILED ( r ) )
6966 WARN("Failed to open stream r = %08lx\n", r);
6967 return r;
6970 /* read the various parts of the structure */
6971 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6972 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6973 goto end;
6974 r = ReadClassStm( stm, &clsid );
6975 if( FAILED( r ) )
6976 goto end;
6978 r = STREAM_ReadString( stm, &szCLSIDName );
6979 if( FAILED( r ) )
6980 goto end;
6982 r = STREAM_ReadString( stm, &szOleTypeName );
6983 if( FAILED( r ) )
6984 goto end;
6986 r = STREAM_ReadString( stm, &szProgIDName );
6987 if( FAILED( r ) )
6988 goto end;
6990 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6991 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6992 goto end;
6994 /* ok, success... now we just need to store what we found */
6995 if( pcf )
6996 *pcf = RegisterClipboardFormatW( szOleTypeName );
6997 CoTaskMemFree( szOleTypeName );
6999 if( lplpszUserType )
7000 *lplpszUserType = szCLSIDName;
7001 CoTaskMemFree( szProgIDName );
7003 end:
7004 IStream_Release( stm );
7006 return r;
7010 /*************************************************************************
7011 * OLECONVERT_CreateCompObjStream [Internal]
7013 * Creates a "\001CompObj" is the destination IStorage if necessary.
7015 * PARAMS
7016 * pStorage [I] The dest IStorage to create the CompObj Stream
7017 * if necessary.
7018 * strOleTypeName [I] The ProgID
7020 * RETURNS
7021 * Success: S_OK
7022 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7024 * NOTES
7025 * This function is used by OleConvertOLESTREAMToIStorage only.
7027 * The stream data is stored in the OLESTREAM and there should be
7028 * no need to recreate the stream. If the stream is manually
7029 * deleted it will attempt to create it by querying the registry.
7033 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7035 IStream *pStream;
7036 HRESULT hStorageRes, hRes = S_OK;
7037 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7038 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7039 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7041 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7042 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7044 /* Initialize the CompObj structure */
7045 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7046 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7047 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7050 /* Create a CompObj stream if it doesn't exist */
7051 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7052 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7053 if(hStorageRes == S_OK)
7055 /* copy the OleTypeName to the compobj struct */
7056 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7057 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7059 /* copy the OleTypeName to the compobj struct */
7060 /* Note: in the test made, these were Identical */
7061 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7062 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7064 /* Get the CLSID */
7065 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7066 bufferW, OLESTREAM_MAX_STR_LEN );
7067 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7069 if(hRes == S_OK)
7071 HKEY hKey;
7072 LONG hErr;
7073 /* Get the CLSID Default Name from the Registry */
7074 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7075 if(hErr == ERROR_SUCCESS)
7077 char strTemp[OLESTREAM_MAX_STR_LEN];
7078 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7079 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
7080 if(hErr == ERROR_SUCCESS)
7082 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7084 RegCloseKey(hKey);
7088 /* Write CompObj Structure to stream */
7089 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7091 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7093 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7094 if(IStorageCompObj.dwCLSIDNameLength > 0)
7096 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7098 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7099 if(IStorageCompObj.dwOleTypeNameLength > 0)
7101 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7103 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7104 if(IStorageCompObj.dwProgIDNameLength > 0)
7106 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7108 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7109 IStream_Release(pStream);
7111 return hRes;
7115 /*************************************************************************
7116 * OLECONVERT_CreateOlePresStream[Internal]
7118 * Creates the "\002OlePres000" Stream with the Metafile data
7120 * PARAMS
7121 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7122 * dwExtentX [I] Width of the Metafile
7123 * dwExtentY [I] Height of the Metafile
7124 * pData [I] Metafile data
7125 * dwDataLength [I] Size of the Metafile data
7127 * RETURNS
7128 * Success: S_OK
7129 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7131 * NOTES
7132 * This function is used by OleConvertOLESTREAMToIStorage only.
7135 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7137 HRESULT hRes;
7138 IStream *pStream;
7139 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7140 BYTE pOlePresStreamHeader [] =
7142 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7143 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7144 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7145 0x00, 0x00, 0x00, 0x00
7148 BYTE pOlePresStreamHeaderEmpty [] =
7150 0x00, 0x00, 0x00, 0x00,
7151 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7152 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7153 0x00, 0x00, 0x00, 0x00
7156 /* Create the OlePres000 Stream */
7157 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7158 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7160 if(hRes == S_OK)
7162 DWORD nHeaderSize;
7163 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7165 memset(&OlePres, 0, sizeof(OlePres));
7166 /* Do we have any metafile data to save */
7167 if(dwDataLength > 0)
7169 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7170 nHeaderSize = sizeof(pOlePresStreamHeader);
7172 else
7174 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7175 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7177 /* Set width and height of the metafile */
7178 OlePres.dwExtentX = dwExtentX;
7179 OlePres.dwExtentY = -dwExtentY;
7181 /* Set Data and Length */
7182 if(dwDataLength > sizeof(METAFILEPICT16))
7184 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7185 OlePres.pData = &(pData[8]);
7187 /* Save OlePres000 Data to Stream */
7188 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7189 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7190 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7191 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7192 if(OlePres.dwSize > 0)
7194 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7196 IStream_Release(pStream);
7200 /*************************************************************************
7201 * OLECONVERT_CreateOle10NativeStream [Internal]
7203 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7205 * PARAMS
7206 * pStorage [I] Dest storage to create the stream in
7207 * pData [I] Ole10 Native Data (ex. bmp)
7208 * dwDataLength [I] Size of the Ole10 Native Data
7210 * RETURNS
7211 * Nothing
7213 * NOTES
7214 * This function is used by OleConvertOLESTREAMToIStorage only.
7216 * Might need to verify the data and return appropriate error message
7219 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7221 HRESULT hRes;
7222 IStream *pStream;
7223 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7225 /* Create the Ole10Native Stream */
7226 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7227 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7229 if(hRes == S_OK)
7231 /* Write info to stream */
7232 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7233 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7234 IStream_Release(pStream);
7239 /*************************************************************************
7240 * OLECONVERT_GetOLE10ProgID [Internal]
7242 * Finds the ProgID (or OleTypeID) from the IStorage
7244 * PARAMS
7245 * pStorage [I] The Src IStorage to get the ProgID
7246 * strProgID [I] the ProgID string to get
7247 * dwSize [I] the size of the string
7249 * RETURNS
7250 * Success: S_OK
7251 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7253 * NOTES
7254 * This function is used by OleConvertIStorageToOLESTREAM only.
7258 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7260 HRESULT hRes;
7261 IStream *pStream;
7262 LARGE_INTEGER iSeekPos;
7263 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7264 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7266 /* Open the CompObj Stream */
7267 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7268 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7269 if(hRes == S_OK)
7272 /*Get the OleType from the CompObj Stream */
7273 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7274 iSeekPos.u.HighPart = 0;
7276 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7277 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7278 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7279 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7280 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7281 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7282 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7284 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7285 if(*dwSize > 0)
7287 IStream_Read(pStream, strProgID, *dwSize, NULL);
7289 IStream_Release(pStream);
7291 else
7293 STATSTG stat;
7294 LPOLESTR wstrProgID;
7296 /* Get the OleType from the registry */
7297 REFCLSID clsid = &(stat.clsid);
7298 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7299 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7300 if(hRes == S_OK)
7302 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7306 return hRes;
7309 /*************************************************************************
7310 * OLECONVERT_GetOle10PresData [Internal]
7312 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7314 * PARAMS
7315 * pStorage [I] Src IStroage
7316 * pOleStream [I] Dest OleStream Mem Struct
7318 * RETURNS
7319 * Nothing
7321 * NOTES
7322 * This function is used by OleConvertIStorageToOLESTREAM only.
7324 * Memory allocated for pData must be freed by the caller
7328 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7331 HRESULT hRes;
7332 IStream *pStream;
7333 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7335 /* Initialize Default data for OLESTREAM */
7336 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7337 pOleStreamData[0].dwTypeID = 2;
7338 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7339 pOleStreamData[1].dwTypeID = 0;
7340 pOleStreamData[0].dwMetaFileWidth = 0;
7341 pOleStreamData[0].dwMetaFileHeight = 0;
7342 pOleStreamData[0].pData = NULL;
7343 pOleStreamData[1].pData = NULL;
7345 /* Open Ole10Native Stream */
7346 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7347 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7348 if(hRes == S_OK)
7351 /* Read Size and Data */
7352 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7353 if(pOleStreamData->dwDataLength > 0)
7355 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7356 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7358 IStream_Release(pStream);
7364 /*************************************************************************
7365 * OLECONVERT_GetOle20PresData[Internal]
7367 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7369 * PARAMS
7370 * pStorage [I] Src IStroage
7371 * pOleStreamData [I] Dest OleStream Mem Struct
7373 * RETURNS
7374 * Nothing
7376 * NOTES
7377 * This function is used by OleConvertIStorageToOLESTREAM only.
7379 * Memory allocated for pData must be freed by the caller
7381 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7383 HRESULT hRes;
7384 IStream *pStream;
7385 OLECONVERT_ISTORAGE_OLEPRES olePress;
7386 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7388 /* Initialize Default data for OLESTREAM */
7389 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7390 pOleStreamData[0].dwTypeID = 2;
7391 pOleStreamData[0].dwMetaFileWidth = 0;
7392 pOleStreamData[0].dwMetaFileHeight = 0;
7393 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7394 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7395 pOleStreamData[1].dwTypeID = 0;
7396 pOleStreamData[1].dwOleTypeNameLength = 0;
7397 pOleStreamData[1].strOleTypeName[0] = 0;
7398 pOleStreamData[1].dwMetaFileWidth = 0;
7399 pOleStreamData[1].dwMetaFileHeight = 0;
7400 pOleStreamData[1].pData = NULL;
7401 pOleStreamData[1].dwDataLength = 0;
7404 /* Open OlePress000 stream */
7405 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7406 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7407 if(hRes == S_OK)
7409 LARGE_INTEGER iSeekPos;
7410 METAFILEPICT16 MetaFilePict;
7411 static const char strMetafilePictName[] = "METAFILEPICT";
7413 /* Set the TypeID for a Metafile */
7414 pOleStreamData[1].dwTypeID = 5;
7416 /* Set the OleTypeName to Metafile */
7417 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7418 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7420 iSeekPos.u.HighPart = 0;
7421 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7423 /* Get Presentation Data */
7424 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7425 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7426 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7427 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7429 /*Set width and Height */
7430 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7431 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7432 if(olePress.dwSize > 0)
7434 /* Set Length */
7435 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7437 /* Set MetaFilePict struct */
7438 MetaFilePict.mm = 8;
7439 MetaFilePict.xExt = olePress.dwExtentX;
7440 MetaFilePict.yExt = olePress.dwExtentY;
7441 MetaFilePict.hMF = 0;
7443 /* Get Metafile Data */
7444 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7445 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7446 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7448 IStream_Release(pStream);
7452 /*************************************************************************
7453 * OleConvertOLESTREAMToIStorage [OLE32.@]
7455 * Read info on MSDN
7457 * TODO
7458 * DVTARGETDEVICE paramenter is not handled
7459 * Still unsure of some mem fields for OLE 10 Stream
7460 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7461 * and "\001OLE" streams
7464 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7465 LPOLESTREAM pOleStream,
7466 LPSTORAGE pstg,
7467 const DVTARGETDEVICE* ptd)
7469 int i;
7470 HRESULT hRes=S_OK;
7471 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7473 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7475 if(ptd != NULL)
7477 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7480 if(pstg == NULL || pOleStream == NULL)
7482 hRes = E_INVALIDARG;
7485 if(hRes == S_OK)
7487 /* Load the OLESTREAM to Memory */
7488 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7491 if(hRes == S_OK)
7493 /* Load the OLESTREAM to Memory (part 2)*/
7494 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7497 if(hRes == S_OK)
7500 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7502 /* Do we have the IStorage Data in the OLESTREAM */
7503 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7505 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7506 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7508 else
7510 /* It must be an original OLE 1.0 source */
7511 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7514 else
7516 /* It must be an original OLE 1.0 source */
7517 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7520 /* Create CompObj Stream if necessary */
7521 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7522 if(hRes == S_OK)
7524 /*Create the Ole Stream if necessary */
7525 OLECONVERT_CreateOleStream(pstg);
7530 /* Free allocated memory */
7531 for(i=0; i < 2; i++)
7533 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7534 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7535 pOleStreamData[i].pstrOleObjFileName = NULL;
7537 return hRes;
7540 /*************************************************************************
7541 * OleConvertIStorageToOLESTREAM [OLE32.@]
7543 * Read info on MSDN
7545 * Read info on MSDN
7547 * TODO
7548 * Still unsure of some mem fields for OLE 10 Stream
7549 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7550 * and "\001OLE" streams.
7553 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7554 LPSTORAGE pstg,
7555 LPOLESTREAM pOleStream)
7557 int i;
7558 HRESULT hRes = S_OK;
7559 IStream *pStream;
7560 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7561 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7564 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7566 if(pstg == NULL || pOleStream == NULL)
7568 hRes = E_INVALIDARG;
7570 if(hRes == S_OK)
7572 /* Get the ProgID */
7573 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7574 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7576 if(hRes == S_OK)
7578 /* Was it originally Ole10 */
7579 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7580 if(hRes == S_OK)
7582 IStream_Release(pStream);
7583 /* Get Presentation Data for Ole10Native */
7584 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7586 else
7588 /* Get Presentation Data (OLE20) */
7589 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7592 /* Save OLESTREAM */
7593 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7594 if(hRes == S_OK)
7596 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7601 /* Free allocated memory */
7602 for(i=0; i < 2; i++)
7604 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7607 return hRes;
7610 /***********************************************************************
7611 * GetConvertStg (OLE32.@)
7613 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7614 FIXME("unimplemented stub!\n");
7615 return E_FAIL;
7618 /******************************************************************************
7619 * StgIsStorageFile [OLE32.@]
7621 HRESULT WINAPI
7622 StgIsStorageFile(LPCOLESTR fn)
7624 HANDLE hf;
7625 BYTE magic[8];
7626 DWORD bytes_read;
7628 TRACE("(\'%s\')\n", debugstr_w(fn));
7629 hf = CreateFileW(fn, GENERIC_READ,
7630 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7631 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7633 if (hf == INVALID_HANDLE_VALUE)
7634 return STG_E_FILENOTFOUND;
7636 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7638 WARN(" unable to read file\n");
7639 CloseHandle(hf);
7640 return S_FALSE;
7643 CloseHandle(hf);
7645 if (bytes_read != 8) {
7646 WARN(" too short\n");
7647 return S_FALSE;
7650 if (!memcmp(magic,STORAGE_magic,8)) {
7651 WARN(" -> YES\n");
7652 return S_OK;
7655 WARN(" -> Invalid header.\n");
7656 return S_FALSE;