ole32: Fix up permissions when opening streams.
[wine/testsucceed.git] / dlls / ole32 / storage32.c
blob08084e41ad7942f320aad06a16bb52841a7f79f1
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
32 * MSDN
33 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
36 #include <assert.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
42 #define COBJMACROS
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
46 #include "windef.h"
47 #include "winbase.h"
48 #include "winnls.h"
49 #include "winuser.h"
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 #include "storage32.h"
54 #include "ole2.h" /* For Write/ReadClassStm */
56 #include "winreg.h"
57 #include "wine/wingdi16.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(storage);
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 #define OLESTREAM_ID 0x501
63 #define OLESTREAM_MAX_STR_LEN 255
65 static const char rootPropertyName[] = "Root Entry";
68 /* OLESTREAM memory structure to use for Get and Put Routines */
69 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
70 typedef struct
72 DWORD dwOleID;
73 DWORD dwTypeID;
74 DWORD dwOleTypeNameLength;
75 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
76 CHAR *pstrOleObjFileName;
77 DWORD dwOleObjFileNameLength;
78 DWORD dwMetaFileWidth;
79 DWORD dwMetaFileHeight;
80 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
81 DWORD dwDataLength;
82 BYTE *pData;
83 }OLECONVERT_OLESTREAM_DATA;
85 /* CompObj Stream structure */
86 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
87 typedef struct
89 BYTE byUnknown1[12];
90 CLSID clsid;
91 DWORD dwCLSIDNameLength;
92 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
93 DWORD dwOleTypeNameLength;
94 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
95 DWORD dwProgIDNameLength;
96 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
97 BYTE byUnknown2[16];
98 }OLECONVERT_ISTORAGE_COMPOBJ;
101 /* Ole Presention Stream structure */
102 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
103 typedef struct
105 BYTE byUnknown1[28];
106 DWORD dwExtentX;
107 DWORD dwExtentY;
108 DWORD dwSize;
109 BYTE *pData;
110 }OLECONVERT_ISTORAGE_OLEPRES;
114 /***********************************************************************
115 * Forward declaration of internal functions used by the method DestroyElement
117 static HRESULT deleteStorageProperty(
118 StorageImpl *parentStorage,
119 ULONG foundPropertyIndexToDelete,
120 StgProperty propertyToDelete);
122 static HRESULT deleteStreamProperty(
123 StorageImpl *parentStorage,
124 ULONG foundPropertyIndexToDelete,
125 StgProperty propertyToDelete);
127 static HRESULT findPlaceholder(
128 StorageImpl *storage,
129 ULONG propertyIndexToStore,
130 ULONG storagePropertyIndex,
131 INT typeOfRelation);
133 static HRESULT adjustPropertyChain(
134 StorageImpl *This,
135 StgProperty propertyToDelete,
136 StgProperty parentProperty,
137 ULONG parentPropertyId,
138 INT typeOfRelation);
140 /***********************************************************************
141 * Declaration of the functions used to manipulate StgProperty
144 static ULONG getFreeProperty(
145 StorageImpl *storage);
147 static void updatePropertyChain(
148 StorageImpl *storage,
149 ULONG newPropertyIndex,
150 StgProperty newProperty);
152 static LONG propertyNameCmp(
153 const OLECHAR *newProperty,
154 const OLECHAR *currentProperty);
157 /***********************************************************************
158 * Declaration of miscellaneous functions...
160 static HRESULT validateSTGM(DWORD stgmValue);
162 static DWORD GetShareModeFromSTGM(DWORD stgm);
163 static DWORD GetAccessModeFromSTGM(DWORD stgm);
164 static DWORD GetCreationModeFromSTGM(DWORD stgm);
166 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
170 /************************************************************************
171 ** Storage32BaseImpl implementatiion
174 /************************************************************************
175 * Storage32BaseImpl_QueryInterface (IUnknown)
177 * This method implements the common QueryInterface for all IStorage32
178 * implementations contained in this file.
180 * See Windows documentation for more details on IUnknown methods.
182 HRESULT WINAPI StorageBaseImpl_QueryInterface(
183 IStorage* iface,
184 REFIID riid,
185 void** ppvObject)
187 StorageBaseImpl *This = (StorageBaseImpl *)iface;
189 * Perform a sanity check on the parameters.
191 if ( (This==0) || (ppvObject==0) )
192 return E_INVALIDARG;
195 * Initialize the return parameter.
197 *ppvObject = 0;
200 * Compare the riid with the interface IDs implemented by this object.
202 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
204 *ppvObject = (IStorage*)This;
206 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
208 *ppvObject = (IStorage*)This;
210 else if (memcmp(&IID_IPropertySetStorage, riid, sizeof(IID_IPropertySetStorage)) == 0)
212 *ppvObject = (IStorage*)&This->pssVtbl;
216 * Check that we obtained an interface.
218 if ((*ppvObject)==0)
219 return E_NOINTERFACE;
222 * Query Interface always increases the reference count by one when it is
223 * successful
225 IStorage_AddRef(iface);
227 return S_OK;
230 /************************************************************************
231 * Storage32BaseImpl_AddRef (IUnknown)
233 * This method implements the common AddRef for all IStorage32
234 * implementations contained in this file.
236 * See Windows documentation for more details on IUnknown methods.
238 ULONG WINAPI StorageBaseImpl_AddRef(
239 IStorage* iface)
241 StorageBaseImpl *This = (StorageBaseImpl *)iface;
242 ULONG ref = InterlockedIncrement(&This->ref);
244 TRACE("(%p) AddRef to %ld\n", This, ref);
246 return ref;
249 /************************************************************************
250 * Storage32BaseImpl_Release (IUnknown)
252 * This method implements the common Release for all IStorage32
253 * implementations contained in this file.
255 * See Windows documentation for more details on IUnknown methods.
257 ULONG WINAPI StorageBaseImpl_Release(
258 IStorage* iface)
260 StorageBaseImpl *This = (StorageBaseImpl *)iface;
262 * Decrease the reference count on this object.
264 ULONG ref = InterlockedDecrement(&This->ref);
266 TRACE("(%p) ReleaseRef to %ld\n", This, ref);
269 * If the reference count goes down to 0, perform suicide.
271 if (ref == 0)
274 * Since we are using a system of base-classes, we want to call the
275 * destructor of the appropriate derived class. To do this, we are
276 * using virtual functions to implement the destructor.
278 This->v_destructor(This);
281 return ref;
284 /************************************************************************
285 * Storage32BaseImpl_OpenStream (IStorage)
287 * This method will open the specified stream object from the current storage.
289 * See Windows documentation for more details on IStorage methods.
291 HRESULT WINAPI StorageBaseImpl_OpenStream(
292 IStorage* iface,
293 const OLECHAR* pwcsName, /* [string][in] */
294 void* reserved1, /* [unique][in] */
295 DWORD grfMode, /* [in] */
296 DWORD reserved2, /* [in] */
297 IStream** ppstm) /* [out] */
299 StorageBaseImpl *This = (StorageBaseImpl *)iface;
300 IEnumSTATSTGImpl* propertyEnumeration;
301 StgStreamImpl* newStream;
302 StgProperty currentProperty;
303 ULONG foundPropertyIndex;
304 HRESULT res = STG_E_UNKNOWN;
305 DWORD parent_grfMode;
307 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
308 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
311 * Perform a sanity check on the parameters.
313 if ( (pwcsName==NULL) || (ppstm==0) )
315 res = E_INVALIDARG;
316 goto end;
320 * Initialize the out parameter
322 *ppstm = NULL;
325 * Validate the STGM flags
327 if ( FAILED( validateSTGM(grfMode) ))
329 res = STG_E_INVALIDFLAG;
330 goto end;
334 * As documented.
336 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
337 (grfMode & STGM_DELETEONRELEASE) ||
338 (grfMode & STGM_TRANSACTED) )
340 res = STG_E_INVALIDFUNCTION;
341 goto end;
345 * Check that we're compatible with the parent's storage mode, but
346 * only if we are not in transacted mode
348 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
349 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
350 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
352 res = STG_E_ACCESSDENIED;
353 goto end;
358 * Create a property enumeration to search the properties
360 propertyEnumeration = IEnumSTATSTGImpl_Construct(
361 This->ancestorStorage,
362 This->rootPropertySetIndex);
365 * Search the enumeration for the property with the given name
367 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
368 propertyEnumeration,
369 pwcsName,
370 &currentProperty);
373 * Delete the property enumeration since we don't need it anymore
375 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
378 * If it was found, construct the stream object and return a pointer to it.
380 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
381 (currentProperty.propertyType==PROPTYPE_STREAM) )
383 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
385 if (newStream!=0)
387 newStream->grfMode = grfMode;
388 *ppstm = (IStream*)newStream;
391 * Since we are returning a pointer to the interface, we have to
392 * nail down the reference.
394 IStream_AddRef(*ppstm);
396 res = S_OK;
397 goto end;
400 res = E_OUTOFMEMORY;
401 goto end;
404 res = STG_E_FILENOTFOUND;
406 end:
407 if (res == S_OK)
408 TRACE("<-- IStream %p\n", *ppstm);
409 TRACE("<-- %08lx\n", res);
410 return res;
413 /************************************************************************
414 * Storage32BaseImpl_OpenStorage (IStorage)
416 * This method will open a new storage object from the current storage.
418 * See Windows documentation for more details on IStorage methods.
420 HRESULT WINAPI StorageBaseImpl_OpenStorage(
421 IStorage* iface,
422 const OLECHAR* pwcsName, /* [string][unique][in] */
423 IStorage* pstgPriority, /* [unique][in] */
424 DWORD grfMode, /* [in] */
425 SNB snbExclude, /* [unique][in] */
426 DWORD reserved, /* [in] */
427 IStorage** ppstg) /* [out] */
429 StorageBaseImpl *This = (StorageBaseImpl *)iface;
430 StorageInternalImpl* newStorage;
431 IEnumSTATSTGImpl* propertyEnumeration;
432 StgProperty currentProperty;
433 ULONG foundPropertyIndex;
434 HRESULT res = STG_E_UNKNOWN;
435 DWORD parent_grfMode;
437 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
438 iface, debugstr_w(pwcsName), pstgPriority,
439 grfMode, snbExclude, reserved, ppstg);
442 * Perform a sanity check on the parameters.
444 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
446 res = E_INVALIDARG;
447 goto end;
450 /* as documented */
451 if (snbExclude != NULL)
453 res = STG_E_INVALIDPARAMETER;
454 goto end;
458 * Validate the STGM flags
460 if ( FAILED( validateSTGM(grfMode) ))
462 res = STG_E_INVALIDFLAG;
463 goto end;
467 * As documented.
469 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
470 (grfMode & STGM_DELETEONRELEASE) ||
471 (grfMode & STGM_PRIORITY) )
473 res = STG_E_INVALIDFUNCTION;
474 goto end;
478 * Check that we're compatible with the parent's storage mode,
479 * but only if we are not transacted
481 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
482 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
483 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
485 res = STG_E_ACCESSDENIED;
486 goto end;
491 * Initialize the out parameter
493 *ppstg = NULL;
496 * Create a property enumeration to search the properties
498 propertyEnumeration = IEnumSTATSTGImpl_Construct(
499 This->ancestorStorage,
500 This->rootPropertySetIndex);
503 * Search the enumeration for the property with the given name
505 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
506 propertyEnumeration,
507 pwcsName,
508 &currentProperty);
511 * Delete the property enumeration since we don't need it anymore
513 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
516 * If it was found, construct the stream object and return a pointer to it.
518 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
519 (currentProperty.propertyType==PROPTYPE_STORAGE) )
522 * Construct a new Storage object
524 newStorage = StorageInternalImpl_Construct(
525 This->ancestorStorage,
526 grfMode,
527 foundPropertyIndex);
529 if (newStorage != 0)
531 *ppstg = (IStorage*)newStorage;
534 * Since we are returning a pointer to the interface,
535 * we have to nail down the reference.
537 StorageBaseImpl_AddRef(*ppstg);
539 res = S_OK;
540 goto end;
543 res = STG_E_INSUFFICIENTMEMORY;
544 goto end;
547 res = STG_E_FILENOTFOUND;
549 end:
550 TRACE("<-- %08lx\n", res);
551 return res;
554 /************************************************************************
555 * Storage32BaseImpl_EnumElements (IStorage)
557 * This method will create an enumerator object that can be used to
558 * retrieve informatino about all the properties in the storage object.
560 * See Windows documentation for more details on IStorage methods.
562 HRESULT WINAPI StorageBaseImpl_EnumElements(
563 IStorage* iface,
564 DWORD reserved1, /* [in] */
565 void* reserved2, /* [size_is][unique][in] */
566 DWORD reserved3, /* [in] */
567 IEnumSTATSTG** ppenum) /* [out] */
569 StorageBaseImpl *This = (StorageBaseImpl *)iface;
570 IEnumSTATSTGImpl* newEnum;
572 TRACE("(%p, %ld, %p, %ld, %p)\n",
573 iface, reserved1, reserved2, reserved3, ppenum);
576 * Perform a sanity check on the parameters.
578 if ( (This==0) || (ppenum==0))
579 return E_INVALIDARG;
582 * Construct the enumerator.
584 newEnum = IEnumSTATSTGImpl_Construct(
585 This->ancestorStorage,
586 This->rootPropertySetIndex);
588 if (newEnum!=0)
590 *ppenum = (IEnumSTATSTG*)newEnum;
593 * Don't forget to nail down a reference to the new object before
594 * returning it.
596 IEnumSTATSTG_AddRef(*ppenum);
598 return S_OK;
601 return E_OUTOFMEMORY;
604 /************************************************************************
605 * Storage32BaseImpl_Stat (IStorage)
607 * This method will retrieve information about this storage object.
609 * See Windows documentation for more details on IStorage methods.
611 HRESULT WINAPI StorageBaseImpl_Stat(
612 IStorage* iface,
613 STATSTG* pstatstg, /* [out] */
614 DWORD grfStatFlag) /* [in] */
616 StorageBaseImpl *This = (StorageBaseImpl *)iface;
617 StgProperty curProperty;
618 BOOL readSuccessful;
619 HRESULT res = STG_E_UNKNOWN;
621 TRACE("(%p, %p, %lx)\n",
622 iface, pstatstg, grfStatFlag);
625 * Perform a sanity check on the parameters.
627 if ( (This==0) || (pstatstg==0))
629 res = E_INVALIDARG;
630 goto end;
634 * Read the information from the property.
636 readSuccessful = StorageImpl_ReadProperty(
637 This->ancestorStorage,
638 This->rootPropertySetIndex,
639 &curProperty);
641 if (readSuccessful)
643 StorageUtl_CopyPropertyToSTATSTG(
644 pstatstg,
645 &curProperty,
646 grfStatFlag);
648 res = S_OK;
649 goto end;
652 res = E_FAIL;
654 end:
655 if (res == S_OK)
657 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);
659 TRACE("<-- %08lx\n", res);
660 return res;
663 /************************************************************************
664 * Storage32BaseImpl_RenameElement (IStorage)
666 * This method will rename the specified element.
668 * See Windows documentation for more details on IStorage methods.
670 * Implementation notes: The method used to rename consists of creating a clone
671 * of the deleted StgProperty object setting it with the new name and to
672 * perform a DestroyElement of the old StgProperty.
674 HRESULT WINAPI StorageBaseImpl_RenameElement(
675 IStorage* iface,
676 const OLECHAR* pwcsOldName, /* [in] */
677 const OLECHAR* pwcsNewName) /* [in] */
679 StorageBaseImpl *This = (StorageBaseImpl *)iface;
680 IEnumSTATSTGImpl* propertyEnumeration;
681 StgProperty currentProperty;
682 ULONG foundPropertyIndex;
684 TRACE("(%p, %s, %s)\n",
685 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
688 * Create a property enumeration to search the properties
690 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
691 This->rootPropertySetIndex);
694 * Search the enumeration for the new property name
696 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
697 pwcsNewName,
698 &currentProperty);
700 if (foundPropertyIndex != PROPERTY_NULL)
703 * There is already a property with the new name
705 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
706 return STG_E_FILEALREADYEXISTS;
709 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
712 * Search the enumeration for the old property name
714 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
715 pwcsOldName,
716 &currentProperty);
719 * Delete the property enumeration since we don't need it anymore
721 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
723 if (foundPropertyIndex != PROPERTY_NULL)
725 StgProperty renamedProperty;
726 ULONG renamedPropertyIndex;
729 * Setup a new property for the renamed property
731 renamedProperty.sizeOfNameString =
732 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
734 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
735 return STG_E_INVALIDNAME;
737 strcpyW(renamedProperty.name, pwcsNewName);
739 renamedProperty.propertyType = currentProperty.propertyType;
740 renamedProperty.startingBlock = currentProperty.startingBlock;
741 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
742 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
744 renamedProperty.previousProperty = PROPERTY_NULL;
745 renamedProperty.nextProperty = PROPERTY_NULL;
748 * Bring the dirProperty link in case it is a storage and in which
749 * case the renamed storage elements don't require to be reorganized.
751 renamedProperty.dirProperty = currentProperty.dirProperty;
753 /* call CoFileTime to get the current time
754 renamedProperty.timeStampS1
755 renamedProperty.timeStampD1
756 renamedProperty.timeStampS2
757 renamedProperty.timeStampD2
758 renamedProperty.propertyUniqueID
762 * Obtain a free property in the property chain
764 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
767 * Save the new property into the new property spot
769 StorageImpl_WriteProperty(
770 This->ancestorStorage,
771 renamedPropertyIndex,
772 &renamedProperty);
775 * Find a spot in the property chain for our newly created property.
777 updatePropertyChain(
778 (StorageImpl*)This,
779 renamedPropertyIndex,
780 renamedProperty);
783 * At this point the renamed property has been inserted in the tree,
784 * now, before to Destroy the old property we must zeroed it's dirProperty
785 * otherwise the DestroyProperty below will zap it all and we do not want
786 * this to happen.
787 * Also, we fake that the old property is a storage so the DestroyProperty
788 * will not do a SetSize(0) on the stream data.
790 * This means that we need to tweek the StgProperty if it is a stream or a
791 * non empty storage.
793 StorageImpl_ReadProperty(This->ancestorStorage,
794 foundPropertyIndex,
795 &currentProperty);
797 currentProperty.dirProperty = PROPERTY_NULL;
798 currentProperty.propertyType = PROPTYPE_STORAGE;
799 StorageImpl_WriteProperty(
800 This->ancestorStorage,
801 foundPropertyIndex,
802 &currentProperty);
805 * Invoke Destroy to get rid of the ole property and automatically redo
806 * the linking of it's previous and next members...
808 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
811 else
814 * There is no property with the old name
816 return STG_E_FILENOTFOUND;
819 return S_OK;
822 /************************************************************************
823 * Storage32BaseImpl_CreateStream (IStorage)
825 * This method will create a stream object within this storage
827 * See Windows documentation for more details on IStorage methods.
829 HRESULT WINAPI StorageBaseImpl_CreateStream(
830 IStorage* iface,
831 const OLECHAR* pwcsName, /* [string][in] */
832 DWORD grfMode, /* [in] */
833 DWORD reserved1, /* [in] */
834 DWORD reserved2, /* [in] */
835 IStream** ppstm) /* [out] */
837 StorageBaseImpl *This = (StorageBaseImpl *)iface;
838 IEnumSTATSTGImpl* propertyEnumeration;
839 StgStreamImpl* newStream;
840 StgProperty currentProperty, newStreamProperty;
841 ULONG foundPropertyIndex, newPropertyIndex;
842 DWORD parent_grfMode;
844 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
845 iface, debugstr_w(pwcsName), grfMode,
846 reserved1, reserved2, ppstm);
849 * Validate parameters
851 if (ppstm == 0)
852 return STG_E_INVALIDPOINTER;
854 if (pwcsName == 0)
855 return STG_E_INVALIDNAME;
857 if (reserved1 || reserved2)
858 return STG_E_INVALIDPARAMETER;
861 * Validate the STGM flags
863 if ( FAILED( validateSTGM(grfMode) ))
864 return STG_E_INVALIDFLAG;
866 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
867 return STG_E_INVALIDFLAG;
870 * As documented.
872 if ((grfMode & STGM_DELETEONRELEASE) ||
873 (grfMode & STGM_TRANSACTED))
874 return STG_E_INVALIDFUNCTION;
877 * Check that we're compatible with the parent's storage mode
878 * if not in transacted mode
880 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
881 if(!(parent_grfMode & STGM_TRANSACTED)) {
882 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
883 return STG_E_ACCESSDENIED;
887 * Initialize the out parameter
889 *ppstm = 0;
892 * Create a property enumeration to search the properties
894 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
895 This->rootPropertySetIndex);
897 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
898 pwcsName,
899 &currentProperty);
901 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
903 if (foundPropertyIndex != PROPERTY_NULL)
906 * An element with this name already exists
908 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
910 IStorage_DestroyElement(iface, pwcsName);
912 else
913 return STG_E_FILEALREADYEXISTS;
917 * memset the empty property
919 memset(&newStreamProperty, 0, sizeof(StgProperty));
921 newStreamProperty.sizeOfNameString =
922 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
924 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
925 return STG_E_INVALIDNAME;
927 strcpyW(newStreamProperty.name, pwcsName);
929 newStreamProperty.propertyType = PROPTYPE_STREAM;
930 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
931 newStreamProperty.size.u.LowPart = 0;
932 newStreamProperty.size.u.HighPart = 0;
934 newStreamProperty.previousProperty = PROPERTY_NULL;
935 newStreamProperty.nextProperty = PROPERTY_NULL;
936 newStreamProperty.dirProperty = PROPERTY_NULL;
938 /* call CoFileTime to get the current time
939 newStreamProperty.timeStampS1
940 newStreamProperty.timeStampD1
941 newStreamProperty.timeStampS2
942 newStreamProperty.timeStampD2
945 /* newStreamProperty.propertyUniqueID */
948 * Get a free property or create a new one
950 newPropertyIndex = getFreeProperty(This->ancestorStorage);
953 * Save the new property into the new property spot
955 StorageImpl_WriteProperty(
956 This->ancestorStorage,
957 newPropertyIndex,
958 &newStreamProperty);
961 * Find a spot in the property chain for our newly created property.
963 updatePropertyChain(
964 (StorageImpl*)This,
965 newPropertyIndex,
966 newStreamProperty);
969 * Open the stream to return it.
971 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
973 if (newStream != 0)
975 *ppstm = (IStream*)newStream;
978 * Since we are returning a pointer to the interface, we have to nail down
979 * the reference.
981 IStream_AddRef(*ppstm);
983 else
985 return STG_E_INSUFFICIENTMEMORY;
988 return S_OK;
991 /************************************************************************
992 * Storage32BaseImpl_SetClass (IStorage)
994 * This method will write the specified CLSID in the property of this
995 * storage.
997 * See Windows documentation for more details on IStorage methods.
999 HRESULT WINAPI StorageBaseImpl_SetClass(
1000 IStorage* iface,
1001 REFCLSID clsid) /* [in] */
1003 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1004 HRESULT hRes = E_FAIL;
1005 StgProperty curProperty;
1006 BOOL success;
1008 TRACE("(%p, %p)\n", iface, clsid);
1010 success = StorageImpl_ReadProperty(This->ancestorStorage,
1011 This->rootPropertySetIndex,
1012 &curProperty);
1013 if (success)
1015 curProperty.propertyUniqueID = *clsid;
1017 success = StorageImpl_WriteProperty(This->ancestorStorage,
1018 This->rootPropertySetIndex,
1019 &curProperty);
1020 if (success)
1021 hRes = S_OK;
1024 return hRes;
1027 /************************************************************************
1028 ** Storage32Impl implementation
1031 /************************************************************************
1032 * Storage32Impl_CreateStorage (IStorage)
1034 * This method will create the storage object within the provided storage.
1036 * See Windows documentation for more details on IStorage methods.
1038 HRESULT WINAPI StorageImpl_CreateStorage(
1039 IStorage* iface,
1040 const OLECHAR *pwcsName, /* [string][in] */
1041 DWORD grfMode, /* [in] */
1042 DWORD reserved1, /* [in] */
1043 DWORD reserved2, /* [in] */
1044 IStorage **ppstg) /* [out] */
1046 StorageImpl* const This=(StorageImpl*)iface;
1048 IEnumSTATSTGImpl *propertyEnumeration;
1049 StgProperty currentProperty;
1050 StgProperty newProperty;
1051 ULONG foundPropertyIndex;
1052 ULONG newPropertyIndex;
1053 HRESULT hr;
1054 DWORD parent_grfMode;
1056 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1057 iface, debugstr_w(pwcsName), grfMode,
1058 reserved1, reserved2, ppstg);
1061 * Validate parameters
1063 if (ppstg == 0)
1064 return STG_E_INVALIDPOINTER;
1066 if (pwcsName == 0)
1067 return STG_E_INVALIDNAME;
1070 * Initialize the out parameter
1072 *ppstg = NULL;
1075 * Validate the STGM flags
1077 if ( FAILED( validateSTGM(grfMode) ) ||
1078 (grfMode & STGM_DELETEONRELEASE) )
1080 WARN("bad grfMode: 0x%lx\n", grfMode);
1081 return STG_E_INVALIDFLAG;
1085 * Check that we're compatible with the parent's storage mode
1087 parent_grfMode = STGM_ACCESS_MODE( This->base.ancestorStorage->base.openFlags );
1088 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
1090 WARN("access denied\n");
1091 return STG_E_ACCESSDENIED;
1095 * Create a property enumeration and search the properties
1097 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1098 This->base.rootPropertySetIndex);
1100 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1101 pwcsName,
1102 &currentProperty);
1103 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1105 if (foundPropertyIndex != PROPERTY_NULL)
1108 * An element with this name already exists
1110 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1111 IStorage_DestroyElement(iface, pwcsName);
1112 else
1114 WARN("file already exists\n");
1115 return STG_E_FILEALREADYEXISTS;
1120 * memset the empty property
1122 memset(&newProperty, 0, sizeof(StgProperty));
1124 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1126 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1128 FIXME("name too long\n");
1129 return STG_E_INVALIDNAME;
1132 strcpyW(newProperty.name, pwcsName);
1134 newProperty.propertyType = PROPTYPE_STORAGE;
1135 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1136 newProperty.size.u.LowPart = 0;
1137 newProperty.size.u.HighPart = 0;
1139 newProperty.previousProperty = PROPERTY_NULL;
1140 newProperty.nextProperty = PROPERTY_NULL;
1141 newProperty.dirProperty = PROPERTY_NULL;
1143 /* call CoFileTime to get the current time
1144 newProperty.timeStampS1
1145 newProperty.timeStampD1
1146 newProperty.timeStampS2
1147 newProperty.timeStampD2
1150 /* newStorageProperty.propertyUniqueID */
1153 * Obtain a free property in the property chain
1155 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1158 * Save the new property into the new property spot
1160 StorageImpl_WriteProperty(
1161 This->base.ancestorStorage,
1162 newPropertyIndex,
1163 &newProperty);
1166 * Find a spot in the property chain for our newly created property.
1168 updatePropertyChain(
1169 This,
1170 newPropertyIndex,
1171 newProperty);
1174 * Open it to get a pointer to return.
1176 hr = IStorage_OpenStorage(
1177 iface,
1178 (const OLECHAR*)pwcsName,
1180 grfMode,
1183 ppstg);
1185 if( (hr != S_OK) || (*ppstg == NULL))
1187 return hr;
1191 return S_OK;
1195 /***************************************************************************
1197 * Internal Method
1199 * Get a free property or create a new one.
1201 static ULONG getFreeProperty(
1202 StorageImpl *storage)
1204 ULONG currentPropertyIndex = 0;
1205 ULONG newPropertyIndex = PROPERTY_NULL;
1206 BOOL readSuccessful = TRUE;
1207 StgProperty currentProperty;
1212 * Start by reading the root property
1214 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1215 currentPropertyIndex,
1216 &currentProperty);
1217 if (readSuccessful)
1219 if (currentProperty.sizeOfNameString == 0)
1222 * The property existis and is available, we found it.
1224 newPropertyIndex = currentPropertyIndex;
1227 else
1230 * We exhausted the property list, we will create more space below
1232 newPropertyIndex = currentPropertyIndex;
1234 currentPropertyIndex++;
1236 } while (newPropertyIndex == PROPERTY_NULL);
1239 * grow the property chain
1241 if (! readSuccessful)
1243 StgProperty emptyProperty;
1244 ULARGE_INTEGER newSize;
1245 ULONG propertyIndex;
1246 ULONG lastProperty = 0;
1247 ULONG blockCount = 0;
1250 * obtain the new count of property blocks
1252 blockCount = BlockChainStream_GetCount(
1253 storage->base.ancestorStorage->rootBlockChain)+1;
1256 * initialize the size used by the property stream
1258 newSize.u.HighPart = 0;
1259 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1262 * add a property block to the property chain
1264 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1267 * memset the empty property in order to initialize the unused newly
1268 * created property
1270 memset(&emptyProperty, 0, sizeof(StgProperty));
1273 * initialize them
1275 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1277 for(
1278 propertyIndex = newPropertyIndex;
1279 propertyIndex < lastProperty;
1280 propertyIndex++)
1282 StorageImpl_WriteProperty(
1283 storage->base.ancestorStorage,
1284 propertyIndex,
1285 &emptyProperty);
1289 return newPropertyIndex;
1292 /****************************************************************************
1294 * Internal Method
1296 * Case insensitive comparaison of StgProperty.name by first considering
1297 * their size.
1299 * Returns <0 when newPrpoerty < currentProperty
1300 * >0 when newPrpoerty > currentProperty
1301 * 0 when newPrpoerty == currentProperty
1303 static LONG propertyNameCmp(
1304 const OLECHAR *newProperty,
1305 const OLECHAR *currentProperty)
1307 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1309 if (diff == 0)
1312 * We compare the string themselves only when they are of the same length
1314 diff = lstrcmpiW( newProperty, currentProperty);
1317 return diff;
1320 /****************************************************************************
1322 * Internal Method
1324 * Properly link this new element in the property chain.
1326 static void updatePropertyChain(
1327 StorageImpl *storage,
1328 ULONG newPropertyIndex,
1329 StgProperty newProperty)
1331 StgProperty currentProperty;
1334 * Read the root property
1336 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1337 storage->base.rootPropertySetIndex,
1338 &currentProperty);
1340 if (currentProperty.dirProperty != PROPERTY_NULL)
1343 * The root storage contains some element, therefore, start the research
1344 * for the appropriate location.
1346 BOOL found = 0;
1347 ULONG current, next, previous, currentPropertyId;
1350 * Keep the StgProperty sequence number of the storage first property
1352 currentPropertyId = currentProperty.dirProperty;
1355 * Read
1357 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1358 currentProperty.dirProperty,
1359 &currentProperty);
1361 previous = currentProperty.previousProperty;
1362 next = currentProperty.nextProperty;
1363 current = currentPropertyId;
1365 while (found == 0)
1367 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1369 if (diff < 0)
1371 if (previous != PROPERTY_NULL)
1373 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1374 previous,
1375 &currentProperty);
1376 current = previous;
1378 else
1380 currentProperty.previousProperty = newPropertyIndex;
1381 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1382 current,
1383 &currentProperty);
1384 found = 1;
1387 else if (diff > 0)
1389 if (next != PROPERTY_NULL)
1391 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1392 next,
1393 &currentProperty);
1394 current = next;
1396 else
1398 currentProperty.nextProperty = newPropertyIndex;
1399 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1400 current,
1401 &currentProperty);
1402 found = 1;
1405 else
1408 * Trying to insert an item with the same name in the
1409 * subtree structure.
1411 assert(FALSE);
1414 previous = currentProperty.previousProperty;
1415 next = currentProperty.nextProperty;
1418 else
1421 * The root storage is empty, link the new property to it's dir property
1423 currentProperty.dirProperty = newPropertyIndex;
1424 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1425 storage->base.rootPropertySetIndex,
1426 &currentProperty);
1431 /*************************************************************************
1432 * CopyTo (IStorage)
1434 HRESULT WINAPI StorageImpl_CopyTo(
1435 IStorage* iface,
1436 DWORD ciidExclude, /* [in] */
1437 const IID* rgiidExclude, /* [size_is][unique][in] */
1438 SNB snbExclude, /* [unique][in] */
1439 IStorage* pstgDest) /* [unique][in] */
1441 IEnumSTATSTG *elements = 0;
1442 STATSTG curElement, strStat;
1443 HRESULT hr;
1444 IStorage *pstgTmp, *pstgChild;
1445 IStream *pstrTmp, *pstrChild;
1447 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1448 FIXME("Exclude option not implemented\n");
1450 TRACE("(%p, %ld, %p, %p, %p)\n",
1451 iface, ciidExclude, rgiidExclude,
1452 snbExclude, pstgDest);
1455 * Perform a sanity check
1457 if ( pstgDest == 0 )
1458 return STG_E_INVALIDPOINTER;
1461 * Enumerate the elements
1463 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1465 if ( hr != S_OK )
1466 return hr;
1469 * set the class ID
1471 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1472 IStorage_SetClass( pstgDest, &curElement.clsid );
1477 * Obtain the next element
1479 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1481 if ( hr == S_FALSE )
1483 hr = S_OK; /* done, every element has been copied */
1484 break;
1487 if (curElement.type == STGTY_STORAGE)
1490 * open child source storage
1492 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1493 STGM_READ|STGM_SHARE_EXCLUSIVE,
1494 NULL, 0, &pstgChild );
1496 if (hr != S_OK)
1497 break;
1500 * Check if destination storage is not a child of the source
1501 * storage, which will cause an infinite loop
1503 if (pstgChild == pstgDest)
1505 IEnumSTATSTG_Release(elements);
1507 return STG_E_ACCESSDENIED;
1511 * create a new storage in destination storage
1513 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1514 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1515 0, 0,
1516 &pstgTmp );
1518 * if it already exist, don't create a new one use this one
1520 if (hr == STG_E_FILEALREADYEXISTS)
1522 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1523 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1524 NULL, 0, &pstgTmp );
1527 if (hr != S_OK)
1528 break;
1532 * do the copy recursively
1534 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1535 snbExclude, pstgTmp );
1537 IStorage_Release( pstgTmp );
1538 IStorage_Release( pstgChild );
1540 else if (curElement.type == STGTY_STREAM)
1543 * create a new stream in destination storage. If the stream already
1544 * exist, it will be deleted and a new one will be created.
1546 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1547 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1548 0, 0, &pstrTmp );
1550 if (hr != S_OK)
1551 break;
1554 * open child stream storage
1556 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1557 STGM_READ|STGM_SHARE_EXCLUSIVE,
1558 0, &pstrChild );
1560 if (hr != S_OK)
1561 break;
1564 * Get the size of the source stream
1566 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1569 * Set the size of the destination stream.
1571 IStream_SetSize(pstrTmp, strStat.cbSize);
1574 * do the copy
1576 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1577 NULL, NULL );
1579 IStream_Release( pstrTmp );
1580 IStream_Release( pstrChild );
1582 else
1584 WARN("unknown element type: %ld\n", curElement.type);
1587 } while (hr == S_OK);
1590 * Clean-up
1592 IEnumSTATSTG_Release(elements);
1594 return hr;
1597 /*************************************************************************
1598 * MoveElementTo (IStorage)
1600 HRESULT WINAPI StorageImpl_MoveElementTo(
1601 IStorage* iface,
1602 const OLECHAR *pwcsName, /* [string][in] */
1603 IStorage *pstgDest, /* [unique][in] */
1604 const OLECHAR *pwcsNewName,/* [string][in] */
1605 DWORD grfFlags) /* [in] */
1607 FIXME("not implemented!\n");
1608 return E_NOTIMPL;
1611 /*************************************************************************
1612 * Commit (IStorage)
1614 * Ensures that any changes made to a storage object open in transacted mode
1615 * are reflected in the parent storage
1617 * NOTES
1618 * Wine doesn't implement transacted mode, which seems to be a basic
1619 * optimization, so we can ignore this stub for now.
1621 HRESULT WINAPI StorageImpl_Commit(
1622 IStorage* iface,
1623 DWORD grfCommitFlags)/* [in] */
1625 FIXME("(%ld): stub!\n", grfCommitFlags);
1626 return S_OK;
1629 /*************************************************************************
1630 * Revert (IStorage)
1632 * Discard all changes that have been made since the last commit operation
1634 HRESULT WINAPI StorageImpl_Revert(
1635 IStorage* iface)
1637 FIXME("not implemented!\n");
1638 return E_NOTIMPL;
1641 /*************************************************************************
1642 * DestroyElement (IStorage)
1644 * Stategy: This implementation is build this way for simplicity not for speed.
1645 * I always delete the top most element of the enumeration and adjust
1646 * the deleted element pointer all the time. This takes longer to
1647 * do but allow to reinvoke DestroyElement whenever we encounter a
1648 * storage object. The optimisation reside in the usage of another
1649 * enumeration stategy that would give all the leaves of a storage
1650 * first. (postfix order)
1652 HRESULT WINAPI StorageImpl_DestroyElement(
1653 IStorage* iface,
1654 const OLECHAR *pwcsName)/* [string][in] */
1656 StorageImpl* const This=(StorageImpl*)iface;
1658 IEnumSTATSTGImpl* propertyEnumeration;
1659 HRESULT hr = S_OK;
1660 BOOL res;
1661 StgProperty propertyToDelete;
1662 StgProperty parentProperty;
1663 ULONG foundPropertyIndexToDelete;
1664 ULONG typeOfRelation;
1665 ULONG parentPropertyId;
1667 TRACE("(%p, %s)\n",
1668 iface, debugstr_w(pwcsName));
1671 * Perform a sanity check on the parameters.
1673 if (pwcsName==NULL)
1674 return STG_E_INVALIDPOINTER;
1677 * Create a property enumeration to search the property with the given name
1679 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1680 This->base.ancestorStorage,
1681 This->base.rootPropertySetIndex);
1683 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1684 propertyEnumeration,
1685 pwcsName,
1686 &propertyToDelete);
1688 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1690 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1692 return STG_E_FILENOTFOUND;
1696 * Find the parent property of the property to delete (the one that
1697 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1698 * the parent is This. Otherwise, the parent is one of it's sibling...
1702 * First, read This's StgProperty..
1704 res = StorageImpl_ReadProperty(
1705 This->base.ancestorStorage,
1706 This->base.rootPropertySetIndex,
1707 &parentProperty);
1709 assert(res);
1712 * Second, check to see if by any chance the actual storage (This) is not
1713 * the parent of the property to delete... We never know...
1715 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1718 * Set data as it would have been done in the else part...
1720 typeOfRelation = PROPERTY_RELATION_DIR;
1721 parentPropertyId = This->base.rootPropertySetIndex;
1723 else
1726 * Create a property enumeration to search the parent properties, and
1727 * delete it once done.
1729 IEnumSTATSTGImpl* propertyEnumeration2;
1731 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1732 This->base.ancestorStorage,
1733 This->base.rootPropertySetIndex);
1735 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1736 propertyEnumeration2,
1737 foundPropertyIndexToDelete,
1738 &parentProperty,
1739 &parentPropertyId);
1741 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1744 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1746 hr = deleteStorageProperty(
1747 This,
1748 foundPropertyIndexToDelete,
1749 propertyToDelete);
1751 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1753 hr = deleteStreamProperty(
1754 This,
1755 foundPropertyIndexToDelete,
1756 propertyToDelete);
1759 if (hr!=S_OK)
1760 return hr;
1763 * Adjust the property chain
1765 hr = adjustPropertyChain(
1766 This,
1767 propertyToDelete,
1768 parentProperty,
1769 parentPropertyId,
1770 typeOfRelation);
1772 return hr;
1776 /************************************************************************
1777 * StorageImpl_Stat (IStorage)
1779 * This method will retrieve information about this storage object.
1781 * See Windows documentation for more details on IStorage methods.
1783 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1784 STATSTG* pstatstg, /* [out] */
1785 DWORD grfStatFlag) /* [in] */
1787 StorageImpl* const This = (StorageImpl*)iface;
1788 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1790 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1792 CoTaskMemFree(pstatstg->pwcsName);
1793 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1794 strcpyW(pstatstg->pwcsName, This->pwcsName);
1797 return result;
1802 /*********************************************************************
1804 * Internal Method
1806 * Perform the deletion of a complete storage node
1809 static HRESULT deleteStorageProperty(
1810 StorageImpl *parentStorage,
1811 ULONG indexOfPropertyToDelete,
1812 StgProperty propertyToDelete)
1814 IEnumSTATSTG *elements = 0;
1815 IStorage *childStorage = 0;
1816 STATSTG currentElement;
1817 HRESULT hr;
1818 HRESULT destroyHr = S_OK;
1821 * Open the storage and enumerate it
1823 hr = StorageBaseImpl_OpenStorage(
1824 (IStorage*)parentStorage,
1825 propertyToDelete.name,
1827 STGM_SHARE_EXCLUSIVE,
1830 &childStorage);
1832 if (hr != S_OK)
1834 return hr;
1838 * Enumerate the elements
1840 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1845 * Obtain the next element
1847 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1848 if (hr==S_OK)
1850 destroyHr = StorageImpl_DestroyElement(
1851 (IStorage*)childStorage,
1852 (OLECHAR*)currentElement.pwcsName);
1854 CoTaskMemFree(currentElement.pwcsName);
1858 * We need to Reset the enumeration every time because we delete elements
1859 * and the enumeration could be invalid
1861 IEnumSTATSTG_Reset(elements);
1863 } while ((hr == S_OK) && (destroyHr == S_OK));
1866 * Invalidate the property by zeroing it's name member.
1868 propertyToDelete.sizeOfNameString = 0;
1870 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1871 indexOfPropertyToDelete,
1872 &propertyToDelete);
1874 IStorage_Release(childStorage);
1875 IEnumSTATSTG_Release(elements);
1877 return destroyHr;
1880 /*********************************************************************
1882 * Internal Method
1884 * Perform the deletion of a stream node
1887 static HRESULT deleteStreamProperty(
1888 StorageImpl *parentStorage,
1889 ULONG indexOfPropertyToDelete,
1890 StgProperty propertyToDelete)
1892 IStream *pis;
1893 HRESULT hr;
1894 ULARGE_INTEGER size;
1896 size.u.HighPart = 0;
1897 size.u.LowPart = 0;
1899 hr = StorageBaseImpl_OpenStream(
1900 (IStorage*)parentStorage,
1901 (OLECHAR*)propertyToDelete.name,
1902 NULL,
1903 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1905 &pis);
1907 if (hr!=S_OK)
1909 return(hr);
1913 * Zap the stream
1915 hr = IStream_SetSize(pis, size);
1917 if(hr != S_OK)
1919 return hr;
1923 * Release the stream object.
1925 IStream_Release(pis);
1928 * Invalidate the property by zeroing it's name member.
1930 propertyToDelete.sizeOfNameString = 0;
1933 * Here we should re-read the property so we get the updated pointer
1934 * but since we are here to zap it, I don't do it...
1936 StorageImpl_WriteProperty(
1937 parentStorage->base.ancestorStorage,
1938 indexOfPropertyToDelete,
1939 &propertyToDelete);
1941 return S_OK;
1944 /*********************************************************************
1946 * Internal Method
1948 * Finds a placeholder for the StgProperty within the Storage
1951 static HRESULT findPlaceholder(
1952 StorageImpl *storage,
1953 ULONG propertyIndexToStore,
1954 ULONG storePropertyIndex,
1955 INT typeOfRelation)
1957 StgProperty storeProperty;
1958 HRESULT hr = S_OK;
1959 BOOL res = TRUE;
1962 * Read the storage property
1964 res = StorageImpl_ReadProperty(
1965 storage->base.ancestorStorage,
1966 storePropertyIndex,
1967 &storeProperty);
1969 if(! res)
1971 return E_FAIL;
1974 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1976 if (storeProperty.previousProperty != PROPERTY_NULL)
1978 return findPlaceholder(
1979 storage,
1980 propertyIndexToStore,
1981 storeProperty.previousProperty,
1982 typeOfRelation);
1984 else
1986 storeProperty.previousProperty = propertyIndexToStore;
1989 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1991 if (storeProperty.nextProperty != PROPERTY_NULL)
1993 return findPlaceholder(
1994 storage,
1995 propertyIndexToStore,
1996 storeProperty.nextProperty,
1997 typeOfRelation);
1999 else
2001 storeProperty.nextProperty = propertyIndexToStore;
2004 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2006 if (storeProperty.dirProperty != PROPERTY_NULL)
2008 return findPlaceholder(
2009 storage,
2010 propertyIndexToStore,
2011 storeProperty.dirProperty,
2012 typeOfRelation);
2014 else
2016 storeProperty.dirProperty = propertyIndexToStore;
2020 hr = StorageImpl_WriteProperty(
2021 storage->base.ancestorStorage,
2022 storePropertyIndex,
2023 &storeProperty);
2025 if(! hr)
2027 return E_FAIL;
2030 return S_OK;
2033 /*************************************************************************
2035 * Internal Method
2037 * This method takes the previous and the next property link of a property
2038 * to be deleted and find them a place in the Storage.
2040 static HRESULT adjustPropertyChain(
2041 StorageImpl *This,
2042 StgProperty propertyToDelete,
2043 StgProperty parentProperty,
2044 ULONG parentPropertyId,
2045 INT typeOfRelation)
2047 ULONG newLinkProperty = PROPERTY_NULL;
2048 BOOL needToFindAPlaceholder = FALSE;
2049 ULONG storeNode = PROPERTY_NULL;
2050 ULONG toStoreNode = PROPERTY_NULL;
2051 INT relationType = 0;
2052 HRESULT hr = S_OK;
2053 BOOL res = TRUE;
2055 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2057 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2060 * Set the parent previous to the property to delete previous
2062 newLinkProperty = propertyToDelete.previousProperty;
2064 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2067 * We also need to find a storage for the other link, setup variables
2068 * to do this at the end...
2070 needToFindAPlaceholder = TRUE;
2071 storeNode = propertyToDelete.previousProperty;
2072 toStoreNode = propertyToDelete.nextProperty;
2073 relationType = PROPERTY_RELATION_NEXT;
2076 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2079 * Set the parent previous to the property to delete next
2081 newLinkProperty = propertyToDelete.nextProperty;
2085 * Link it for real...
2087 parentProperty.previousProperty = newLinkProperty;
2090 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2092 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2095 * Set the parent next to the property to delete next previous
2097 newLinkProperty = propertyToDelete.previousProperty;
2099 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2102 * We also need to find a storage for the other link, setup variables
2103 * to do this at the end...
2105 needToFindAPlaceholder = TRUE;
2106 storeNode = propertyToDelete.previousProperty;
2107 toStoreNode = propertyToDelete.nextProperty;
2108 relationType = PROPERTY_RELATION_NEXT;
2111 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2114 * Set the parent next to the property to delete next
2116 newLinkProperty = propertyToDelete.nextProperty;
2120 * Link it for real...
2122 parentProperty.nextProperty = newLinkProperty;
2124 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2126 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2129 * Set the parent dir to the property to delete previous
2131 newLinkProperty = propertyToDelete.previousProperty;
2133 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2136 * We also need to find a storage for the other link, setup variables
2137 * to do this at the end...
2139 needToFindAPlaceholder = TRUE;
2140 storeNode = propertyToDelete.previousProperty;
2141 toStoreNode = propertyToDelete.nextProperty;
2142 relationType = PROPERTY_RELATION_NEXT;
2145 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2148 * Set the parent dir to the property to delete next
2150 newLinkProperty = propertyToDelete.nextProperty;
2154 * Link it for real...
2156 parentProperty.dirProperty = newLinkProperty;
2160 * Write back the parent property
2162 res = StorageImpl_WriteProperty(
2163 This->base.ancestorStorage,
2164 parentPropertyId,
2165 &parentProperty);
2166 if(! res)
2168 return E_FAIL;
2172 * If a placeholder is required for the other link, then, find one and
2173 * get out of here...
2175 if (needToFindAPlaceholder)
2177 hr = findPlaceholder(
2178 This,
2179 toStoreNode,
2180 storeNode,
2181 relationType);
2184 return hr;
2188 /******************************************************************************
2189 * SetElementTimes (IStorage)
2191 HRESULT WINAPI StorageImpl_SetElementTimes(
2192 IStorage* iface,
2193 const OLECHAR *pwcsName,/* [string][in] */
2194 const FILETIME *pctime, /* [in] */
2195 const FILETIME *patime, /* [in] */
2196 const FILETIME *pmtime) /* [in] */
2198 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2199 return S_OK;
2202 /******************************************************************************
2203 * SetStateBits (IStorage)
2205 HRESULT WINAPI StorageImpl_SetStateBits(
2206 IStorage* iface,
2207 DWORD grfStateBits,/* [in] */
2208 DWORD grfMask) /* [in] */
2210 FIXME("not implemented!\n");
2211 return E_NOTIMPL;
2215 * Virtual function table for the IStorage32Impl class.
2217 static const IStorageVtbl Storage32Impl_Vtbl =
2219 StorageBaseImpl_QueryInterface,
2220 StorageBaseImpl_AddRef,
2221 StorageBaseImpl_Release,
2222 StorageBaseImpl_CreateStream,
2223 StorageBaseImpl_OpenStream,
2224 StorageImpl_CreateStorage,
2225 StorageBaseImpl_OpenStorage,
2226 StorageImpl_CopyTo,
2227 StorageImpl_MoveElementTo,
2228 StorageImpl_Commit,
2229 StorageImpl_Revert,
2230 StorageBaseImpl_EnumElements,
2231 StorageImpl_DestroyElement,
2232 StorageBaseImpl_RenameElement,
2233 StorageImpl_SetElementTimes,
2234 StorageBaseImpl_SetClass,
2235 StorageImpl_SetStateBits,
2236 StorageImpl_Stat
2239 HRESULT StorageImpl_Construct(
2240 StorageImpl* This,
2241 HANDLE hFile,
2242 LPCOLESTR pwcsName,
2243 ILockBytes* pLkbyt,
2244 DWORD openFlags,
2245 BOOL fileBased,
2246 BOOL fileCreate)
2248 HRESULT hr = S_OK;
2249 StgProperty currentProperty;
2250 BOOL readSuccessful;
2251 ULONG currentPropertyIndex;
2253 if ( FAILED( validateSTGM(openFlags) ))
2254 return STG_E_INVALIDFLAG;
2256 memset(This, 0, sizeof(StorageImpl));
2259 * Initialize the virtual function table.
2261 This->base.lpVtbl = &Storage32Impl_Vtbl;
2262 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2263 This->base.v_destructor = &StorageImpl_Destroy;
2264 This->base.openFlags = openFlags;
2267 * This is the top-level storage so initialize the ancestor pointer
2268 * to this.
2270 This->base.ancestorStorage = This;
2273 * Initialize the physical support of the storage.
2275 This->hFile = hFile;
2278 * Store copy of file path.
2280 if(pwcsName) {
2281 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2282 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2283 if (!This->pwcsName)
2284 return STG_E_INSUFFICIENTMEMORY;
2285 strcpyW(This->pwcsName, pwcsName);
2289 * Initialize the big block cache.
2291 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2292 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2293 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2294 pLkbyt,
2295 openFlags,
2296 This->bigBlockSize,
2297 fileBased);
2299 if (This->bigBlockFile == 0)
2300 return E_FAIL;
2302 if (fileCreate)
2304 ULARGE_INTEGER size;
2305 BYTE* bigBlockBuffer;
2308 * Initialize all header variables:
2309 * - The big block depot consists of one block and it is at block 0
2310 * - The properties start at block 1
2311 * - There is no small block depot
2313 memset( This->bigBlockDepotStart,
2314 BLOCK_UNUSED,
2315 sizeof(This->bigBlockDepotStart));
2317 This->bigBlockDepotCount = 1;
2318 This->bigBlockDepotStart[0] = 0;
2319 This->rootStartBlock = 1;
2320 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2321 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2322 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2323 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2324 This->extBigBlockDepotCount = 0;
2326 StorageImpl_SaveFileHeader(This);
2329 * Add one block for the big block depot and one block for the properties
2331 size.u.HighPart = 0;
2332 size.u.LowPart = This->bigBlockSize * 3;
2333 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2336 * Initialize the big block depot
2338 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2339 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2340 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2341 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2342 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2344 else
2347 * Load the header for the file.
2349 hr = StorageImpl_LoadFileHeader(This);
2351 if (FAILED(hr))
2353 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2355 return hr;
2360 * There is no block depot cached yet.
2362 This->indexBlockDepotCached = 0xFFFFFFFF;
2365 * Start searching for free blocks with block 0.
2367 This->prevFreeBlock = 0;
2370 * Create the block chain abstractions.
2372 if(!(This->rootBlockChain =
2373 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2374 return STG_E_READFAULT;
2376 if(!(This->smallBlockDepotChain =
2377 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2378 PROPERTY_NULL)))
2379 return STG_E_READFAULT;
2382 * Write the root property
2384 if (fileCreate)
2386 StgProperty rootProp;
2388 * Initialize the property chain
2390 memset(&rootProp, 0, sizeof(rootProp));
2391 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2392 sizeof(rootProp.name)/sizeof(WCHAR) );
2393 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2394 rootProp.propertyType = PROPTYPE_ROOT;
2395 rootProp.previousProperty = PROPERTY_NULL;
2396 rootProp.nextProperty = PROPERTY_NULL;
2397 rootProp.dirProperty = PROPERTY_NULL;
2398 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2399 rootProp.size.u.HighPart = 0;
2400 rootProp.size.u.LowPart = 0;
2402 StorageImpl_WriteProperty(This, 0, &rootProp);
2406 * Find the ID of the root in the property sets.
2408 currentPropertyIndex = 0;
2412 readSuccessful = StorageImpl_ReadProperty(
2413 This,
2414 currentPropertyIndex,
2415 &currentProperty);
2417 if (readSuccessful)
2419 if ( (currentProperty.sizeOfNameString != 0 ) &&
2420 (currentProperty.propertyType == PROPTYPE_ROOT) )
2422 This->base.rootPropertySetIndex = currentPropertyIndex;
2426 currentPropertyIndex++;
2428 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2430 if (!readSuccessful)
2432 /* TODO CLEANUP */
2433 return STG_E_READFAULT;
2437 * Create the block chain abstraction for the small block root chain.
2439 if(!(This->smallBlockRootChain =
2440 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2441 return STG_E_READFAULT;
2443 return hr;
2446 void StorageImpl_Destroy(StorageBaseImpl* iface)
2448 StorageImpl *This = (StorageImpl*) iface;
2449 TRACE("(%p)\n", This);
2451 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2453 BlockChainStream_Destroy(This->smallBlockRootChain);
2454 BlockChainStream_Destroy(This->rootBlockChain);
2455 BlockChainStream_Destroy(This->smallBlockDepotChain);
2457 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2458 HeapFree(GetProcessHeap(), 0, This);
2461 /******************************************************************************
2462 * Storage32Impl_GetNextFreeBigBlock
2464 * Returns the index of the next free big block.
2465 * If the big block depot is filled, this method will enlarge it.
2468 ULONG StorageImpl_GetNextFreeBigBlock(
2469 StorageImpl* This)
2471 ULONG depotBlockIndexPos;
2472 void *depotBuffer;
2473 ULONG depotBlockOffset;
2474 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2475 ULONG nextBlockIndex = BLOCK_SPECIAL;
2476 int depotIndex = 0;
2477 ULONG freeBlock = BLOCK_UNUSED;
2479 depotIndex = This->prevFreeBlock / blocksPerDepot;
2480 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2483 * Scan the entire big block depot until we find a block marked free
2485 while (nextBlockIndex != BLOCK_UNUSED)
2487 if (depotIndex < COUNT_BBDEPOTINHEADER)
2489 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2492 * Grow the primary depot.
2494 if (depotBlockIndexPos == BLOCK_UNUSED)
2496 depotBlockIndexPos = depotIndex*blocksPerDepot;
2499 * Add a block depot.
2501 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2502 This->bigBlockDepotCount++;
2503 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2506 * Flag it as a block depot.
2508 StorageImpl_SetNextBlockInChain(This,
2509 depotBlockIndexPos,
2510 BLOCK_SPECIAL);
2512 /* Save new header information.
2514 StorageImpl_SaveFileHeader(This);
2517 else
2519 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2521 if (depotBlockIndexPos == BLOCK_UNUSED)
2524 * Grow the extended depot.
2526 ULONG extIndex = BLOCK_UNUSED;
2527 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2528 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2530 if (extBlockOffset == 0)
2532 /* We need an extended block.
2534 extIndex = Storage32Impl_AddExtBlockDepot(This);
2535 This->extBigBlockDepotCount++;
2536 depotBlockIndexPos = extIndex + 1;
2538 else
2539 depotBlockIndexPos = depotIndex * blocksPerDepot;
2542 * Add a block depot and mark it in the extended block.
2544 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2545 This->bigBlockDepotCount++;
2546 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2548 /* Flag the block depot.
2550 StorageImpl_SetNextBlockInChain(This,
2551 depotBlockIndexPos,
2552 BLOCK_SPECIAL);
2554 /* If necessary, flag the extended depot block.
2556 if (extIndex != BLOCK_UNUSED)
2557 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2559 /* Save header information.
2561 StorageImpl_SaveFileHeader(This);
2565 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2567 if (depotBuffer != 0)
2569 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2570 ( nextBlockIndex != BLOCK_UNUSED))
2572 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2574 if (nextBlockIndex == BLOCK_UNUSED)
2576 freeBlock = (depotIndex * blocksPerDepot) +
2577 (depotBlockOffset/sizeof(ULONG));
2580 depotBlockOffset += sizeof(ULONG);
2583 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2586 depotIndex++;
2587 depotBlockOffset = 0;
2590 This->prevFreeBlock = freeBlock;
2592 return freeBlock;
2595 /******************************************************************************
2596 * Storage32Impl_AddBlockDepot
2598 * This will create a depot block, essentially it is a block initialized
2599 * to BLOCK_UNUSEDs.
2601 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2603 BYTE* blockBuffer;
2605 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2608 * Initialize blocks as free
2610 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2612 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2615 /******************************************************************************
2616 * Storage32Impl_GetExtDepotBlock
2618 * Returns the index of the block that corresponds to the specified depot
2619 * index. This method is only for depot indexes equal or greater than
2620 * COUNT_BBDEPOTINHEADER.
2622 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2624 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2625 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2626 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2627 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2628 ULONG blockIndex = BLOCK_UNUSED;
2629 ULONG extBlockIndex = This->extBigBlockDepotStart;
2631 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2633 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2634 return BLOCK_UNUSED;
2636 while (extBlockCount > 0)
2638 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2639 extBlockCount--;
2642 if (extBlockIndex != BLOCK_UNUSED)
2644 BYTE* depotBuffer;
2646 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2648 if (depotBuffer != 0)
2650 StorageUtl_ReadDWord(depotBuffer,
2651 extBlockOffset * sizeof(ULONG),
2652 &blockIndex);
2654 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2658 return blockIndex;
2661 /******************************************************************************
2662 * Storage32Impl_SetExtDepotBlock
2664 * Associates the specified block index to the specified depot index.
2665 * This method is only for depot indexes equal or greater than
2666 * COUNT_BBDEPOTINHEADER.
2668 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2669 ULONG depotIndex,
2670 ULONG blockIndex)
2672 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2673 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2674 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2675 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2676 ULONG extBlockIndex = This->extBigBlockDepotStart;
2678 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2680 while (extBlockCount > 0)
2682 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2683 extBlockCount--;
2686 if (extBlockIndex != BLOCK_UNUSED)
2688 BYTE* depotBuffer;
2690 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2692 if (depotBuffer != 0)
2694 StorageUtl_WriteDWord(depotBuffer,
2695 extBlockOffset * sizeof(ULONG),
2696 blockIndex);
2698 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2703 /******************************************************************************
2704 * Storage32Impl_AddExtBlockDepot
2706 * Creates an extended depot block.
2708 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2710 ULONG numExtBlocks = This->extBigBlockDepotCount;
2711 ULONG nextExtBlock = This->extBigBlockDepotStart;
2712 BYTE* depotBuffer = NULL;
2713 ULONG index = BLOCK_UNUSED;
2714 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2715 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2716 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2718 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2719 blocksPerDepotBlock;
2721 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2724 * The first extended block.
2726 This->extBigBlockDepotStart = index;
2728 else
2730 unsigned int i;
2732 * Follow the chain to the last one.
2734 for (i = 0; i < (numExtBlocks - 1); i++)
2736 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2740 * Add the new extended block to the chain.
2742 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2743 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2744 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2748 * Initialize this block.
2750 depotBuffer = StorageImpl_GetBigBlock(This, index);
2751 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2752 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2754 return index;
2757 /******************************************************************************
2758 * Storage32Impl_FreeBigBlock
2760 * This method will flag the specified block as free in the big block depot.
2762 void StorageImpl_FreeBigBlock(
2763 StorageImpl* This,
2764 ULONG blockIndex)
2766 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2768 if (blockIndex < This->prevFreeBlock)
2769 This->prevFreeBlock = blockIndex;
2772 /************************************************************************
2773 * Storage32Impl_GetNextBlockInChain
2775 * This method will retrieve the block index of the next big block in
2776 * in the chain.
2778 * Params: This - Pointer to the Storage object.
2779 * blockIndex - Index of the block to retrieve the chain
2780 * for.
2781 * nextBlockIndex - receives the return value.
2783 * Returns: This method returns the index of the next block in the chain.
2784 * It will return the constants:
2785 * BLOCK_SPECIAL - If the block given was not part of a
2786 * chain.
2787 * BLOCK_END_OF_CHAIN - If the block given was the last in
2788 * a chain.
2789 * BLOCK_UNUSED - If the block given was not past of a chain
2790 * and is available.
2791 * BLOCK_EXTBBDEPOT - This block is part of the extended
2792 * big block depot.
2794 * See Windows documentation for more details on IStorage methods.
2796 HRESULT StorageImpl_GetNextBlockInChain(
2797 StorageImpl* This,
2798 ULONG blockIndex,
2799 ULONG* nextBlockIndex)
2801 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2802 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2803 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2804 void* depotBuffer;
2805 ULONG depotBlockIndexPos;
2806 int index;
2808 *nextBlockIndex = BLOCK_SPECIAL;
2810 if(depotBlockCount >= This->bigBlockDepotCount)
2812 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2813 This->bigBlockDepotCount);
2814 return STG_E_READFAULT;
2818 * Cache the currently accessed depot block.
2820 if (depotBlockCount != This->indexBlockDepotCached)
2822 This->indexBlockDepotCached = depotBlockCount;
2824 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2826 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2828 else
2831 * We have to look in the extended depot.
2833 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2836 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2838 if (!depotBuffer)
2839 return STG_E_READFAULT;
2841 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2843 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2844 This->blockDepotCached[index] = *nextBlockIndex;
2846 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2849 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2851 return S_OK;
2854 /******************************************************************************
2855 * Storage32Impl_GetNextExtendedBlock
2857 * Given an extended block this method will return the next extended block.
2859 * NOTES:
2860 * The last ULONG of an extended block is the block index of the next
2861 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2862 * depot.
2864 * Return values:
2865 * - The index of the next extended block
2866 * - BLOCK_UNUSED: there is no next extended block.
2867 * - Any other return values denotes failure.
2869 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2871 ULONG nextBlockIndex = BLOCK_SPECIAL;
2872 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2873 void* depotBuffer;
2875 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2877 if (depotBuffer!=0)
2879 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2881 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2884 return nextBlockIndex;
2887 /******************************************************************************
2888 * Storage32Impl_SetNextBlockInChain
2890 * This method will write the index of the specified block's next block
2891 * in the big block depot.
2893 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2894 * do the following
2896 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2897 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2898 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2901 void StorageImpl_SetNextBlockInChain(
2902 StorageImpl* This,
2903 ULONG blockIndex,
2904 ULONG nextBlock)
2906 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2907 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2908 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2909 ULONG depotBlockIndexPos;
2910 void* depotBuffer;
2912 assert(depotBlockCount < This->bigBlockDepotCount);
2913 assert(blockIndex != nextBlock);
2915 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2917 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2919 else
2922 * We have to look in the extended depot.
2924 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2927 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2929 if (depotBuffer!=0)
2931 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2932 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2936 * Update the cached block depot, if necessary.
2938 if (depotBlockCount == This->indexBlockDepotCached)
2940 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2944 /******************************************************************************
2945 * Storage32Impl_LoadFileHeader
2947 * This method will read in the file header, i.e. big block index -1.
2949 HRESULT StorageImpl_LoadFileHeader(
2950 StorageImpl* This)
2952 HRESULT hr = STG_E_FILENOTFOUND;
2953 void* headerBigBlock = NULL;
2954 int index;
2957 * Get a pointer to the big block of data containing the header.
2959 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2962 * Extract the information from the header.
2964 if (headerBigBlock!=0)
2967 * Check for the "magic number" signature and return an error if it is not
2968 * found.
2970 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2972 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2973 return STG_E_OLDFORMAT;
2976 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2978 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2979 return STG_E_INVALIDHEADER;
2982 StorageUtl_ReadWord(
2983 headerBigBlock,
2984 OFFSET_BIGBLOCKSIZEBITS,
2985 &This->bigBlockSizeBits);
2987 StorageUtl_ReadWord(
2988 headerBigBlock,
2989 OFFSET_SMALLBLOCKSIZEBITS,
2990 &This->smallBlockSizeBits);
2992 StorageUtl_ReadDWord(
2993 headerBigBlock,
2994 OFFSET_BBDEPOTCOUNT,
2995 &This->bigBlockDepotCount);
2997 StorageUtl_ReadDWord(
2998 headerBigBlock,
2999 OFFSET_ROOTSTARTBLOCK,
3000 &This->rootStartBlock);
3002 StorageUtl_ReadDWord(
3003 headerBigBlock,
3004 OFFSET_SBDEPOTSTART,
3005 &This->smallBlockDepotStart);
3007 StorageUtl_ReadDWord(
3008 headerBigBlock,
3009 OFFSET_EXTBBDEPOTSTART,
3010 &This->extBigBlockDepotStart);
3012 StorageUtl_ReadDWord(
3013 headerBigBlock,
3014 OFFSET_EXTBBDEPOTCOUNT,
3015 &This->extBigBlockDepotCount);
3017 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3019 StorageUtl_ReadDWord(
3020 headerBigBlock,
3021 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3022 &(This->bigBlockDepotStart[index]));
3026 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3028 if ((1 << 2) == 4)
3030 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3031 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3033 else
3035 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3036 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3040 * Right now, the code is making some assumptions about the size of the
3041 * blocks, just make sure they are what we're expecting.
3043 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3044 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3046 WARN("Broken OLE storage file\n");
3047 hr = STG_E_INVALIDHEADER;
3049 else
3050 hr = S_OK;
3053 * Release the block.
3055 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3058 return hr;
3061 /******************************************************************************
3062 * Storage32Impl_SaveFileHeader
3064 * This method will save to the file the header, i.e. big block -1.
3066 void StorageImpl_SaveFileHeader(
3067 StorageImpl* This)
3069 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3070 int index;
3071 BOOL success;
3074 * Get a pointer to the big block of data containing the header.
3076 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3079 * If the block read failed, the file is probably new.
3081 if (!success)
3084 * Initialize for all unknown fields.
3086 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3089 * Initialize the magic number.
3091 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3094 * And a bunch of things we don't know what they mean
3096 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3097 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3098 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3099 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3103 * Write the information to the header.
3105 StorageUtl_WriteWord(
3106 headerBigBlock,
3107 OFFSET_BIGBLOCKSIZEBITS,
3108 This->bigBlockSizeBits);
3110 StorageUtl_WriteWord(
3111 headerBigBlock,
3112 OFFSET_SMALLBLOCKSIZEBITS,
3113 This->smallBlockSizeBits);
3115 StorageUtl_WriteDWord(
3116 headerBigBlock,
3117 OFFSET_BBDEPOTCOUNT,
3118 This->bigBlockDepotCount);
3120 StorageUtl_WriteDWord(
3121 headerBigBlock,
3122 OFFSET_ROOTSTARTBLOCK,
3123 This->rootStartBlock);
3125 StorageUtl_WriteDWord(
3126 headerBigBlock,
3127 OFFSET_SBDEPOTSTART,
3128 This->smallBlockDepotStart);
3130 StorageUtl_WriteDWord(
3131 headerBigBlock,
3132 OFFSET_SBDEPOTCOUNT,
3133 This->smallBlockDepotChain ?
3134 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3136 StorageUtl_WriteDWord(
3137 headerBigBlock,
3138 OFFSET_EXTBBDEPOTSTART,
3139 This->extBigBlockDepotStart);
3141 StorageUtl_WriteDWord(
3142 headerBigBlock,
3143 OFFSET_EXTBBDEPOTCOUNT,
3144 This->extBigBlockDepotCount);
3146 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3148 StorageUtl_WriteDWord(
3149 headerBigBlock,
3150 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3151 (This->bigBlockDepotStart[index]));
3155 * Write the big block back to the file.
3157 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3160 /******************************************************************************
3161 * Storage32Impl_ReadProperty
3163 * This method will read the specified property from the property chain.
3165 BOOL StorageImpl_ReadProperty(
3166 StorageImpl* This,
3167 ULONG index,
3168 StgProperty* buffer)
3170 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3171 ULARGE_INTEGER offsetInPropSet;
3172 BOOL readSuccessful;
3173 ULONG bytesRead;
3175 offsetInPropSet.u.HighPart = 0;
3176 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3178 readSuccessful = BlockChainStream_ReadAt(
3179 This->rootBlockChain,
3180 offsetInPropSet,
3181 PROPSET_BLOCK_SIZE,
3182 currentProperty,
3183 &bytesRead);
3185 if (readSuccessful)
3187 /* replace the name of root entry (often "Root Entry") by the file name */
3188 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3189 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3191 memset(buffer->name, 0, sizeof(buffer->name));
3192 memcpy(
3193 buffer->name,
3194 propName,
3195 PROPERTY_NAME_BUFFER_LEN );
3196 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3198 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3200 StorageUtl_ReadWord(
3201 currentProperty,
3202 OFFSET_PS_NAMELENGTH,
3203 &buffer->sizeOfNameString);
3205 StorageUtl_ReadDWord(
3206 currentProperty,
3207 OFFSET_PS_PREVIOUSPROP,
3208 &buffer->previousProperty);
3210 StorageUtl_ReadDWord(
3211 currentProperty,
3212 OFFSET_PS_NEXTPROP,
3213 &buffer->nextProperty);
3215 StorageUtl_ReadDWord(
3216 currentProperty,
3217 OFFSET_PS_DIRPROP,
3218 &buffer->dirProperty);
3220 StorageUtl_ReadGUID(
3221 currentProperty,
3222 OFFSET_PS_GUID,
3223 &buffer->propertyUniqueID);
3225 StorageUtl_ReadDWord(
3226 currentProperty,
3227 OFFSET_PS_TSS1,
3228 &buffer->timeStampS1);
3230 StorageUtl_ReadDWord(
3231 currentProperty,
3232 OFFSET_PS_TSD1,
3233 &buffer->timeStampD1);
3235 StorageUtl_ReadDWord(
3236 currentProperty,
3237 OFFSET_PS_TSS2,
3238 &buffer->timeStampS2);
3240 StorageUtl_ReadDWord(
3241 currentProperty,
3242 OFFSET_PS_TSD2,
3243 &buffer->timeStampD2);
3245 StorageUtl_ReadDWord(
3246 currentProperty,
3247 OFFSET_PS_STARTBLOCK,
3248 &buffer->startingBlock);
3250 StorageUtl_ReadDWord(
3251 currentProperty,
3252 OFFSET_PS_SIZE,
3253 &buffer->size.u.LowPart);
3255 buffer->size.u.HighPart = 0;
3258 return readSuccessful;
3261 /*********************************************************************
3262 * Write the specified property into the property chain
3264 BOOL StorageImpl_WriteProperty(
3265 StorageImpl* This,
3266 ULONG index,
3267 StgProperty* buffer)
3269 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3270 ULARGE_INTEGER offsetInPropSet;
3271 BOOL writeSuccessful;
3272 ULONG bytesWritten;
3274 offsetInPropSet.u.HighPart = 0;
3275 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3277 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3279 memcpy(
3280 currentProperty + OFFSET_PS_NAME,
3281 buffer->name,
3282 PROPERTY_NAME_BUFFER_LEN );
3284 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3286 StorageUtl_WriteWord(
3287 currentProperty,
3288 OFFSET_PS_NAMELENGTH,
3289 buffer->sizeOfNameString);
3291 StorageUtl_WriteDWord(
3292 currentProperty,
3293 OFFSET_PS_PREVIOUSPROP,
3294 buffer->previousProperty);
3296 StorageUtl_WriteDWord(
3297 currentProperty,
3298 OFFSET_PS_NEXTPROP,
3299 buffer->nextProperty);
3301 StorageUtl_WriteDWord(
3302 currentProperty,
3303 OFFSET_PS_DIRPROP,
3304 buffer->dirProperty);
3306 StorageUtl_WriteGUID(
3307 currentProperty,
3308 OFFSET_PS_GUID,
3309 &buffer->propertyUniqueID);
3311 StorageUtl_WriteDWord(
3312 currentProperty,
3313 OFFSET_PS_TSS1,
3314 buffer->timeStampS1);
3316 StorageUtl_WriteDWord(
3317 currentProperty,
3318 OFFSET_PS_TSD1,
3319 buffer->timeStampD1);
3321 StorageUtl_WriteDWord(
3322 currentProperty,
3323 OFFSET_PS_TSS2,
3324 buffer->timeStampS2);
3326 StorageUtl_WriteDWord(
3327 currentProperty,
3328 OFFSET_PS_TSD2,
3329 buffer->timeStampD2);
3331 StorageUtl_WriteDWord(
3332 currentProperty,
3333 OFFSET_PS_STARTBLOCK,
3334 buffer->startingBlock);
3336 StorageUtl_WriteDWord(
3337 currentProperty,
3338 OFFSET_PS_SIZE,
3339 buffer->size.u.LowPart);
3341 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3342 offsetInPropSet,
3343 PROPSET_BLOCK_SIZE,
3344 currentProperty,
3345 &bytesWritten);
3346 return writeSuccessful;
3349 BOOL StorageImpl_ReadBigBlock(
3350 StorageImpl* This,
3351 ULONG blockIndex,
3352 void* buffer)
3354 void* bigBlockBuffer;
3356 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3358 if (bigBlockBuffer!=0)
3360 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3362 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3364 return TRUE;
3367 return FALSE;
3370 BOOL StorageImpl_WriteBigBlock(
3371 StorageImpl* This,
3372 ULONG blockIndex,
3373 void* buffer)
3375 void* bigBlockBuffer;
3377 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3379 if (bigBlockBuffer!=0)
3381 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3383 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3385 return TRUE;
3388 return FALSE;
3391 void* StorageImpl_GetROBigBlock(
3392 StorageImpl* This,
3393 ULONG blockIndex)
3395 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3398 void* StorageImpl_GetBigBlock(
3399 StorageImpl* This,
3400 ULONG blockIndex)
3402 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3405 void StorageImpl_ReleaseBigBlock(
3406 StorageImpl* This,
3407 void* pBigBlock)
3409 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3412 /******************************************************************************
3413 * Storage32Impl_SmallBlocksToBigBlocks
3415 * This method will convert a small block chain to a big block chain.
3416 * The small block chain will be destroyed.
3418 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3419 StorageImpl* This,
3420 SmallBlockChainStream** ppsbChain)
3422 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3423 ULARGE_INTEGER size, offset;
3424 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3425 ULONG propertyIndex;
3426 BOOL successWrite;
3427 HRESULT successRead;
3428 StgProperty chainProperty;
3429 BYTE *buffer;
3430 BlockChainStream *bbTempChain = NULL;
3431 BlockChainStream *bigBlockChain = NULL;
3434 * Create a temporary big block chain that doesn't have
3435 * an associated property. This temporary chain will be
3436 * used to copy data from small blocks to big blocks.
3438 bbTempChain = BlockChainStream_Construct(This,
3439 &bbHeadOfChain,
3440 PROPERTY_NULL);
3441 if(!bbTempChain) return NULL;
3443 * Grow the big block chain.
3445 size = SmallBlockChainStream_GetSize(*ppsbChain);
3446 BlockChainStream_SetSize(bbTempChain, size);
3449 * Copy the contents of the small block chain to the big block chain
3450 * by small block size increments.
3452 offset.u.LowPart = 0;
3453 offset.u.HighPart = 0;
3454 cbTotalRead = 0;
3455 cbTotalWritten = 0;
3457 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3460 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3461 offset,
3462 DEF_SMALL_BLOCK_SIZE,
3463 buffer,
3464 &cbRead);
3465 if (FAILED(successRead))
3466 break;
3468 if (cbRead > 0)
3470 cbTotalRead += cbRead;
3472 successWrite = BlockChainStream_WriteAt(bbTempChain,
3473 offset,
3474 cbRead,
3475 buffer,
3476 &cbWritten);
3478 if (!successWrite)
3479 break;
3481 cbTotalWritten += cbWritten;
3482 offset.u.LowPart += This->smallBlockSize;
3484 } while (cbRead > 0);
3485 HeapFree(GetProcessHeap(),0,buffer);
3487 assert(cbTotalRead == cbTotalWritten);
3490 * Destroy the small block chain.
3492 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3493 size.u.HighPart = 0;
3494 size.u.LowPart = 0;
3495 SmallBlockChainStream_SetSize(*ppsbChain, size);
3496 SmallBlockChainStream_Destroy(*ppsbChain);
3497 *ppsbChain = 0;
3500 * Change the property information. This chain is now a big block chain
3501 * and it doesn't reside in the small blocks chain anymore.
3503 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3505 chainProperty.startingBlock = bbHeadOfChain;
3507 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3510 * Destroy the temporary propertyless big block chain.
3511 * Create a new big block chain associated with this property.
3513 BlockChainStream_Destroy(bbTempChain);
3514 bigBlockChain = BlockChainStream_Construct(This,
3515 NULL,
3516 propertyIndex);
3518 return bigBlockChain;
3521 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3523 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3525 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3526 HeapFree(GetProcessHeap(), 0, This);
3529 /******************************************************************************
3531 ** Storage32InternalImpl_Commit
3533 ** The non-root storages cannot be opened in transacted mode thus this function
3534 ** does nothing.
3536 HRESULT WINAPI StorageInternalImpl_Commit(
3537 IStorage* iface,
3538 DWORD grfCommitFlags) /* [in] */
3540 return S_OK;
3543 /******************************************************************************
3545 ** Storage32InternalImpl_Revert
3547 ** The non-root storages cannot be opened in transacted mode thus this function
3548 ** does nothing.
3550 HRESULT WINAPI StorageInternalImpl_Revert(
3551 IStorage* iface)
3553 return S_OK;
3556 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3558 IStorage_Release((IStorage*)This->parentStorage);
3559 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3560 HeapFree(GetProcessHeap(), 0, This);
3563 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3564 IEnumSTATSTG* iface,
3565 REFIID riid,
3566 void** ppvObject)
3568 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3571 * Perform a sanity check on the parameters.
3573 if (ppvObject==0)
3574 return E_INVALIDARG;
3577 * Initialize the return parameter.
3579 *ppvObject = 0;
3582 * Compare the riid with the interface IDs implemented by this object.
3584 if (IsEqualGUID(&IID_IUnknown, riid) ||
3585 IsEqualGUID(&IID_IStorage, riid))
3587 *ppvObject = (IEnumSTATSTG*)This;
3588 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3589 return S_OK;
3592 return E_NOINTERFACE;
3595 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3596 IEnumSTATSTG* iface)
3598 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3599 return InterlockedIncrement(&This->ref);
3602 ULONG WINAPI IEnumSTATSTGImpl_Release(
3603 IEnumSTATSTG* iface)
3605 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3607 ULONG newRef;
3609 newRef = InterlockedDecrement(&This->ref);
3612 * If the reference count goes down to 0, perform suicide.
3614 if (newRef==0)
3616 IEnumSTATSTGImpl_Destroy(This);
3619 return newRef;
3622 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3623 IEnumSTATSTG* iface,
3624 ULONG celt,
3625 STATSTG* rgelt,
3626 ULONG* pceltFetched)
3628 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3630 StgProperty currentProperty;
3631 STATSTG* currentReturnStruct = rgelt;
3632 ULONG objectFetched = 0;
3633 ULONG currentSearchNode;
3636 * Perform a sanity check on the parameters.
3638 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3639 return E_INVALIDARG;
3642 * To avoid the special case, get another pointer to a ULONG value if
3643 * the caller didn't supply one.
3645 if (pceltFetched==0)
3646 pceltFetched = &objectFetched;
3649 * Start the iteration, we will iterate until we hit the end of the
3650 * linked list or until we hit the number of items to iterate through
3652 *pceltFetched = 0;
3655 * Start with the node at the top of the stack.
3657 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3659 while ( ( *pceltFetched < celt) &&
3660 ( currentSearchNode!=PROPERTY_NULL) )
3663 * Remove the top node from the stack
3665 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3668 * Read the property from the storage.
3670 StorageImpl_ReadProperty(This->parentStorage,
3671 currentSearchNode,
3672 &currentProperty);
3675 * Copy the information to the return buffer.
3677 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3678 &currentProperty,
3679 STATFLAG_DEFAULT);
3682 * Step to the next item in the iteration
3684 (*pceltFetched)++;
3685 currentReturnStruct++;
3688 * Push the next search node in the search stack.
3690 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3693 * continue the iteration.
3695 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3698 if (*pceltFetched == celt)
3699 return S_OK;
3701 return S_FALSE;
3705 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3706 IEnumSTATSTG* iface,
3707 ULONG celt)
3709 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3711 StgProperty currentProperty;
3712 ULONG objectFetched = 0;
3713 ULONG currentSearchNode;
3716 * Start with the node at the top of the stack.
3718 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3720 while ( (objectFetched < celt) &&
3721 (currentSearchNode!=PROPERTY_NULL) )
3724 * Remove the top node from the stack
3726 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3729 * Read the property from the storage.
3731 StorageImpl_ReadProperty(This->parentStorage,
3732 currentSearchNode,
3733 &currentProperty);
3736 * Step to the next item in the iteration
3738 objectFetched++;
3741 * Push the next search node in the search stack.
3743 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3746 * continue the iteration.
3748 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3751 if (objectFetched == celt)
3752 return S_OK;
3754 return S_FALSE;
3757 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3758 IEnumSTATSTG* iface)
3760 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3762 StgProperty rootProperty;
3763 BOOL readSuccessful;
3766 * Re-initialize the search stack to an empty stack
3768 This->stackSize = 0;
3771 * Read the root property from the storage.
3773 readSuccessful = StorageImpl_ReadProperty(
3774 This->parentStorage,
3775 This->firstPropertyNode,
3776 &rootProperty);
3778 if (readSuccessful)
3780 assert(rootProperty.sizeOfNameString!=0);
3783 * Push the search node in the search stack.
3785 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3788 return S_OK;
3791 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3792 IEnumSTATSTG* iface,
3793 IEnumSTATSTG** ppenum)
3795 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3797 IEnumSTATSTGImpl* newClone;
3800 * Perform a sanity check on the parameters.
3802 if (ppenum==0)
3803 return E_INVALIDARG;
3805 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3806 This->firstPropertyNode);
3810 * The new clone enumeration must point to the same current node as
3811 * the ole one.
3813 newClone->stackSize = This->stackSize ;
3814 newClone->stackMaxSize = This->stackMaxSize ;
3815 newClone->stackToVisit =
3816 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3818 memcpy(
3819 newClone->stackToVisit,
3820 This->stackToVisit,
3821 sizeof(ULONG) * newClone->stackSize);
3823 *ppenum = (IEnumSTATSTG*)newClone;
3826 * Don't forget to nail down a reference to the clone before
3827 * returning it.
3829 IEnumSTATSTGImpl_AddRef(*ppenum);
3831 return S_OK;
3834 INT IEnumSTATSTGImpl_FindParentProperty(
3835 IEnumSTATSTGImpl *This,
3836 ULONG childProperty,
3837 StgProperty *currentProperty,
3838 ULONG *thisNodeId)
3840 ULONG currentSearchNode;
3841 ULONG foundNode;
3844 * To avoid the special case, get another pointer to a ULONG value if
3845 * the caller didn't supply one.
3847 if (thisNodeId==0)
3848 thisNodeId = &foundNode;
3851 * Start with the node at the top of the stack.
3853 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3856 while (currentSearchNode!=PROPERTY_NULL)
3859 * Store the current node in the returned parameters
3861 *thisNodeId = currentSearchNode;
3864 * Remove the top node from the stack
3866 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3869 * Read the property from the storage.
3871 StorageImpl_ReadProperty(
3872 This->parentStorage,
3873 currentSearchNode,
3874 currentProperty);
3876 if (currentProperty->previousProperty == childProperty)
3877 return PROPERTY_RELATION_PREVIOUS;
3879 else if (currentProperty->nextProperty == childProperty)
3880 return PROPERTY_RELATION_NEXT;
3882 else if (currentProperty->dirProperty == childProperty)
3883 return PROPERTY_RELATION_DIR;
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 ULONG IEnumSTATSTGImpl_FindProperty(
3900 IEnumSTATSTGImpl* This,
3901 const OLECHAR* lpszPropName,
3902 StgProperty* currentProperty)
3904 ULONG currentSearchNode;
3907 * Start with the node at the top of the stack.
3909 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3911 while (currentSearchNode!=PROPERTY_NULL)
3914 * Remove the top node from the stack
3916 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3919 * Read the property from the storage.
3921 StorageImpl_ReadProperty(This->parentStorage,
3922 currentSearchNode,
3923 currentProperty);
3925 if ( propertyNameCmp(
3926 (const OLECHAR*)currentProperty->name,
3927 (const OLECHAR*)lpszPropName) == 0)
3928 return currentSearchNode;
3931 * Push the next search node in the search stack.
3933 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3936 * continue the iteration.
3938 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3941 return PROPERTY_NULL;
3944 void IEnumSTATSTGImpl_PushSearchNode(
3945 IEnumSTATSTGImpl* This,
3946 ULONG nodeToPush)
3948 StgProperty rootProperty;
3949 BOOL readSuccessful;
3952 * First, make sure we're not trying to push an unexisting node.
3954 if (nodeToPush==PROPERTY_NULL)
3955 return;
3958 * First push the node to the stack
3960 if (This->stackSize == This->stackMaxSize)
3962 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3964 This->stackToVisit = HeapReAlloc(
3965 GetProcessHeap(),
3967 This->stackToVisit,
3968 sizeof(ULONG) * This->stackMaxSize);
3971 This->stackToVisit[This->stackSize] = nodeToPush;
3972 This->stackSize++;
3975 * Read the root property from the storage.
3977 readSuccessful = StorageImpl_ReadProperty(
3978 This->parentStorage,
3979 nodeToPush,
3980 &rootProperty);
3982 if (readSuccessful)
3984 assert(rootProperty.sizeOfNameString!=0);
3987 * Push the previous search node in the search stack.
3989 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3993 ULONG IEnumSTATSTGImpl_PopSearchNode(
3994 IEnumSTATSTGImpl* This,
3995 BOOL remove)
3997 ULONG topNode;
3999 if (This->stackSize == 0)
4000 return PROPERTY_NULL;
4002 topNode = This->stackToVisit[This->stackSize-1];
4004 if (remove)
4005 This->stackSize--;
4007 return topNode;
4011 * Virtual function table for the IEnumSTATSTGImpl class.
4013 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4015 IEnumSTATSTGImpl_QueryInterface,
4016 IEnumSTATSTGImpl_AddRef,
4017 IEnumSTATSTGImpl_Release,
4018 IEnumSTATSTGImpl_Next,
4019 IEnumSTATSTGImpl_Skip,
4020 IEnumSTATSTGImpl_Reset,
4021 IEnumSTATSTGImpl_Clone
4024 /******************************************************************************
4025 ** IEnumSTATSTGImpl implementation
4028 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4029 StorageImpl* parentStorage,
4030 ULONG firstPropertyNode)
4032 IEnumSTATSTGImpl* newEnumeration;
4034 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4036 if (newEnumeration!=0)
4039 * Set-up the virtual function table and reference count.
4041 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4042 newEnumeration->ref = 0;
4045 * We want to nail-down the reference to the storage in case the
4046 * enumeration out-lives the storage in the client application.
4048 newEnumeration->parentStorage = parentStorage;
4049 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4051 newEnumeration->firstPropertyNode = firstPropertyNode;
4054 * Initialize the search stack
4056 newEnumeration->stackSize = 0;
4057 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4058 newEnumeration->stackToVisit =
4059 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4062 * Make sure the current node of the iterator is the first one.
4064 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4067 return newEnumeration;
4071 * Virtual function table for the Storage32InternalImpl class.
4073 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4075 StorageBaseImpl_QueryInterface,
4076 StorageBaseImpl_AddRef,
4077 StorageBaseImpl_Release,
4078 StorageBaseImpl_CreateStream,
4079 StorageBaseImpl_OpenStream,
4080 StorageImpl_CreateStorage,
4081 StorageBaseImpl_OpenStorage,
4082 StorageImpl_CopyTo,
4083 StorageImpl_MoveElementTo,
4084 StorageInternalImpl_Commit,
4085 StorageInternalImpl_Revert,
4086 StorageBaseImpl_EnumElements,
4087 StorageImpl_DestroyElement,
4088 StorageBaseImpl_RenameElement,
4089 StorageImpl_SetElementTimes,
4090 StorageBaseImpl_SetClass,
4091 StorageImpl_SetStateBits,
4092 StorageBaseImpl_Stat
4095 /******************************************************************************
4096 ** Storage32InternalImpl implementation
4099 StorageInternalImpl* StorageInternalImpl_Construct(
4100 StorageImpl* ancestorStorage,
4101 DWORD openFlags,
4102 ULONG rootPropertyIndex)
4104 StorageInternalImpl* newStorage;
4107 * Allocate space for the new storage object
4109 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4111 if (newStorage!=0)
4113 memset(newStorage, 0, sizeof(StorageInternalImpl));
4116 * Initialize the virtual function table.
4118 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4119 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4120 newStorage->base.openFlags = openFlags;
4123 * Keep the ancestor storage pointer and nail a reference to it.
4125 newStorage->base.ancestorStorage = ancestorStorage;
4126 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4129 * Keep the index of the root property set for this storage,
4131 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4133 return newStorage;
4136 return 0;
4139 /******************************************************************************
4140 ** StorageUtl implementation
4143 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4145 WORD tmp;
4147 memcpy(&tmp, buffer+offset, sizeof(WORD));
4148 *value = le16toh(tmp);
4151 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4153 value = htole16(value);
4154 memcpy(buffer+offset, &value, sizeof(WORD));
4157 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4159 DWORD tmp;
4161 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4162 *value = le32toh(tmp);
4165 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4167 value = htole32(value);
4168 memcpy(buffer+offset, &value, sizeof(DWORD));
4171 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4172 ULARGE_INTEGER* value)
4174 #ifdef WORDS_BIGENDIAN
4175 ULARGE_INTEGER tmp;
4177 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4178 value->u.LowPart = htole32(tmp.u.HighPart);
4179 value->u.HighPart = htole32(tmp.u.LowPart);
4180 #else
4181 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4182 #endif
4185 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4186 const ULARGE_INTEGER *value)
4188 #ifdef WORDS_BIGENDIAN
4189 ULARGE_INTEGER tmp;
4191 tmp.u.LowPart = htole32(value->u.HighPart);
4192 tmp.u.HighPart = htole32(value->u.LowPart);
4193 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4194 #else
4195 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4196 #endif
4199 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4201 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4202 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4203 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4205 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4208 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4210 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4211 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4212 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4214 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4217 void StorageUtl_CopyPropertyToSTATSTG(
4218 STATSTG* destination,
4219 StgProperty* source,
4220 int statFlags)
4223 * The copy of the string occurs only when the flag is not set
4225 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4226 (source->name == NULL) ||
4227 (source->name[0] == 0) )
4229 destination->pwcsName = 0;
4231 else
4233 destination->pwcsName =
4234 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4236 strcpyW((LPWSTR)destination->pwcsName, source->name);
4239 switch (source->propertyType)
4241 case PROPTYPE_STORAGE:
4242 case PROPTYPE_ROOT:
4243 destination->type = STGTY_STORAGE;
4244 break;
4245 case PROPTYPE_STREAM:
4246 destination->type = STGTY_STREAM;
4247 break;
4248 default:
4249 destination->type = STGTY_STREAM;
4250 break;
4253 destination->cbSize = source->size;
4255 currentReturnStruct->mtime = {0}; TODO
4256 currentReturnStruct->ctime = {0};
4257 currentReturnStruct->atime = {0};
4259 destination->grfMode = 0;
4260 destination->grfLocksSupported = 0;
4261 destination->clsid = source->propertyUniqueID;
4262 destination->grfStateBits = 0;
4263 destination->reserved = 0;
4266 /******************************************************************************
4267 ** BlockChainStream implementation
4270 BlockChainStream* BlockChainStream_Construct(
4271 StorageImpl* parentStorage,
4272 ULONG* headOfStreamPlaceHolder,
4273 ULONG propertyIndex)
4275 BlockChainStream* newStream;
4276 ULONG blockIndex;
4278 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4280 newStream->parentStorage = parentStorage;
4281 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4282 newStream->ownerPropertyIndex = propertyIndex;
4283 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4284 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4285 newStream->numBlocks = 0;
4287 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4289 while (blockIndex != BLOCK_END_OF_CHAIN)
4291 newStream->numBlocks++;
4292 newStream->tailIndex = blockIndex;
4294 if(FAILED(StorageImpl_GetNextBlockInChain(
4295 parentStorage,
4296 blockIndex,
4297 &blockIndex)))
4299 HeapFree(GetProcessHeap(), 0, newStream);
4300 return NULL;
4304 return newStream;
4307 void BlockChainStream_Destroy(BlockChainStream* This)
4309 HeapFree(GetProcessHeap(), 0, This);
4312 /******************************************************************************
4313 * BlockChainStream_GetHeadOfChain
4315 * Returns the head of this stream chain.
4316 * Some special chains don't have properties, their heads are kept in
4317 * This->headOfStreamPlaceHolder.
4320 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4322 StgProperty chainProperty;
4323 BOOL readSuccessful;
4325 if (This->headOfStreamPlaceHolder != 0)
4326 return *(This->headOfStreamPlaceHolder);
4328 if (This->ownerPropertyIndex != PROPERTY_NULL)
4330 readSuccessful = StorageImpl_ReadProperty(
4331 This->parentStorage,
4332 This->ownerPropertyIndex,
4333 &chainProperty);
4335 if (readSuccessful)
4337 return chainProperty.startingBlock;
4341 return BLOCK_END_OF_CHAIN;
4344 /******************************************************************************
4345 * BlockChainStream_GetCount
4347 * Returns the number of blocks that comprises this chain.
4348 * This is not the size of the stream as the last block may not be full!
4351 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4353 ULONG blockIndex;
4354 ULONG count = 0;
4356 blockIndex = BlockChainStream_GetHeadOfChain(This);
4358 while (blockIndex != BLOCK_END_OF_CHAIN)
4360 count++;
4362 if(FAILED(StorageImpl_GetNextBlockInChain(
4363 This->parentStorage,
4364 blockIndex,
4365 &blockIndex)))
4366 return 0;
4369 return count;
4372 /******************************************************************************
4373 * BlockChainStream_ReadAt
4375 * Reads a specified number of bytes from this chain at the specified offset.
4376 * bytesRead may be NULL.
4377 * Failure will be returned if the specified number of bytes has not been read.
4379 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4380 ULARGE_INTEGER offset,
4381 ULONG size,
4382 void* buffer,
4383 ULONG* bytesRead)
4385 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4386 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4387 ULONG bytesToReadInBuffer;
4388 ULONG blockIndex;
4389 BYTE* bufferWalker;
4390 BYTE* bigBlockBuffer;
4393 * Find the first block in the stream that contains part of the buffer.
4395 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4396 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4397 (blockNoInSequence < This->lastBlockNoInSequence) )
4399 blockIndex = BlockChainStream_GetHeadOfChain(This);
4400 This->lastBlockNoInSequence = blockNoInSequence;
4402 else
4404 ULONG temp = blockNoInSequence;
4406 blockIndex = This->lastBlockNoInSequenceIndex;
4407 blockNoInSequence -= This->lastBlockNoInSequence;
4408 This->lastBlockNoInSequence = temp;
4411 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4413 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4414 return FALSE;
4415 blockNoInSequence--;
4418 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4419 return FALSE; /* We failed to find the starting block */
4421 This->lastBlockNoInSequenceIndex = blockIndex;
4424 * Start reading the buffer.
4426 *bytesRead = 0;
4427 bufferWalker = buffer;
4429 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4432 * Calculate how many bytes we can copy from this big block.
4434 bytesToReadInBuffer =
4435 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4438 * Copy those bytes to the buffer
4440 bigBlockBuffer =
4441 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4443 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4445 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4448 * Step to the next big block.
4450 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4451 return FALSE;
4453 bufferWalker += bytesToReadInBuffer;
4454 size -= bytesToReadInBuffer;
4455 *bytesRead += bytesToReadInBuffer;
4456 offsetInBlock = 0; /* There is no offset on the next block */
4460 return (size == 0);
4463 /******************************************************************************
4464 * BlockChainStream_WriteAt
4466 * Writes the specified number of bytes to this chain at the specified offset.
4467 * bytesWritten may be NULL.
4468 * Will fail if not all specified number of bytes have been written.
4470 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4471 ULARGE_INTEGER offset,
4472 ULONG size,
4473 const void* buffer,
4474 ULONG* bytesWritten)
4476 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4477 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4478 ULONG bytesToWrite;
4479 ULONG blockIndex;
4480 const BYTE* bufferWalker;
4481 BYTE* bigBlockBuffer;
4484 * Find the first block in the stream that contains part of the buffer.
4486 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4487 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4488 (blockNoInSequence < This->lastBlockNoInSequence) )
4490 blockIndex = BlockChainStream_GetHeadOfChain(This);
4491 This->lastBlockNoInSequence = blockNoInSequence;
4493 else
4495 ULONG temp = blockNoInSequence;
4497 blockIndex = This->lastBlockNoInSequenceIndex;
4498 blockNoInSequence -= This->lastBlockNoInSequence;
4499 This->lastBlockNoInSequence = temp;
4502 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4504 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4505 &blockIndex)))
4506 return FALSE;
4507 blockNoInSequence--;
4510 This->lastBlockNoInSequenceIndex = blockIndex;
4513 * Here, I'm casting away the constness on the buffer variable
4514 * This is OK since we don't intend to modify that buffer.
4516 *bytesWritten = 0;
4517 bufferWalker = (const BYTE*)buffer;
4519 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4522 * Calculate how many bytes we can copy from this big block.
4524 bytesToWrite =
4525 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4528 * Copy those bytes to the buffer
4530 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4532 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4534 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4537 * Step to the next big block.
4539 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4540 &blockIndex)))
4541 return FALSE;
4542 bufferWalker += bytesToWrite;
4543 size -= bytesToWrite;
4544 *bytesWritten += bytesToWrite;
4545 offsetInBlock = 0; /* There is no offset on the next block */
4548 return (size == 0);
4551 /******************************************************************************
4552 * BlockChainStream_Shrink
4554 * Shrinks this chain in the big block depot.
4556 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4557 ULARGE_INTEGER newSize)
4559 ULONG blockIndex, extraBlock;
4560 ULONG numBlocks;
4561 ULONG count = 1;
4564 * Reset the last accessed block cache.
4566 This->lastBlockNoInSequence = 0xFFFFFFFF;
4567 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4570 * Figure out how many blocks are needed to contain the new size
4572 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4574 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4575 numBlocks++;
4577 blockIndex = BlockChainStream_GetHeadOfChain(This);
4580 * Go to the new end of chain
4582 while (count < numBlocks)
4584 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4585 &blockIndex)))
4586 return FALSE;
4587 count++;
4590 /* Get the next block before marking the new end */
4591 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4592 &extraBlock)))
4593 return FALSE;
4595 /* Mark the new end of chain */
4596 StorageImpl_SetNextBlockInChain(
4597 This->parentStorage,
4598 blockIndex,
4599 BLOCK_END_OF_CHAIN);
4601 This->tailIndex = blockIndex;
4602 This->numBlocks = numBlocks;
4605 * Mark the extra blocks as free
4607 while (extraBlock != BLOCK_END_OF_CHAIN)
4609 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4610 &blockIndex)))
4611 return FALSE;
4612 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4613 extraBlock = blockIndex;
4616 return TRUE;
4619 /******************************************************************************
4620 * BlockChainStream_Enlarge
4622 * Grows this chain in the big block depot.
4624 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4625 ULARGE_INTEGER newSize)
4627 ULONG blockIndex, currentBlock;
4628 ULONG newNumBlocks;
4629 ULONG oldNumBlocks = 0;
4631 blockIndex = BlockChainStream_GetHeadOfChain(This);
4634 * Empty chain. Create the head.
4636 if (blockIndex == BLOCK_END_OF_CHAIN)
4638 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4639 StorageImpl_SetNextBlockInChain(This->parentStorage,
4640 blockIndex,
4641 BLOCK_END_OF_CHAIN);
4643 if (This->headOfStreamPlaceHolder != 0)
4645 *(This->headOfStreamPlaceHolder) = blockIndex;
4647 else
4649 StgProperty chainProp;
4650 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4652 StorageImpl_ReadProperty(
4653 This->parentStorage,
4654 This->ownerPropertyIndex,
4655 &chainProp);
4657 chainProp.startingBlock = blockIndex;
4659 StorageImpl_WriteProperty(
4660 This->parentStorage,
4661 This->ownerPropertyIndex,
4662 &chainProp);
4665 This->tailIndex = blockIndex;
4666 This->numBlocks = 1;
4670 * Figure out how many blocks are needed to contain this stream
4672 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4674 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4675 newNumBlocks++;
4678 * Go to the current end of chain
4680 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4682 currentBlock = blockIndex;
4684 while (blockIndex != BLOCK_END_OF_CHAIN)
4686 This->numBlocks++;
4687 currentBlock = blockIndex;
4689 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4690 &blockIndex)))
4691 return FALSE;
4694 This->tailIndex = currentBlock;
4697 currentBlock = This->tailIndex;
4698 oldNumBlocks = This->numBlocks;
4701 * Add new blocks to the chain
4703 if (oldNumBlocks < newNumBlocks)
4705 while (oldNumBlocks < newNumBlocks)
4707 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4709 StorageImpl_SetNextBlockInChain(
4710 This->parentStorage,
4711 currentBlock,
4712 blockIndex);
4714 StorageImpl_SetNextBlockInChain(
4715 This->parentStorage,
4716 blockIndex,
4717 BLOCK_END_OF_CHAIN);
4719 currentBlock = blockIndex;
4720 oldNumBlocks++;
4723 This->tailIndex = blockIndex;
4724 This->numBlocks = newNumBlocks;
4727 return TRUE;
4730 /******************************************************************************
4731 * BlockChainStream_SetSize
4733 * Sets the size of this stream. The big block depot will be updated.
4734 * The file will grow if we grow the chain.
4736 * TODO: Free the actual blocks in the file when we shrink the chain.
4737 * Currently, the blocks are still in the file. So the file size
4738 * doesn't shrink even if we shrink streams.
4740 BOOL BlockChainStream_SetSize(
4741 BlockChainStream* This,
4742 ULARGE_INTEGER newSize)
4744 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4746 if (newSize.u.LowPart == size.u.LowPart)
4747 return TRUE;
4749 if (newSize.u.LowPart < size.u.LowPart)
4751 BlockChainStream_Shrink(This, newSize);
4753 else
4755 ULARGE_INTEGER fileSize =
4756 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4758 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4761 * Make sure the file stays a multiple of blocksize
4763 if ((diff % This->parentStorage->bigBlockSize) != 0)
4764 diff += (This->parentStorage->bigBlockSize -
4765 (diff % This->parentStorage->bigBlockSize) );
4767 fileSize.u.LowPart += diff;
4768 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4770 BlockChainStream_Enlarge(This, newSize);
4773 return TRUE;
4776 /******************************************************************************
4777 * BlockChainStream_GetSize
4779 * Returns the size of this chain.
4780 * Will return the block count if this chain doesn't have a property.
4782 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4784 StgProperty chainProperty;
4786 if(This->headOfStreamPlaceHolder == NULL)
4789 * This chain is a data stream read the property and return
4790 * the appropriate size
4792 StorageImpl_ReadProperty(
4793 This->parentStorage,
4794 This->ownerPropertyIndex,
4795 &chainProperty);
4797 return chainProperty.size;
4799 else
4802 * this chain is a chain that does not have a property, figure out the
4803 * size by making the product number of used blocks times the
4804 * size of them
4806 ULARGE_INTEGER result;
4807 result.u.HighPart = 0;
4809 result.u.LowPart =
4810 BlockChainStream_GetCount(This) *
4811 This->parentStorage->bigBlockSize;
4813 return result;
4817 /******************************************************************************
4818 ** SmallBlockChainStream implementation
4821 SmallBlockChainStream* SmallBlockChainStream_Construct(
4822 StorageImpl* parentStorage,
4823 ULONG propertyIndex)
4825 SmallBlockChainStream* newStream;
4827 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4829 newStream->parentStorage = parentStorage;
4830 newStream->ownerPropertyIndex = propertyIndex;
4832 return newStream;
4835 void SmallBlockChainStream_Destroy(
4836 SmallBlockChainStream* This)
4838 HeapFree(GetProcessHeap(), 0, This);
4841 /******************************************************************************
4842 * SmallBlockChainStream_GetHeadOfChain
4844 * Returns the head of this chain of small blocks.
4846 ULONG SmallBlockChainStream_GetHeadOfChain(
4847 SmallBlockChainStream* This)
4849 StgProperty chainProperty;
4850 BOOL readSuccessful;
4852 if (This->ownerPropertyIndex)
4854 readSuccessful = StorageImpl_ReadProperty(
4855 This->parentStorage,
4856 This->ownerPropertyIndex,
4857 &chainProperty);
4859 if (readSuccessful)
4861 return chainProperty.startingBlock;
4866 return BLOCK_END_OF_CHAIN;
4869 /******************************************************************************
4870 * SmallBlockChainStream_GetNextBlockInChain
4872 * Returns the index of the next small block in this chain.
4874 * Return Values:
4875 * - BLOCK_END_OF_CHAIN: end of this chain
4876 * - BLOCK_UNUSED: small block 'blockIndex' is free
4878 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4879 SmallBlockChainStream* This,
4880 ULONG blockIndex,
4881 ULONG* nextBlockInChain)
4883 ULARGE_INTEGER offsetOfBlockInDepot;
4884 DWORD buffer;
4885 ULONG bytesRead;
4886 BOOL success;
4888 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4890 offsetOfBlockInDepot.u.HighPart = 0;
4891 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4894 * Read those bytes in the buffer from the small block file.
4896 success = BlockChainStream_ReadAt(
4897 This->parentStorage->smallBlockDepotChain,
4898 offsetOfBlockInDepot,
4899 sizeof(DWORD),
4900 &buffer,
4901 &bytesRead);
4903 if (success)
4905 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4906 return S_OK;
4909 return STG_E_READFAULT;
4912 /******************************************************************************
4913 * SmallBlockChainStream_SetNextBlockInChain
4915 * Writes the index of the next block of the specified block in the small
4916 * block depot.
4917 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4918 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4920 void SmallBlockChainStream_SetNextBlockInChain(
4921 SmallBlockChainStream* This,
4922 ULONG blockIndex,
4923 ULONG nextBlock)
4925 ULARGE_INTEGER offsetOfBlockInDepot;
4926 DWORD buffer;
4927 ULONG bytesWritten;
4929 offsetOfBlockInDepot.u.HighPart = 0;
4930 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4932 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4935 * Read those bytes in the buffer from the small block file.
4937 BlockChainStream_WriteAt(
4938 This->parentStorage->smallBlockDepotChain,
4939 offsetOfBlockInDepot,
4940 sizeof(DWORD),
4941 &buffer,
4942 &bytesWritten);
4945 /******************************************************************************
4946 * SmallBlockChainStream_FreeBlock
4948 * Flag small block 'blockIndex' as free in the small block depot.
4950 void SmallBlockChainStream_FreeBlock(
4951 SmallBlockChainStream* This,
4952 ULONG blockIndex)
4954 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4957 /******************************************************************************
4958 * SmallBlockChainStream_GetNextFreeBlock
4960 * Returns the index of a free small block. The small block depot will be
4961 * enlarged if necessary. The small block chain will also be enlarged if
4962 * necessary.
4964 ULONG SmallBlockChainStream_GetNextFreeBlock(
4965 SmallBlockChainStream* This)
4967 ULARGE_INTEGER offsetOfBlockInDepot;
4968 DWORD buffer;
4969 ULONG bytesRead;
4970 ULONG blockIndex = 0;
4971 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4972 BOOL success = TRUE;
4973 ULONG smallBlocksPerBigBlock;
4975 offsetOfBlockInDepot.u.HighPart = 0;
4978 * Scan the small block depot for a free block
4980 while (nextBlockIndex != BLOCK_UNUSED)
4982 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4984 success = BlockChainStream_ReadAt(
4985 This->parentStorage->smallBlockDepotChain,
4986 offsetOfBlockInDepot,
4987 sizeof(DWORD),
4988 &buffer,
4989 &bytesRead);
4992 * If we run out of space for the small block depot, enlarge it
4994 if (success)
4996 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
4998 if (nextBlockIndex != BLOCK_UNUSED)
4999 blockIndex++;
5001 else
5003 ULONG count =
5004 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5006 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5007 ULONG nextBlock, newsbdIndex;
5008 BYTE* smallBlockDepot;
5010 nextBlock = sbdIndex;
5011 while (nextBlock != BLOCK_END_OF_CHAIN)
5013 sbdIndex = nextBlock;
5014 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5017 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5018 if (sbdIndex != BLOCK_END_OF_CHAIN)
5019 StorageImpl_SetNextBlockInChain(
5020 This->parentStorage,
5021 sbdIndex,
5022 newsbdIndex);
5024 StorageImpl_SetNextBlockInChain(
5025 This->parentStorage,
5026 newsbdIndex,
5027 BLOCK_END_OF_CHAIN);
5030 * Initialize all the small blocks to free
5032 smallBlockDepot =
5033 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5035 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5036 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5038 if (count == 0)
5041 * We have just created the small block depot.
5043 StgProperty rootProp;
5044 ULONG sbStartIndex;
5047 * Save it in the header
5049 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5050 StorageImpl_SaveFileHeader(This->parentStorage);
5053 * And allocate the first big block that will contain small blocks
5055 sbStartIndex =
5056 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5058 StorageImpl_SetNextBlockInChain(
5059 This->parentStorage,
5060 sbStartIndex,
5061 BLOCK_END_OF_CHAIN);
5063 StorageImpl_ReadProperty(
5064 This->parentStorage,
5065 This->parentStorage->base.rootPropertySetIndex,
5066 &rootProp);
5068 rootProp.startingBlock = sbStartIndex;
5069 rootProp.size.u.HighPart = 0;
5070 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5072 StorageImpl_WriteProperty(
5073 This->parentStorage,
5074 This->parentStorage->base.rootPropertySetIndex,
5075 &rootProp);
5080 smallBlocksPerBigBlock =
5081 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5084 * Verify if we have to allocate big blocks to contain small blocks
5086 if (blockIndex % smallBlocksPerBigBlock == 0)
5088 StgProperty rootProp;
5089 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5091 StorageImpl_ReadProperty(
5092 This->parentStorage,
5093 This->parentStorage->base.rootPropertySetIndex,
5094 &rootProp);
5096 if (rootProp.size.u.LowPart <
5097 (blocksRequired * This->parentStorage->bigBlockSize))
5099 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5101 BlockChainStream_SetSize(
5102 This->parentStorage->smallBlockRootChain,
5103 rootProp.size);
5105 StorageImpl_WriteProperty(
5106 This->parentStorage,
5107 This->parentStorage->base.rootPropertySetIndex,
5108 &rootProp);
5112 return blockIndex;
5115 /******************************************************************************
5116 * SmallBlockChainStream_ReadAt
5118 * Reads a specified number of bytes from this chain at the specified offset.
5119 * bytesRead may be NULL.
5120 * Failure will be returned if the specified number of bytes has not been read.
5122 HRESULT SmallBlockChainStream_ReadAt(
5123 SmallBlockChainStream* This,
5124 ULARGE_INTEGER offset,
5125 ULONG size,
5126 void* buffer,
5127 ULONG* bytesRead)
5129 HRESULT rc = S_OK;
5130 ULARGE_INTEGER offsetInBigBlockFile;
5131 ULONG blockNoInSequence =
5132 offset.u.LowPart / This->parentStorage->smallBlockSize;
5134 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5135 ULONG bytesToReadInBuffer;
5136 ULONG blockIndex;
5137 ULONG bytesReadFromBigBlockFile;
5138 BYTE* bufferWalker;
5141 * This should never happen on a small block file.
5143 assert(offset.u.HighPart==0);
5146 * Find the first block in the stream that contains part of the buffer.
5148 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5150 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5152 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5153 if(FAILED(rc))
5154 return rc;
5155 blockNoInSequence--;
5159 * Start reading the buffer.
5161 *bytesRead = 0;
5162 bufferWalker = buffer;
5164 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5167 * Calculate how many bytes we can copy from this small block.
5169 bytesToReadInBuffer =
5170 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5173 * Calculate the offset of the small block in the small block file.
5175 offsetInBigBlockFile.u.HighPart = 0;
5176 offsetInBigBlockFile.u.LowPart =
5177 blockIndex * This->parentStorage->smallBlockSize;
5179 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5182 * Read those bytes in the buffer from the small block file.
5183 * The small block has already been identified so it shouldn't fail
5184 * unless the file is corrupt.
5186 if (!BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5187 offsetInBigBlockFile,
5188 bytesToReadInBuffer,
5189 bufferWalker,
5190 &bytesReadFromBigBlockFile))
5191 return STG_E_DOCFILECORRUPT;
5193 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5196 * Step to the next big block.
5198 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5199 if(FAILED(rc))
5200 return rc;
5202 bufferWalker += bytesToReadInBuffer;
5203 size -= bytesToReadInBuffer;
5204 *bytesRead += bytesToReadInBuffer;
5205 offsetInBlock = 0; /* There is no offset on the next block */
5208 return rc;
5211 /******************************************************************************
5212 * SmallBlockChainStream_WriteAt
5214 * Writes the specified number of bytes to this chain at the specified offset.
5215 * bytesWritten may be NULL.
5216 * Will fail if not all specified number of bytes have been written.
5218 BOOL SmallBlockChainStream_WriteAt(
5219 SmallBlockChainStream* This,
5220 ULARGE_INTEGER offset,
5221 ULONG size,
5222 const void* buffer,
5223 ULONG* bytesWritten)
5225 ULARGE_INTEGER offsetInBigBlockFile;
5226 ULONG blockNoInSequence =
5227 offset.u.LowPart / This->parentStorage->smallBlockSize;
5229 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5230 ULONG bytesToWriteInBuffer;
5231 ULONG blockIndex;
5232 ULONG bytesWrittenFromBigBlockFile;
5233 const BYTE* bufferWalker;
5236 * This should never happen on a small block file.
5238 assert(offset.u.HighPart==0);
5241 * Find the first block in the stream that contains part of the buffer.
5243 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5245 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5247 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5248 return FALSE;
5249 blockNoInSequence--;
5253 * Start writing the buffer.
5255 * Here, I'm casting away the constness on the buffer variable
5256 * This is OK since we don't intend to modify that buffer.
5258 *bytesWritten = 0;
5259 bufferWalker = (const BYTE*)buffer;
5260 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5263 * Calculate how many bytes we can copy to this small block.
5265 bytesToWriteInBuffer =
5266 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5269 * Calculate the offset of the small block in the small block file.
5271 offsetInBigBlockFile.u.HighPart = 0;
5272 offsetInBigBlockFile.u.LowPart =
5273 blockIndex * This->parentStorage->smallBlockSize;
5275 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5278 * Write those bytes in the buffer to the small block file.
5280 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5281 offsetInBigBlockFile,
5282 bytesToWriteInBuffer,
5283 bufferWalker,
5284 &bytesWrittenFromBigBlockFile);
5286 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5289 * Step to the next big block.
5291 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5292 &blockIndex)))
5293 return FALSE;
5294 bufferWalker += bytesToWriteInBuffer;
5295 size -= bytesToWriteInBuffer;
5296 *bytesWritten += bytesToWriteInBuffer;
5297 offsetInBlock = 0; /* There is no offset on the next block */
5300 return (size == 0);
5303 /******************************************************************************
5304 * SmallBlockChainStream_Shrink
5306 * Shrinks this chain in the small block depot.
5308 BOOL SmallBlockChainStream_Shrink(
5309 SmallBlockChainStream* This,
5310 ULARGE_INTEGER newSize)
5312 ULONG blockIndex, extraBlock;
5313 ULONG numBlocks;
5314 ULONG count = 0;
5316 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5318 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5319 numBlocks++;
5321 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5324 * Go to the new end of chain
5326 while (count < numBlocks)
5328 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5329 &blockIndex)))
5330 return FALSE;
5331 count++;
5335 * If the count is 0, we have a special case, the head of the chain was
5336 * just freed.
5338 if (count == 0)
5340 StgProperty chainProp;
5342 StorageImpl_ReadProperty(This->parentStorage,
5343 This->ownerPropertyIndex,
5344 &chainProp);
5346 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5348 StorageImpl_WriteProperty(This->parentStorage,
5349 This->ownerPropertyIndex,
5350 &chainProp);
5353 * We start freeing the chain at the head block.
5355 extraBlock = blockIndex;
5357 else
5359 /* Get the next block before marking the new end */
5360 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5361 &extraBlock)))
5362 return FALSE;
5364 /* Mark the new end of chain */
5365 SmallBlockChainStream_SetNextBlockInChain(
5366 This,
5367 blockIndex,
5368 BLOCK_END_OF_CHAIN);
5372 * Mark the extra blocks as free
5374 while (extraBlock != BLOCK_END_OF_CHAIN)
5376 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5377 &blockIndex)))
5378 return FALSE;
5379 SmallBlockChainStream_FreeBlock(This, extraBlock);
5380 extraBlock = blockIndex;
5383 return TRUE;
5386 /******************************************************************************
5387 * SmallBlockChainStream_Enlarge
5389 * Grows this chain in the small block depot.
5391 BOOL SmallBlockChainStream_Enlarge(
5392 SmallBlockChainStream* This,
5393 ULARGE_INTEGER newSize)
5395 ULONG blockIndex, currentBlock;
5396 ULONG newNumBlocks;
5397 ULONG oldNumBlocks = 0;
5399 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5402 * Empty chain
5404 if (blockIndex == BLOCK_END_OF_CHAIN)
5407 StgProperty chainProp;
5409 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5410 &chainProp);
5412 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5414 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5415 &chainProp);
5417 blockIndex = chainProp.startingBlock;
5418 SmallBlockChainStream_SetNextBlockInChain(
5419 This,
5420 blockIndex,
5421 BLOCK_END_OF_CHAIN);
5424 currentBlock = blockIndex;
5427 * Figure out how many blocks are needed to contain this stream
5429 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5431 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5432 newNumBlocks++;
5435 * Go to the current end of chain
5437 while (blockIndex != BLOCK_END_OF_CHAIN)
5439 oldNumBlocks++;
5440 currentBlock = blockIndex;
5441 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5442 return FALSE;
5446 * Add new blocks to the chain
5448 while (oldNumBlocks < newNumBlocks)
5450 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5451 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5453 SmallBlockChainStream_SetNextBlockInChain(
5454 This,
5455 blockIndex,
5456 BLOCK_END_OF_CHAIN);
5458 currentBlock = blockIndex;
5459 oldNumBlocks++;
5462 return TRUE;
5465 /******************************************************************************
5466 * SmallBlockChainStream_GetCount
5468 * Returns the number of blocks that comprises this chain.
5469 * This is not the size of this chain as the last block may not be full!
5471 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5473 ULONG blockIndex;
5474 ULONG count = 0;
5476 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5478 while (blockIndex != BLOCK_END_OF_CHAIN)
5480 count++;
5482 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5483 return 0;
5486 return count;
5489 /******************************************************************************
5490 * SmallBlockChainStream_SetSize
5492 * Sets the size of this stream.
5493 * The file will grow if we grow the chain.
5495 * TODO: Free the actual blocks in the file when we shrink the chain.
5496 * Currently, the blocks are still in the file. So the file size
5497 * doesn't shrink even if we shrink streams.
5499 BOOL SmallBlockChainStream_SetSize(
5500 SmallBlockChainStream* This,
5501 ULARGE_INTEGER newSize)
5503 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5505 if (newSize.u.LowPart == size.u.LowPart)
5506 return TRUE;
5508 if (newSize.u.LowPart < size.u.LowPart)
5510 SmallBlockChainStream_Shrink(This, newSize);
5512 else
5514 SmallBlockChainStream_Enlarge(This, newSize);
5517 return TRUE;
5520 /******************************************************************************
5521 * SmallBlockChainStream_GetSize
5523 * Returns the size of this chain.
5525 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5527 StgProperty chainProperty;
5529 StorageImpl_ReadProperty(
5530 This->parentStorage,
5531 This->ownerPropertyIndex,
5532 &chainProperty);
5534 return chainProperty.size;
5537 /******************************************************************************
5538 * StgCreateDocfile [OLE32.@]
5540 HRESULT WINAPI StgCreateDocfile(
5541 LPCOLESTR pwcsName,
5542 DWORD grfMode,
5543 DWORD reserved,
5544 IStorage **ppstgOpen)
5546 StorageImpl* newStorage = 0;
5547 HANDLE hFile = INVALID_HANDLE_VALUE;
5548 HRESULT hr = STG_E_INVALIDFLAG;
5549 DWORD shareMode;
5550 DWORD accessMode;
5551 DWORD creationMode;
5552 DWORD fileAttributes;
5553 WCHAR tempFileName[MAX_PATH];
5555 TRACE("(%s, %lx, %ld, %p)\n",
5556 debugstr_w(pwcsName), grfMode,
5557 reserved, ppstgOpen);
5560 * Validate the parameters
5562 if (ppstgOpen == 0)
5563 return STG_E_INVALIDPOINTER;
5564 if (reserved != 0)
5565 return STG_E_INVALIDPARAMETER;
5568 * Validate the STGM flags
5570 if ( FAILED( validateSTGM(grfMode) ))
5571 goto end;
5573 /* StgCreateDocFile always opens for write */
5574 switch(STGM_ACCESS_MODE(grfMode))
5576 case STGM_WRITE:
5577 case STGM_READWRITE:
5578 break;
5579 default:
5580 goto end;
5583 /* can't share write */
5584 switch(STGM_SHARE_MODE(grfMode))
5586 case STGM_SHARE_EXCLUSIVE:
5587 case STGM_SHARE_DENY_WRITE:
5588 break;
5589 default:
5590 goto end;
5593 /* shared reading requires transacted mode */
5594 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5595 !(grfMode&STGM_TRANSACTED) )
5596 goto end;
5599 * Generate a unique name.
5601 if (pwcsName == 0)
5603 WCHAR tempPath[MAX_PATH];
5604 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5606 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
5607 goto end;
5609 memset(tempPath, 0, sizeof(tempPath));
5610 memset(tempFileName, 0, sizeof(tempFileName));
5612 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5613 tempPath[0] = '.';
5615 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5616 pwcsName = tempFileName;
5617 else
5619 hr = STG_E_INSUFFICIENTMEMORY;
5620 goto end;
5623 creationMode = TRUNCATE_EXISTING;
5625 else
5627 creationMode = GetCreationModeFromSTGM(grfMode);
5631 * Interpret the STGM value grfMode
5633 shareMode = GetShareModeFromSTGM(grfMode);
5634 accessMode = GetAccessModeFromSTGM(grfMode);
5636 if (grfMode & STGM_DELETEONRELEASE)
5637 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5638 else
5639 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5641 if (grfMode & STGM_TRANSACTED)
5642 FIXME("Transacted mode not implemented.\n");
5645 * Initialize the "out" parameter.
5647 *ppstgOpen = 0;
5649 hFile = CreateFileW(pwcsName,
5650 accessMode,
5651 shareMode,
5652 NULL,
5653 creationMode,
5654 fileAttributes,
5657 if (hFile == INVALID_HANDLE_VALUE)
5659 if(GetLastError() == ERROR_FILE_EXISTS)
5660 hr = STG_E_FILEALREADYEXISTS;
5661 else
5662 hr = E_FAIL;
5663 goto end;
5667 * Allocate and initialize the new IStorage32object.
5669 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5671 if (newStorage == 0)
5673 hr = STG_E_INSUFFICIENTMEMORY;
5674 goto end;
5677 hr = StorageImpl_Construct(
5678 newStorage,
5679 hFile,
5680 pwcsName,
5681 NULL,
5682 grfMode,
5683 TRUE,
5684 TRUE);
5686 if (FAILED(hr))
5688 HeapFree(GetProcessHeap(), 0, newStorage);
5689 goto end;
5693 * Get an "out" pointer for the caller.
5695 hr = StorageBaseImpl_QueryInterface(
5696 (IStorage*)newStorage,
5697 (REFIID)&IID_IStorage,
5698 (void**)ppstgOpen);
5699 end:
5700 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5702 return hr;
5705 /******************************************************************************
5706 * StgCreateStorageEx [OLE32.@]
5708 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5710 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5711 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5713 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5715 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5716 return STG_E_INVALIDPARAMETER;
5719 if (stgfmt != STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5721 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5722 return STG_E_INVALIDPARAMETER;
5725 if (stgfmt == STGFMT_FILE)
5727 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5728 return STG_E_INVALIDPARAMETER;
5731 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5733 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5734 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5737 ERR("Invalid stgfmt argument\n");
5738 return STG_E_INVALIDPARAMETER;
5741 /******************************************************************************
5742 * StgCreatePropSetStg [OLE32.@]
5744 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5745 IPropertySetStorage **ppPropSetStg)
5747 HRESULT hr;
5749 TRACE("(%p, 0x%lx, %p)\n", pstg, reserved, ppPropSetStg);
5750 if (reserved)
5751 hr = STG_E_INVALIDPARAMETER;
5752 else
5753 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5754 (void**)ppPropSetStg);
5755 return hr;
5758 /******************************************************************************
5759 * StgOpenStorageEx [OLE32.@]
5761 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5763 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5764 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5766 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5768 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5769 return STG_E_INVALIDPARAMETER;
5772 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5774 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5775 return STG_E_INVALIDPARAMETER;
5778 if (stgfmt == STGFMT_FILE)
5780 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5781 return STG_E_INVALIDPARAMETER;
5784 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE || stgfmt == STGFMT_ANY)
5786 if (stgfmt == STGFMT_ANY)
5787 WARN("STGFMT_ANY assuming storage\n");
5788 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5789 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5792 ERR("Invalid stgfmt argument\n");
5793 return STG_E_INVALIDPARAMETER;
5797 /******************************************************************************
5798 * StgOpenStorage [OLE32.@]
5800 HRESULT WINAPI StgOpenStorage(
5801 const OLECHAR *pwcsName,
5802 IStorage *pstgPriority,
5803 DWORD grfMode,
5804 SNB snbExclude,
5805 DWORD reserved,
5806 IStorage **ppstgOpen)
5808 StorageImpl* newStorage = 0;
5809 HRESULT hr = S_OK;
5810 HANDLE hFile = 0;
5811 DWORD shareMode;
5812 DWORD accessMode;
5813 WCHAR fullname[MAX_PATH];
5814 DWORD length;
5816 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5817 debugstr_w(pwcsName), pstgPriority, grfMode,
5818 snbExclude, reserved, ppstgOpen);
5821 * Perform sanity checks
5823 if (pwcsName == 0)
5825 hr = STG_E_INVALIDNAME;
5826 goto end;
5829 if (ppstgOpen == 0)
5831 hr = STG_E_INVALIDPOINTER;
5832 goto end;
5835 if (reserved)
5837 hr = STG_E_INVALIDPARAMETER;
5838 goto end;
5842 * Validate the sharing mode
5844 if (!(grfMode & STGM_TRANSACTED))
5845 switch(STGM_SHARE_MODE(grfMode))
5847 case STGM_SHARE_EXCLUSIVE:
5848 case STGM_SHARE_DENY_WRITE:
5849 break;
5850 default:
5851 hr = STG_E_INVALIDFLAG;
5852 goto end;
5856 * Validate the STGM flags
5858 if ( FAILED( validateSTGM(grfMode) ) ||
5859 (grfMode&STGM_CREATE))
5861 hr = STG_E_INVALIDFLAG;
5862 goto end;
5865 /* shared reading requires transacted mode */
5866 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5867 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5868 !(grfMode&STGM_TRANSACTED) )
5870 hr = STG_E_INVALIDFLAG;
5871 goto end;
5875 * Interpret the STGM value grfMode
5877 shareMode = GetShareModeFromSTGM(grfMode);
5878 accessMode = GetAccessModeFromSTGM(grfMode);
5881 * Initialize the "out" parameter.
5883 *ppstgOpen = 0;
5885 hFile = CreateFileW( pwcsName,
5886 accessMode,
5887 shareMode,
5888 NULL,
5889 OPEN_EXISTING,
5890 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5893 if (hFile==INVALID_HANDLE_VALUE)
5895 DWORD last_error = GetLastError();
5897 hr = E_FAIL;
5899 switch (last_error)
5901 case ERROR_FILE_NOT_FOUND:
5902 hr = STG_E_FILENOTFOUND;
5903 break;
5905 case ERROR_PATH_NOT_FOUND:
5906 hr = STG_E_PATHNOTFOUND;
5907 break;
5909 case ERROR_ACCESS_DENIED:
5910 case ERROR_WRITE_PROTECT:
5911 hr = STG_E_ACCESSDENIED;
5912 break;
5914 case ERROR_SHARING_VIOLATION:
5915 hr = STG_E_SHAREVIOLATION;
5916 break;
5918 default:
5919 hr = E_FAIL;
5922 goto end;
5926 * Refuse to open the file if it's too small to be a structured storage file
5927 * FIXME: verify the file when reading instead of here
5929 length = GetFileSize(hFile, NULL);
5930 if (length < 0x100)
5932 CloseHandle(hFile);
5933 hr = STG_E_FILEALREADYEXISTS;
5934 goto end;
5938 * Allocate and initialize the new IStorage32object.
5940 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5942 if (newStorage == 0)
5944 hr = STG_E_INSUFFICIENTMEMORY;
5945 goto end;
5948 /* if the file's length was zero, initialize the storage */
5949 hr = StorageImpl_Construct(
5950 newStorage,
5951 hFile,
5952 pwcsName,
5953 NULL,
5954 grfMode,
5955 TRUE,
5956 FALSE );
5958 if (FAILED(hr))
5960 HeapFree(GetProcessHeap(), 0, newStorage);
5962 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5964 if(hr == STG_E_INVALIDHEADER)
5965 hr = STG_E_FILEALREADYEXISTS;
5966 goto end;
5969 /* prepare the file name string given in lieu of the root property name */
5970 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5971 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5972 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5975 * Get an "out" pointer for the caller.
5977 hr = StorageBaseImpl_QueryInterface(
5978 (IStorage*)newStorage,
5979 (REFIID)&IID_IStorage,
5980 (void**)ppstgOpen);
5982 end:
5983 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5984 return hr;
5987 /******************************************************************************
5988 * StgCreateDocfileOnILockBytes [OLE32.@]
5990 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5991 ILockBytes *plkbyt,
5992 DWORD grfMode,
5993 DWORD reserved,
5994 IStorage** ppstgOpen)
5996 StorageImpl* newStorage = 0;
5997 HRESULT hr = S_OK;
6000 * Validate the parameters
6002 if ((ppstgOpen == 0) || (plkbyt == 0))
6003 return STG_E_INVALIDPOINTER;
6006 * Allocate and initialize the new IStorage object.
6008 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6010 if (newStorage == 0)
6011 return STG_E_INSUFFICIENTMEMORY;
6013 hr = StorageImpl_Construct(
6014 newStorage,
6017 plkbyt,
6018 grfMode,
6019 FALSE,
6020 TRUE);
6022 if (FAILED(hr))
6024 HeapFree(GetProcessHeap(), 0, newStorage);
6025 return hr;
6029 * Get an "out" pointer for the caller.
6031 hr = StorageBaseImpl_QueryInterface(
6032 (IStorage*)newStorage,
6033 (REFIID)&IID_IStorage,
6034 (void**)ppstgOpen);
6036 return hr;
6039 /******************************************************************************
6040 * StgOpenStorageOnILockBytes [OLE32.@]
6042 HRESULT WINAPI StgOpenStorageOnILockBytes(
6043 ILockBytes *plkbyt,
6044 IStorage *pstgPriority,
6045 DWORD grfMode,
6046 SNB snbExclude,
6047 DWORD reserved,
6048 IStorage **ppstgOpen)
6050 StorageImpl* newStorage = 0;
6051 HRESULT hr = S_OK;
6054 * Perform a sanity check
6056 if ((plkbyt == 0) || (ppstgOpen == 0))
6057 return STG_E_INVALIDPOINTER;
6060 * Validate the STGM flags
6062 if ( FAILED( validateSTGM(grfMode) ))
6063 return STG_E_INVALIDFLAG;
6066 * Initialize the "out" parameter.
6068 *ppstgOpen = 0;
6071 * Allocate and initialize the new IStorage object.
6073 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6075 if (newStorage == 0)
6076 return STG_E_INSUFFICIENTMEMORY;
6078 hr = StorageImpl_Construct(
6079 newStorage,
6082 plkbyt,
6083 grfMode,
6084 FALSE,
6085 FALSE);
6087 if (FAILED(hr))
6089 HeapFree(GetProcessHeap(), 0, newStorage);
6090 return hr;
6094 * Get an "out" pointer for the caller.
6096 hr = StorageBaseImpl_QueryInterface(
6097 (IStorage*)newStorage,
6098 (REFIID)&IID_IStorage,
6099 (void**)ppstgOpen);
6101 return hr;
6104 /******************************************************************************
6105 * StgSetTimes [ole32.@]
6106 * StgSetTimes [OLE32.@]
6110 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6111 FILETIME const *patime, FILETIME const *pmtime)
6113 IStorage *stg = NULL;
6114 HRESULT r;
6116 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6118 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6119 0, 0, &stg);
6120 if( SUCCEEDED(r) )
6122 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6123 IStorage_Release(stg);
6126 return r;
6129 /******************************************************************************
6130 * StgIsStorageILockBytes [OLE32.@]
6132 * Determines if the ILockBytes contains a storage object.
6134 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6136 BYTE sig[8];
6137 ULARGE_INTEGER offset;
6139 offset.u.HighPart = 0;
6140 offset.u.LowPart = 0;
6142 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6144 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6145 return S_OK;
6147 return S_FALSE;
6150 /******************************************************************************
6151 * WriteClassStg [OLE32.@]
6153 * This method will store the specified CLSID in the specified storage object
6155 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6157 HRESULT hRes;
6159 if(!pStg)
6160 return E_INVALIDARG;
6162 hRes = IStorage_SetClass(pStg, rclsid);
6164 return hRes;
6167 /***********************************************************************
6168 * ReadClassStg (OLE32.@)
6170 * This method reads the CLSID previously written to a storage object with
6171 * the WriteClassStg.
6173 * PARAMS
6174 * pstg [I] IStorage pointer
6175 * pclsid [O] Pointer to where the CLSID is written
6177 * RETURNS
6178 * Success: S_OK.
6179 * Failure: HRESULT code.
6181 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6183 STATSTG pstatstg;
6184 HRESULT hRes;
6186 TRACE("(%p, %p)\n", pstg, pclsid);
6188 if(!pstg || !pclsid)
6189 return E_INVALIDARG;
6192 * read a STATSTG structure (contains the clsid) from the storage
6194 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6196 if(SUCCEEDED(hRes))
6197 *pclsid=pstatstg.clsid;
6199 return hRes;
6202 /***********************************************************************
6203 * OleLoadFromStream (OLE32.@)
6205 * This function loads an object from stream
6207 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6209 CLSID clsid;
6210 HRESULT res;
6211 LPPERSISTSTREAM xstm;
6213 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6215 res=ReadClassStm(pStm,&clsid);
6216 if (!SUCCEEDED(res))
6217 return res;
6218 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6219 if (!SUCCEEDED(res))
6220 return res;
6221 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6222 if (!SUCCEEDED(res)) {
6223 IUnknown_Release((IUnknown*)*ppvObj);
6224 return res;
6226 res=IPersistStream_Load(xstm,pStm);
6227 IPersistStream_Release(xstm);
6228 /* FIXME: all refcounts ok at this point? I think they should be:
6229 * pStm : unchanged
6230 * ppvObj : 1
6231 * xstm : 0 (released)
6233 return res;
6236 /***********************************************************************
6237 * OleSaveToStream (OLE32.@)
6239 * This function saves an object with the IPersistStream interface on it
6240 * to the specified stream.
6242 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6245 CLSID clsid;
6246 HRESULT res;
6248 TRACE("(%p,%p)\n",pPStm,pStm);
6250 res=IPersistStream_GetClassID(pPStm,&clsid);
6252 if (SUCCEEDED(res)){
6254 res=WriteClassStm(pStm,&clsid);
6256 if (SUCCEEDED(res))
6258 res=IPersistStream_Save(pPStm,pStm,TRUE);
6261 TRACE("Finished Save\n");
6262 return res;
6265 /****************************************************************************
6266 * This method validate a STGM parameter that can contain the values below
6268 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6269 * The stgm values contained in 0xffff0000 are bitmasks.
6271 * STGM_DIRECT 0x00000000
6272 * STGM_TRANSACTED 0x00010000
6273 * STGM_SIMPLE 0x08000000
6275 * STGM_READ 0x00000000
6276 * STGM_WRITE 0x00000001
6277 * STGM_READWRITE 0x00000002
6279 * STGM_SHARE_DENY_NONE 0x00000040
6280 * STGM_SHARE_DENY_READ 0x00000030
6281 * STGM_SHARE_DENY_WRITE 0x00000020
6282 * STGM_SHARE_EXCLUSIVE 0x00000010
6284 * STGM_PRIORITY 0x00040000
6285 * STGM_DELETEONRELEASE 0x04000000
6287 * STGM_CREATE 0x00001000
6288 * STGM_CONVERT 0x00020000
6289 * STGM_FAILIFTHERE 0x00000000
6291 * STGM_NOSCRATCH 0x00100000
6292 * STGM_NOSNAPSHOT 0x00200000
6294 static HRESULT validateSTGM(DWORD stgm)
6296 DWORD access = STGM_ACCESS_MODE(stgm);
6297 DWORD share = STGM_SHARE_MODE(stgm);
6298 DWORD create = STGM_CREATE_MODE(stgm);
6300 if (stgm&~STGM_KNOWN_FLAGS)
6302 ERR("unknown flags %08lx\n", stgm);
6303 return E_FAIL;
6306 switch (access)
6308 case STGM_READ:
6309 case STGM_WRITE:
6310 case STGM_READWRITE:
6311 break;
6312 default:
6313 return E_FAIL;
6316 switch (share)
6318 case STGM_SHARE_DENY_NONE:
6319 case STGM_SHARE_DENY_READ:
6320 case STGM_SHARE_DENY_WRITE:
6321 case STGM_SHARE_EXCLUSIVE:
6322 break;
6323 default:
6324 return E_FAIL;
6327 switch (create)
6329 case STGM_CREATE:
6330 case STGM_FAILIFTHERE:
6331 break;
6332 default:
6333 return E_FAIL;
6337 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6339 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6340 return E_FAIL;
6343 * STGM_CREATE | STGM_CONVERT
6344 * if both are false, STGM_FAILIFTHERE is set to TRUE
6346 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6347 return E_FAIL;
6350 * STGM_NOSCRATCH requires STGM_TRANSACTED
6352 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6353 return E_FAIL;
6356 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6357 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6359 if ( (stgm & STGM_NOSNAPSHOT) &&
6360 (!(stgm & STGM_TRANSACTED) ||
6361 share == STGM_SHARE_EXCLUSIVE ||
6362 share == STGM_SHARE_DENY_WRITE) )
6363 return E_FAIL;
6365 return S_OK;
6368 /****************************************************************************
6369 * GetShareModeFromSTGM
6371 * This method will return a share mode flag from a STGM value.
6372 * The STGM value is assumed valid.
6374 static DWORD GetShareModeFromSTGM(DWORD stgm)
6376 switch (STGM_SHARE_MODE(stgm))
6378 case STGM_SHARE_DENY_NONE:
6379 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6380 case STGM_SHARE_DENY_READ:
6381 return FILE_SHARE_WRITE;
6382 case STGM_SHARE_DENY_WRITE:
6383 return FILE_SHARE_READ;
6384 case STGM_SHARE_EXCLUSIVE:
6385 return 0;
6387 ERR("Invalid share mode!\n");
6388 assert(0);
6389 return 0;
6392 /****************************************************************************
6393 * GetAccessModeFromSTGM
6395 * This method will return an access mode flag from a STGM value.
6396 * The STGM value is assumed valid.
6398 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6400 switch (STGM_ACCESS_MODE(stgm))
6402 case STGM_READ:
6403 return GENERIC_READ;
6404 case STGM_WRITE:
6405 case STGM_READWRITE:
6406 return GENERIC_READ | GENERIC_WRITE;
6408 ERR("Invalid access mode!\n");
6409 assert(0);
6410 return 0;
6413 /****************************************************************************
6414 * GetCreationModeFromSTGM
6416 * This method will return a creation mode flag from a STGM value.
6417 * The STGM value is assumed valid.
6419 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6421 switch(STGM_CREATE_MODE(stgm))
6423 case STGM_CREATE:
6424 return CREATE_ALWAYS;
6425 case STGM_CONVERT:
6426 FIXME("STGM_CONVERT not implemented!\n");
6427 return CREATE_NEW;
6428 case STGM_FAILIFTHERE:
6429 return CREATE_NEW;
6431 ERR("Invalid create mode!\n");
6432 assert(0);
6433 return 0;
6437 /*************************************************************************
6438 * OLECONVERT_LoadOLE10 [Internal]
6440 * Loads the OLE10 STREAM to memory
6442 * PARAMS
6443 * pOleStream [I] The OLESTREAM
6444 * pData [I] Data Structure for the OLESTREAM Data
6446 * RETURNS
6447 * Success: S_OK
6448 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6449 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6451 * NOTES
6452 * This function is used by OleConvertOLESTREAMToIStorage only.
6454 * Memory allocated for pData must be freed by the caller
6456 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6458 DWORD dwSize;
6459 HRESULT hRes = S_OK;
6460 int nTryCnt=0;
6461 int max_try = 6;
6463 pData->pData = NULL;
6464 pData->pstrOleObjFileName = (CHAR *) NULL;
6466 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6468 /* Get the OleID */
6469 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6470 if(dwSize != sizeof(pData->dwOleID))
6472 hRes = CONVERT10_E_OLESTREAM_GET;
6474 else if(pData->dwOleID != OLESTREAM_ID)
6476 hRes = CONVERT10_E_OLESTREAM_FMT;
6478 else
6480 hRes = S_OK;
6481 break;
6485 if(hRes == S_OK)
6487 /* Get the TypeID...more info needed for this field */
6488 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6489 if(dwSize != sizeof(pData->dwTypeID))
6491 hRes = CONVERT10_E_OLESTREAM_GET;
6494 if(hRes == S_OK)
6496 if(pData->dwTypeID != 0)
6498 /* Get the length of the OleTypeName */
6499 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6500 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6502 hRes = CONVERT10_E_OLESTREAM_GET;
6505 if(hRes == S_OK)
6507 if(pData->dwOleTypeNameLength > 0)
6509 /* Get the OleTypeName */
6510 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6511 if(dwSize != pData->dwOleTypeNameLength)
6513 hRes = CONVERT10_E_OLESTREAM_GET;
6517 if(bStrem1)
6519 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6520 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6522 hRes = CONVERT10_E_OLESTREAM_GET;
6524 if(hRes == S_OK)
6526 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6527 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6528 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6529 if(pData->pstrOleObjFileName)
6531 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6532 if(dwSize != pData->dwOleObjFileNameLength)
6534 hRes = CONVERT10_E_OLESTREAM_GET;
6537 else
6538 hRes = CONVERT10_E_OLESTREAM_GET;
6541 else
6543 /* Get the Width of the Metafile */
6544 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6545 if(dwSize != sizeof(pData->dwMetaFileWidth))
6547 hRes = CONVERT10_E_OLESTREAM_GET;
6549 if(hRes == S_OK)
6551 /* Get the Height of the Metafile */
6552 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6553 if(dwSize != sizeof(pData->dwMetaFileHeight))
6555 hRes = CONVERT10_E_OLESTREAM_GET;
6559 if(hRes == S_OK)
6561 /* Get the Length of the Data */
6562 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6563 if(dwSize != sizeof(pData->dwDataLength))
6565 hRes = CONVERT10_E_OLESTREAM_GET;
6569 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6571 if(!bStrem1) /* if it is a second OLE stream data */
6573 pData->dwDataLength -= 8;
6574 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6575 if(dwSize != sizeof(pData->strUnknown))
6577 hRes = CONVERT10_E_OLESTREAM_GET;
6581 if(hRes == S_OK)
6583 if(pData->dwDataLength > 0)
6585 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6587 /* Get Data (ex. IStorage, Metafile, or BMP) */
6588 if(pData->pData)
6590 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6591 if(dwSize != pData->dwDataLength)
6593 hRes = CONVERT10_E_OLESTREAM_GET;
6596 else
6598 hRes = CONVERT10_E_OLESTREAM_GET;
6604 return hRes;
6607 /*************************************************************************
6608 * OLECONVERT_SaveOLE10 [Internal]
6610 * Saves the OLE10 STREAM From memory
6612 * PARAMS
6613 * pData [I] Data Structure for the OLESTREAM Data
6614 * pOleStream [I] The OLESTREAM to save
6616 * RETURNS
6617 * Success: S_OK
6618 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6620 * NOTES
6621 * This function is used by OleConvertIStorageToOLESTREAM only.
6624 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6626 DWORD dwSize;
6627 HRESULT hRes = S_OK;
6630 /* Set the OleID */
6631 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6632 if(dwSize != sizeof(pData->dwOleID))
6634 hRes = CONVERT10_E_OLESTREAM_PUT;
6637 if(hRes == S_OK)
6639 /* Set the TypeID */
6640 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6641 if(dwSize != sizeof(pData->dwTypeID))
6643 hRes = CONVERT10_E_OLESTREAM_PUT;
6647 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6649 /* Set the Length of the OleTypeName */
6650 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6651 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6653 hRes = CONVERT10_E_OLESTREAM_PUT;
6656 if(hRes == S_OK)
6658 if(pData->dwOleTypeNameLength > 0)
6660 /* Set the OleTypeName */
6661 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6662 if(dwSize != pData->dwOleTypeNameLength)
6664 hRes = CONVERT10_E_OLESTREAM_PUT;
6669 if(hRes == S_OK)
6671 /* Set the width of the Metafile */
6672 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6673 if(dwSize != sizeof(pData->dwMetaFileWidth))
6675 hRes = CONVERT10_E_OLESTREAM_PUT;
6679 if(hRes == S_OK)
6681 /* Set the height of the Metafile */
6682 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6683 if(dwSize != sizeof(pData->dwMetaFileHeight))
6685 hRes = CONVERT10_E_OLESTREAM_PUT;
6689 if(hRes == S_OK)
6691 /* Set the length of the Data */
6692 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6693 if(dwSize != sizeof(pData->dwDataLength))
6695 hRes = CONVERT10_E_OLESTREAM_PUT;
6699 if(hRes == S_OK)
6701 if(pData->dwDataLength > 0)
6703 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6704 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6705 if(dwSize != pData->dwDataLength)
6707 hRes = CONVERT10_E_OLESTREAM_PUT;
6712 return hRes;
6715 /*************************************************************************
6716 * OLECONVERT_GetOLE20FromOLE10[Internal]
6718 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6719 * opens it, and copies the content to the dest IStorage for
6720 * OleConvertOLESTREAMToIStorage
6723 * PARAMS
6724 * pDestStorage [I] The IStorage to copy the data to
6725 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6726 * nBufferLength [I] The size of the buffer
6728 * RETURNS
6729 * Nothing
6731 * NOTES
6735 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6737 HRESULT hRes;
6738 HANDLE hFile;
6739 IStorage *pTempStorage;
6740 DWORD dwNumOfBytesWritten;
6741 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6742 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6744 /* Create a temp File */
6745 GetTempPathW(MAX_PATH, wstrTempDir);
6746 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6747 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6749 if(hFile != INVALID_HANDLE_VALUE)
6751 /* Write IStorage Data to File */
6752 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6753 CloseHandle(hFile);
6755 /* Open and copy temp storage to the Dest Storage */
6756 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6757 if(hRes == S_OK)
6759 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6760 StorageBaseImpl_Release(pTempStorage);
6762 DeleteFileW(wstrTempFile);
6767 /*************************************************************************
6768 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6770 * Saves the OLE10 STREAM From memory
6772 * PARAMS
6773 * pStorage [I] The Src IStorage to copy
6774 * pData [I] The Dest Memory to write to.
6776 * RETURNS
6777 * The size in bytes allocated for pData
6779 * NOTES
6780 * Memory allocated for pData must be freed by the caller
6782 * Used by OleConvertIStorageToOLESTREAM only.
6785 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6787 HANDLE hFile;
6788 HRESULT hRes;
6789 DWORD nDataLength = 0;
6790 IStorage *pTempStorage;
6791 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6792 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6794 *pData = NULL;
6796 /* Create temp Storage */
6797 GetTempPathW(MAX_PATH, wstrTempDir);
6798 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6799 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6801 if(hRes == S_OK)
6803 /* Copy Src Storage to the Temp Storage */
6804 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6805 StorageBaseImpl_Release(pTempStorage);
6807 /* Open Temp Storage as a file and copy to memory */
6808 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6809 if(hFile != INVALID_HANDLE_VALUE)
6811 nDataLength = GetFileSize(hFile, NULL);
6812 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6813 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6814 CloseHandle(hFile);
6816 DeleteFileW(wstrTempFile);
6818 return nDataLength;
6821 /*************************************************************************
6822 * OLECONVERT_CreateOleStream [Internal]
6824 * Creates the "\001OLE" stream in the IStorage if necessary.
6826 * PARAMS
6827 * pStorage [I] Dest storage to create the stream in
6829 * RETURNS
6830 * Nothing
6832 * NOTES
6833 * This function is used by OleConvertOLESTREAMToIStorage only.
6835 * This stream is still unknown, MS Word seems to have extra data
6836 * but since the data is stored in the OLESTREAM there should be
6837 * no need to recreate the stream. If the stream is manually
6838 * deleted it will create it with this default data.
6841 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6843 HRESULT hRes;
6844 IStream *pStream;
6845 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6846 BYTE pOleStreamHeader [] =
6848 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6849 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6850 0x00, 0x00, 0x00, 0x00
6853 /* Create stream if not present */
6854 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6855 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6857 if(hRes == S_OK)
6859 /* Write default Data */
6860 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6861 IStream_Release(pStream);
6865 /* write a string to a stream, preceded by its length */
6866 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6868 HRESULT r;
6869 LPSTR str;
6870 DWORD len = 0;
6872 if( string )
6873 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6874 r = IStream_Write( stm, &len, sizeof(len), NULL);
6875 if( FAILED( r ) )
6876 return r;
6877 if(len == 0)
6878 return r;
6879 str = CoTaskMemAlloc( len );
6880 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6881 r = IStream_Write( stm, str, len, NULL);
6882 CoTaskMemFree( str );
6883 return r;
6886 /* read a string preceded by its length from a stream */
6887 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6889 HRESULT r;
6890 DWORD len, count = 0;
6891 LPSTR str;
6892 LPWSTR wstr;
6894 r = IStream_Read( stm, &len, sizeof(len), &count );
6895 if( FAILED( r ) )
6896 return r;
6897 if( count != sizeof(len) )
6898 return E_OUTOFMEMORY;
6900 TRACE("%ld bytes\n",len);
6902 str = CoTaskMemAlloc( len );
6903 if( !str )
6904 return E_OUTOFMEMORY;
6905 count = 0;
6906 r = IStream_Read( stm, str, len, &count );
6907 if( FAILED( r ) )
6908 return r;
6909 if( count != len )
6911 CoTaskMemFree( str );
6912 return E_OUTOFMEMORY;
6915 TRACE("Read string %s\n",debugstr_an(str,len));
6917 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6918 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6919 if( wstr )
6920 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6921 CoTaskMemFree( str );
6923 *string = wstr;
6925 return r;
6929 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6930 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6932 IStream *pstm;
6933 HRESULT r = S_OK;
6934 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6936 static const BYTE unknown1[12] =
6937 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6938 0xFF, 0xFF, 0xFF, 0xFF};
6939 static const BYTE unknown2[16] =
6940 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6941 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6943 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6944 debugstr_w(lpszUserType), debugstr_w(szClipName),
6945 debugstr_w(szProgIDName));
6947 /* Create a CompObj stream if it doesn't exist */
6948 r = IStorage_CreateStream(pstg, szwStreamName,
6949 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6950 if( FAILED (r) )
6951 return r;
6953 /* Write CompObj Structure to stream */
6954 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6956 if( SUCCEEDED( r ) )
6957 r = WriteClassStm( pstm, clsid );
6959 if( SUCCEEDED( r ) )
6960 r = STREAM_WriteString( pstm, lpszUserType );
6961 if( SUCCEEDED( r ) )
6962 r = STREAM_WriteString( pstm, szClipName );
6963 if( SUCCEEDED( r ) )
6964 r = STREAM_WriteString( pstm, szProgIDName );
6965 if( SUCCEEDED( r ) )
6966 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6968 IStream_Release( pstm );
6970 return r;
6973 /***********************************************************************
6974 * WriteFmtUserTypeStg (OLE32.@)
6976 HRESULT WINAPI WriteFmtUserTypeStg(
6977 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6979 HRESULT r;
6980 WCHAR szwClipName[0x40];
6981 CLSID clsid = CLSID_NULL;
6982 LPWSTR wstrProgID = NULL;
6983 DWORD n;
6985 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6987 /* get the clipboard format name */
6988 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6989 szwClipName[n]=0;
6991 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6993 /* FIXME: There's room to save a CLSID and its ProgID, but
6994 the CLSID is not looked up in the registry and in all the
6995 tests I wrote it was CLSID_NULL. Where does it come from?
6998 /* get the real program ID. This may fail, but that's fine */
6999 ProgIDFromCLSID(&clsid, &wstrProgID);
7001 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7003 r = STORAGE_WriteCompObj( pstg, &clsid,
7004 lpszUserType, szwClipName, wstrProgID );
7006 CoTaskMemFree(wstrProgID);
7008 return r;
7012 /******************************************************************************
7013 * ReadFmtUserTypeStg [OLE32.@]
7015 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7017 HRESULT r;
7018 IStream *stm = 0;
7019 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7020 unsigned char unknown1[12];
7021 unsigned char unknown2[16];
7022 DWORD count;
7023 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7024 CLSID clsid;
7026 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7028 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7029 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7030 if( FAILED ( r ) )
7032 WARN("Failed to open stream r = %08lx\n", r);
7033 return r;
7036 /* read the various parts of the structure */
7037 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7038 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7039 goto end;
7040 r = ReadClassStm( stm, &clsid );
7041 if( FAILED( r ) )
7042 goto end;
7044 r = STREAM_ReadString( stm, &szCLSIDName );
7045 if( FAILED( r ) )
7046 goto end;
7048 r = STREAM_ReadString( stm, &szOleTypeName );
7049 if( FAILED( r ) )
7050 goto end;
7052 r = STREAM_ReadString( stm, &szProgIDName );
7053 if( FAILED( r ) )
7054 goto end;
7056 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7057 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7058 goto end;
7060 /* ok, success... now we just need to store what we found */
7061 if( pcf )
7062 *pcf = RegisterClipboardFormatW( szOleTypeName );
7063 CoTaskMemFree( szOleTypeName );
7065 if( lplpszUserType )
7066 *lplpszUserType = szCLSIDName;
7067 CoTaskMemFree( szProgIDName );
7069 end:
7070 IStream_Release( stm );
7072 return r;
7076 /*************************************************************************
7077 * OLECONVERT_CreateCompObjStream [Internal]
7079 * Creates a "\001CompObj" is the destination IStorage if necessary.
7081 * PARAMS
7082 * pStorage [I] The dest IStorage to create the CompObj Stream
7083 * if necessary.
7084 * strOleTypeName [I] The ProgID
7086 * RETURNS
7087 * Success: S_OK
7088 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7090 * NOTES
7091 * This function is used by OleConvertOLESTREAMToIStorage only.
7093 * The stream data is stored in the OLESTREAM and there should be
7094 * no need to recreate the stream. If the stream is manually
7095 * deleted it will attempt to create it by querying the registry.
7099 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7101 IStream *pStream;
7102 HRESULT hStorageRes, hRes = S_OK;
7103 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7104 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7105 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7107 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7108 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7110 /* Initialize the CompObj structure */
7111 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7112 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7113 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7116 /* Create a CompObj stream if it doesn't exist */
7117 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7118 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7119 if(hStorageRes == S_OK)
7121 /* copy the OleTypeName to the compobj struct */
7122 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7123 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7125 /* copy the OleTypeName to the compobj struct */
7126 /* Note: in the test made, these were Identical */
7127 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7128 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7130 /* Get the CLSID */
7131 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7132 bufferW, OLESTREAM_MAX_STR_LEN );
7133 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7135 if(hRes == S_OK)
7137 HKEY hKey;
7138 LONG hErr;
7139 /* Get the CLSID Default Name from the Registry */
7140 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7141 if(hErr == ERROR_SUCCESS)
7143 char strTemp[OLESTREAM_MAX_STR_LEN];
7144 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7145 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7146 if(hErr == ERROR_SUCCESS)
7148 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7150 RegCloseKey(hKey);
7154 /* Write CompObj Structure to stream */
7155 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7157 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7159 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7160 if(IStorageCompObj.dwCLSIDNameLength > 0)
7162 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7164 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7165 if(IStorageCompObj.dwOleTypeNameLength > 0)
7167 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7169 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7170 if(IStorageCompObj.dwProgIDNameLength > 0)
7172 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7174 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7175 IStream_Release(pStream);
7177 return hRes;
7181 /*************************************************************************
7182 * OLECONVERT_CreateOlePresStream[Internal]
7184 * Creates the "\002OlePres000" Stream with the Metafile data
7186 * PARAMS
7187 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7188 * dwExtentX [I] Width of the Metafile
7189 * dwExtentY [I] Height of the Metafile
7190 * pData [I] Metafile data
7191 * dwDataLength [I] Size of the Metafile data
7193 * RETURNS
7194 * Success: S_OK
7195 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7197 * NOTES
7198 * This function is used by OleConvertOLESTREAMToIStorage only.
7201 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7203 HRESULT hRes;
7204 IStream *pStream;
7205 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7206 BYTE pOlePresStreamHeader [] =
7208 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7209 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7210 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7211 0x00, 0x00, 0x00, 0x00
7214 BYTE pOlePresStreamHeaderEmpty [] =
7216 0x00, 0x00, 0x00, 0x00,
7217 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7218 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7219 0x00, 0x00, 0x00, 0x00
7222 /* Create the OlePres000 Stream */
7223 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7224 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7226 if(hRes == S_OK)
7228 DWORD nHeaderSize;
7229 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7231 memset(&OlePres, 0, sizeof(OlePres));
7232 /* Do we have any metafile data to save */
7233 if(dwDataLength > 0)
7235 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7236 nHeaderSize = sizeof(pOlePresStreamHeader);
7238 else
7240 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7241 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7243 /* Set width and height of the metafile */
7244 OlePres.dwExtentX = dwExtentX;
7245 OlePres.dwExtentY = -dwExtentY;
7247 /* Set Data and Length */
7248 if(dwDataLength > sizeof(METAFILEPICT16))
7250 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7251 OlePres.pData = &(pData[8]);
7253 /* Save OlePres000 Data to Stream */
7254 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7255 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7256 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7257 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7258 if(OlePres.dwSize > 0)
7260 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7262 IStream_Release(pStream);
7266 /*************************************************************************
7267 * OLECONVERT_CreateOle10NativeStream [Internal]
7269 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7271 * PARAMS
7272 * pStorage [I] Dest storage to create the stream in
7273 * pData [I] Ole10 Native Data (ex. bmp)
7274 * dwDataLength [I] Size of the Ole10 Native Data
7276 * RETURNS
7277 * Nothing
7279 * NOTES
7280 * This function is used by OleConvertOLESTREAMToIStorage only.
7282 * Might need to verify the data and return appropriate error message
7285 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7287 HRESULT hRes;
7288 IStream *pStream;
7289 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7291 /* Create the Ole10Native Stream */
7292 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7293 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7295 if(hRes == S_OK)
7297 /* Write info to stream */
7298 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7299 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7300 IStream_Release(pStream);
7305 /*************************************************************************
7306 * OLECONVERT_GetOLE10ProgID [Internal]
7308 * Finds the ProgID (or OleTypeID) from the IStorage
7310 * PARAMS
7311 * pStorage [I] The Src IStorage to get the ProgID
7312 * strProgID [I] the ProgID string to get
7313 * dwSize [I] the size of the string
7315 * RETURNS
7316 * Success: S_OK
7317 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7319 * NOTES
7320 * This function is used by OleConvertIStorageToOLESTREAM only.
7324 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7326 HRESULT hRes;
7327 IStream *pStream;
7328 LARGE_INTEGER iSeekPos;
7329 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7330 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7332 /* Open the CompObj Stream */
7333 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7334 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7335 if(hRes == S_OK)
7338 /*Get the OleType from the CompObj Stream */
7339 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7340 iSeekPos.u.HighPart = 0;
7342 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7343 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7344 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7345 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7346 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7347 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7348 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7350 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7351 if(*dwSize > 0)
7353 IStream_Read(pStream, strProgID, *dwSize, NULL);
7355 IStream_Release(pStream);
7357 else
7359 STATSTG stat;
7360 LPOLESTR wstrProgID;
7362 /* Get the OleType from the registry */
7363 REFCLSID clsid = &(stat.clsid);
7364 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7365 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7366 if(hRes == S_OK)
7368 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7372 return hRes;
7375 /*************************************************************************
7376 * OLECONVERT_GetOle10PresData [Internal]
7378 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7380 * PARAMS
7381 * pStorage [I] Src IStroage
7382 * pOleStream [I] Dest OleStream Mem Struct
7384 * RETURNS
7385 * Nothing
7387 * NOTES
7388 * This function is used by OleConvertIStorageToOLESTREAM only.
7390 * Memory allocated for pData must be freed by the caller
7394 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7397 HRESULT hRes;
7398 IStream *pStream;
7399 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7401 /* Initialize Default data for OLESTREAM */
7402 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7403 pOleStreamData[0].dwTypeID = 2;
7404 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7405 pOleStreamData[1].dwTypeID = 0;
7406 pOleStreamData[0].dwMetaFileWidth = 0;
7407 pOleStreamData[0].dwMetaFileHeight = 0;
7408 pOleStreamData[0].pData = NULL;
7409 pOleStreamData[1].pData = NULL;
7411 /* Open Ole10Native Stream */
7412 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7413 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7414 if(hRes == S_OK)
7417 /* Read Size and Data */
7418 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7419 if(pOleStreamData->dwDataLength > 0)
7421 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7422 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7424 IStream_Release(pStream);
7430 /*************************************************************************
7431 * OLECONVERT_GetOle20PresData[Internal]
7433 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7435 * PARAMS
7436 * pStorage [I] Src IStroage
7437 * pOleStreamData [I] Dest OleStream Mem Struct
7439 * RETURNS
7440 * Nothing
7442 * NOTES
7443 * This function is used by OleConvertIStorageToOLESTREAM only.
7445 * Memory allocated for pData must be freed by the caller
7447 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7449 HRESULT hRes;
7450 IStream *pStream;
7451 OLECONVERT_ISTORAGE_OLEPRES olePress;
7452 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7454 /* Initialize Default data for OLESTREAM */
7455 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7456 pOleStreamData[0].dwTypeID = 2;
7457 pOleStreamData[0].dwMetaFileWidth = 0;
7458 pOleStreamData[0].dwMetaFileHeight = 0;
7459 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7460 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7461 pOleStreamData[1].dwTypeID = 0;
7462 pOleStreamData[1].dwOleTypeNameLength = 0;
7463 pOleStreamData[1].strOleTypeName[0] = 0;
7464 pOleStreamData[1].dwMetaFileWidth = 0;
7465 pOleStreamData[1].dwMetaFileHeight = 0;
7466 pOleStreamData[1].pData = NULL;
7467 pOleStreamData[1].dwDataLength = 0;
7470 /* Open OlePress000 stream */
7471 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7472 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7473 if(hRes == S_OK)
7475 LARGE_INTEGER iSeekPos;
7476 METAFILEPICT16 MetaFilePict;
7477 static const char strMetafilePictName[] = "METAFILEPICT";
7479 /* Set the TypeID for a Metafile */
7480 pOleStreamData[1].dwTypeID = 5;
7482 /* Set the OleTypeName to Metafile */
7483 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7484 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7486 iSeekPos.u.HighPart = 0;
7487 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7489 /* Get Presentation Data */
7490 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7491 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7492 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7493 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7495 /*Set width and Height */
7496 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7497 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7498 if(olePress.dwSize > 0)
7500 /* Set Length */
7501 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7503 /* Set MetaFilePict struct */
7504 MetaFilePict.mm = 8;
7505 MetaFilePict.xExt = olePress.dwExtentX;
7506 MetaFilePict.yExt = olePress.dwExtentY;
7507 MetaFilePict.hMF = 0;
7509 /* Get Metafile Data */
7510 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7511 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7512 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7514 IStream_Release(pStream);
7518 /*************************************************************************
7519 * OleConvertOLESTREAMToIStorage [OLE32.@]
7521 * Read info on MSDN
7523 * TODO
7524 * DVTARGETDEVICE paramenter is not handled
7525 * Still unsure of some mem fields for OLE 10 Stream
7526 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7527 * and "\001OLE" streams
7530 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7531 LPOLESTREAM pOleStream,
7532 LPSTORAGE pstg,
7533 const DVTARGETDEVICE* ptd)
7535 int i;
7536 HRESULT hRes=S_OK;
7537 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7539 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7541 if(ptd != NULL)
7543 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7546 if(pstg == NULL || pOleStream == NULL)
7548 hRes = E_INVALIDARG;
7551 if(hRes == S_OK)
7553 /* Load the OLESTREAM to Memory */
7554 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7557 if(hRes == S_OK)
7559 /* Load the OLESTREAM to Memory (part 2)*/
7560 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7563 if(hRes == S_OK)
7566 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7568 /* Do we have the IStorage Data in the OLESTREAM */
7569 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7571 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7572 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7574 else
7576 /* It must be an original OLE 1.0 source */
7577 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7580 else
7582 /* It must be an original OLE 1.0 source */
7583 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7586 /* Create CompObj Stream if necessary */
7587 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7588 if(hRes == S_OK)
7590 /*Create the Ole Stream if necessary */
7591 OLECONVERT_CreateOleStream(pstg);
7596 /* Free allocated memory */
7597 for(i=0; i < 2; i++)
7599 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7600 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7601 pOleStreamData[i].pstrOleObjFileName = NULL;
7603 return hRes;
7606 /*************************************************************************
7607 * OleConvertIStorageToOLESTREAM [OLE32.@]
7609 * Read info on MSDN
7611 * Read info on MSDN
7613 * TODO
7614 * Still unsure of some mem fields for OLE 10 Stream
7615 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7616 * and "\001OLE" streams.
7619 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7620 LPSTORAGE pstg,
7621 LPOLESTREAM pOleStream)
7623 int i;
7624 HRESULT hRes = S_OK;
7625 IStream *pStream;
7626 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7627 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7630 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7632 if(pstg == NULL || pOleStream == NULL)
7634 hRes = E_INVALIDARG;
7636 if(hRes == S_OK)
7638 /* Get the ProgID */
7639 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7640 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7642 if(hRes == S_OK)
7644 /* Was it originally Ole10 */
7645 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7646 if(hRes == S_OK)
7648 IStream_Release(pStream);
7649 /* Get Presentation Data for Ole10Native */
7650 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7652 else
7654 /* Get Presentation Data (OLE20) */
7655 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7658 /* Save OLESTREAM */
7659 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7660 if(hRes == S_OK)
7662 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7667 /* Free allocated memory */
7668 for(i=0; i < 2; i++)
7670 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7673 return hRes;
7676 /***********************************************************************
7677 * GetConvertStg (OLE32.@)
7679 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7680 FIXME("unimplemented stub!\n");
7681 return E_FAIL;
7684 /******************************************************************************
7685 * StgIsStorageFile [OLE32.@]
7687 HRESULT WINAPI
7688 StgIsStorageFile(LPCOLESTR fn)
7690 HANDLE hf;
7691 BYTE magic[8];
7692 DWORD bytes_read;
7694 TRACE("(\'%s\')\n", debugstr_w(fn));
7695 hf = CreateFileW(fn, GENERIC_READ,
7696 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7697 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7699 if (hf == INVALID_HANDLE_VALUE)
7700 return STG_E_FILENOTFOUND;
7702 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7704 WARN(" unable to read file\n");
7705 CloseHandle(hf);
7706 return S_FALSE;
7709 CloseHandle(hf);
7711 if (bytes_read != 8) {
7712 WARN(" too short\n");
7713 return S_FALSE;
7716 if (!memcmp(magic,STORAGE_magic,8)) {
7717 WARN(" -> YES\n");
7718 return S_OK;
7721 WARN(" -> Invalid header.\n");
7722 return S_FALSE;