server: Moved some common bits of get/set_thread_context to thread.c.
[wine/wine64.git] / dlls / ole32 / storage32.c
blobacebb98950b9cdeb7727487b303da5eebe06cf94
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 static 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 static 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 static 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 static HRESULT WINAPI StorageBaseImpl_OpenStream(
292 IStorage* iface,
293 const OLECHAR* pwcsName, /* [string][in] */
294 void* reserved1, /* [unique][in] */
295 DWORD grfMode, /* [in] */
296 DWORD reserved2, /* [in] */
297 IStream** ppstm) /* [out] */
299 StorageBaseImpl *This = (StorageBaseImpl *)iface;
300 IEnumSTATSTGImpl* propertyEnumeration;
301 StgStreamImpl* newStream;
302 StgProperty currentProperty;
303 ULONG foundPropertyIndex;
304 HRESULT res = STG_E_UNKNOWN;
306 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
307 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
310 * Perform a sanity check on the parameters.
312 if ( (pwcsName==NULL) || (ppstm==0) )
314 res = E_INVALIDARG;
315 goto end;
319 * Initialize the out parameter
321 *ppstm = NULL;
324 * Validate the STGM flags
326 if ( FAILED( validateSTGM(grfMode) ))
328 res = STG_E_INVALIDFLAG;
329 goto end;
333 * As documented.
335 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
336 (grfMode & STGM_DELETEONRELEASE) ||
337 (grfMode & STGM_TRANSACTED) )
339 res = STG_E_INVALIDFUNCTION;
340 goto end;
344 * Check that we're compatible with the parent's storage mode, but
345 * only if we are not in transacted mode
347 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
348 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
350 res = STG_E_ACCESSDENIED;
351 goto end;
356 * Create a property enumeration to search the properties
358 propertyEnumeration = IEnumSTATSTGImpl_Construct(
359 This->ancestorStorage,
360 This->rootPropertySetIndex);
363 * Search the enumeration for the property with the given name
365 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
366 propertyEnumeration,
367 pwcsName,
368 &currentProperty);
371 * Delete the property enumeration since we don't need it anymore
373 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
376 * If it was found, construct the stream object and return a pointer to it.
378 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
379 (currentProperty.propertyType==PROPTYPE_STREAM) )
381 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
383 if (newStream!=0)
385 newStream->grfMode = grfMode;
386 *ppstm = (IStream*)newStream;
389 * Since we are returning a pointer to the interface, we have to
390 * nail down the reference.
392 IStream_AddRef(*ppstm);
395 * add us to the storage's list of active streams
398 StorageBaseImpl_AddStream(This,newStream);
400 res = S_OK;
401 goto end;
404 res = E_OUTOFMEMORY;
405 goto end;
408 res = STG_E_FILENOTFOUND;
410 end:
411 if (res == S_OK)
412 TRACE("<-- IStream %p\n", *ppstm);
413 TRACE("<-- %08lx\n", res);
414 return res;
417 /************************************************************************
418 * Storage32BaseImpl_OpenStorage (IStorage)
420 * This method will open a new storage object from the current storage.
422 * See Windows documentation for more details on IStorage methods.
424 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
425 IStorage* iface,
426 const OLECHAR* pwcsName, /* [string][unique][in] */
427 IStorage* pstgPriority, /* [unique][in] */
428 DWORD grfMode, /* [in] */
429 SNB snbExclude, /* [unique][in] */
430 DWORD reserved, /* [in] */
431 IStorage** ppstg) /* [out] */
433 StorageBaseImpl *This = (StorageBaseImpl *)iface;
434 StorageInternalImpl* newStorage;
435 IEnumSTATSTGImpl* propertyEnumeration;
436 StgProperty currentProperty;
437 ULONG foundPropertyIndex;
438 HRESULT res = STG_E_UNKNOWN;
440 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
441 iface, debugstr_w(pwcsName), pstgPriority,
442 grfMode, snbExclude, reserved, ppstg);
445 * Perform a sanity check on the parameters.
447 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
449 res = E_INVALIDARG;
450 goto end;
453 /* as documented */
454 if (snbExclude != NULL)
456 res = STG_E_INVALIDPARAMETER;
457 goto end;
461 * Validate the STGM flags
463 if ( FAILED( validateSTGM(grfMode) ))
465 res = STG_E_INVALIDFLAG;
466 goto end;
470 * As documented.
472 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
473 (grfMode & STGM_DELETEONRELEASE) ||
474 (grfMode & STGM_PRIORITY) )
476 res = STG_E_INVALIDFUNCTION;
477 goto end;
481 * Check that we're compatible with the parent's storage mode,
482 * but only if we are not transacted
484 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
485 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
487 res = STG_E_ACCESSDENIED;
488 goto end;
493 * Initialize the out parameter
495 *ppstg = NULL;
498 * Create a property enumeration to search the properties
500 propertyEnumeration = IEnumSTATSTGImpl_Construct(
501 This->ancestorStorage,
502 This->rootPropertySetIndex);
505 * Search the enumeration for the property with the given name
507 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
508 propertyEnumeration,
509 pwcsName,
510 &currentProperty);
513 * Delete the property enumeration since we don't need it anymore
515 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
518 * If it was found, construct the stream object and return a pointer to it.
520 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
521 (currentProperty.propertyType==PROPTYPE_STORAGE) )
524 * Construct a new Storage object
526 newStorage = StorageInternalImpl_Construct(
527 This->ancestorStorage,
528 grfMode,
529 foundPropertyIndex);
531 if (newStorage != 0)
533 *ppstg = (IStorage*)newStorage;
536 * Since we are returning a pointer to the interface,
537 * we have to nail down the reference.
539 StorageBaseImpl_AddRef(*ppstg);
541 res = S_OK;
542 goto end;
545 res = STG_E_INSUFFICIENTMEMORY;
546 goto end;
549 res = STG_E_FILENOTFOUND;
551 end:
552 TRACE("<-- %08lx\n", res);
553 return res;
556 /************************************************************************
557 * Storage32BaseImpl_EnumElements (IStorage)
559 * This method will create an enumerator object that can be used to
560 * retrieve informatino about all the properties in the storage object.
562 * See Windows documentation for more details on IStorage methods.
564 static HRESULT WINAPI StorageBaseImpl_EnumElements(
565 IStorage* iface,
566 DWORD reserved1, /* [in] */
567 void* reserved2, /* [size_is][unique][in] */
568 DWORD reserved3, /* [in] */
569 IEnumSTATSTG** ppenum) /* [out] */
571 StorageBaseImpl *This = (StorageBaseImpl *)iface;
572 IEnumSTATSTGImpl* newEnum;
574 TRACE("(%p, %ld, %p, %ld, %p)\n",
575 iface, reserved1, reserved2, reserved3, ppenum);
578 * Perform a sanity check on the parameters.
580 if ( (This==0) || (ppenum==0))
581 return E_INVALIDARG;
584 * Construct the enumerator.
586 newEnum = IEnumSTATSTGImpl_Construct(
587 This->ancestorStorage,
588 This->rootPropertySetIndex);
590 if (newEnum!=0)
592 *ppenum = (IEnumSTATSTG*)newEnum;
595 * Don't forget to nail down a reference to the new object before
596 * returning it.
598 IEnumSTATSTG_AddRef(*ppenum);
600 return S_OK;
603 return E_OUTOFMEMORY;
606 /************************************************************************
607 * Storage32BaseImpl_Stat (IStorage)
609 * This method will retrieve information about this storage object.
611 * See Windows documentation for more details on IStorage methods.
613 static HRESULT WINAPI StorageBaseImpl_Stat(
614 IStorage* iface,
615 STATSTG* pstatstg, /* [out] */
616 DWORD grfStatFlag) /* [in] */
618 StorageBaseImpl *This = (StorageBaseImpl *)iface;
619 StgProperty curProperty;
620 BOOL readSuccessful;
621 HRESULT res = STG_E_UNKNOWN;
623 TRACE("(%p, %p, %lx)\n",
624 iface, pstatstg, grfStatFlag);
627 * Perform a sanity check on the parameters.
629 if ( (This==0) || (pstatstg==0))
631 res = E_INVALIDARG;
632 goto end;
636 * Read the information from the property.
638 readSuccessful = StorageImpl_ReadProperty(
639 This->ancestorStorage,
640 This->rootPropertySetIndex,
641 &curProperty);
643 if (readSuccessful)
645 StorageUtl_CopyPropertyToSTATSTG(
646 pstatstg,
647 &curProperty,
648 grfStatFlag);
650 pstatstg->grfMode = This->openFlags;
652 res = S_OK;
653 goto end;
656 res = E_FAIL;
658 end:
659 if (res == S_OK)
661 TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %08lx, grfLocksSupported: %ld, grfStateBits: %08lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
663 TRACE("<-- %08lx\n", res);
664 return res;
667 /************************************************************************
668 * Storage32BaseImpl_RenameElement (IStorage)
670 * This method will rename the specified element.
672 * See Windows documentation for more details on IStorage methods.
674 * Implementation notes: The method used to rename consists of creating a clone
675 * of the deleted StgProperty object setting it with the new name and to
676 * perform a DestroyElement of the old StgProperty.
678 static HRESULT WINAPI StorageBaseImpl_RenameElement(
679 IStorage* iface,
680 const OLECHAR* pwcsOldName, /* [in] */
681 const OLECHAR* pwcsNewName) /* [in] */
683 StorageBaseImpl *This = (StorageBaseImpl *)iface;
684 IEnumSTATSTGImpl* propertyEnumeration;
685 StgProperty currentProperty;
686 ULONG foundPropertyIndex;
688 TRACE("(%p, %s, %s)\n",
689 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
692 * Create a property enumeration to search the properties
694 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
695 This->rootPropertySetIndex);
698 * Search the enumeration for the new property name
700 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
701 pwcsNewName,
702 &currentProperty);
704 if (foundPropertyIndex != PROPERTY_NULL)
707 * There is already a property with the new name
709 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
710 return STG_E_FILEALREADYEXISTS;
713 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
716 * Search the enumeration for the old property name
718 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
719 pwcsOldName,
720 &currentProperty);
723 * Delete the property enumeration since we don't need it anymore
725 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
727 if (foundPropertyIndex != PROPERTY_NULL)
729 StgProperty renamedProperty;
730 ULONG renamedPropertyIndex;
733 * Setup a new property for the renamed property
735 renamedProperty.sizeOfNameString =
736 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
738 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
739 return STG_E_INVALIDNAME;
741 strcpyW(renamedProperty.name, pwcsNewName);
743 renamedProperty.propertyType = currentProperty.propertyType;
744 renamedProperty.startingBlock = currentProperty.startingBlock;
745 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
746 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
748 renamedProperty.previousProperty = PROPERTY_NULL;
749 renamedProperty.nextProperty = PROPERTY_NULL;
752 * Bring the dirProperty link in case it is a storage and in which
753 * case the renamed storage elements don't require to be reorganized.
755 renamedProperty.dirProperty = currentProperty.dirProperty;
757 /* call CoFileTime to get the current time
758 renamedProperty.timeStampS1
759 renamedProperty.timeStampD1
760 renamedProperty.timeStampS2
761 renamedProperty.timeStampD2
762 renamedProperty.propertyUniqueID
766 * Obtain a free property in the property chain
768 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
771 * Save the new property into the new property spot
773 StorageImpl_WriteProperty(
774 This->ancestorStorage,
775 renamedPropertyIndex,
776 &renamedProperty);
779 * Find a spot in the property chain for our newly created property.
781 updatePropertyChain(
782 (StorageImpl*)This,
783 renamedPropertyIndex,
784 renamedProperty);
787 * At this point the renamed property has been inserted in the tree,
788 * now, before Destroying the old property we must zero its dirProperty
789 * otherwise the DestroyProperty below will zap it all and we do not want
790 * this to happen.
791 * Also, we fake that the old property is a storage so the DestroyProperty
792 * will not do a SetSize(0) on the stream data.
794 * This means that we need to tweak the StgProperty if it is a stream or a
795 * non empty storage.
797 StorageImpl_ReadProperty(This->ancestorStorage,
798 foundPropertyIndex,
799 &currentProperty);
801 currentProperty.dirProperty = PROPERTY_NULL;
802 currentProperty.propertyType = PROPTYPE_STORAGE;
803 StorageImpl_WriteProperty(
804 This->ancestorStorage,
805 foundPropertyIndex,
806 &currentProperty);
809 * Invoke Destroy to get rid of the ole property and automatically redo
810 * the linking of its previous and next members...
812 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
815 else
818 * There is no property with the old name
820 return STG_E_FILENOTFOUND;
823 return S_OK;
826 /************************************************************************
827 * Storage32BaseImpl_CreateStream (IStorage)
829 * This method will create a stream object within this storage
831 * See Windows documentation for more details on IStorage methods.
833 static HRESULT WINAPI StorageBaseImpl_CreateStream(
834 IStorage* iface,
835 const OLECHAR* pwcsName, /* [string][in] */
836 DWORD grfMode, /* [in] */
837 DWORD reserved1, /* [in] */
838 DWORD reserved2, /* [in] */
839 IStream** ppstm) /* [out] */
841 StorageBaseImpl *This = (StorageBaseImpl *)iface;
842 IEnumSTATSTGImpl* propertyEnumeration;
843 StgStreamImpl* newStream;
844 StgProperty currentProperty, newStreamProperty;
845 ULONG foundPropertyIndex, newPropertyIndex;
847 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
848 iface, debugstr_w(pwcsName), grfMode,
849 reserved1, reserved2, ppstm);
852 * Validate parameters
854 if (ppstm == 0)
855 return STG_E_INVALIDPOINTER;
857 if (pwcsName == 0)
858 return STG_E_INVALIDNAME;
860 if (reserved1 || reserved2)
861 return STG_E_INVALIDPARAMETER;
864 * Validate the STGM flags
866 if ( FAILED( validateSTGM(grfMode) ))
867 return STG_E_INVALIDFLAG;
869 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
870 return STG_E_INVALIDFLAG;
873 * As documented.
875 if ((grfMode & STGM_DELETEONRELEASE) ||
876 (grfMode & STGM_TRANSACTED))
877 return STG_E_INVALIDFUNCTION;
880 * Check that we're compatible with the parent's storage mode
881 * if not in transacted mode
883 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
884 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
885 return STG_E_ACCESSDENIED;
889 * Initialize the out parameter
891 *ppstm = 0;
894 * Create a property enumeration to search the properties
896 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
897 This->rootPropertySetIndex);
899 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
900 pwcsName,
901 &currentProperty);
903 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
905 if (foundPropertyIndex != PROPERTY_NULL)
908 * An element with this name already exists
910 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
912 IStorage_DestroyElement(iface, pwcsName);
914 else
915 return STG_E_FILEALREADYEXISTS;
917 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
919 WARN("read-only storage\n");
920 return STG_E_ACCESSDENIED;
924 * memset the empty property
926 memset(&newStreamProperty, 0, sizeof(StgProperty));
928 newStreamProperty.sizeOfNameString =
929 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
931 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
932 return STG_E_INVALIDNAME;
934 strcpyW(newStreamProperty.name, pwcsName);
936 newStreamProperty.propertyType = PROPTYPE_STREAM;
937 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
938 newStreamProperty.size.u.LowPart = 0;
939 newStreamProperty.size.u.HighPart = 0;
941 newStreamProperty.previousProperty = PROPERTY_NULL;
942 newStreamProperty.nextProperty = PROPERTY_NULL;
943 newStreamProperty.dirProperty = PROPERTY_NULL;
945 /* call CoFileTime to get the current time
946 newStreamProperty.timeStampS1
947 newStreamProperty.timeStampD1
948 newStreamProperty.timeStampS2
949 newStreamProperty.timeStampD2
952 /* newStreamProperty.propertyUniqueID */
955 * Get a free property or create a new one
957 newPropertyIndex = getFreeProperty(This->ancestorStorage);
960 * Save the new property into the new property spot
962 StorageImpl_WriteProperty(
963 This->ancestorStorage,
964 newPropertyIndex,
965 &newStreamProperty);
968 * Find a spot in the property chain for our newly created property.
970 updatePropertyChain(
971 (StorageImpl*)This,
972 newPropertyIndex,
973 newStreamProperty);
976 * Open the stream to return it.
978 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
980 if (newStream != 0)
982 *ppstm = (IStream*)newStream;
985 * Since we are returning a pointer to the interface, we have to nail down
986 * the reference.
988 IStream_AddRef(*ppstm);
990 /* add us to the storage's list of active streams
992 StorageBaseImpl_AddStream(This,newStream);
995 else
997 return STG_E_INSUFFICIENTMEMORY;
1000 return S_OK;
1003 /************************************************************************
1004 * Storage32BaseImpl_SetClass (IStorage)
1006 * This method will write the specified CLSID in the property of this
1007 * storage.
1009 * See Windows documentation for more details on IStorage methods.
1011 static HRESULT WINAPI StorageBaseImpl_SetClass(
1012 IStorage* iface,
1013 REFCLSID clsid) /* [in] */
1015 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1016 HRESULT hRes = E_FAIL;
1017 StgProperty curProperty;
1018 BOOL success;
1020 TRACE("(%p, %p)\n", iface, clsid);
1022 success = StorageImpl_ReadProperty(This->ancestorStorage,
1023 This->rootPropertySetIndex,
1024 &curProperty);
1025 if (success)
1027 curProperty.propertyUniqueID = *clsid;
1029 success = StorageImpl_WriteProperty(This->ancestorStorage,
1030 This->rootPropertySetIndex,
1031 &curProperty);
1032 if (success)
1033 hRes = S_OK;
1036 return hRes;
1039 /************************************************************************
1040 ** Storage32Impl implementation
1043 /************************************************************************
1044 * Storage32Impl_CreateStorage (IStorage)
1046 * This method will create the storage object within the provided storage.
1048 * See Windows documentation for more details on IStorage methods.
1050 static HRESULT WINAPI StorageImpl_CreateStorage(
1051 IStorage* iface,
1052 const OLECHAR *pwcsName, /* [string][in] */
1053 DWORD grfMode, /* [in] */
1054 DWORD reserved1, /* [in] */
1055 DWORD reserved2, /* [in] */
1056 IStorage **ppstg) /* [out] */
1058 StorageImpl* const This=(StorageImpl*)iface;
1060 IEnumSTATSTGImpl *propertyEnumeration;
1061 StgProperty currentProperty;
1062 StgProperty newProperty;
1063 ULONG foundPropertyIndex;
1064 ULONG newPropertyIndex;
1065 HRESULT hr;
1067 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1068 iface, debugstr_w(pwcsName), grfMode,
1069 reserved1, reserved2, ppstg);
1072 * Validate parameters
1074 if (ppstg == 0)
1075 return STG_E_INVALIDPOINTER;
1077 if (pwcsName == 0)
1078 return STG_E_INVALIDNAME;
1081 * Initialize the out parameter
1083 *ppstg = NULL;
1086 * Validate the STGM flags
1088 if ( FAILED( validateSTGM(grfMode) ) ||
1089 (grfMode & STGM_DELETEONRELEASE) )
1091 WARN("bad grfMode: 0x%lx\n", grfMode);
1092 return STG_E_INVALIDFLAG;
1096 * Check that we're compatible with the parent's storage mode
1098 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1100 WARN("access denied\n");
1101 return STG_E_ACCESSDENIED;
1105 * Create a property enumeration and search the properties
1107 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1108 This->base.rootPropertySetIndex);
1110 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1111 pwcsName,
1112 &currentProperty);
1113 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1115 if (foundPropertyIndex != PROPERTY_NULL)
1118 * An element with this name already exists
1120 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1121 IStorage_DestroyElement(iface, pwcsName);
1122 else
1124 WARN("file already exists\n");
1125 return STG_E_FILEALREADYEXISTS;
1128 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1130 WARN("read-only storage\n");
1131 return STG_E_ACCESSDENIED;
1135 * memset the empty property
1137 memset(&newProperty, 0, sizeof(StgProperty));
1139 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1141 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1143 FIXME("name too long\n");
1144 return STG_E_INVALIDNAME;
1147 strcpyW(newProperty.name, pwcsName);
1149 newProperty.propertyType = PROPTYPE_STORAGE;
1150 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1151 newProperty.size.u.LowPart = 0;
1152 newProperty.size.u.HighPart = 0;
1154 newProperty.previousProperty = PROPERTY_NULL;
1155 newProperty.nextProperty = PROPERTY_NULL;
1156 newProperty.dirProperty = PROPERTY_NULL;
1158 /* call CoFileTime to get the current time
1159 newProperty.timeStampS1
1160 newProperty.timeStampD1
1161 newProperty.timeStampS2
1162 newProperty.timeStampD2
1165 /* newStorageProperty.propertyUniqueID */
1168 * Obtain a free property in the property chain
1170 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1173 * Save the new property into the new property spot
1175 StorageImpl_WriteProperty(
1176 This->base.ancestorStorage,
1177 newPropertyIndex,
1178 &newProperty);
1181 * Find a spot in the property chain for our newly created property.
1183 updatePropertyChain(
1184 This,
1185 newPropertyIndex,
1186 newProperty);
1189 * Open it to get a pointer to return.
1191 hr = IStorage_OpenStorage(
1192 iface,
1193 (const OLECHAR*)pwcsName,
1195 grfMode,
1198 ppstg);
1200 if( (hr != S_OK) || (*ppstg == NULL))
1202 return hr;
1206 return S_OK;
1210 /***************************************************************************
1212 * Internal Method
1214 * Get a free property or create a new one.
1216 static ULONG getFreeProperty(
1217 StorageImpl *storage)
1219 ULONG currentPropertyIndex = 0;
1220 ULONG newPropertyIndex = PROPERTY_NULL;
1221 BOOL readSuccessful = TRUE;
1222 StgProperty currentProperty;
1227 * Start by reading the root property
1229 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1230 currentPropertyIndex,
1231 &currentProperty);
1232 if (readSuccessful)
1234 if (currentProperty.sizeOfNameString == 0)
1237 * The property existis and is available, we found it.
1239 newPropertyIndex = currentPropertyIndex;
1242 else
1245 * We exhausted the property list, we will create more space below
1247 newPropertyIndex = currentPropertyIndex;
1249 currentPropertyIndex++;
1251 } while (newPropertyIndex == PROPERTY_NULL);
1254 * grow the property chain
1256 if (! readSuccessful)
1258 StgProperty emptyProperty;
1259 ULARGE_INTEGER newSize;
1260 ULONG propertyIndex;
1261 ULONG lastProperty = 0;
1262 ULONG blockCount = 0;
1265 * obtain the new count of property blocks
1267 blockCount = BlockChainStream_GetCount(
1268 storage->base.ancestorStorage->rootBlockChain)+1;
1271 * initialize the size used by the property stream
1273 newSize.u.HighPart = 0;
1274 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1277 * add a property block to the property chain
1279 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1282 * memset the empty property in order to initialize the unused newly
1283 * created property
1285 memset(&emptyProperty, 0, sizeof(StgProperty));
1288 * initialize them
1290 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1292 for(
1293 propertyIndex = newPropertyIndex;
1294 propertyIndex < lastProperty;
1295 propertyIndex++)
1297 StorageImpl_WriteProperty(
1298 storage->base.ancestorStorage,
1299 propertyIndex,
1300 &emptyProperty);
1304 return newPropertyIndex;
1307 /****************************************************************************
1309 * Internal Method
1311 * Case insensitive comparaison of StgProperty.name by first considering
1312 * their size.
1314 * Returns <0 when newPrpoerty < currentProperty
1315 * >0 when newPrpoerty > currentProperty
1316 * 0 when newPrpoerty == currentProperty
1318 static LONG propertyNameCmp(
1319 const OLECHAR *newProperty,
1320 const OLECHAR *currentProperty)
1322 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1324 if (diff == 0)
1327 * We compare the string themselves only when they are of the same length
1329 diff = lstrcmpiW( newProperty, currentProperty);
1332 return diff;
1335 /****************************************************************************
1337 * Internal Method
1339 * Properly link this new element in the property chain.
1341 static void updatePropertyChain(
1342 StorageImpl *storage,
1343 ULONG newPropertyIndex,
1344 StgProperty newProperty)
1346 StgProperty currentProperty;
1349 * Read the root property
1351 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1352 storage->base.rootPropertySetIndex,
1353 &currentProperty);
1355 if (currentProperty.dirProperty != PROPERTY_NULL)
1358 * The root storage contains some element, therefore, start the research
1359 * for the appropriate location.
1361 BOOL found = 0;
1362 ULONG current, next, previous, currentPropertyId;
1365 * Keep the StgProperty sequence number of the storage first property
1367 currentPropertyId = currentProperty.dirProperty;
1370 * Read
1372 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1373 currentProperty.dirProperty,
1374 &currentProperty);
1376 previous = currentProperty.previousProperty;
1377 next = currentProperty.nextProperty;
1378 current = currentPropertyId;
1380 while (found == 0)
1382 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1384 if (diff < 0)
1386 if (previous != PROPERTY_NULL)
1388 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1389 previous,
1390 &currentProperty);
1391 current = previous;
1393 else
1395 currentProperty.previousProperty = newPropertyIndex;
1396 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1397 current,
1398 &currentProperty);
1399 found = 1;
1402 else if (diff > 0)
1404 if (next != PROPERTY_NULL)
1406 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1407 next,
1408 &currentProperty);
1409 current = next;
1411 else
1413 currentProperty.nextProperty = newPropertyIndex;
1414 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1415 current,
1416 &currentProperty);
1417 found = 1;
1420 else
1423 * Trying to insert an item with the same name in the
1424 * subtree structure.
1426 assert(FALSE);
1429 previous = currentProperty.previousProperty;
1430 next = currentProperty.nextProperty;
1433 else
1436 * The root storage is empty, link the new property to its dir property
1438 currentProperty.dirProperty = newPropertyIndex;
1439 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1440 storage->base.rootPropertySetIndex,
1441 &currentProperty);
1446 /*************************************************************************
1447 * CopyTo (IStorage)
1449 static HRESULT WINAPI StorageImpl_CopyTo(
1450 IStorage* iface,
1451 DWORD ciidExclude, /* [in] */
1452 const IID* rgiidExclude, /* [size_is][unique][in] */
1453 SNB snbExclude, /* [unique][in] */
1454 IStorage* pstgDest) /* [unique][in] */
1456 IEnumSTATSTG *elements = 0;
1457 STATSTG curElement, strStat;
1458 HRESULT hr;
1459 IStorage *pstgTmp, *pstgChild;
1460 IStream *pstrTmp, *pstrChild;
1462 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1463 FIXME("Exclude option not implemented\n");
1465 TRACE("(%p, %ld, %p, %p, %p)\n",
1466 iface, ciidExclude, rgiidExclude,
1467 snbExclude, pstgDest);
1470 * Perform a sanity check
1472 if ( pstgDest == 0 )
1473 return STG_E_INVALIDPOINTER;
1476 * Enumerate the elements
1478 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1480 if ( hr != S_OK )
1481 return hr;
1484 * set the class ID
1486 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1487 IStorage_SetClass( pstgDest, &curElement.clsid );
1492 * Obtain the next element
1494 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1496 if ( hr == S_FALSE )
1498 hr = S_OK; /* done, every element has been copied */
1499 break;
1502 if (curElement.type == STGTY_STORAGE)
1505 * open child source storage
1507 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1508 STGM_READ|STGM_SHARE_EXCLUSIVE,
1509 NULL, 0, &pstgChild );
1511 if (hr != S_OK)
1512 break;
1515 * Check if destination storage is not a child of the source
1516 * storage, which will cause an infinite loop
1518 if (pstgChild == pstgDest)
1520 IEnumSTATSTG_Release(elements);
1522 return STG_E_ACCESSDENIED;
1526 * create a new storage in destination storage
1528 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1529 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1530 0, 0,
1531 &pstgTmp );
1533 * if it already exist, don't create a new one use this one
1535 if (hr == STG_E_FILEALREADYEXISTS)
1537 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1538 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1539 NULL, 0, &pstgTmp );
1542 if (hr != S_OK)
1543 break;
1547 * do the copy recursively
1549 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1550 snbExclude, pstgTmp );
1552 IStorage_Release( pstgTmp );
1553 IStorage_Release( pstgChild );
1555 else if (curElement.type == STGTY_STREAM)
1558 * create a new stream in destination storage. If the stream already
1559 * exist, it will be deleted and a new one will be created.
1561 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1562 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1563 0, 0, &pstrTmp );
1565 if (hr != S_OK)
1566 break;
1569 * open child stream storage
1571 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1572 STGM_READ|STGM_SHARE_EXCLUSIVE,
1573 0, &pstrChild );
1575 if (hr != S_OK)
1576 break;
1579 * Get the size of the source stream
1581 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1584 * Set the size of the destination stream.
1586 IStream_SetSize(pstrTmp, strStat.cbSize);
1589 * do the copy
1591 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1592 NULL, NULL );
1594 IStream_Release( pstrTmp );
1595 IStream_Release( pstrChild );
1597 else
1599 WARN("unknown element type: %ld\n", curElement.type);
1602 } while (hr == S_OK);
1605 * Clean-up
1607 IEnumSTATSTG_Release(elements);
1609 return hr;
1612 /*************************************************************************
1613 * MoveElementTo (IStorage)
1615 static HRESULT WINAPI StorageImpl_MoveElementTo(
1616 IStorage* iface,
1617 const OLECHAR *pwcsName, /* [string][in] */
1618 IStorage *pstgDest, /* [unique][in] */
1619 const OLECHAR *pwcsNewName,/* [string][in] */
1620 DWORD grfFlags) /* [in] */
1622 FIXME("not implemented!\n");
1623 return E_NOTIMPL;
1626 /*************************************************************************
1627 * Commit (IStorage)
1629 * Ensures that any changes made to a storage object open in transacted mode
1630 * are reflected in the parent storage
1632 * NOTES
1633 * Wine doesn't implement transacted mode, which seems to be a basic
1634 * optimization, so we can ignore this stub for now.
1636 static HRESULT WINAPI StorageImpl_Commit(
1637 IStorage* iface,
1638 DWORD grfCommitFlags)/* [in] */
1640 FIXME("(%ld): stub!\n", grfCommitFlags);
1641 return S_OK;
1644 /*************************************************************************
1645 * Revert (IStorage)
1647 * Discard all changes that have been made since the last commit operation
1649 static HRESULT WINAPI StorageImpl_Revert(
1650 IStorage* iface)
1652 FIXME("not implemented!\n");
1653 return E_NOTIMPL;
1656 /*************************************************************************
1657 * DestroyElement (IStorage)
1659 * Strategy: This implementation is built this way for simplicity not for speed.
1660 * I always delete the topmost element of the enumeration and adjust
1661 * the deleted element pointer all the time. This takes longer to
1662 * do but allow to reinvoke DestroyElement whenever we encounter a
1663 * storage object. The optimisation resides in the usage of another
1664 * enumeration strategy that would give all the leaves of a storage
1665 * first. (postfix order)
1667 static HRESULT WINAPI StorageImpl_DestroyElement(
1668 IStorage* iface,
1669 const OLECHAR *pwcsName)/* [string][in] */
1671 StorageImpl* const This=(StorageImpl*)iface;
1673 IEnumSTATSTGImpl* propertyEnumeration;
1674 HRESULT hr = S_OK;
1675 BOOL res;
1676 StgProperty propertyToDelete;
1677 StgProperty parentProperty;
1678 ULONG foundPropertyIndexToDelete;
1679 ULONG typeOfRelation;
1680 ULONG parentPropertyId;
1682 TRACE("(%p, %s)\n",
1683 iface, debugstr_w(pwcsName));
1686 * Perform a sanity check on the parameters.
1688 if (pwcsName==NULL)
1689 return STG_E_INVALIDPOINTER;
1692 * Create a property enumeration to search the property with the given name
1694 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1695 This->base.ancestorStorage,
1696 This->base.rootPropertySetIndex);
1698 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1699 propertyEnumeration,
1700 pwcsName,
1701 &propertyToDelete);
1703 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1705 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1707 return STG_E_FILENOTFOUND;
1711 * Find the parent property of the property to delete (the one that
1712 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1713 * the parent is This. Otherwise, the parent is one of its sibling...
1717 * First, read This's StgProperty..
1719 res = StorageImpl_ReadProperty(
1720 This->base.ancestorStorage,
1721 This->base.rootPropertySetIndex,
1722 &parentProperty);
1724 assert(res);
1727 * Second, check to see if by any chance the actual storage (This) is not
1728 * the parent of the property to delete... We never know...
1730 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1733 * Set data as it would have been done in the else part...
1735 typeOfRelation = PROPERTY_RELATION_DIR;
1736 parentPropertyId = This->base.rootPropertySetIndex;
1738 else
1741 * Create a property enumeration to search the parent properties, and
1742 * delete it once done.
1744 IEnumSTATSTGImpl* propertyEnumeration2;
1746 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1747 This->base.ancestorStorage,
1748 This->base.rootPropertySetIndex);
1750 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1751 propertyEnumeration2,
1752 foundPropertyIndexToDelete,
1753 &parentProperty,
1754 &parentPropertyId);
1756 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1759 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1761 hr = deleteStorageProperty(
1762 This,
1763 foundPropertyIndexToDelete,
1764 propertyToDelete);
1766 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1768 hr = deleteStreamProperty(
1769 This,
1770 foundPropertyIndexToDelete,
1771 propertyToDelete);
1774 if (hr!=S_OK)
1775 return hr;
1778 * Adjust the property chain
1780 hr = adjustPropertyChain(
1781 This,
1782 propertyToDelete,
1783 parentProperty,
1784 parentPropertyId,
1785 typeOfRelation);
1787 return hr;
1791 /************************************************************************
1792 * StorageImpl_Stat (IStorage)
1794 * This method will retrieve information about this storage object.
1796 * See Windows documentation for more details on IStorage methods.
1798 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1799 STATSTG* pstatstg, /* [out] */
1800 DWORD grfStatFlag) /* [in] */
1802 StorageImpl* const This = (StorageImpl*)iface;
1803 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1805 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1807 CoTaskMemFree(pstatstg->pwcsName);
1808 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1809 strcpyW(pstatstg->pwcsName, This->pwcsName);
1812 return result;
1815 /******************************************************************************
1816 * Internal stream list handlers
1819 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1821 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1822 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1825 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1827 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1828 list_remove(&(strm->StrmListEntry));
1831 void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1833 struct list *cur, *cur2;
1834 StgStreamImpl *strm=NULL;
1836 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1837 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1838 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1839 strm->parentStorage = NULL;
1840 list_remove(cur);
1845 /*********************************************************************
1847 * Internal Method
1849 * Perform the deletion of a complete storage node
1852 static HRESULT deleteStorageProperty(
1853 StorageImpl *parentStorage,
1854 ULONG indexOfPropertyToDelete,
1855 StgProperty propertyToDelete)
1857 IEnumSTATSTG *elements = 0;
1858 IStorage *childStorage = 0;
1859 STATSTG currentElement;
1860 HRESULT hr;
1861 HRESULT destroyHr = S_OK;
1864 * Open the storage and enumerate it
1866 hr = StorageBaseImpl_OpenStorage(
1867 (IStorage*)parentStorage,
1868 propertyToDelete.name,
1870 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1873 &childStorage);
1875 if (hr != S_OK)
1877 return hr;
1881 * Enumerate the elements
1883 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1888 * Obtain the next element
1890 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1891 if (hr==S_OK)
1893 destroyHr = StorageImpl_DestroyElement(
1894 (IStorage*)childStorage,
1895 (OLECHAR*)currentElement.pwcsName);
1897 CoTaskMemFree(currentElement.pwcsName);
1901 * We need to Reset the enumeration every time because we delete elements
1902 * and the enumeration could be invalid
1904 IEnumSTATSTG_Reset(elements);
1906 } while ((hr == S_OK) && (destroyHr == S_OK));
1909 * Invalidate the property by zeroing its name member.
1911 propertyToDelete.sizeOfNameString = 0;
1913 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1914 indexOfPropertyToDelete,
1915 &propertyToDelete);
1917 IStorage_Release(childStorage);
1918 IEnumSTATSTG_Release(elements);
1920 return destroyHr;
1923 /*********************************************************************
1925 * Internal Method
1927 * Perform the deletion of a stream node
1930 static HRESULT deleteStreamProperty(
1931 StorageImpl *parentStorage,
1932 ULONG indexOfPropertyToDelete,
1933 StgProperty propertyToDelete)
1935 IStream *pis;
1936 HRESULT hr;
1937 ULARGE_INTEGER size;
1939 size.u.HighPart = 0;
1940 size.u.LowPart = 0;
1942 hr = StorageBaseImpl_OpenStream(
1943 (IStorage*)parentStorage,
1944 (OLECHAR*)propertyToDelete.name,
1945 NULL,
1946 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1948 &pis);
1950 if (hr!=S_OK)
1952 return(hr);
1956 * Zap the stream
1958 hr = IStream_SetSize(pis, size);
1960 if(hr != S_OK)
1962 return hr;
1966 * Release the stream object.
1968 IStream_Release(pis);
1971 * Invalidate the property by zeroing its name member.
1973 propertyToDelete.sizeOfNameString = 0;
1976 * Here we should re-read the property so we get the updated pointer
1977 * but since we are here to zap it, I don't do it...
1979 StorageImpl_WriteProperty(
1980 parentStorage->base.ancestorStorage,
1981 indexOfPropertyToDelete,
1982 &propertyToDelete);
1984 return S_OK;
1987 /*********************************************************************
1989 * Internal Method
1991 * Finds a placeholder for the StgProperty within the Storage
1994 static HRESULT findPlaceholder(
1995 StorageImpl *storage,
1996 ULONG propertyIndexToStore,
1997 ULONG storePropertyIndex,
1998 INT typeOfRelation)
2000 StgProperty storeProperty;
2001 HRESULT hr = S_OK;
2002 BOOL res = TRUE;
2005 * Read the storage property
2007 res = StorageImpl_ReadProperty(
2008 storage->base.ancestorStorage,
2009 storePropertyIndex,
2010 &storeProperty);
2012 if(! res)
2014 return E_FAIL;
2017 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2019 if (storeProperty.previousProperty != PROPERTY_NULL)
2021 return findPlaceholder(
2022 storage,
2023 propertyIndexToStore,
2024 storeProperty.previousProperty,
2025 typeOfRelation);
2027 else
2029 storeProperty.previousProperty = propertyIndexToStore;
2032 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2034 if (storeProperty.nextProperty != PROPERTY_NULL)
2036 return findPlaceholder(
2037 storage,
2038 propertyIndexToStore,
2039 storeProperty.nextProperty,
2040 typeOfRelation);
2042 else
2044 storeProperty.nextProperty = propertyIndexToStore;
2047 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2049 if (storeProperty.dirProperty != PROPERTY_NULL)
2051 return findPlaceholder(
2052 storage,
2053 propertyIndexToStore,
2054 storeProperty.dirProperty,
2055 typeOfRelation);
2057 else
2059 storeProperty.dirProperty = propertyIndexToStore;
2063 hr = StorageImpl_WriteProperty(
2064 storage->base.ancestorStorage,
2065 storePropertyIndex,
2066 &storeProperty);
2068 if(! hr)
2070 return E_FAIL;
2073 return S_OK;
2076 /*************************************************************************
2078 * Internal Method
2080 * This method takes the previous and the next property link of a property
2081 * to be deleted and find them a place in the Storage.
2083 static HRESULT adjustPropertyChain(
2084 StorageImpl *This,
2085 StgProperty propertyToDelete,
2086 StgProperty parentProperty,
2087 ULONG parentPropertyId,
2088 INT typeOfRelation)
2090 ULONG newLinkProperty = PROPERTY_NULL;
2091 BOOL needToFindAPlaceholder = FALSE;
2092 ULONG storeNode = PROPERTY_NULL;
2093 ULONG toStoreNode = PROPERTY_NULL;
2094 INT relationType = 0;
2095 HRESULT hr = S_OK;
2096 BOOL res = TRUE;
2098 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2100 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2103 * Set the parent previous to the property to delete previous
2105 newLinkProperty = propertyToDelete.previousProperty;
2107 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2110 * We also need to find a storage for the other link, setup variables
2111 * to do this at the end...
2113 needToFindAPlaceholder = TRUE;
2114 storeNode = propertyToDelete.previousProperty;
2115 toStoreNode = propertyToDelete.nextProperty;
2116 relationType = PROPERTY_RELATION_NEXT;
2119 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2122 * Set the parent previous to the property to delete next
2124 newLinkProperty = propertyToDelete.nextProperty;
2128 * Link it for real...
2130 parentProperty.previousProperty = newLinkProperty;
2133 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2135 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2138 * Set the parent next to the property to delete next previous
2140 newLinkProperty = propertyToDelete.previousProperty;
2142 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2145 * We also need to find a storage for the other link, setup variables
2146 * to do this at the end...
2148 needToFindAPlaceholder = TRUE;
2149 storeNode = propertyToDelete.previousProperty;
2150 toStoreNode = propertyToDelete.nextProperty;
2151 relationType = PROPERTY_RELATION_NEXT;
2154 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2157 * Set the parent next to the property to delete next
2159 newLinkProperty = propertyToDelete.nextProperty;
2163 * Link it for real...
2165 parentProperty.nextProperty = newLinkProperty;
2167 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2169 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2172 * Set the parent dir to the property to delete previous
2174 newLinkProperty = propertyToDelete.previousProperty;
2176 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2179 * We also need to find a storage for the other link, setup variables
2180 * to do this at the end...
2182 needToFindAPlaceholder = TRUE;
2183 storeNode = propertyToDelete.previousProperty;
2184 toStoreNode = propertyToDelete.nextProperty;
2185 relationType = PROPERTY_RELATION_NEXT;
2188 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2191 * Set the parent dir to the property to delete next
2193 newLinkProperty = propertyToDelete.nextProperty;
2197 * Link it for real...
2199 parentProperty.dirProperty = newLinkProperty;
2203 * Write back the parent property
2205 res = StorageImpl_WriteProperty(
2206 This->base.ancestorStorage,
2207 parentPropertyId,
2208 &parentProperty);
2209 if(! res)
2211 return E_FAIL;
2215 * If a placeholder is required for the other link, then, find one and
2216 * get out of here...
2218 if (needToFindAPlaceholder)
2220 hr = findPlaceholder(
2221 This,
2222 toStoreNode,
2223 storeNode,
2224 relationType);
2227 return hr;
2231 /******************************************************************************
2232 * SetElementTimes (IStorage)
2234 static HRESULT WINAPI StorageImpl_SetElementTimes(
2235 IStorage* iface,
2236 const OLECHAR *pwcsName,/* [string][in] */
2237 const FILETIME *pctime, /* [in] */
2238 const FILETIME *patime, /* [in] */
2239 const FILETIME *pmtime) /* [in] */
2241 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2242 return S_OK;
2245 /******************************************************************************
2246 * SetStateBits (IStorage)
2248 static HRESULT WINAPI StorageImpl_SetStateBits(
2249 IStorage* iface,
2250 DWORD grfStateBits,/* [in] */
2251 DWORD grfMask) /* [in] */
2253 FIXME("not implemented!\n");
2254 return E_NOTIMPL;
2258 * Virtual function table for the IStorage32Impl class.
2260 static const IStorageVtbl Storage32Impl_Vtbl =
2262 StorageBaseImpl_QueryInterface,
2263 StorageBaseImpl_AddRef,
2264 StorageBaseImpl_Release,
2265 StorageBaseImpl_CreateStream,
2266 StorageBaseImpl_OpenStream,
2267 StorageImpl_CreateStorage,
2268 StorageBaseImpl_OpenStorage,
2269 StorageImpl_CopyTo,
2270 StorageImpl_MoveElementTo,
2271 StorageImpl_Commit,
2272 StorageImpl_Revert,
2273 StorageBaseImpl_EnumElements,
2274 StorageImpl_DestroyElement,
2275 StorageBaseImpl_RenameElement,
2276 StorageImpl_SetElementTimes,
2277 StorageBaseImpl_SetClass,
2278 StorageImpl_SetStateBits,
2279 StorageImpl_Stat
2282 HRESULT StorageImpl_Construct(
2283 StorageImpl* This,
2284 HANDLE hFile,
2285 LPCOLESTR pwcsName,
2286 ILockBytes* pLkbyt,
2287 DWORD openFlags,
2288 BOOL fileBased,
2289 BOOL fileCreate)
2291 HRESULT hr = S_OK;
2292 StgProperty currentProperty;
2293 BOOL readSuccessful;
2294 ULONG currentPropertyIndex;
2296 if ( FAILED( validateSTGM(openFlags) ))
2297 return STG_E_INVALIDFLAG;
2299 memset(This, 0, sizeof(StorageImpl));
2302 * Initialize stream list
2305 list_init(&This->base.strmHead);
2308 * Initialize the virtual function table.
2310 This->base.lpVtbl = &Storage32Impl_Vtbl;
2311 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2312 This->base.v_destructor = &StorageImpl_Destroy;
2313 This->base.openFlags = (openFlags & ~STGM_CREATE);
2316 * This is the top-level storage so initialize the ancestor pointer
2317 * to this.
2319 This->base.ancestorStorage = This;
2322 * Initialize the physical support of the storage.
2324 This->hFile = hFile;
2327 * Store copy of file path.
2329 if(pwcsName) {
2330 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2331 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2332 if (!This->pwcsName)
2333 return STG_E_INSUFFICIENTMEMORY;
2334 strcpyW(This->pwcsName, pwcsName);
2338 * Initialize the big block cache.
2340 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2341 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2342 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2343 pLkbyt,
2344 openFlags,
2345 This->bigBlockSize,
2346 fileBased);
2348 if (This->bigBlockFile == 0)
2349 return E_FAIL;
2351 if (fileCreate)
2353 ULARGE_INTEGER size;
2354 BYTE* bigBlockBuffer;
2357 * Initialize all header variables:
2358 * - The big block depot consists of one block and it is at block 0
2359 * - The properties start at block 1
2360 * - There is no small block depot
2362 memset( This->bigBlockDepotStart,
2363 BLOCK_UNUSED,
2364 sizeof(This->bigBlockDepotStart));
2366 This->bigBlockDepotCount = 1;
2367 This->bigBlockDepotStart[0] = 0;
2368 This->rootStartBlock = 1;
2369 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2370 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2371 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2372 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2373 This->extBigBlockDepotCount = 0;
2375 StorageImpl_SaveFileHeader(This);
2378 * Add one block for the big block depot and one block for the properties
2380 size.u.HighPart = 0;
2381 size.u.LowPart = This->bigBlockSize * 3;
2382 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2385 * Initialize the big block depot
2387 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2388 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2389 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2390 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2391 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2393 else
2396 * Load the header for the file.
2398 hr = StorageImpl_LoadFileHeader(This);
2400 if (FAILED(hr))
2402 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2404 return hr;
2409 * There is no block depot cached yet.
2411 This->indexBlockDepotCached = 0xFFFFFFFF;
2414 * Start searching for free blocks with block 0.
2416 This->prevFreeBlock = 0;
2419 * Create the block chain abstractions.
2421 if(!(This->rootBlockChain =
2422 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2423 return STG_E_READFAULT;
2425 if(!(This->smallBlockDepotChain =
2426 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2427 PROPERTY_NULL)))
2428 return STG_E_READFAULT;
2431 * Write the root property (memory only)
2433 if (fileCreate)
2435 StgProperty rootProp;
2437 * Initialize the property chain
2439 memset(&rootProp, 0, sizeof(rootProp));
2440 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2441 sizeof(rootProp.name)/sizeof(WCHAR) );
2442 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2443 rootProp.propertyType = PROPTYPE_ROOT;
2444 rootProp.previousProperty = PROPERTY_NULL;
2445 rootProp.nextProperty = PROPERTY_NULL;
2446 rootProp.dirProperty = PROPERTY_NULL;
2447 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2448 rootProp.size.u.HighPart = 0;
2449 rootProp.size.u.LowPart = 0;
2451 StorageImpl_WriteProperty(This, 0, &rootProp);
2455 * Find the ID of the root in the property sets.
2457 currentPropertyIndex = 0;
2461 readSuccessful = StorageImpl_ReadProperty(
2462 This,
2463 currentPropertyIndex,
2464 &currentProperty);
2466 if (readSuccessful)
2468 if ( (currentProperty.sizeOfNameString != 0 ) &&
2469 (currentProperty.propertyType == PROPTYPE_ROOT) )
2471 This->base.rootPropertySetIndex = currentPropertyIndex;
2475 currentPropertyIndex++;
2477 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2479 if (!readSuccessful)
2481 /* TODO CLEANUP */
2482 return STG_E_READFAULT;
2486 * Create the block chain abstraction for the small block root chain.
2488 if(!(This->smallBlockRootChain =
2489 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2490 return STG_E_READFAULT;
2492 return hr;
2495 void StorageImpl_Destroy(StorageBaseImpl* iface)
2497 StorageImpl *This = (StorageImpl*) iface;
2498 TRACE("(%p)\n", This);
2500 StorageBaseImpl_DeleteAll(&This->base);
2502 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2504 BlockChainStream_Destroy(This->smallBlockRootChain);
2505 BlockChainStream_Destroy(This->rootBlockChain);
2506 BlockChainStream_Destroy(This->smallBlockDepotChain);
2508 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2509 HeapFree(GetProcessHeap(), 0, This);
2512 /******************************************************************************
2513 * Storage32Impl_GetNextFreeBigBlock
2515 * Returns the index of the next free big block.
2516 * If the big block depot is filled, this method will enlarge it.
2519 ULONG StorageImpl_GetNextFreeBigBlock(
2520 StorageImpl* This)
2522 ULONG depotBlockIndexPos;
2523 void *depotBuffer;
2524 ULONG depotBlockOffset;
2525 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2526 ULONG nextBlockIndex = BLOCK_SPECIAL;
2527 int depotIndex = 0;
2528 ULONG freeBlock = BLOCK_UNUSED;
2530 depotIndex = This->prevFreeBlock / blocksPerDepot;
2531 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2534 * Scan the entire big block depot until we find a block marked free
2536 while (nextBlockIndex != BLOCK_UNUSED)
2538 if (depotIndex < COUNT_BBDEPOTINHEADER)
2540 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2543 * Grow the primary depot.
2545 if (depotBlockIndexPos == BLOCK_UNUSED)
2547 depotBlockIndexPos = depotIndex*blocksPerDepot;
2550 * Add a block depot.
2552 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2553 This->bigBlockDepotCount++;
2554 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2557 * Flag it as a block depot.
2559 StorageImpl_SetNextBlockInChain(This,
2560 depotBlockIndexPos,
2561 BLOCK_SPECIAL);
2563 /* Save new header information.
2565 StorageImpl_SaveFileHeader(This);
2568 else
2570 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2572 if (depotBlockIndexPos == BLOCK_UNUSED)
2575 * Grow the extended depot.
2577 ULONG extIndex = BLOCK_UNUSED;
2578 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2579 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2581 if (extBlockOffset == 0)
2583 /* We need an extended block.
2585 extIndex = Storage32Impl_AddExtBlockDepot(This);
2586 This->extBigBlockDepotCount++;
2587 depotBlockIndexPos = extIndex + 1;
2589 else
2590 depotBlockIndexPos = depotIndex * blocksPerDepot;
2593 * Add a block depot and mark it in the extended block.
2595 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2596 This->bigBlockDepotCount++;
2597 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2599 /* Flag the block depot.
2601 StorageImpl_SetNextBlockInChain(This,
2602 depotBlockIndexPos,
2603 BLOCK_SPECIAL);
2605 /* If necessary, flag the extended depot block.
2607 if (extIndex != BLOCK_UNUSED)
2608 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2610 /* Save header information.
2612 StorageImpl_SaveFileHeader(This);
2616 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2618 if (depotBuffer != 0)
2620 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2621 ( nextBlockIndex != BLOCK_UNUSED))
2623 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2625 if (nextBlockIndex == BLOCK_UNUSED)
2627 freeBlock = (depotIndex * blocksPerDepot) +
2628 (depotBlockOffset/sizeof(ULONG));
2631 depotBlockOffset += sizeof(ULONG);
2634 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2637 depotIndex++;
2638 depotBlockOffset = 0;
2641 This->prevFreeBlock = freeBlock;
2643 return freeBlock;
2646 /******************************************************************************
2647 * Storage32Impl_AddBlockDepot
2649 * This will create a depot block, essentially it is a block initialized
2650 * to BLOCK_UNUSEDs.
2652 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2654 BYTE* blockBuffer;
2656 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2659 * Initialize blocks as free
2661 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2663 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2666 /******************************************************************************
2667 * Storage32Impl_GetExtDepotBlock
2669 * Returns the index of the block that corresponds to the specified depot
2670 * index. This method is only for depot indexes equal or greater than
2671 * COUNT_BBDEPOTINHEADER.
2673 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2675 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2676 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2677 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2678 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2679 ULONG blockIndex = BLOCK_UNUSED;
2680 ULONG extBlockIndex = This->extBigBlockDepotStart;
2682 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2684 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2685 return BLOCK_UNUSED;
2687 while (extBlockCount > 0)
2689 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2690 extBlockCount--;
2693 if (extBlockIndex != BLOCK_UNUSED)
2695 BYTE* depotBuffer;
2697 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2699 if (depotBuffer != 0)
2701 StorageUtl_ReadDWord(depotBuffer,
2702 extBlockOffset * sizeof(ULONG),
2703 &blockIndex);
2705 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2709 return blockIndex;
2712 /******************************************************************************
2713 * Storage32Impl_SetExtDepotBlock
2715 * Associates the specified block index to the specified depot index.
2716 * This method is only for depot indexes equal or greater than
2717 * COUNT_BBDEPOTINHEADER.
2719 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2720 ULONG depotIndex,
2721 ULONG blockIndex)
2723 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2724 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2725 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2726 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2727 ULONG extBlockIndex = This->extBigBlockDepotStart;
2729 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2731 while (extBlockCount > 0)
2733 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2734 extBlockCount--;
2737 if (extBlockIndex != BLOCK_UNUSED)
2739 BYTE* depotBuffer;
2741 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2743 if (depotBuffer != 0)
2745 StorageUtl_WriteDWord(depotBuffer,
2746 extBlockOffset * sizeof(ULONG),
2747 blockIndex);
2749 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2754 /******************************************************************************
2755 * Storage32Impl_AddExtBlockDepot
2757 * Creates an extended depot block.
2759 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2761 ULONG numExtBlocks = This->extBigBlockDepotCount;
2762 ULONG nextExtBlock = This->extBigBlockDepotStart;
2763 BYTE* depotBuffer = NULL;
2764 ULONG index = BLOCK_UNUSED;
2765 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2766 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2767 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2769 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2770 blocksPerDepotBlock;
2772 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2775 * The first extended block.
2777 This->extBigBlockDepotStart = index;
2779 else
2781 unsigned int i;
2783 * Follow the chain to the last one.
2785 for (i = 0; i < (numExtBlocks - 1); i++)
2787 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2791 * Add the new extended block to the chain.
2793 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2794 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2795 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2799 * Initialize this block.
2801 depotBuffer = StorageImpl_GetBigBlock(This, index);
2802 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2803 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2805 return index;
2808 /******************************************************************************
2809 * Storage32Impl_FreeBigBlock
2811 * This method will flag the specified block as free in the big block depot.
2813 void StorageImpl_FreeBigBlock(
2814 StorageImpl* This,
2815 ULONG blockIndex)
2817 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2819 if (blockIndex < This->prevFreeBlock)
2820 This->prevFreeBlock = blockIndex;
2823 /************************************************************************
2824 * Storage32Impl_GetNextBlockInChain
2826 * This method will retrieve the block index of the next big block in
2827 * in the chain.
2829 * Params: This - Pointer to the Storage object.
2830 * blockIndex - Index of the block to retrieve the chain
2831 * for.
2832 * nextBlockIndex - receives the return value.
2834 * Returns: This method returns the index of the next block in the chain.
2835 * It will return the constants:
2836 * BLOCK_SPECIAL - If the block given was not part of a
2837 * chain.
2838 * BLOCK_END_OF_CHAIN - If the block given was the last in
2839 * a chain.
2840 * BLOCK_UNUSED - If the block given was not past of a chain
2841 * and is available.
2842 * BLOCK_EXTBBDEPOT - This block is part of the extended
2843 * big block depot.
2845 * See Windows documentation for more details on IStorage methods.
2847 HRESULT StorageImpl_GetNextBlockInChain(
2848 StorageImpl* This,
2849 ULONG blockIndex,
2850 ULONG* nextBlockIndex)
2852 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2853 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2854 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2855 void* depotBuffer;
2856 ULONG depotBlockIndexPos;
2857 int index;
2859 *nextBlockIndex = BLOCK_SPECIAL;
2861 if(depotBlockCount >= This->bigBlockDepotCount)
2863 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2864 This->bigBlockDepotCount);
2865 return STG_E_READFAULT;
2869 * Cache the currently accessed depot block.
2871 if (depotBlockCount != This->indexBlockDepotCached)
2873 This->indexBlockDepotCached = depotBlockCount;
2875 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2877 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2879 else
2882 * We have to look in the extended depot.
2884 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2887 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2889 if (!depotBuffer)
2890 return STG_E_READFAULT;
2892 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2894 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2895 This->blockDepotCached[index] = *nextBlockIndex;
2897 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2900 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2902 return S_OK;
2905 /******************************************************************************
2906 * Storage32Impl_GetNextExtendedBlock
2908 * Given an extended block this method will return the next extended block.
2910 * NOTES:
2911 * The last ULONG of an extended block is the block index of the next
2912 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2913 * depot.
2915 * Return values:
2916 * - The index of the next extended block
2917 * - BLOCK_UNUSED: there is no next extended block.
2918 * - Any other return values denotes failure.
2920 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2922 ULONG nextBlockIndex = BLOCK_SPECIAL;
2923 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2924 void* depotBuffer;
2926 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2928 if (depotBuffer!=0)
2930 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2932 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2935 return nextBlockIndex;
2938 /******************************************************************************
2939 * Storage32Impl_SetNextBlockInChain
2941 * This method will write the index of the specified block's next block
2942 * in the big block depot.
2944 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2945 * do the following
2947 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2948 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2949 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2952 void StorageImpl_SetNextBlockInChain(
2953 StorageImpl* This,
2954 ULONG blockIndex,
2955 ULONG nextBlock)
2957 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2958 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2959 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2960 ULONG depotBlockIndexPos;
2961 void* depotBuffer;
2963 assert(depotBlockCount < This->bigBlockDepotCount);
2964 assert(blockIndex != nextBlock);
2966 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2968 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2970 else
2973 * We have to look in the extended depot.
2975 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2978 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2980 if (depotBuffer!=0)
2982 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2983 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2987 * Update the cached block depot, if necessary.
2989 if (depotBlockCount == This->indexBlockDepotCached)
2991 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2995 /******************************************************************************
2996 * Storage32Impl_LoadFileHeader
2998 * This method will read in the file header, i.e. big block index -1.
3000 HRESULT StorageImpl_LoadFileHeader(
3001 StorageImpl* This)
3003 HRESULT hr = STG_E_FILENOTFOUND;
3004 void* headerBigBlock = NULL;
3005 int index;
3007 TRACE("\n");
3009 * Get a pointer to the big block of data containing the header.
3011 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
3014 * Extract the information from the header.
3016 if (headerBigBlock!=0)
3019 * Check for the "magic number" signature and return an error if it is not
3020 * found.
3022 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3024 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3025 return STG_E_OLDFORMAT;
3028 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3030 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3031 return STG_E_INVALIDHEADER;
3034 StorageUtl_ReadWord(
3035 headerBigBlock,
3036 OFFSET_BIGBLOCKSIZEBITS,
3037 &This->bigBlockSizeBits);
3039 StorageUtl_ReadWord(
3040 headerBigBlock,
3041 OFFSET_SMALLBLOCKSIZEBITS,
3042 &This->smallBlockSizeBits);
3044 StorageUtl_ReadDWord(
3045 headerBigBlock,
3046 OFFSET_BBDEPOTCOUNT,
3047 &This->bigBlockDepotCount);
3049 StorageUtl_ReadDWord(
3050 headerBigBlock,
3051 OFFSET_ROOTSTARTBLOCK,
3052 &This->rootStartBlock);
3054 StorageUtl_ReadDWord(
3055 headerBigBlock,
3056 OFFSET_SBDEPOTSTART,
3057 &This->smallBlockDepotStart);
3059 StorageUtl_ReadDWord(
3060 headerBigBlock,
3061 OFFSET_EXTBBDEPOTSTART,
3062 &This->extBigBlockDepotStart);
3064 StorageUtl_ReadDWord(
3065 headerBigBlock,
3066 OFFSET_EXTBBDEPOTCOUNT,
3067 &This->extBigBlockDepotCount);
3069 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3071 StorageUtl_ReadDWord(
3072 headerBigBlock,
3073 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3074 &(This->bigBlockDepotStart[index]));
3078 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3080 if ((1 << 2) == 4)
3082 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3083 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3085 else
3087 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3088 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3092 * Right now, the code is making some assumptions about the size of the
3093 * blocks, just make sure they are what we're expecting.
3095 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3096 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3098 WARN("Broken OLE storage file\n");
3099 hr = STG_E_INVALIDHEADER;
3101 else
3102 hr = S_OK;
3105 * Release the block.
3107 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3110 return hr;
3113 /******************************************************************************
3114 * Storage32Impl_SaveFileHeader
3116 * This method will save to the file the header, i.e. big block -1.
3118 void StorageImpl_SaveFileHeader(
3119 StorageImpl* This)
3121 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3122 int index;
3123 BOOL success;
3126 * Get a pointer to the big block of data containing the header.
3128 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3131 * If the block read failed, the file is probably new.
3133 if (!success)
3136 * Initialize for all unknown fields.
3138 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3141 * Initialize the magic number.
3143 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3146 * And a bunch of things we don't know what they mean
3148 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3149 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3150 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3151 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3155 * Write the information to the header.
3157 StorageUtl_WriteWord(
3158 headerBigBlock,
3159 OFFSET_BIGBLOCKSIZEBITS,
3160 This->bigBlockSizeBits);
3162 StorageUtl_WriteWord(
3163 headerBigBlock,
3164 OFFSET_SMALLBLOCKSIZEBITS,
3165 This->smallBlockSizeBits);
3167 StorageUtl_WriteDWord(
3168 headerBigBlock,
3169 OFFSET_BBDEPOTCOUNT,
3170 This->bigBlockDepotCount);
3172 StorageUtl_WriteDWord(
3173 headerBigBlock,
3174 OFFSET_ROOTSTARTBLOCK,
3175 This->rootStartBlock);
3177 StorageUtl_WriteDWord(
3178 headerBigBlock,
3179 OFFSET_SBDEPOTSTART,
3180 This->smallBlockDepotStart);
3182 StorageUtl_WriteDWord(
3183 headerBigBlock,
3184 OFFSET_SBDEPOTCOUNT,
3185 This->smallBlockDepotChain ?
3186 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3188 StorageUtl_WriteDWord(
3189 headerBigBlock,
3190 OFFSET_EXTBBDEPOTSTART,
3191 This->extBigBlockDepotStart);
3193 StorageUtl_WriteDWord(
3194 headerBigBlock,
3195 OFFSET_EXTBBDEPOTCOUNT,
3196 This->extBigBlockDepotCount);
3198 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3200 StorageUtl_WriteDWord(
3201 headerBigBlock,
3202 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3203 (This->bigBlockDepotStart[index]));
3207 * Write the big block back to the file.
3209 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3212 /******************************************************************************
3213 * Storage32Impl_ReadProperty
3215 * This method will read the specified property from the property chain.
3217 BOOL StorageImpl_ReadProperty(
3218 StorageImpl* This,
3219 ULONG index,
3220 StgProperty* buffer)
3222 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3223 ULARGE_INTEGER offsetInPropSet;
3224 BOOL readSuccessful;
3225 ULONG bytesRead;
3227 offsetInPropSet.u.HighPart = 0;
3228 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3230 readSuccessful = BlockChainStream_ReadAt(
3231 This->rootBlockChain,
3232 offsetInPropSet,
3233 PROPSET_BLOCK_SIZE,
3234 currentProperty,
3235 &bytesRead);
3237 if (readSuccessful)
3239 /* replace the name of root entry (often "Root Entry") by the file name */
3240 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3241 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3243 memset(buffer->name, 0, sizeof(buffer->name));
3244 memcpy(
3245 buffer->name,
3246 propName,
3247 PROPERTY_NAME_BUFFER_LEN );
3248 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3250 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3252 StorageUtl_ReadWord(
3253 currentProperty,
3254 OFFSET_PS_NAMELENGTH,
3255 &buffer->sizeOfNameString);
3257 StorageUtl_ReadDWord(
3258 currentProperty,
3259 OFFSET_PS_PREVIOUSPROP,
3260 &buffer->previousProperty);
3262 StorageUtl_ReadDWord(
3263 currentProperty,
3264 OFFSET_PS_NEXTPROP,
3265 &buffer->nextProperty);
3267 StorageUtl_ReadDWord(
3268 currentProperty,
3269 OFFSET_PS_DIRPROP,
3270 &buffer->dirProperty);
3272 StorageUtl_ReadGUID(
3273 currentProperty,
3274 OFFSET_PS_GUID,
3275 &buffer->propertyUniqueID);
3277 StorageUtl_ReadDWord(
3278 currentProperty,
3279 OFFSET_PS_TSS1,
3280 &buffer->timeStampS1);
3282 StorageUtl_ReadDWord(
3283 currentProperty,
3284 OFFSET_PS_TSD1,
3285 &buffer->timeStampD1);
3287 StorageUtl_ReadDWord(
3288 currentProperty,
3289 OFFSET_PS_TSS2,
3290 &buffer->timeStampS2);
3292 StorageUtl_ReadDWord(
3293 currentProperty,
3294 OFFSET_PS_TSD2,
3295 &buffer->timeStampD2);
3297 StorageUtl_ReadDWord(
3298 currentProperty,
3299 OFFSET_PS_STARTBLOCK,
3300 &buffer->startingBlock);
3302 StorageUtl_ReadDWord(
3303 currentProperty,
3304 OFFSET_PS_SIZE,
3305 &buffer->size.u.LowPart);
3307 buffer->size.u.HighPart = 0;
3310 return readSuccessful;
3313 /*********************************************************************
3314 * Write the specified property into the property chain
3316 BOOL StorageImpl_WriteProperty(
3317 StorageImpl* This,
3318 ULONG index,
3319 StgProperty* buffer)
3321 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3322 ULARGE_INTEGER offsetInPropSet;
3323 BOOL writeSuccessful;
3324 ULONG bytesWritten;
3326 offsetInPropSet.u.HighPart = 0;
3327 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3329 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3331 memcpy(
3332 currentProperty + OFFSET_PS_NAME,
3333 buffer->name,
3334 PROPERTY_NAME_BUFFER_LEN );
3336 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3338 StorageUtl_WriteWord(
3339 currentProperty,
3340 OFFSET_PS_NAMELENGTH,
3341 buffer->sizeOfNameString);
3343 StorageUtl_WriteDWord(
3344 currentProperty,
3345 OFFSET_PS_PREVIOUSPROP,
3346 buffer->previousProperty);
3348 StorageUtl_WriteDWord(
3349 currentProperty,
3350 OFFSET_PS_NEXTPROP,
3351 buffer->nextProperty);
3353 StorageUtl_WriteDWord(
3354 currentProperty,
3355 OFFSET_PS_DIRPROP,
3356 buffer->dirProperty);
3358 StorageUtl_WriteGUID(
3359 currentProperty,
3360 OFFSET_PS_GUID,
3361 &buffer->propertyUniqueID);
3363 StorageUtl_WriteDWord(
3364 currentProperty,
3365 OFFSET_PS_TSS1,
3366 buffer->timeStampS1);
3368 StorageUtl_WriteDWord(
3369 currentProperty,
3370 OFFSET_PS_TSD1,
3371 buffer->timeStampD1);
3373 StorageUtl_WriteDWord(
3374 currentProperty,
3375 OFFSET_PS_TSS2,
3376 buffer->timeStampS2);
3378 StorageUtl_WriteDWord(
3379 currentProperty,
3380 OFFSET_PS_TSD2,
3381 buffer->timeStampD2);
3383 StorageUtl_WriteDWord(
3384 currentProperty,
3385 OFFSET_PS_STARTBLOCK,
3386 buffer->startingBlock);
3388 StorageUtl_WriteDWord(
3389 currentProperty,
3390 OFFSET_PS_SIZE,
3391 buffer->size.u.LowPart);
3393 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3394 offsetInPropSet,
3395 PROPSET_BLOCK_SIZE,
3396 currentProperty,
3397 &bytesWritten);
3398 return writeSuccessful;
3401 BOOL StorageImpl_ReadBigBlock(
3402 StorageImpl* This,
3403 ULONG blockIndex,
3404 void* buffer)
3406 void* bigBlockBuffer;
3408 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3410 if (bigBlockBuffer!=0)
3412 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3414 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3416 return TRUE;
3419 return FALSE;
3422 BOOL StorageImpl_WriteBigBlock(
3423 StorageImpl* This,
3424 ULONG blockIndex,
3425 void* buffer)
3427 void* bigBlockBuffer;
3429 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3431 if (bigBlockBuffer!=0)
3433 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3435 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3437 return TRUE;
3440 return FALSE;
3443 void* StorageImpl_GetROBigBlock(
3444 StorageImpl* This,
3445 ULONG blockIndex)
3447 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3450 void* StorageImpl_GetBigBlock(
3451 StorageImpl* This,
3452 ULONG blockIndex)
3454 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3457 void StorageImpl_ReleaseBigBlock(
3458 StorageImpl* This,
3459 void* pBigBlock)
3461 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3464 /******************************************************************************
3465 * Storage32Impl_SmallBlocksToBigBlocks
3467 * This method will convert a small block chain to a big block chain.
3468 * The small block chain will be destroyed.
3470 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3471 StorageImpl* This,
3472 SmallBlockChainStream** ppsbChain)
3474 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3475 ULARGE_INTEGER size, offset;
3476 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3477 ULONG propertyIndex;
3478 BOOL successWrite;
3479 HRESULT successRead;
3480 StgProperty chainProperty;
3481 BYTE *buffer;
3482 BlockChainStream *bbTempChain = NULL;
3483 BlockChainStream *bigBlockChain = NULL;
3486 * Create a temporary big block chain that doesn't have
3487 * an associated property. This temporary chain will be
3488 * used to copy data from small blocks to big blocks.
3490 bbTempChain = BlockChainStream_Construct(This,
3491 &bbHeadOfChain,
3492 PROPERTY_NULL);
3493 if(!bbTempChain) return NULL;
3495 * Grow the big block chain.
3497 size = SmallBlockChainStream_GetSize(*ppsbChain);
3498 BlockChainStream_SetSize(bbTempChain, size);
3501 * Copy the contents of the small block chain to the big block chain
3502 * by small block size increments.
3504 offset.u.LowPart = 0;
3505 offset.u.HighPart = 0;
3506 cbTotalRead = 0;
3507 cbTotalWritten = 0;
3509 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3512 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3513 offset,
3514 DEF_SMALL_BLOCK_SIZE,
3515 buffer,
3516 &cbRead);
3517 if (FAILED(successRead))
3518 break;
3520 if (cbRead > 0)
3522 cbTotalRead += cbRead;
3524 successWrite = BlockChainStream_WriteAt(bbTempChain,
3525 offset,
3526 cbRead,
3527 buffer,
3528 &cbWritten);
3530 if (!successWrite)
3531 break;
3533 cbTotalWritten += cbWritten;
3534 offset.u.LowPart += This->smallBlockSize;
3536 } while (cbRead > 0);
3537 HeapFree(GetProcessHeap(),0,buffer);
3539 assert(cbTotalRead == cbTotalWritten);
3542 * Destroy the small block chain.
3544 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3545 size.u.HighPart = 0;
3546 size.u.LowPart = 0;
3547 SmallBlockChainStream_SetSize(*ppsbChain, size);
3548 SmallBlockChainStream_Destroy(*ppsbChain);
3549 *ppsbChain = 0;
3552 * Change the property information. This chain is now a big block chain
3553 * and it doesn't reside in the small blocks chain anymore.
3555 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3557 chainProperty.startingBlock = bbHeadOfChain;
3559 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3562 * Destroy the temporary propertyless big block chain.
3563 * Create a new big block chain associated with this property.
3565 BlockChainStream_Destroy(bbTempChain);
3566 bigBlockChain = BlockChainStream_Construct(This,
3567 NULL,
3568 propertyIndex);
3570 return bigBlockChain;
3573 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3575 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3577 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3578 HeapFree(GetProcessHeap(), 0, This);
3581 /******************************************************************************
3583 ** Storage32InternalImpl_Commit
3585 ** The non-root storages cannot be opened in transacted mode thus this function
3586 ** does nothing.
3588 static HRESULT WINAPI StorageInternalImpl_Commit(
3589 IStorage* iface,
3590 DWORD grfCommitFlags) /* [in] */
3592 return S_OK;
3595 /******************************************************************************
3597 ** Storage32InternalImpl_Revert
3599 ** The non-root storages cannot be opened in transacted mode thus this function
3600 ** does nothing.
3602 static HRESULT WINAPI StorageInternalImpl_Revert(
3603 IStorage* iface)
3605 return S_OK;
3608 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3610 IStorage_Release((IStorage*)This->parentStorage);
3611 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3612 HeapFree(GetProcessHeap(), 0, This);
3615 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3616 IEnumSTATSTG* iface,
3617 REFIID riid,
3618 void** ppvObject)
3620 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3623 * Perform a sanity check on the parameters.
3625 if (ppvObject==0)
3626 return E_INVALIDARG;
3629 * Initialize the return parameter.
3631 *ppvObject = 0;
3634 * Compare the riid with the interface IDs implemented by this object.
3636 if (IsEqualGUID(&IID_IUnknown, riid) ||
3637 IsEqualGUID(&IID_IStorage, riid))
3639 *ppvObject = (IEnumSTATSTG*)This;
3640 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3641 return S_OK;
3644 return E_NOINTERFACE;
3647 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3648 IEnumSTATSTG* iface)
3650 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3651 return InterlockedIncrement(&This->ref);
3654 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3655 IEnumSTATSTG* iface)
3657 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3659 ULONG newRef;
3661 newRef = InterlockedDecrement(&This->ref);
3664 * If the reference count goes down to 0, perform suicide.
3666 if (newRef==0)
3668 IEnumSTATSTGImpl_Destroy(This);
3671 return newRef;
3674 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3675 IEnumSTATSTG* iface,
3676 ULONG celt,
3677 STATSTG* rgelt,
3678 ULONG* pceltFetched)
3680 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3682 StgProperty currentProperty;
3683 STATSTG* currentReturnStruct = rgelt;
3684 ULONG objectFetched = 0;
3685 ULONG currentSearchNode;
3688 * Perform a sanity check on the parameters.
3690 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3691 return E_INVALIDARG;
3694 * To avoid the special case, get another pointer to a ULONG value if
3695 * the caller didn't supply one.
3697 if (pceltFetched==0)
3698 pceltFetched = &objectFetched;
3701 * Start the iteration, we will iterate until we hit the end of the
3702 * linked list or until we hit the number of items to iterate through
3704 *pceltFetched = 0;
3707 * Start with the node at the top of the stack.
3709 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3711 while ( ( *pceltFetched < celt) &&
3712 ( currentSearchNode!=PROPERTY_NULL) )
3715 * Remove the top node from the stack
3717 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3720 * Read the property from the storage.
3722 StorageImpl_ReadProperty(This->parentStorage,
3723 currentSearchNode,
3724 &currentProperty);
3727 * Copy the information to the return buffer.
3729 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3730 &currentProperty,
3731 STATFLAG_DEFAULT);
3734 * Step to the next item in the iteration
3736 (*pceltFetched)++;
3737 currentReturnStruct++;
3740 * Push the next search node in the search stack.
3742 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3745 * continue the iteration.
3747 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3750 if (*pceltFetched == celt)
3751 return S_OK;
3753 return S_FALSE;
3757 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3758 IEnumSTATSTG* iface,
3759 ULONG celt)
3761 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3763 StgProperty currentProperty;
3764 ULONG objectFetched = 0;
3765 ULONG currentSearchNode;
3768 * Start with the node at the top of the stack.
3770 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3772 while ( (objectFetched < celt) &&
3773 (currentSearchNode!=PROPERTY_NULL) )
3776 * Remove the top node from the stack
3778 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3781 * Read the property from the storage.
3783 StorageImpl_ReadProperty(This->parentStorage,
3784 currentSearchNode,
3785 &currentProperty);
3788 * Step to the next item in the iteration
3790 objectFetched++;
3793 * Push the next search node in the search stack.
3795 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3798 * continue the iteration.
3800 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3803 if (objectFetched == celt)
3804 return S_OK;
3806 return S_FALSE;
3809 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3810 IEnumSTATSTG* iface)
3812 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3814 StgProperty rootProperty;
3815 BOOL readSuccessful;
3818 * Re-initialize the search stack to an empty stack
3820 This->stackSize = 0;
3823 * Read the root property from the storage.
3825 readSuccessful = StorageImpl_ReadProperty(
3826 This->parentStorage,
3827 This->firstPropertyNode,
3828 &rootProperty);
3830 if (readSuccessful)
3832 assert(rootProperty.sizeOfNameString!=0);
3835 * Push the search node in the search stack.
3837 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3840 return S_OK;
3843 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3844 IEnumSTATSTG* iface,
3845 IEnumSTATSTG** ppenum)
3847 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3849 IEnumSTATSTGImpl* newClone;
3852 * Perform a sanity check on the parameters.
3854 if (ppenum==0)
3855 return E_INVALIDARG;
3857 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3858 This->firstPropertyNode);
3862 * The new clone enumeration must point to the same current node as
3863 * the ole one.
3865 newClone->stackSize = This->stackSize ;
3866 newClone->stackMaxSize = This->stackMaxSize ;
3867 newClone->stackToVisit =
3868 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3870 memcpy(
3871 newClone->stackToVisit,
3872 This->stackToVisit,
3873 sizeof(ULONG) * newClone->stackSize);
3875 *ppenum = (IEnumSTATSTG*)newClone;
3878 * Don't forget to nail down a reference to the clone before
3879 * returning it.
3881 IEnumSTATSTGImpl_AddRef(*ppenum);
3883 return S_OK;
3886 INT IEnumSTATSTGImpl_FindParentProperty(
3887 IEnumSTATSTGImpl *This,
3888 ULONG childProperty,
3889 StgProperty *currentProperty,
3890 ULONG *thisNodeId)
3892 ULONG currentSearchNode;
3893 ULONG foundNode;
3896 * To avoid the special case, get another pointer to a ULONG value if
3897 * the caller didn't supply one.
3899 if (thisNodeId==0)
3900 thisNodeId = &foundNode;
3903 * Start with the node at the top of the stack.
3905 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3908 while (currentSearchNode!=PROPERTY_NULL)
3911 * Store the current node in the returned parameters
3913 *thisNodeId = currentSearchNode;
3916 * Remove the top node from the stack
3918 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3921 * Read the property from the storage.
3923 StorageImpl_ReadProperty(
3924 This->parentStorage,
3925 currentSearchNode,
3926 currentProperty);
3928 if (currentProperty->previousProperty == childProperty)
3929 return PROPERTY_RELATION_PREVIOUS;
3931 else if (currentProperty->nextProperty == childProperty)
3932 return PROPERTY_RELATION_NEXT;
3934 else if (currentProperty->dirProperty == childProperty)
3935 return PROPERTY_RELATION_DIR;
3938 * Push the next search node in the search stack.
3940 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3943 * continue the iteration.
3945 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3948 return PROPERTY_NULL;
3951 ULONG IEnumSTATSTGImpl_FindProperty(
3952 IEnumSTATSTGImpl* This,
3953 const OLECHAR* lpszPropName,
3954 StgProperty* currentProperty)
3956 ULONG currentSearchNode;
3959 * Start with the node at the top of the stack.
3961 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3963 while (currentSearchNode!=PROPERTY_NULL)
3966 * Remove the top node from the stack
3968 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3971 * Read the property from the storage.
3973 StorageImpl_ReadProperty(This->parentStorage,
3974 currentSearchNode,
3975 currentProperty);
3977 if ( propertyNameCmp(
3978 (const OLECHAR*)currentProperty->name,
3979 (const OLECHAR*)lpszPropName) == 0)
3980 return currentSearchNode;
3983 * Push the next search node in the search stack.
3985 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3988 * continue the iteration.
3990 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3993 return PROPERTY_NULL;
3996 void IEnumSTATSTGImpl_PushSearchNode(
3997 IEnumSTATSTGImpl* This,
3998 ULONG nodeToPush)
4000 StgProperty rootProperty;
4001 BOOL readSuccessful;
4004 * First, make sure we're not trying to push an unexisting node.
4006 if (nodeToPush==PROPERTY_NULL)
4007 return;
4010 * First push the node to the stack
4012 if (This->stackSize == This->stackMaxSize)
4014 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4016 This->stackToVisit = HeapReAlloc(
4017 GetProcessHeap(),
4019 This->stackToVisit,
4020 sizeof(ULONG) * This->stackMaxSize);
4023 This->stackToVisit[This->stackSize] = nodeToPush;
4024 This->stackSize++;
4027 * Read the root property from the storage.
4029 readSuccessful = StorageImpl_ReadProperty(
4030 This->parentStorage,
4031 nodeToPush,
4032 &rootProperty);
4034 if (readSuccessful)
4036 assert(rootProperty.sizeOfNameString!=0);
4039 * Push the previous search node in the search stack.
4041 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4045 ULONG IEnumSTATSTGImpl_PopSearchNode(
4046 IEnumSTATSTGImpl* This,
4047 BOOL remove)
4049 ULONG topNode;
4051 if (This->stackSize == 0)
4052 return PROPERTY_NULL;
4054 topNode = This->stackToVisit[This->stackSize-1];
4056 if (remove)
4057 This->stackSize--;
4059 return topNode;
4063 * Virtual function table for the IEnumSTATSTGImpl class.
4065 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4067 IEnumSTATSTGImpl_QueryInterface,
4068 IEnumSTATSTGImpl_AddRef,
4069 IEnumSTATSTGImpl_Release,
4070 IEnumSTATSTGImpl_Next,
4071 IEnumSTATSTGImpl_Skip,
4072 IEnumSTATSTGImpl_Reset,
4073 IEnumSTATSTGImpl_Clone
4076 /******************************************************************************
4077 ** IEnumSTATSTGImpl implementation
4080 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4081 StorageImpl* parentStorage,
4082 ULONG firstPropertyNode)
4084 IEnumSTATSTGImpl* newEnumeration;
4086 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4088 if (newEnumeration!=0)
4091 * Set-up the virtual function table and reference count.
4093 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4094 newEnumeration->ref = 0;
4097 * We want to nail-down the reference to the storage in case the
4098 * enumeration out-lives the storage in the client application.
4100 newEnumeration->parentStorage = parentStorage;
4101 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4103 newEnumeration->firstPropertyNode = firstPropertyNode;
4106 * Initialize the search stack
4108 newEnumeration->stackSize = 0;
4109 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4110 newEnumeration->stackToVisit =
4111 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4114 * Make sure the current node of the iterator is the first one.
4116 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4119 return newEnumeration;
4123 * Virtual function table for the Storage32InternalImpl class.
4125 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4127 StorageBaseImpl_QueryInterface,
4128 StorageBaseImpl_AddRef,
4129 StorageBaseImpl_Release,
4130 StorageBaseImpl_CreateStream,
4131 StorageBaseImpl_OpenStream,
4132 StorageImpl_CreateStorage,
4133 StorageBaseImpl_OpenStorage,
4134 StorageImpl_CopyTo,
4135 StorageImpl_MoveElementTo,
4136 StorageInternalImpl_Commit,
4137 StorageInternalImpl_Revert,
4138 StorageBaseImpl_EnumElements,
4139 StorageImpl_DestroyElement,
4140 StorageBaseImpl_RenameElement,
4141 StorageImpl_SetElementTimes,
4142 StorageBaseImpl_SetClass,
4143 StorageImpl_SetStateBits,
4144 StorageBaseImpl_Stat
4147 /******************************************************************************
4148 ** Storage32InternalImpl implementation
4151 StorageInternalImpl* StorageInternalImpl_Construct(
4152 StorageImpl* ancestorStorage,
4153 DWORD openFlags,
4154 ULONG rootPropertyIndex)
4156 StorageInternalImpl* newStorage;
4159 * Allocate space for the new storage object
4161 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4163 if (newStorage!=0)
4165 memset(newStorage, 0, sizeof(StorageInternalImpl));
4168 * Initialize the stream list
4171 list_init(&newStorage->base.strmHead);
4174 * Initialize the virtual function table.
4176 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4177 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4178 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4181 * Keep the ancestor storage pointer and nail a reference to it.
4183 newStorage->base.ancestorStorage = ancestorStorage;
4184 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4187 * Keep the index of the root property set for this storage,
4189 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4191 return newStorage;
4194 return 0;
4197 /******************************************************************************
4198 ** StorageUtl implementation
4201 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4203 WORD tmp;
4205 memcpy(&tmp, buffer+offset, sizeof(WORD));
4206 *value = le16toh(tmp);
4209 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4211 value = htole16(value);
4212 memcpy(buffer+offset, &value, sizeof(WORD));
4215 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4217 DWORD tmp;
4219 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4220 *value = le32toh(tmp);
4223 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4225 value = htole32(value);
4226 memcpy(buffer+offset, &value, sizeof(DWORD));
4229 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4230 ULARGE_INTEGER* value)
4232 #ifdef WORDS_BIGENDIAN
4233 ULARGE_INTEGER tmp;
4235 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4236 value->u.LowPart = htole32(tmp.u.HighPart);
4237 value->u.HighPart = htole32(tmp.u.LowPart);
4238 #else
4239 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4240 #endif
4243 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4244 const ULARGE_INTEGER *value)
4246 #ifdef WORDS_BIGENDIAN
4247 ULARGE_INTEGER tmp;
4249 tmp.u.LowPart = htole32(value->u.HighPart);
4250 tmp.u.HighPart = htole32(value->u.LowPart);
4251 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4252 #else
4253 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4254 #endif
4257 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4259 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4260 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4261 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4263 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4266 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4268 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4269 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4270 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4272 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4275 void StorageUtl_CopyPropertyToSTATSTG(
4276 STATSTG* destination,
4277 StgProperty* source,
4278 int statFlags)
4281 * The copy of the string occurs only when the flag is not set
4283 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4284 (source->name == NULL) ||
4285 (source->name[0] == 0) )
4287 destination->pwcsName = 0;
4289 else
4291 destination->pwcsName =
4292 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4294 strcpyW((LPWSTR)destination->pwcsName, source->name);
4297 switch (source->propertyType)
4299 case PROPTYPE_STORAGE:
4300 case PROPTYPE_ROOT:
4301 destination->type = STGTY_STORAGE;
4302 break;
4303 case PROPTYPE_STREAM:
4304 destination->type = STGTY_STREAM;
4305 break;
4306 default:
4307 destination->type = STGTY_STREAM;
4308 break;
4311 destination->cbSize = source->size;
4313 currentReturnStruct->mtime = {0}; TODO
4314 currentReturnStruct->ctime = {0};
4315 currentReturnStruct->atime = {0};
4317 destination->grfMode = 0;
4318 destination->grfLocksSupported = 0;
4319 destination->clsid = source->propertyUniqueID;
4320 destination->grfStateBits = 0;
4321 destination->reserved = 0;
4324 /******************************************************************************
4325 ** BlockChainStream implementation
4328 BlockChainStream* BlockChainStream_Construct(
4329 StorageImpl* parentStorage,
4330 ULONG* headOfStreamPlaceHolder,
4331 ULONG propertyIndex)
4333 BlockChainStream* newStream;
4334 ULONG blockIndex;
4336 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4338 newStream->parentStorage = parentStorage;
4339 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4340 newStream->ownerPropertyIndex = propertyIndex;
4341 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4342 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4343 newStream->numBlocks = 0;
4345 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4347 while (blockIndex != BLOCK_END_OF_CHAIN)
4349 newStream->numBlocks++;
4350 newStream->tailIndex = blockIndex;
4352 if(FAILED(StorageImpl_GetNextBlockInChain(
4353 parentStorage,
4354 blockIndex,
4355 &blockIndex)))
4357 HeapFree(GetProcessHeap(), 0, newStream);
4358 return NULL;
4362 return newStream;
4365 void BlockChainStream_Destroy(BlockChainStream* This)
4367 HeapFree(GetProcessHeap(), 0, This);
4370 /******************************************************************************
4371 * BlockChainStream_GetHeadOfChain
4373 * Returns the head of this stream chain.
4374 * Some special chains don't have properties, their heads are kept in
4375 * This->headOfStreamPlaceHolder.
4378 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4380 StgProperty chainProperty;
4381 BOOL readSuccessful;
4383 if (This->headOfStreamPlaceHolder != 0)
4384 return *(This->headOfStreamPlaceHolder);
4386 if (This->ownerPropertyIndex != PROPERTY_NULL)
4388 readSuccessful = StorageImpl_ReadProperty(
4389 This->parentStorage,
4390 This->ownerPropertyIndex,
4391 &chainProperty);
4393 if (readSuccessful)
4395 return chainProperty.startingBlock;
4399 return BLOCK_END_OF_CHAIN;
4402 /******************************************************************************
4403 * BlockChainStream_GetCount
4405 * Returns the number of blocks that comprises this chain.
4406 * This is not the size of the stream as the last block may not be full!
4409 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4411 ULONG blockIndex;
4412 ULONG count = 0;
4414 blockIndex = BlockChainStream_GetHeadOfChain(This);
4416 while (blockIndex != BLOCK_END_OF_CHAIN)
4418 count++;
4420 if(FAILED(StorageImpl_GetNextBlockInChain(
4421 This->parentStorage,
4422 blockIndex,
4423 &blockIndex)))
4424 return 0;
4427 return count;
4430 /******************************************************************************
4431 * BlockChainStream_ReadAt
4433 * Reads a specified number of bytes from this chain at the specified offset.
4434 * bytesRead may be NULL.
4435 * Failure will be returned if the specified number of bytes has not been read.
4437 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4438 ULARGE_INTEGER offset,
4439 ULONG size,
4440 void* buffer,
4441 ULONG* bytesRead)
4443 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4444 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4445 ULONG bytesToReadInBuffer;
4446 ULONG blockIndex;
4447 BYTE* bufferWalker;
4448 BYTE* bigBlockBuffer;
4451 * Find the first block in the stream that contains part of the buffer.
4453 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4454 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4455 (blockNoInSequence < This->lastBlockNoInSequence) )
4457 blockIndex = BlockChainStream_GetHeadOfChain(This);
4458 This->lastBlockNoInSequence = blockNoInSequence;
4460 else
4462 ULONG temp = blockNoInSequence;
4464 blockIndex = This->lastBlockNoInSequenceIndex;
4465 blockNoInSequence -= This->lastBlockNoInSequence;
4466 This->lastBlockNoInSequence = temp;
4469 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4471 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4472 return FALSE;
4473 blockNoInSequence--;
4476 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4477 return FALSE; /* We failed to find the starting block */
4479 This->lastBlockNoInSequenceIndex = blockIndex;
4482 * Start reading the buffer.
4484 *bytesRead = 0;
4485 bufferWalker = buffer;
4487 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4490 * Calculate how many bytes we can copy from this big block.
4492 bytesToReadInBuffer =
4493 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4496 * Copy those bytes to the buffer
4498 bigBlockBuffer =
4499 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4500 if (!bigBlockBuffer)
4501 return FALSE;
4503 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4505 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4508 * Step to the next big block.
4510 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4511 return FALSE;
4513 bufferWalker += bytesToReadInBuffer;
4514 size -= bytesToReadInBuffer;
4515 *bytesRead += bytesToReadInBuffer;
4516 offsetInBlock = 0; /* There is no offset on the next block */
4520 return (size == 0);
4523 /******************************************************************************
4524 * BlockChainStream_WriteAt
4526 * Writes the specified number of bytes to this chain at the specified offset.
4527 * bytesWritten may be NULL.
4528 * Will fail if not all specified number of bytes have been written.
4530 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4531 ULARGE_INTEGER offset,
4532 ULONG size,
4533 const void* buffer,
4534 ULONG* bytesWritten)
4536 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4537 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4538 ULONG bytesToWrite;
4539 ULONG blockIndex;
4540 const BYTE* bufferWalker;
4541 BYTE* bigBlockBuffer;
4544 * Find the first block in the stream that contains part of the buffer.
4546 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4547 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4548 (blockNoInSequence < This->lastBlockNoInSequence) )
4550 blockIndex = BlockChainStream_GetHeadOfChain(This);
4551 This->lastBlockNoInSequence = blockNoInSequence;
4553 else
4555 ULONG temp = blockNoInSequence;
4557 blockIndex = This->lastBlockNoInSequenceIndex;
4558 blockNoInSequence -= This->lastBlockNoInSequence;
4559 This->lastBlockNoInSequence = temp;
4562 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4564 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4565 &blockIndex)))
4566 return FALSE;
4567 blockNoInSequence--;
4570 This->lastBlockNoInSequenceIndex = blockIndex;
4573 * Here, I'm casting away the constness on the buffer variable
4574 * This is OK since we don't intend to modify that buffer.
4576 *bytesWritten = 0;
4577 bufferWalker = (const BYTE*)buffer;
4579 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4582 * Calculate how many bytes we can copy from this big block.
4584 bytesToWrite =
4585 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4588 * Copy those bytes to the buffer
4590 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4592 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4594 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4597 * Step to the next big block.
4599 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4600 &blockIndex)))
4601 return FALSE;
4602 bufferWalker += bytesToWrite;
4603 size -= bytesToWrite;
4604 *bytesWritten += bytesToWrite;
4605 offsetInBlock = 0; /* There is no offset on the next block */
4608 return (size == 0);
4611 /******************************************************************************
4612 * BlockChainStream_Shrink
4614 * Shrinks this chain in the big block depot.
4616 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4617 ULARGE_INTEGER newSize)
4619 ULONG blockIndex, extraBlock;
4620 ULONG numBlocks;
4621 ULONG count = 1;
4624 * Reset the last accessed block cache.
4626 This->lastBlockNoInSequence = 0xFFFFFFFF;
4627 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4630 * Figure out how many blocks are needed to contain the new size
4632 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4634 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4635 numBlocks++;
4637 blockIndex = BlockChainStream_GetHeadOfChain(This);
4640 * Go to the new end of chain
4642 while (count < numBlocks)
4644 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4645 &blockIndex)))
4646 return FALSE;
4647 count++;
4650 /* Get the next block before marking the new end */
4651 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4652 &extraBlock)))
4653 return FALSE;
4655 /* Mark the new end of chain */
4656 StorageImpl_SetNextBlockInChain(
4657 This->parentStorage,
4658 blockIndex,
4659 BLOCK_END_OF_CHAIN);
4661 This->tailIndex = blockIndex;
4662 This->numBlocks = numBlocks;
4665 * Mark the extra blocks as free
4667 while (extraBlock != BLOCK_END_OF_CHAIN)
4669 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4670 &blockIndex)))
4671 return FALSE;
4672 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4673 extraBlock = blockIndex;
4676 return TRUE;
4679 /******************************************************************************
4680 * BlockChainStream_Enlarge
4682 * Grows this chain in the big block depot.
4684 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4685 ULARGE_INTEGER newSize)
4687 ULONG blockIndex, currentBlock;
4688 ULONG newNumBlocks;
4689 ULONG oldNumBlocks = 0;
4691 blockIndex = BlockChainStream_GetHeadOfChain(This);
4694 * Empty chain. Create the head.
4696 if (blockIndex == BLOCK_END_OF_CHAIN)
4698 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4699 StorageImpl_SetNextBlockInChain(This->parentStorage,
4700 blockIndex,
4701 BLOCK_END_OF_CHAIN);
4703 if (This->headOfStreamPlaceHolder != 0)
4705 *(This->headOfStreamPlaceHolder) = blockIndex;
4707 else
4709 StgProperty chainProp;
4710 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4712 StorageImpl_ReadProperty(
4713 This->parentStorage,
4714 This->ownerPropertyIndex,
4715 &chainProp);
4717 chainProp.startingBlock = blockIndex;
4719 StorageImpl_WriteProperty(
4720 This->parentStorage,
4721 This->ownerPropertyIndex,
4722 &chainProp);
4725 This->tailIndex = blockIndex;
4726 This->numBlocks = 1;
4730 * Figure out how many blocks are needed to contain this stream
4732 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4734 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4735 newNumBlocks++;
4738 * Go to the current end of chain
4740 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4742 currentBlock = blockIndex;
4744 while (blockIndex != BLOCK_END_OF_CHAIN)
4746 This->numBlocks++;
4747 currentBlock = blockIndex;
4749 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4750 &blockIndex)))
4751 return FALSE;
4754 This->tailIndex = currentBlock;
4757 currentBlock = This->tailIndex;
4758 oldNumBlocks = This->numBlocks;
4761 * Add new blocks to the chain
4763 if (oldNumBlocks < newNumBlocks)
4765 while (oldNumBlocks < newNumBlocks)
4767 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4769 StorageImpl_SetNextBlockInChain(
4770 This->parentStorage,
4771 currentBlock,
4772 blockIndex);
4774 StorageImpl_SetNextBlockInChain(
4775 This->parentStorage,
4776 blockIndex,
4777 BLOCK_END_OF_CHAIN);
4779 currentBlock = blockIndex;
4780 oldNumBlocks++;
4783 This->tailIndex = blockIndex;
4784 This->numBlocks = newNumBlocks;
4787 return TRUE;
4790 /******************************************************************************
4791 * BlockChainStream_SetSize
4793 * Sets the size of this stream. The big block depot will be updated.
4794 * The file will grow if we grow the chain.
4796 * TODO: Free the actual blocks in the file when we shrink the chain.
4797 * Currently, the blocks are still in the file. So the file size
4798 * doesn't shrink even if we shrink streams.
4800 BOOL BlockChainStream_SetSize(
4801 BlockChainStream* This,
4802 ULARGE_INTEGER newSize)
4804 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4806 if (newSize.u.LowPart == size.u.LowPart)
4807 return TRUE;
4809 if (newSize.u.LowPart < size.u.LowPart)
4811 BlockChainStream_Shrink(This, newSize);
4813 else
4815 BlockChainStream_Enlarge(This, newSize);
4818 return TRUE;
4821 /******************************************************************************
4822 * BlockChainStream_GetSize
4824 * Returns the size of this chain.
4825 * Will return the block count if this chain doesn't have a property.
4827 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4829 StgProperty chainProperty;
4831 if(This->headOfStreamPlaceHolder == NULL)
4834 * This chain is a data stream read the property and return
4835 * the appropriate size
4837 StorageImpl_ReadProperty(
4838 This->parentStorage,
4839 This->ownerPropertyIndex,
4840 &chainProperty);
4842 return chainProperty.size;
4844 else
4847 * this chain is a chain that does not have a property, figure out the
4848 * size by making the product number of used blocks times the
4849 * size of them
4851 ULARGE_INTEGER result;
4852 result.u.HighPart = 0;
4854 result.u.LowPart =
4855 BlockChainStream_GetCount(This) *
4856 This->parentStorage->bigBlockSize;
4858 return result;
4862 /******************************************************************************
4863 ** SmallBlockChainStream implementation
4866 SmallBlockChainStream* SmallBlockChainStream_Construct(
4867 StorageImpl* parentStorage,
4868 ULONG propertyIndex)
4870 SmallBlockChainStream* newStream;
4872 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4874 newStream->parentStorage = parentStorage;
4875 newStream->ownerPropertyIndex = propertyIndex;
4877 return newStream;
4880 void SmallBlockChainStream_Destroy(
4881 SmallBlockChainStream* This)
4883 HeapFree(GetProcessHeap(), 0, This);
4886 /******************************************************************************
4887 * SmallBlockChainStream_GetHeadOfChain
4889 * Returns the head of this chain of small blocks.
4891 ULONG SmallBlockChainStream_GetHeadOfChain(
4892 SmallBlockChainStream* This)
4894 StgProperty chainProperty;
4895 BOOL readSuccessful;
4897 if (This->ownerPropertyIndex)
4899 readSuccessful = StorageImpl_ReadProperty(
4900 This->parentStorage,
4901 This->ownerPropertyIndex,
4902 &chainProperty);
4904 if (readSuccessful)
4906 return chainProperty.startingBlock;
4911 return BLOCK_END_OF_CHAIN;
4914 /******************************************************************************
4915 * SmallBlockChainStream_GetNextBlockInChain
4917 * Returns the index of the next small block in this chain.
4919 * Return Values:
4920 * - BLOCK_END_OF_CHAIN: end of this chain
4921 * - BLOCK_UNUSED: small block 'blockIndex' is free
4923 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4924 SmallBlockChainStream* This,
4925 ULONG blockIndex,
4926 ULONG* nextBlockInChain)
4928 ULARGE_INTEGER offsetOfBlockInDepot;
4929 DWORD buffer;
4930 ULONG bytesRead;
4931 BOOL success;
4933 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4935 offsetOfBlockInDepot.u.HighPart = 0;
4936 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4939 * Read those bytes in the buffer from the small block file.
4941 success = BlockChainStream_ReadAt(
4942 This->parentStorage->smallBlockDepotChain,
4943 offsetOfBlockInDepot,
4944 sizeof(DWORD),
4945 &buffer,
4946 &bytesRead);
4948 if (success)
4950 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4951 return S_OK;
4954 return STG_E_READFAULT;
4957 /******************************************************************************
4958 * SmallBlockChainStream_SetNextBlockInChain
4960 * Writes the index of the next block of the specified block in the small
4961 * block depot.
4962 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4963 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4965 void SmallBlockChainStream_SetNextBlockInChain(
4966 SmallBlockChainStream* This,
4967 ULONG blockIndex,
4968 ULONG nextBlock)
4970 ULARGE_INTEGER offsetOfBlockInDepot;
4971 DWORD buffer;
4972 ULONG bytesWritten;
4974 offsetOfBlockInDepot.u.HighPart = 0;
4975 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4977 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4980 * Read those bytes in the buffer from the small block file.
4982 BlockChainStream_WriteAt(
4983 This->parentStorage->smallBlockDepotChain,
4984 offsetOfBlockInDepot,
4985 sizeof(DWORD),
4986 &buffer,
4987 &bytesWritten);
4990 /******************************************************************************
4991 * SmallBlockChainStream_FreeBlock
4993 * Flag small block 'blockIndex' as free in the small block depot.
4995 void SmallBlockChainStream_FreeBlock(
4996 SmallBlockChainStream* This,
4997 ULONG blockIndex)
4999 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5002 /******************************************************************************
5003 * SmallBlockChainStream_GetNextFreeBlock
5005 * Returns the index of a free small block. The small block depot will be
5006 * enlarged if necessary. The small block chain will also be enlarged if
5007 * necessary.
5009 ULONG SmallBlockChainStream_GetNextFreeBlock(
5010 SmallBlockChainStream* This)
5012 ULARGE_INTEGER offsetOfBlockInDepot;
5013 DWORD buffer;
5014 ULONG bytesRead;
5015 ULONG blockIndex = 0;
5016 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5017 BOOL success = TRUE;
5018 ULONG smallBlocksPerBigBlock;
5020 offsetOfBlockInDepot.u.HighPart = 0;
5023 * Scan the small block depot for a free block
5025 while (nextBlockIndex != BLOCK_UNUSED)
5027 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5029 success = BlockChainStream_ReadAt(
5030 This->parentStorage->smallBlockDepotChain,
5031 offsetOfBlockInDepot,
5032 sizeof(DWORD),
5033 &buffer,
5034 &bytesRead);
5037 * If we run out of space for the small block depot, enlarge it
5039 if (success)
5041 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5043 if (nextBlockIndex != BLOCK_UNUSED)
5044 blockIndex++;
5046 else
5048 ULONG count =
5049 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5051 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5052 ULONG nextBlock, newsbdIndex;
5053 BYTE* smallBlockDepot;
5055 nextBlock = sbdIndex;
5056 while (nextBlock != BLOCK_END_OF_CHAIN)
5058 sbdIndex = nextBlock;
5059 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5062 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5063 if (sbdIndex != BLOCK_END_OF_CHAIN)
5064 StorageImpl_SetNextBlockInChain(
5065 This->parentStorage,
5066 sbdIndex,
5067 newsbdIndex);
5069 StorageImpl_SetNextBlockInChain(
5070 This->parentStorage,
5071 newsbdIndex,
5072 BLOCK_END_OF_CHAIN);
5075 * Initialize all the small blocks to free
5077 smallBlockDepot =
5078 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5080 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5081 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5083 if (count == 0)
5086 * We have just created the small block depot.
5088 StgProperty rootProp;
5089 ULONG sbStartIndex;
5092 * Save it in the header
5094 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5095 StorageImpl_SaveFileHeader(This->parentStorage);
5098 * And allocate the first big block that will contain small blocks
5100 sbStartIndex =
5101 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5103 StorageImpl_SetNextBlockInChain(
5104 This->parentStorage,
5105 sbStartIndex,
5106 BLOCK_END_OF_CHAIN);
5108 StorageImpl_ReadProperty(
5109 This->parentStorage,
5110 This->parentStorage->base.rootPropertySetIndex,
5111 &rootProp);
5113 rootProp.startingBlock = sbStartIndex;
5114 rootProp.size.u.HighPart = 0;
5115 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5117 StorageImpl_WriteProperty(
5118 This->parentStorage,
5119 This->parentStorage->base.rootPropertySetIndex,
5120 &rootProp);
5125 smallBlocksPerBigBlock =
5126 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5129 * Verify if we have to allocate big blocks to contain small blocks
5131 if (blockIndex % smallBlocksPerBigBlock == 0)
5133 StgProperty rootProp;
5134 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5136 StorageImpl_ReadProperty(
5137 This->parentStorage,
5138 This->parentStorage->base.rootPropertySetIndex,
5139 &rootProp);
5141 if (rootProp.size.u.LowPart <
5142 (blocksRequired * This->parentStorage->bigBlockSize))
5144 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5146 BlockChainStream_SetSize(
5147 This->parentStorage->smallBlockRootChain,
5148 rootProp.size);
5150 StorageImpl_WriteProperty(
5151 This->parentStorage,
5152 This->parentStorage->base.rootPropertySetIndex,
5153 &rootProp);
5157 return blockIndex;
5160 /******************************************************************************
5161 * SmallBlockChainStream_ReadAt
5163 * Reads a specified number of bytes from this chain at the specified offset.
5164 * bytesRead may be NULL.
5165 * Failure will be returned if the specified number of bytes has not been read.
5167 HRESULT SmallBlockChainStream_ReadAt(
5168 SmallBlockChainStream* This,
5169 ULARGE_INTEGER offset,
5170 ULONG size,
5171 void* buffer,
5172 ULONG* bytesRead)
5174 HRESULT rc = S_OK;
5175 ULARGE_INTEGER offsetInBigBlockFile;
5176 ULONG blockNoInSequence =
5177 offset.u.LowPart / This->parentStorage->smallBlockSize;
5179 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5180 ULONG bytesToReadInBuffer;
5181 ULONG blockIndex;
5182 ULONG bytesReadFromBigBlockFile;
5183 BYTE* bufferWalker;
5186 * This should never happen on a small block file.
5188 assert(offset.u.HighPart==0);
5191 * Find the first block in the stream that contains part of the buffer.
5193 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5195 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5197 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5198 if(FAILED(rc))
5199 return rc;
5200 blockNoInSequence--;
5204 * Start reading the buffer.
5206 *bytesRead = 0;
5207 bufferWalker = buffer;
5209 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5212 * Calculate how many bytes we can copy from this small block.
5214 bytesToReadInBuffer =
5215 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5218 * Calculate the offset of the small block in the small block file.
5220 offsetInBigBlockFile.u.HighPart = 0;
5221 offsetInBigBlockFile.u.LowPart =
5222 blockIndex * This->parentStorage->smallBlockSize;
5224 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5227 * Read those bytes in the buffer from the small block file.
5228 * The small block has already been identified so it shouldn't fail
5229 * unless the file is corrupt.
5231 if (!BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5232 offsetInBigBlockFile,
5233 bytesToReadInBuffer,
5234 bufferWalker,
5235 &bytesReadFromBigBlockFile))
5236 return STG_E_DOCFILECORRUPT;
5238 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5241 * Step to the next big block.
5243 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5244 if(FAILED(rc))
5245 return rc;
5247 bufferWalker += bytesToReadInBuffer;
5248 size -= bytesToReadInBuffer;
5249 *bytesRead += bytesToReadInBuffer;
5250 offsetInBlock = 0; /* There is no offset on the next block */
5253 return rc;
5256 /******************************************************************************
5257 * SmallBlockChainStream_WriteAt
5259 * Writes the specified number of bytes to this chain at the specified offset.
5260 * bytesWritten may be NULL.
5261 * Will fail if not all specified number of bytes have been written.
5263 BOOL SmallBlockChainStream_WriteAt(
5264 SmallBlockChainStream* This,
5265 ULARGE_INTEGER offset,
5266 ULONG size,
5267 const void* buffer,
5268 ULONG* bytesWritten)
5270 ULARGE_INTEGER offsetInBigBlockFile;
5271 ULONG blockNoInSequence =
5272 offset.u.LowPart / This->parentStorage->smallBlockSize;
5274 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5275 ULONG bytesToWriteInBuffer;
5276 ULONG blockIndex;
5277 ULONG bytesWrittenFromBigBlockFile;
5278 const BYTE* bufferWalker;
5281 * This should never happen on a small block file.
5283 assert(offset.u.HighPart==0);
5286 * Find the first block in the stream that contains part of the buffer.
5288 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5290 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5292 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5293 return FALSE;
5294 blockNoInSequence--;
5298 * Start writing the buffer.
5300 * Here, I'm casting away the constness on the buffer variable
5301 * This is OK since we don't intend to modify that buffer.
5303 *bytesWritten = 0;
5304 bufferWalker = (const BYTE*)buffer;
5305 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5308 * Calculate how many bytes we can copy to this small block.
5310 bytesToWriteInBuffer =
5311 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5314 * Calculate the offset of the small block in the small block file.
5316 offsetInBigBlockFile.u.HighPart = 0;
5317 offsetInBigBlockFile.u.LowPart =
5318 blockIndex * This->parentStorage->smallBlockSize;
5320 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5323 * Write those bytes in the buffer to the small block file.
5325 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5326 offsetInBigBlockFile,
5327 bytesToWriteInBuffer,
5328 bufferWalker,
5329 &bytesWrittenFromBigBlockFile);
5331 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5334 * Step to the next big block.
5336 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5337 &blockIndex)))
5338 return FALSE;
5339 bufferWalker += bytesToWriteInBuffer;
5340 size -= bytesToWriteInBuffer;
5341 *bytesWritten += bytesToWriteInBuffer;
5342 offsetInBlock = 0; /* There is no offset on the next block */
5345 return (size == 0);
5348 /******************************************************************************
5349 * SmallBlockChainStream_Shrink
5351 * Shrinks this chain in the small block depot.
5353 BOOL SmallBlockChainStream_Shrink(
5354 SmallBlockChainStream* This,
5355 ULARGE_INTEGER newSize)
5357 ULONG blockIndex, extraBlock;
5358 ULONG numBlocks;
5359 ULONG count = 0;
5361 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5363 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5364 numBlocks++;
5366 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5369 * Go to the new end of chain
5371 while (count < numBlocks)
5373 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5374 &blockIndex)))
5375 return FALSE;
5376 count++;
5380 * If the count is 0, we have a special case, the head of the chain was
5381 * just freed.
5383 if (count == 0)
5385 StgProperty chainProp;
5387 StorageImpl_ReadProperty(This->parentStorage,
5388 This->ownerPropertyIndex,
5389 &chainProp);
5391 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5393 StorageImpl_WriteProperty(This->parentStorage,
5394 This->ownerPropertyIndex,
5395 &chainProp);
5398 * We start freeing the chain at the head block.
5400 extraBlock = blockIndex;
5402 else
5404 /* Get the next block before marking the new end */
5405 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5406 &extraBlock)))
5407 return FALSE;
5409 /* Mark the new end of chain */
5410 SmallBlockChainStream_SetNextBlockInChain(
5411 This,
5412 blockIndex,
5413 BLOCK_END_OF_CHAIN);
5417 * Mark the extra blocks as free
5419 while (extraBlock != BLOCK_END_OF_CHAIN)
5421 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5422 &blockIndex)))
5423 return FALSE;
5424 SmallBlockChainStream_FreeBlock(This, extraBlock);
5425 extraBlock = blockIndex;
5428 return TRUE;
5431 /******************************************************************************
5432 * SmallBlockChainStream_Enlarge
5434 * Grows this chain in the small block depot.
5436 BOOL SmallBlockChainStream_Enlarge(
5437 SmallBlockChainStream* This,
5438 ULARGE_INTEGER newSize)
5440 ULONG blockIndex, currentBlock;
5441 ULONG newNumBlocks;
5442 ULONG oldNumBlocks = 0;
5444 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5447 * Empty chain
5449 if (blockIndex == BLOCK_END_OF_CHAIN)
5452 StgProperty chainProp;
5454 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5455 &chainProp);
5457 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5459 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5460 &chainProp);
5462 blockIndex = chainProp.startingBlock;
5463 SmallBlockChainStream_SetNextBlockInChain(
5464 This,
5465 blockIndex,
5466 BLOCK_END_OF_CHAIN);
5469 currentBlock = blockIndex;
5472 * Figure out how many blocks are needed to contain this stream
5474 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5476 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5477 newNumBlocks++;
5480 * Go to the current end of chain
5482 while (blockIndex != BLOCK_END_OF_CHAIN)
5484 oldNumBlocks++;
5485 currentBlock = blockIndex;
5486 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5487 return FALSE;
5491 * Add new blocks to the chain
5493 while (oldNumBlocks < newNumBlocks)
5495 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5496 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5498 SmallBlockChainStream_SetNextBlockInChain(
5499 This,
5500 blockIndex,
5501 BLOCK_END_OF_CHAIN);
5503 currentBlock = blockIndex;
5504 oldNumBlocks++;
5507 return TRUE;
5510 /******************************************************************************
5511 * SmallBlockChainStream_GetCount
5513 * Returns the number of blocks that comprises this chain.
5514 * This is not the size of this chain as the last block may not be full!
5516 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5518 ULONG blockIndex;
5519 ULONG count = 0;
5521 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5523 while (blockIndex != BLOCK_END_OF_CHAIN)
5525 count++;
5527 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5528 return 0;
5531 return count;
5534 /******************************************************************************
5535 * SmallBlockChainStream_SetSize
5537 * Sets the size of this stream.
5538 * The file will grow if we grow the chain.
5540 * TODO: Free the actual blocks in the file when we shrink the chain.
5541 * Currently, the blocks are still in the file. So the file size
5542 * doesn't shrink even if we shrink streams.
5544 BOOL SmallBlockChainStream_SetSize(
5545 SmallBlockChainStream* This,
5546 ULARGE_INTEGER newSize)
5548 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5550 if (newSize.u.LowPart == size.u.LowPart)
5551 return TRUE;
5553 if (newSize.u.LowPart < size.u.LowPart)
5555 SmallBlockChainStream_Shrink(This, newSize);
5557 else
5559 SmallBlockChainStream_Enlarge(This, newSize);
5562 return TRUE;
5565 /******************************************************************************
5566 * SmallBlockChainStream_GetSize
5568 * Returns the size of this chain.
5570 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5572 StgProperty chainProperty;
5574 StorageImpl_ReadProperty(
5575 This->parentStorage,
5576 This->ownerPropertyIndex,
5577 &chainProperty);
5579 return chainProperty.size;
5582 /******************************************************************************
5583 * StgCreateDocfile [OLE32.@]
5584 * Creates a new compound file storage object
5586 * PARAMS
5587 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5588 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5589 * reserved [ ?] unused?, usually 0
5590 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5592 * RETURNS
5593 * S_OK if the file was successfully created
5594 * some STG_E_ value if error
5595 * NOTES
5596 * if pwcsName is NULL, create file with new unique name
5597 * the function can returns
5598 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5599 * (unrealized now)
5601 HRESULT WINAPI StgCreateDocfile(
5602 LPCOLESTR pwcsName,
5603 DWORD grfMode,
5604 DWORD reserved,
5605 IStorage **ppstgOpen)
5607 StorageImpl* newStorage = 0;
5608 HANDLE hFile = INVALID_HANDLE_VALUE;
5609 HRESULT hr = STG_E_INVALIDFLAG;
5610 DWORD shareMode;
5611 DWORD accessMode;
5612 DWORD creationMode;
5613 DWORD fileAttributes;
5614 WCHAR tempFileName[MAX_PATH];
5616 TRACE("(%s, %lx, %ld, %p)\n",
5617 debugstr_w(pwcsName), grfMode,
5618 reserved, ppstgOpen);
5621 * Validate the parameters
5623 if (ppstgOpen == 0)
5624 return STG_E_INVALIDPOINTER;
5625 if (reserved != 0)
5626 return STG_E_INVALIDPARAMETER;
5629 * Validate the STGM flags
5631 if ( FAILED( validateSTGM(grfMode) ))
5632 goto end;
5634 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5635 switch(STGM_ACCESS_MODE(grfMode))
5637 case STGM_WRITE:
5638 case STGM_READWRITE:
5639 break;
5640 default:
5641 goto end;
5644 /* if no share mode given then DENY_NONE is the default */
5645 if (STGM_SHARE_MODE(grfMode) == 0)
5646 grfMode |= STGM_SHARE_DENY_NONE;
5648 /* must have at least one access mode */
5649 if (STGM_ACCESS_MODE(grfMode) == 0)
5650 goto end;
5652 /* in direct mode, can only use SHARE_EXCLUSIVE */
5653 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5654 goto end;
5656 /* but in transacted mode, any share mode is valid */
5659 * Generate a unique name.
5661 if (pwcsName == 0)
5663 WCHAR tempPath[MAX_PATH];
5664 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5666 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
5667 goto end;
5669 memset(tempPath, 0, sizeof(tempPath));
5670 memset(tempFileName, 0, sizeof(tempFileName));
5672 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5673 tempPath[0] = '.';
5675 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5676 pwcsName = tempFileName;
5677 else
5679 hr = STG_E_INSUFFICIENTMEMORY;
5680 goto end;
5683 creationMode = TRUNCATE_EXISTING;
5685 else
5687 creationMode = GetCreationModeFromSTGM(grfMode);
5691 * Interpret the STGM value grfMode
5693 shareMode = GetShareModeFromSTGM(grfMode);
5694 accessMode = GetAccessModeFromSTGM(grfMode);
5696 if (grfMode & STGM_DELETEONRELEASE)
5697 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5698 else
5699 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5701 if (grfMode & STGM_TRANSACTED)
5702 FIXME("Transacted mode not implemented.\n");
5705 * Initialize the "out" parameter.
5707 *ppstgOpen = 0;
5709 hFile = CreateFileW(pwcsName,
5710 accessMode,
5711 shareMode,
5712 NULL,
5713 creationMode,
5714 fileAttributes,
5717 if (hFile == INVALID_HANDLE_VALUE)
5719 if(GetLastError() == ERROR_FILE_EXISTS)
5720 hr = STG_E_FILEALREADYEXISTS;
5721 else
5722 hr = E_FAIL;
5723 goto end;
5727 * Allocate and initialize the new IStorage32object.
5729 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5731 if (newStorage == 0)
5733 hr = STG_E_INSUFFICIENTMEMORY;
5734 goto end;
5737 hr = StorageImpl_Construct(
5738 newStorage,
5739 hFile,
5740 pwcsName,
5741 NULL,
5742 grfMode,
5743 TRUE,
5744 TRUE);
5746 if (FAILED(hr))
5748 HeapFree(GetProcessHeap(), 0, newStorage);
5749 goto end;
5753 * Get an "out" pointer for the caller.
5755 hr = StorageBaseImpl_QueryInterface(
5756 (IStorage*)newStorage,
5757 (REFIID)&IID_IStorage,
5758 (void**)ppstgOpen);
5759 end:
5760 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5762 return hr;
5765 /******************************************************************************
5766 * StgCreateStorageEx [OLE32.@]
5768 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5770 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5771 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5773 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5775 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5776 return STG_E_INVALIDPARAMETER;
5779 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5781 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5782 return STG_E_INVALIDPARAMETER;
5785 if (stgfmt == STGFMT_FILE)
5787 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5788 return STG_E_INVALIDPARAMETER;
5791 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5793 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5794 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5797 ERR("Invalid stgfmt argument\n");
5798 return STG_E_INVALIDPARAMETER;
5801 /******************************************************************************
5802 * StgCreatePropSetStg [OLE32.@]
5804 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5805 IPropertySetStorage **ppPropSetStg)
5807 HRESULT hr;
5809 TRACE("(%p, 0x%lx, %p)\n", pstg, reserved, ppPropSetStg);
5810 if (reserved)
5811 hr = STG_E_INVALIDPARAMETER;
5812 else
5813 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5814 (void**)ppPropSetStg);
5815 return hr;
5818 /******************************************************************************
5819 * StgOpenStorageEx [OLE32.@]
5821 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5823 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5824 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5826 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5828 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5829 return STG_E_INVALIDPARAMETER;
5832 if (stgfmt == STGFMT_DOCFILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5834 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5835 return STG_E_INVALIDPARAMETER;
5838 if (stgfmt == STGFMT_FILE)
5840 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5841 return STG_E_INVALIDPARAMETER;
5844 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE || stgfmt == STGFMT_ANY)
5846 if (stgfmt == STGFMT_ANY)
5847 WARN("STGFMT_ANY assuming storage\n");
5848 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5849 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5852 ERR("Invalid stgfmt argument\n");
5853 return STG_E_INVALIDPARAMETER;
5857 /******************************************************************************
5858 * StgOpenStorage [OLE32.@]
5860 HRESULT WINAPI StgOpenStorage(
5861 const OLECHAR *pwcsName,
5862 IStorage *pstgPriority,
5863 DWORD grfMode,
5864 SNB snbExclude,
5865 DWORD reserved,
5866 IStorage **ppstgOpen)
5868 StorageImpl* newStorage = 0;
5869 HRESULT hr = S_OK;
5870 HANDLE hFile = 0;
5871 DWORD shareMode;
5872 DWORD accessMode;
5873 WCHAR fullname[MAX_PATH];
5874 DWORD length;
5876 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5877 debugstr_w(pwcsName), pstgPriority, grfMode,
5878 snbExclude, reserved, ppstgOpen);
5881 * Perform sanity checks
5883 if (pwcsName == 0)
5885 hr = STG_E_INVALIDNAME;
5886 goto end;
5889 if (ppstgOpen == 0)
5891 hr = STG_E_INVALIDPOINTER;
5892 goto end;
5895 if (reserved)
5897 hr = STG_E_INVALIDPARAMETER;
5898 goto end;
5901 if (grfMode & STGM_PRIORITY)
5903 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5904 return STG_E_INVALIDFLAG;
5905 if (grfMode & STGM_DELETEONRELEASE)
5906 return STG_E_INVALIDFUNCTION;
5907 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5908 return STG_E_INVALIDFLAG;
5909 grfMode &= ~0xf0; /* remove the existing sharing mode */
5910 grfMode |= STGM_SHARE_DENY_NONE;
5912 /* STGM_PRIORITY stops other IStorage objects on the same file from
5913 * committing until the STGM_PRIORITY IStorage is closed. it also
5914 * stops non-transacted mode StgOpenStorage calls with write access from
5915 * succeeding. obviously, both of these cannot be achieved through just
5916 * file share flags */
5917 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5921 * Validate the sharing mode
5923 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5924 switch(STGM_SHARE_MODE(grfMode))
5926 case STGM_SHARE_EXCLUSIVE:
5927 case STGM_SHARE_DENY_WRITE:
5928 break;
5929 default:
5930 hr = STG_E_INVALIDFLAG;
5931 goto end;
5935 * Validate the STGM flags
5937 if ( FAILED( validateSTGM(grfMode) ) ||
5938 (grfMode&STGM_CREATE))
5940 hr = STG_E_INVALIDFLAG;
5941 goto end;
5944 /* shared reading requires transacted mode */
5945 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5946 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5947 !(grfMode&STGM_TRANSACTED) )
5949 hr = STG_E_INVALIDFLAG;
5950 goto end;
5954 * Interpret the STGM value grfMode
5956 shareMode = GetShareModeFromSTGM(grfMode);
5957 accessMode = GetAccessModeFromSTGM(grfMode);
5960 * Initialize the "out" parameter.
5962 *ppstgOpen = 0;
5964 hFile = CreateFileW( pwcsName,
5965 accessMode,
5966 shareMode,
5967 NULL,
5968 OPEN_EXISTING,
5969 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5972 if (hFile==INVALID_HANDLE_VALUE)
5974 DWORD last_error = GetLastError();
5976 hr = E_FAIL;
5978 switch (last_error)
5980 case ERROR_FILE_NOT_FOUND:
5981 hr = STG_E_FILENOTFOUND;
5982 break;
5984 case ERROR_PATH_NOT_FOUND:
5985 hr = STG_E_PATHNOTFOUND;
5986 break;
5988 case ERROR_ACCESS_DENIED:
5989 case ERROR_WRITE_PROTECT:
5990 hr = STG_E_ACCESSDENIED;
5991 break;
5993 case ERROR_SHARING_VIOLATION:
5994 hr = STG_E_SHAREVIOLATION;
5995 break;
5997 default:
5998 hr = E_FAIL;
6001 goto end;
6005 * Refuse to open the file if it's too small to be a structured storage file
6006 * FIXME: verify the file when reading instead of here
6008 length = GetFileSize(hFile, NULL);
6009 if (length < 0x100)
6011 CloseHandle(hFile);
6012 hr = STG_E_FILEALREADYEXISTS;
6013 goto end;
6017 * Allocate and initialize the new IStorage32object.
6019 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6021 if (newStorage == 0)
6023 hr = STG_E_INSUFFICIENTMEMORY;
6024 goto end;
6027 /* if the file's length was zero, initialize the storage */
6028 hr = StorageImpl_Construct(
6029 newStorage,
6030 hFile,
6031 pwcsName,
6032 NULL,
6033 grfMode,
6034 TRUE,
6035 FALSE );
6037 if (FAILED(hr))
6039 HeapFree(GetProcessHeap(), 0, newStorage);
6041 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6043 if(hr == STG_E_INVALIDHEADER)
6044 hr = STG_E_FILEALREADYEXISTS;
6045 goto end;
6048 /* prepare the file name string given in lieu of the root property name */
6049 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6050 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6051 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6054 * Get an "out" pointer for the caller.
6056 hr = StorageBaseImpl_QueryInterface(
6057 (IStorage*)newStorage,
6058 (REFIID)&IID_IStorage,
6059 (void**)ppstgOpen);
6061 end:
6062 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6063 return hr;
6066 /******************************************************************************
6067 * StgCreateDocfileOnILockBytes [OLE32.@]
6069 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6070 ILockBytes *plkbyt,
6071 DWORD grfMode,
6072 DWORD reserved,
6073 IStorage** ppstgOpen)
6075 StorageImpl* newStorage = 0;
6076 HRESULT hr = S_OK;
6079 * Validate the parameters
6081 if ((ppstgOpen == 0) || (plkbyt == 0))
6082 return STG_E_INVALIDPOINTER;
6085 * Allocate and initialize the new IStorage object.
6087 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6089 if (newStorage == 0)
6090 return STG_E_INSUFFICIENTMEMORY;
6092 hr = StorageImpl_Construct(
6093 newStorage,
6096 plkbyt,
6097 grfMode,
6098 FALSE,
6099 TRUE);
6101 if (FAILED(hr))
6103 HeapFree(GetProcessHeap(), 0, newStorage);
6104 return hr;
6108 * Get an "out" pointer for the caller.
6110 hr = StorageBaseImpl_QueryInterface(
6111 (IStorage*)newStorage,
6112 (REFIID)&IID_IStorage,
6113 (void**)ppstgOpen);
6115 return hr;
6118 /******************************************************************************
6119 * StgOpenStorageOnILockBytes [OLE32.@]
6121 HRESULT WINAPI StgOpenStorageOnILockBytes(
6122 ILockBytes *plkbyt,
6123 IStorage *pstgPriority,
6124 DWORD grfMode,
6125 SNB snbExclude,
6126 DWORD reserved,
6127 IStorage **ppstgOpen)
6129 StorageImpl* newStorage = 0;
6130 HRESULT hr = S_OK;
6133 * Perform a sanity check
6135 if ((plkbyt == 0) || (ppstgOpen == 0))
6136 return STG_E_INVALIDPOINTER;
6139 * Validate the STGM flags
6141 if ( FAILED( validateSTGM(grfMode) ))
6142 return STG_E_INVALIDFLAG;
6145 * Initialize the "out" parameter.
6147 *ppstgOpen = 0;
6150 * Allocate and initialize the new IStorage object.
6152 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6154 if (newStorage == 0)
6155 return STG_E_INSUFFICIENTMEMORY;
6157 hr = StorageImpl_Construct(
6158 newStorage,
6161 plkbyt,
6162 grfMode,
6163 FALSE,
6164 FALSE);
6166 if (FAILED(hr))
6168 HeapFree(GetProcessHeap(), 0, newStorage);
6169 return hr;
6173 * Get an "out" pointer for the caller.
6175 hr = StorageBaseImpl_QueryInterface(
6176 (IStorage*)newStorage,
6177 (REFIID)&IID_IStorage,
6178 (void**)ppstgOpen);
6180 return hr;
6183 /******************************************************************************
6184 * StgSetTimes [ole32.@]
6185 * StgSetTimes [OLE32.@]
6189 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6190 FILETIME const *patime, FILETIME const *pmtime)
6192 IStorage *stg = NULL;
6193 HRESULT r;
6195 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6197 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6198 0, 0, &stg);
6199 if( SUCCEEDED(r) )
6201 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6202 IStorage_Release(stg);
6205 return r;
6208 /******************************************************************************
6209 * StgIsStorageILockBytes [OLE32.@]
6211 * Determines if the ILockBytes contains a storage object.
6213 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6215 BYTE sig[8];
6216 ULARGE_INTEGER offset;
6218 offset.u.HighPart = 0;
6219 offset.u.LowPart = 0;
6221 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6223 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6224 return S_OK;
6226 return S_FALSE;
6229 /******************************************************************************
6230 * WriteClassStg [OLE32.@]
6232 * This method will store the specified CLSID in the specified storage object
6234 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6236 HRESULT hRes;
6238 if(!pStg)
6239 return E_INVALIDARG;
6241 hRes = IStorage_SetClass(pStg, rclsid);
6243 return hRes;
6246 /***********************************************************************
6247 * ReadClassStg (OLE32.@)
6249 * This method reads the CLSID previously written to a storage object with
6250 * the WriteClassStg.
6252 * PARAMS
6253 * pstg [I] IStorage pointer
6254 * pclsid [O] Pointer to where the CLSID is written
6256 * RETURNS
6257 * Success: S_OK.
6258 * Failure: HRESULT code.
6260 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6262 STATSTG pstatstg;
6263 HRESULT hRes;
6265 TRACE("(%p, %p)\n", pstg, pclsid);
6267 if(!pstg || !pclsid)
6268 return E_INVALIDARG;
6271 * read a STATSTG structure (contains the clsid) from the storage
6273 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6275 if(SUCCEEDED(hRes))
6276 *pclsid=pstatstg.clsid;
6278 return hRes;
6281 /***********************************************************************
6282 * OleLoadFromStream (OLE32.@)
6284 * This function loads an object from stream
6286 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6288 CLSID clsid;
6289 HRESULT res;
6290 LPPERSISTSTREAM xstm;
6292 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6294 res=ReadClassStm(pStm,&clsid);
6295 if (!SUCCEEDED(res))
6296 return res;
6297 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6298 if (!SUCCEEDED(res))
6299 return res;
6300 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6301 if (!SUCCEEDED(res)) {
6302 IUnknown_Release((IUnknown*)*ppvObj);
6303 return res;
6305 res=IPersistStream_Load(xstm,pStm);
6306 IPersistStream_Release(xstm);
6307 /* FIXME: all refcounts ok at this point? I think they should be:
6308 * pStm : unchanged
6309 * ppvObj : 1
6310 * xstm : 0 (released)
6312 return res;
6315 /***********************************************************************
6316 * OleSaveToStream (OLE32.@)
6318 * This function saves an object with the IPersistStream interface on it
6319 * to the specified stream.
6321 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6324 CLSID clsid;
6325 HRESULT res;
6327 TRACE("(%p,%p)\n",pPStm,pStm);
6329 res=IPersistStream_GetClassID(pPStm,&clsid);
6331 if (SUCCEEDED(res)){
6333 res=WriteClassStm(pStm,&clsid);
6335 if (SUCCEEDED(res))
6337 res=IPersistStream_Save(pPStm,pStm,TRUE);
6340 TRACE("Finished Save\n");
6341 return res;
6344 /****************************************************************************
6345 * This method validate a STGM parameter that can contain the values below
6347 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6348 * The stgm values contained in 0xffff0000 are bitmasks.
6350 * STGM_DIRECT 0x00000000
6351 * STGM_TRANSACTED 0x00010000
6352 * STGM_SIMPLE 0x08000000
6354 * STGM_READ 0x00000000
6355 * STGM_WRITE 0x00000001
6356 * STGM_READWRITE 0x00000002
6358 * STGM_SHARE_DENY_NONE 0x00000040
6359 * STGM_SHARE_DENY_READ 0x00000030
6360 * STGM_SHARE_DENY_WRITE 0x00000020
6361 * STGM_SHARE_EXCLUSIVE 0x00000010
6363 * STGM_PRIORITY 0x00040000
6364 * STGM_DELETEONRELEASE 0x04000000
6366 * STGM_CREATE 0x00001000
6367 * STGM_CONVERT 0x00020000
6368 * STGM_FAILIFTHERE 0x00000000
6370 * STGM_NOSCRATCH 0x00100000
6371 * STGM_NOSNAPSHOT 0x00200000
6373 static HRESULT validateSTGM(DWORD stgm)
6375 DWORD access = STGM_ACCESS_MODE(stgm);
6376 DWORD share = STGM_SHARE_MODE(stgm);
6377 DWORD create = STGM_CREATE_MODE(stgm);
6379 if (stgm&~STGM_KNOWN_FLAGS)
6381 ERR("unknown flags %08lx\n", stgm);
6382 return E_FAIL;
6385 switch (access)
6387 case STGM_READ:
6388 case STGM_WRITE:
6389 case STGM_READWRITE:
6390 break;
6391 default:
6392 return E_FAIL;
6395 switch (share)
6397 case STGM_SHARE_DENY_NONE:
6398 case STGM_SHARE_DENY_READ:
6399 case STGM_SHARE_DENY_WRITE:
6400 case STGM_SHARE_EXCLUSIVE:
6401 break;
6402 default:
6403 return E_FAIL;
6406 switch (create)
6408 case STGM_CREATE:
6409 case STGM_FAILIFTHERE:
6410 break;
6411 default:
6412 return E_FAIL;
6416 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6418 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6419 return E_FAIL;
6422 * STGM_CREATE | STGM_CONVERT
6423 * if both are false, STGM_FAILIFTHERE is set to TRUE
6425 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6426 return E_FAIL;
6429 * STGM_NOSCRATCH requires STGM_TRANSACTED
6431 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6432 return E_FAIL;
6435 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6436 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6438 if ( (stgm & STGM_NOSNAPSHOT) &&
6439 (!(stgm & STGM_TRANSACTED) ||
6440 share == STGM_SHARE_EXCLUSIVE ||
6441 share == STGM_SHARE_DENY_WRITE) )
6442 return E_FAIL;
6444 return S_OK;
6447 /****************************************************************************
6448 * GetShareModeFromSTGM
6450 * This method will return a share mode flag from a STGM value.
6451 * The STGM value is assumed valid.
6453 static DWORD GetShareModeFromSTGM(DWORD stgm)
6455 switch (STGM_SHARE_MODE(stgm))
6457 case STGM_SHARE_DENY_NONE:
6458 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6459 case STGM_SHARE_DENY_READ:
6460 return FILE_SHARE_WRITE;
6461 case STGM_SHARE_DENY_WRITE:
6462 return FILE_SHARE_READ;
6463 case STGM_SHARE_EXCLUSIVE:
6464 return 0;
6466 ERR("Invalid share mode!\n");
6467 assert(0);
6468 return 0;
6471 /****************************************************************************
6472 * GetAccessModeFromSTGM
6474 * This method will return an access mode flag from a STGM value.
6475 * The STGM value is assumed valid.
6477 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6479 switch (STGM_ACCESS_MODE(stgm))
6481 case STGM_READ:
6482 return GENERIC_READ;
6483 case STGM_WRITE:
6484 case STGM_READWRITE:
6485 return GENERIC_READ | GENERIC_WRITE;
6487 ERR("Invalid access mode!\n");
6488 assert(0);
6489 return 0;
6492 /****************************************************************************
6493 * GetCreationModeFromSTGM
6495 * This method will return a creation mode flag from a STGM value.
6496 * The STGM value is assumed valid.
6498 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6500 switch(STGM_CREATE_MODE(stgm))
6502 case STGM_CREATE:
6503 return CREATE_ALWAYS;
6504 case STGM_CONVERT:
6505 FIXME("STGM_CONVERT not implemented!\n");
6506 return CREATE_NEW;
6507 case STGM_FAILIFTHERE:
6508 return CREATE_NEW;
6510 ERR("Invalid create mode!\n");
6511 assert(0);
6512 return 0;
6516 /*************************************************************************
6517 * OLECONVERT_LoadOLE10 [Internal]
6519 * Loads the OLE10 STREAM to memory
6521 * PARAMS
6522 * pOleStream [I] The OLESTREAM
6523 * pData [I] Data Structure for the OLESTREAM Data
6525 * RETURNS
6526 * Success: S_OK
6527 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6528 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6530 * NOTES
6531 * This function is used by OleConvertOLESTREAMToIStorage only.
6533 * Memory allocated for pData must be freed by the caller
6535 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6537 DWORD dwSize;
6538 HRESULT hRes = S_OK;
6539 int nTryCnt=0;
6540 int max_try = 6;
6542 pData->pData = NULL;
6543 pData->pstrOleObjFileName = (CHAR *) NULL;
6545 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6547 /* Get the OleID */
6548 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6549 if(dwSize != sizeof(pData->dwOleID))
6551 hRes = CONVERT10_E_OLESTREAM_GET;
6553 else if(pData->dwOleID != OLESTREAM_ID)
6555 hRes = CONVERT10_E_OLESTREAM_FMT;
6557 else
6559 hRes = S_OK;
6560 break;
6564 if(hRes == S_OK)
6566 /* Get the TypeID...more info needed for this field */
6567 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6568 if(dwSize != sizeof(pData->dwTypeID))
6570 hRes = CONVERT10_E_OLESTREAM_GET;
6573 if(hRes == S_OK)
6575 if(pData->dwTypeID != 0)
6577 /* Get the length of the OleTypeName */
6578 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6579 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6581 hRes = CONVERT10_E_OLESTREAM_GET;
6584 if(hRes == S_OK)
6586 if(pData->dwOleTypeNameLength > 0)
6588 /* Get the OleTypeName */
6589 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6590 if(dwSize != pData->dwOleTypeNameLength)
6592 hRes = CONVERT10_E_OLESTREAM_GET;
6596 if(bStrem1)
6598 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6599 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6601 hRes = CONVERT10_E_OLESTREAM_GET;
6603 if(hRes == S_OK)
6605 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6606 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6607 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6608 if(pData->pstrOleObjFileName)
6610 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6611 if(dwSize != pData->dwOleObjFileNameLength)
6613 hRes = CONVERT10_E_OLESTREAM_GET;
6616 else
6617 hRes = CONVERT10_E_OLESTREAM_GET;
6620 else
6622 /* Get the Width of the Metafile */
6623 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6624 if(dwSize != sizeof(pData->dwMetaFileWidth))
6626 hRes = CONVERT10_E_OLESTREAM_GET;
6628 if(hRes == S_OK)
6630 /* Get the Height of the Metafile */
6631 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6632 if(dwSize != sizeof(pData->dwMetaFileHeight))
6634 hRes = CONVERT10_E_OLESTREAM_GET;
6638 if(hRes == S_OK)
6640 /* Get the Length of the Data */
6641 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6642 if(dwSize != sizeof(pData->dwDataLength))
6644 hRes = CONVERT10_E_OLESTREAM_GET;
6648 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6650 if(!bStrem1) /* if it is a second OLE stream data */
6652 pData->dwDataLength -= 8;
6653 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6654 if(dwSize != sizeof(pData->strUnknown))
6656 hRes = CONVERT10_E_OLESTREAM_GET;
6660 if(hRes == S_OK)
6662 if(pData->dwDataLength > 0)
6664 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6666 /* Get Data (ex. IStorage, Metafile, or BMP) */
6667 if(pData->pData)
6669 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6670 if(dwSize != pData->dwDataLength)
6672 hRes = CONVERT10_E_OLESTREAM_GET;
6675 else
6677 hRes = CONVERT10_E_OLESTREAM_GET;
6683 return hRes;
6686 /*************************************************************************
6687 * OLECONVERT_SaveOLE10 [Internal]
6689 * Saves the OLE10 STREAM From memory
6691 * PARAMS
6692 * pData [I] Data Structure for the OLESTREAM Data
6693 * pOleStream [I] The OLESTREAM to save
6695 * RETURNS
6696 * Success: S_OK
6697 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6699 * NOTES
6700 * This function is used by OleConvertIStorageToOLESTREAM only.
6703 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6705 DWORD dwSize;
6706 HRESULT hRes = S_OK;
6709 /* Set the OleID */
6710 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6711 if(dwSize != sizeof(pData->dwOleID))
6713 hRes = CONVERT10_E_OLESTREAM_PUT;
6716 if(hRes == S_OK)
6718 /* Set the TypeID */
6719 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6720 if(dwSize != sizeof(pData->dwTypeID))
6722 hRes = CONVERT10_E_OLESTREAM_PUT;
6726 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6728 /* Set the Length of the OleTypeName */
6729 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6730 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6732 hRes = CONVERT10_E_OLESTREAM_PUT;
6735 if(hRes == S_OK)
6737 if(pData->dwOleTypeNameLength > 0)
6739 /* Set the OleTypeName */
6740 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6741 if(dwSize != pData->dwOleTypeNameLength)
6743 hRes = CONVERT10_E_OLESTREAM_PUT;
6748 if(hRes == S_OK)
6750 /* Set the width of the Metafile */
6751 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6752 if(dwSize != sizeof(pData->dwMetaFileWidth))
6754 hRes = CONVERT10_E_OLESTREAM_PUT;
6758 if(hRes == S_OK)
6760 /* Set the height of the Metafile */
6761 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6762 if(dwSize != sizeof(pData->dwMetaFileHeight))
6764 hRes = CONVERT10_E_OLESTREAM_PUT;
6768 if(hRes == S_OK)
6770 /* Set the length of the Data */
6771 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6772 if(dwSize != sizeof(pData->dwDataLength))
6774 hRes = CONVERT10_E_OLESTREAM_PUT;
6778 if(hRes == S_OK)
6780 if(pData->dwDataLength > 0)
6782 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6783 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6784 if(dwSize != pData->dwDataLength)
6786 hRes = CONVERT10_E_OLESTREAM_PUT;
6791 return hRes;
6794 /*************************************************************************
6795 * OLECONVERT_GetOLE20FromOLE10[Internal]
6797 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6798 * opens it, and copies the content to the dest IStorage for
6799 * OleConvertOLESTREAMToIStorage
6802 * PARAMS
6803 * pDestStorage [I] The IStorage to copy the data to
6804 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6805 * nBufferLength [I] The size of the buffer
6807 * RETURNS
6808 * Nothing
6810 * NOTES
6814 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6816 HRESULT hRes;
6817 HANDLE hFile;
6818 IStorage *pTempStorage;
6819 DWORD dwNumOfBytesWritten;
6820 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6821 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6823 /* Create a temp File */
6824 GetTempPathW(MAX_PATH, wstrTempDir);
6825 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6826 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6828 if(hFile != INVALID_HANDLE_VALUE)
6830 /* Write IStorage Data to File */
6831 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6832 CloseHandle(hFile);
6834 /* Open and copy temp storage to the Dest Storage */
6835 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6836 if(hRes == S_OK)
6838 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6839 StorageBaseImpl_Release(pTempStorage);
6841 DeleteFileW(wstrTempFile);
6846 /*************************************************************************
6847 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6849 * Saves the OLE10 STREAM From memory
6851 * PARAMS
6852 * pStorage [I] The Src IStorage to copy
6853 * pData [I] The Dest Memory to write to.
6855 * RETURNS
6856 * The size in bytes allocated for pData
6858 * NOTES
6859 * Memory allocated for pData must be freed by the caller
6861 * Used by OleConvertIStorageToOLESTREAM only.
6864 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6866 HANDLE hFile;
6867 HRESULT hRes;
6868 DWORD nDataLength = 0;
6869 IStorage *pTempStorage;
6870 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6871 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6873 *pData = NULL;
6875 /* Create temp Storage */
6876 GetTempPathW(MAX_PATH, wstrTempDir);
6877 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6878 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6880 if(hRes == S_OK)
6882 /* Copy Src Storage to the Temp Storage */
6883 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6884 StorageBaseImpl_Release(pTempStorage);
6886 /* Open Temp Storage as a file and copy to memory */
6887 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6888 if(hFile != INVALID_HANDLE_VALUE)
6890 nDataLength = GetFileSize(hFile, NULL);
6891 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6892 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6893 CloseHandle(hFile);
6895 DeleteFileW(wstrTempFile);
6897 return nDataLength;
6900 /*************************************************************************
6901 * OLECONVERT_CreateOleStream [Internal]
6903 * Creates the "\001OLE" stream in the IStorage if necessary.
6905 * PARAMS
6906 * pStorage [I] Dest storage to create the stream in
6908 * RETURNS
6909 * Nothing
6911 * NOTES
6912 * This function is used by OleConvertOLESTREAMToIStorage only.
6914 * This stream is still unknown, MS Word seems to have extra data
6915 * but since the data is stored in the OLESTREAM there should be
6916 * no need to recreate the stream. If the stream is manually
6917 * deleted it will create it with this default data.
6920 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6922 HRESULT hRes;
6923 IStream *pStream;
6924 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6925 BYTE pOleStreamHeader [] =
6927 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6928 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6929 0x00, 0x00, 0x00, 0x00
6932 /* Create stream if not present */
6933 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6934 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6936 if(hRes == S_OK)
6938 /* Write default Data */
6939 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6940 IStream_Release(pStream);
6944 /* write a string to a stream, preceded by its length */
6945 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6947 HRESULT r;
6948 LPSTR str;
6949 DWORD len = 0;
6951 if( string )
6952 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6953 r = IStream_Write( stm, &len, sizeof(len), NULL);
6954 if( FAILED( r ) )
6955 return r;
6956 if(len == 0)
6957 return r;
6958 str = CoTaskMemAlloc( len );
6959 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6960 r = IStream_Write( stm, str, len, NULL);
6961 CoTaskMemFree( str );
6962 return r;
6965 /* read a string preceded by its length from a stream */
6966 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6968 HRESULT r;
6969 DWORD len, count = 0;
6970 LPSTR str;
6971 LPWSTR wstr;
6973 r = IStream_Read( stm, &len, sizeof(len), &count );
6974 if( FAILED( r ) )
6975 return r;
6976 if( count != sizeof(len) )
6977 return E_OUTOFMEMORY;
6979 TRACE("%ld bytes\n",len);
6981 str = CoTaskMemAlloc( len );
6982 if( !str )
6983 return E_OUTOFMEMORY;
6984 count = 0;
6985 r = IStream_Read( stm, str, len, &count );
6986 if( FAILED( r ) )
6987 return r;
6988 if( count != len )
6990 CoTaskMemFree( str );
6991 return E_OUTOFMEMORY;
6994 TRACE("Read string %s\n",debugstr_an(str,len));
6996 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6997 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6998 if( wstr )
6999 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7000 CoTaskMemFree( str );
7002 *string = wstr;
7004 return r;
7008 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7009 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7011 IStream *pstm;
7012 HRESULT r = S_OK;
7013 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7015 static const BYTE unknown1[12] =
7016 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7017 0xFF, 0xFF, 0xFF, 0xFF};
7018 static const BYTE unknown2[16] =
7019 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7020 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7022 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7023 debugstr_w(lpszUserType), debugstr_w(szClipName),
7024 debugstr_w(szProgIDName));
7026 /* Create a CompObj stream if it doesn't exist */
7027 r = IStorage_CreateStream(pstg, szwStreamName,
7028 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7029 if( FAILED (r) )
7030 return r;
7032 /* Write CompObj Structure to stream */
7033 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7035 if( SUCCEEDED( r ) )
7036 r = WriteClassStm( pstm, clsid );
7038 if( SUCCEEDED( r ) )
7039 r = STREAM_WriteString( pstm, lpszUserType );
7040 if( SUCCEEDED( r ) )
7041 r = STREAM_WriteString( pstm, szClipName );
7042 if( SUCCEEDED( r ) )
7043 r = STREAM_WriteString( pstm, szProgIDName );
7044 if( SUCCEEDED( r ) )
7045 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7047 IStream_Release( pstm );
7049 return r;
7052 /***********************************************************************
7053 * WriteFmtUserTypeStg (OLE32.@)
7055 HRESULT WINAPI WriteFmtUserTypeStg(
7056 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7058 HRESULT r;
7059 WCHAR szwClipName[0x40];
7060 CLSID clsid = CLSID_NULL;
7061 LPWSTR wstrProgID = NULL;
7062 DWORD n;
7064 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7066 /* get the clipboard format name */
7067 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7068 szwClipName[n]=0;
7070 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7072 /* FIXME: There's room to save a CLSID and its ProgID, but
7073 the CLSID is not looked up in the registry and in all the
7074 tests I wrote it was CLSID_NULL. Where does it come from?
7077 /* get the real program ID. This may fail, but that's fine */
7078 ProgIDFromCLSID(&clsid, &wstrProgID);
7080 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7082 r = STORAGE_WriteCompObj( pstg, &clsid,
7083 lpszUserType, szwClipName, wstrProgID );
7085 CoTaskMemFree(wstrProgID);
7087 return r;
7091 /******************************************************************************
7092 * ReadFmtUserTypeStg [OLE32.@]
7094 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7096 HRESULT r;
7097 IStream *stm = 0;
7098 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7099 unsigned char unknown1[12];
7100 unsigned char unknown2[16];
7101 DWORD count;
7102 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7103 CLSID clsid;
7105 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7107 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7108 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7109 if( FAILED ( r ) )
7111 WARN("Failed to open stream r = %08lx\n", r);
7112 return r;
7115 /* read the various parts of the structure */
7116 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7117 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7118 goto end;
7119 r = ReadClassStm( stm, &clsid );
7120 if( FAILED( r ) )
7121 goto end;
7123 r = STREAM_ReadString( stm, &szCLSIDName );
7124 if( FAILED( r ) )
7125 goto end;
7127 r = STREAM_ReadString( stm, &szOleTypeName );
7128 if( FAILED( r ) )
7129 goto end;
7131 r = STREAM_ReadString( stm, &szProgIDName );
7132 if( FAILED( r ) )
7133 goto end;
7135 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7136 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7137 goto end;
7139 /* ok, success... now we just need to store what we found */
7140 if( pcf )
7141 *pcf = RegisterClipboardFormatW( szOleTypeName );
7142 CoTaskMemFree( szOleTypeName );
7144 if( lplpszUserType )
7145 *lplpszUserType = szCLSIDName;
7146 CoTaskMemFree( szProgIDName );
7148 end:
7149 IStream_Release( stm );
7151 return r;
7155 /*************************************************************************
7156 * OLECONVERT_CreateCompObjStream [Internal]
7158 * Creates a "\001CompObj" is the destination IStorage if necessary.
7160 * PARAMS
7161 * pStorage [I] The dest IStorage to create the CompObj Stream
7162 * if necessary.
7163 * strOleTypeName [I] The ProgID
7165 * RETURNS
7166 * Success: S_OK
7167 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7169 * NOTES
7170 * This function is used by OleConvertOLESTREAMToIStorage only.
7172 * The stream data is stored in the OLESTREAM and there should be
7173 * no need to recreate the stream. If the stream is manually
7174 * deleted it will attempt to create it by querying the registry.
7178 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7180 IStream *pStream;
7181 HRESULT hStorageRes, hRes = S_OK;
7182 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7183 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7184 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7186 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7187 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7189 /* Initialize the CompObj structure */
7190 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7191 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7192 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7195 /* Create a CompObj stream if it doesn't exist */
7196 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7197 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7198 if(hStorageRes == S_OK)
7200 /* copy the OleTypeName to the compobj struct */
7201 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7202 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7204 /* copy the OleTypeName to the compobj struct */
7205 /* Note: in the test made, these were Identical */
7206 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7207 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7209 /* Get the CLSID */
7210 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7211 bufferW, OLESTREAM_MAX_STR_LEN );
7212 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7214 if(hRes == S_OK)
7216 HKEY hKey;
7217 LONG hErr;
7218 /* Get the CLSID Default Name from the Registry */
7219 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7220 if(hErr == ERROR_SUCCESS)
7222 char strTemp[OLESTREAM_MAX_STR_LEN];
7223 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7224 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7225 if(hErr == ERROR_SUCCESS)
7227 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7229 RegCloseKey(hKey);
7233 /* Write CompObj Structure to stream */
7234 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7236 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7238 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7239 if(IStorageCompObj.dwCLSIDNameLength > 0)
7241 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7243 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7244 if(IStorageCompObj.dwOleTypeNameLength > 0)
7246 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7248 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7249 if(IStorageCompObj.dwProgIDNameLength > 0)
7251 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7253 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7254 IStream_Release(pStream);
7256 return hRes;
7260 /*************************************************************************
7261 * OLECONVERT_CreateOlePresStream[Internal]
7263 * Creates the "\002OlePres000" Stream with the Metafile data
7265 * PARAMS
7266 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7267 * dwExtentX [I] Width of the Metafile
7268 * dwExtentY [I] Height of the Metafile
7269 * pData [I] Metafile data
7270 * dwDataLength [I] Size of the Metafile data
7272 * RETURNS
7273 * Success: S_OK
7274 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7276 * NOTES
7277 * This function is used by OleConvertOLESTREAMToIStorage only.
7280 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7282 HRESULT hRes;
7283 IStream *pStream;
7284 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7285 BYTE pOlePresStreamHeader [] =
7287 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7288 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7289 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7290 0x00, 0x00, 0x00, 0x00
7293 BYTE pOlePresStreamHeaderEmpty [] =
7295 0x00, 0x00, 0x00, 0x00,
7296 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7297 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7298 0x00, 0x00, 0x00, 0x00
7301 /* Create the OlePres000 Stream */
7302 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7303 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7305 if(hRes == S_OK)
7307 DWORD nHeaderSize;
7308 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7310 memset(&OlePres, 0, sizeof(OlePres));
7311 /* Do we have any metafile data to save */
7312 if(dwDataLength > 0)
7314 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7315 nHeaderSize = sizeof(pOlePresStreamHeader);
7317 else
7319 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7320 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7322 /* Set width and height of the metafile */
7323 OlePres.dwExtentX = dwExtentX;
7324 OlePres.dwExtentY = -dwExtentY;
7326 /* Set Data and Length */
7327 if(dwDataLength > sizeof(METAFILEPICT16))
7329 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7330 OlePres.pData = &(pData[8]);
7332 /* Save OlePres000 Data to Stream */
7333 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7334 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7335 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7336 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7337 if(OlePres.dwSize > 0)
7339 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7341 IStream_Release(pStream);
7345 /*************************************************************************
7346 * OLECONVERT_CreateOle10NativeStream [Internal]
7348 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7350 * PARAMS
7351 * pStorage [I] Dest storage to create the stream in
7352 * pData [I] Ole10 Native Data (ex. bmp)
7353 * dwDataLength [I] Size of the Ole10 Native Data
7355 * RETURNS
7356 * Nothing
7358 * NOTES
7359 * This function is used by OleConvertOLESTREAMToIStorage only.
7361 * Might need to verify the data and return appropriate error message
7364 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7366 HRESULT hRes;
7367 IStream *pStream;
7368 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7370 /* Create the Ole10Native Stream */
7371 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7372 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7374 if(hRes == S_OK)
7376 /* Write info to stream */
7377 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7378 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7379 IStream_Release(pStream);
7384 /*************************************************************************
7385 * OLECONVERT_GetOLE10ProgID [Internal]
7387 * Finds the ProgID (or OleTypeID) from the IStorage
7389 * PARAMS
7390 * pStorage [I] The Src IStorage to get the ProgID
7391 * strProgID [I] the ProgID string to get
7392 * dwSize [I] the size of the string
7394 * RETURNS
7395 * Success: S_OK
7396 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7398 * NOTES
7399 * This function is used by OleConvertIStorageToOLESTREAM only.
7403 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7405 HRESULT hRes;
7406 IStream *pStream;
7407 LARGE_INTEGER iSeekPos;
7408 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7409 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7411 /* Open the CompObj Stream */
7412 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7413 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7414 if(hRes == S_OK)
7417 /*Get the OleType from the CompObj Stream */
7418 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7419 iSeekPos.u.HighPart = 0;
7421 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7422 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7423 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7424 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7425 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7426 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7427 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7429 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7430 if(*dwSize > 0)
7432 IStream_Read(pStream, strProgID, *dwSize, NULL);
7434 IStream_Release(pStream);
7436 else
7438 STATSTG stat;
7439 LPOLESTR wstrProgID;
7441 /* Get the OleType from the registry */
7442 REFCLSID clsid = &(stat.clsid);
7443 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7444 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7445 if(hRes == S_OK)
7447 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7451 return hRes;
7454 /*************************************************************************
7455 * OLECONVERT_GetOle10PresData [Internal]
7457 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7459 * PARAMS
7460 * pStorage [I] Src IStroage
7461 * pOleStream [I] Dest OleStream Mem Struct
7463 * RETURNS
7464 * Nothing
7466 * NOTES
7467 * This function is used by OleConvertIStorageToOLESTREAM only.
7469 * Memory allocated for pData must be freed by the caller
7473 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7476 HRESULT hRes;
7477 IStream *pStream;
7478 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7480 /* Initialize Default data for OLESTREAM */
7481 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7482 pOleStreamData[0].dwTypeID = 2;
7483 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7484 pOleStreamData[1].dwTypeID = 0;
7485 pOleStreamData[0].dwMetaFileWidth = 0;
7486 pOleStreamData[0].dwMetaFileHeight = 0;
7487 pOleStreamData[0].pData = NULL;
7488 pOleStreamData[1].pData = NULL;
7490 /* Open Ole10Native Stream */
7491 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7492 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7493 if(hRes == S_OK)
7496 /* Read Size and Data */
7497 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7498 if(pOleStreamData->dwDataLength > 0)
7500 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7501 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7503 IStream_Release(pStream);
7509 /*************************************************************************
7510 * OLECONVERT_GetOle20PresData[Internal]
7512 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7514 * PARAMS
7515 * pStorage [I] Src IStroage
7516 * pOleStreamData [I] Dest OleStream Mem Struct
7518 * RETURNS
7519 * Nothing
7521 * NOTES
7522 * This function is used by OleConvertIStorageToOLESTREAM only.
7524 * Memory allocated for pData must be freed by the caller
7526 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7528 HRESULT hRes;
7529 IStream *pStream;
7530 OLECONVERT_ISTORAGE_OLEPRES olePress;
7531 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7533 /* Initialize Default data for OLESTREAM */
7534 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7535 pOleStreamData[0].dwTypeID = 2;
7536 pOleStreamData[0].dwMetaFileWidth = 0;
7537 pOleStreamData[0].dwMetaFileHeight = 0;
7538 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7539 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7540 pOleStreamData[1].dwTypeID = 0;
7541 pOleStreamData[1].dwOleTypeNameLength = 0;
7542 pOleStreamData[1].strOleTypeName[0] = 0;
7543 pOleStreamData[1].dwMetaFileWidth = 0;
7544 pOleStreamData[1].dwMetaFileHeight = 0;
7545 pOleStreamData[1].pData = NULL;
7546 pOleStreamData[1].dwDataLength = 0;
7549 /* Open OlePress000 stream */
7550 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7551 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7552 if(hRes == S_OK)
7554 LARGE_INTEGER iSeekPos;
7555 METAFILEPICT16 MetaFilePict;
7556 static const char strMetafilePictName[] = "METAFILEPICT";
7558 /* Set the TypeID for a Metafile */
7559 pOleStreamData[1].dwTypeID = 5;
7561 /* Set the OleTypeName to Metafile */
7562 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7563 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7565 iSeekPos.u.HighPart = 0;
7566 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7568 /* Get Presentation Data */
7569 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7570 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7571 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7572 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7574 /*Set width and Height */
7575 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7576 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7577 if(olePress.dwSize > 0)
7579 /* Set Length */
7580 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7582 /* Set MetaFilePict struct */
7583 MetaFilePict.mm = 8;
7584 MetaFilePict.xExt = olePress.dwExtentX;
7585 MetaFilePict.yExt = olePress.dwExtentY;
7586 MetaFilePict.hMF = 0;
7588 /* Get Metafile Data */
7589 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7590 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7591 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7593 IStream_Release(pStream);
7597 /*************************************************************************
7598 * OleConvertOLESTREAMToIStorage [OLE32.@]
7600 * Read info on MSDN
7602 * TODO
7603 * DVTARGETDEVICE paramenter is not handled
7604 * Still unsure of some mem fields for OLE 10 Stream
7605 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7606 * and "\001OLE" streams
7609 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7610 LPOLESTREAM pOleStream,
7611 LPSTORAGE pstg,
7612 const DVTARGETDEVICE* ptd)
7614 int i;
7615 HRESULT hRes=S_OK;
7616 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7618 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7620 if(ptd != NULL)
7622 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7625 if(pstg == NULL || pOleStream == NULL)
7627 hRes = E_INVALIDARG;
7630 if(hRes == S_OK)
7632 /* Load the OLESTREAM to Memory */
7633 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7636 if(hRes == S_OK)
7638 /* Load the OLESTREAM to Memory (part 2)*/
7639 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7642 if(hRes == S_OK)
7645 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7647 /* Do we have the IStorage Data in the OLESTREAM */
7648 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7650 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7651 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7653 else
7655 /* It must be an original OLE 1.0 source */
7656 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7659 else
7661 /* It must be an original OLE 1.0 source */
7662 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7665 /* Create CompObj Stream if necessary */
7666 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7667 if(hRes == S_OK)
7669 /*Create the Ole Stream if necessary */
7670 OLECONVERT_CreateOleStream(pstg);
7675 /* Free allocated memory */
7676 for(i=0; i < 2; i++)
7678 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7679 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7680 pOleStreamData[i].pstrOleObjFileName = NULL;
7682 return hRes;
7685 /*************************************************************************
7686 * OleConvertIStorageToOLESTREAM [OLE32.@]
7688 * Read info on MSDN
7690 * Read info on MSDN
7692 * TODO
7693 * Still unsure of some mem fields for OLE 10 Stream
7694 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7695 * and "\001OLE" streams.
7698 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7699 LPSTORAGE pstg,
7700 LPOLESTREAM pOleStream)
7702 int i;
7703 HRESULT hRes = S_OK;
7704 IStream *pStream;
7705 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7706 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7709 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7711 if(pstg == NULL || pOleStream == NULL)
7713 hRes = E_INVALIDARG;
7715 if(hRes == S_OK)
7717 /* Get the ProgID */
7718 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7719 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7721 if(hRes == S_OK)
7723 /* Was it originally Ole10 */
7724 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7725 if(hRes == S_OK)
7727 IStream_Release(pStream);
7728 /* Get Presentation Data for Ole10Native */
7729 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7731 else
7733 /* Get Presentation Data (OLE20) */
7734 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7737 /* Save OLESTREAM */
7738 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7739 if(hRes == S_OK)
7741 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7746 /* Free allocated memory */
7747 for(i=0; i < 2; i++)
7749 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7752 return hRes;
7755 /***********************************************************************
7756 * GetConvertStg (OLE32.@)
7758 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7759 FIXME("unimplemented stub!\n");
7760 return E_FAIL;
7763 /******************************************************************************
7764 * StgIsStorageFile [OLE32.@]
7765 * Verify if the file contains a storage object
7767 * PARAMS
7768 * fn [ I] Filename
7770 * RETURNS
7771 * S_OK if file has magic bytes as a storage object
7772 * S_FALSE if file is not storage
7774 HRESULT WINAPI
7775 StgIsStorageFile(LPCOLESTR fn)
7777 HANDLE hf;
7778 BYTE magic[8];
7779 DWORD bytes_read;
7781 TRACE("(\'%s\')\n", debugstr_w(fn));
7782 hf = CreateFileW(fn, GENERIC_READ,
7783 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7784 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7786 if (hf == INVALID_HANDLE_VALUE)
7787 return STG_E_FILENOTFOUND;
7789 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7791 WARN(" unable to read file\n");
7792 CloseHandle(hf);
7793 return S_FALSE;
7796 CloseHandle(hf);
7798 if (bytes_read != 8) {
7799 WARN(" too short\n");
7800 return S_FALSE;
7803 if (!memcmp(magic,STORAGE_magic,8)) {
7804 WARN(" -> YES\n");
7805 return S_OK;
7808 WARN(" -> Invalid header.\n");
7809 return S_FALSE;