winmm: Get rid of the bFrom32 parameter in MMDRV_Alloc and MMDRV_Message.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blobb9c2f29aab1bf3e44ab516cd2d5c34d3f1df2d0d
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.
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #define COBJMACROS
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winuser.h"
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
53 #include "winreg.h"
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootPropertyName[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base;
81 * There is no specific data for this class.
84 typedef struct StorageInternalImpl StorageInternalImpl;
86 /* Method definitions for the Storage32InternalImpl class. */
87 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
88 DWORD openFlags, ULONG rootTropertyIndex);
89 static void StorageImpl_Destroy(StorageBaseImpl* iface);
90 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
91 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
92 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
93 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
94 static void StorageImpl_SaveFileHeader(StorageImpl* This);
96 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
97 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
98 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
99 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
100 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
102 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
103 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
104 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
106 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
107 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
108 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
109 ULONG blockIndex, ULONG offset, DWORD value);
110 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
111 ULONG blockIndex, ULONG offset, DWORD* value);
113 /* OLESTREAM memory structure to use for Get and Put Routines */
114 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
115 typedef struct
117 DWORD dwOleID;
118 DWORD dwTypeID;
119 DWORD dwOleTypeNameLength;
120 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
121 CHAR *pstrOleObjFileName;
122 DWORD dwOleObjFileNameLength;
123 DWORD dwMetaFileWidth;
124 DWORD dwMetaFileHeight;
125 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
126 DWORD dwDataLength;
127 BYTE *pData;
128 }OLECONVERT_OLESTREAM_DATA;
130 /* CompObj Stream structure */
131 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
132 typedef struct
134 BYTE byUnknown1[12];
135 CLSID clsid;
136 DWORD dwCLSIDNameLength;
137 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
138 DWORD dwOleTypeNameLength;
139 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
140 DWORD dwProgIDNameLength;
141 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
142 BYTE byUnknown2[16];
143 }OLECONVERT_ISTORAGE_COMPOBJ;
146 /* Ole Presentation Stream structure */
147 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
148 typedef struct
150 BYTE byUnknown1[28];
151 DWORD dwExtentX;
152 DWORD dwExtentY;
153 DWORD dwSize;
154 BYTE *pData;
155 }OLECONVERT_ISTORAGE_OLEPRES;
159 /***********************************************************************
160 * Forward declaration of internal functions used by the method DestroyElement
162 static HRESULT deleteStorageProperty(
163 StorageImpl *parentStorage,
164 ULONG foundPropertyIndexToDelete,
165 StgProperty propertyToDelete);
167 static HRESULT deleteStreamProperty(
168 StorageImpl *parentStorage,
169 ULONG foundPropertyIndexToDelete,
170 StgProperty propertyToDelete);
172 static HRESULT findPlaceholder(
173 StorageImpl *storage,
174 ULONG propertyIndexToStore,
175 ULONG storagePropertyIndex,
176 INT typeOfRelation);
178 static HRESULT adjustPropertyChain(
179 StorageImpl *This,
180 StgProperty propertyToDelete,
181 StgProperty parentProperty,
182 ULONG parentPropertyId,
183 INT typeOfRelation);
185 /***********************************************************************
186 * Declaration of the functions used to manipulate StgProperty
189 static ULONG getFreeProperty(
190 StorageImpl *storage);
192 static void updatePropertyChain(
193 StorageImpl *storage,
194 ULONG newPropertyIndex,
195 StgProperty newProperty);
197 static LONG propertyNameCmp(
198 const OLECHAR *newProperty,
199 const OLECHAR *currentProperty);
202 /***********************************************************************
203 * Declaration of miscellaneous functions...
205 static HRESULT validateSTGM(DWORD stgmValue);
207 static DWORD GetShareModeFromSTGM(DWORD stgm);
208 static DWORD GetAccessModeFromSTGM(DWORD stgm);
209 static DWORD GetCreationModeFromSTGM(DWORD stgm);
211 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
214 /****************************************************************************
215 * IEnumSTATSTGImpl definitions.
217 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
218 * This class allows iterating through the content of a storage and to find
219 * specific items inside it.
221 struct IEnumSTATSTGImpl
223 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
224 * since we want to cast this in an IEnumSTATSTG pointer */
226 LONG ref; /* Reference count */
227 StorageImpl* parentStorage; /* Reference to the parent storage */
228 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
231 * The current implementation of the IEnumSTATSTGImpl class uses a stack
232 * to walk the property sets to get the content of a storage. This stack
233 * is implemented by the following 3 data members
235 ULONG stackSize;
236 ULONG stackMaxSize;
237 ULONG* stackToVisit;
239 #define ENUMSTATSGT_SIZE_INCREMENT 10
243 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
244 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
245 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
246 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
247 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
248 StgProperty* buffer);
249 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
250 StgProperty *currentProperty, ULONG *propertyId);
252 /************************************************************************
253 ** Block Functions
256 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
258 if (index == 0xffffffff)
259 index = 0;
260 else
261 index ++;
263 return index * BIG_BLOCK_SIZE;
266 /************************************************************************
267 ** Storage32BaseImpl implementation
269 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
270 ULARGE_INTEGER offset,
271 void* buffer,
272 ULONG size,
273 ULONG* bytesRead)
275 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
278 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
279 ULARGE_INTEGER offset,
280 const void* buffer,
281 const ULONG size,
282 ULONG* bytesWritten)
284 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
287 /************************************************************************
288 * Storage32BaseImpl_QueryInterface (IUnknown)
290 * This method implements the common QueryInterface for all IStorage32
291 * implementations contained in this file.
293 * See Windows documentation for more details on IUnknown methods.
295 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
296 IStorage* iface,
297 REFIID riid,
298 void** ppvObject)
300 StorageBaseImpl *This = (StorageBaseImpl *)iface;
302 if ( (This==0) || (ppvObject==0) )
303 return E_INVALIDARG;
305 *ppvObject = 0;
307 if (IsEqualGUID(&IID_IUnknown, riid) ||
308 IsEqualGUID(&IID_IStorage, riid))
310 *ppvObject = This;
312 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
314 *ppvObject = &This->pssVtbl;
317 if ((*ppvObject)==0)
318 return E_NOINTERFACE;
320 IStorage_AddRef(iface);
322 return S_OK;
325 /************************************************************************
326 * Storage32BaseImpl_AddRef (IUnknown)
328 * This method implements the common AddRef for all IStorage32
329 * implementations contained in this file.
331 * See Windows documentation for more details on IUnknown methods.
333 static ULONG WINAPI StorageBaseImpl_AddRef(
334 IStorage* iface)
336 StorageBaseImpl *This = (StorageBaseImpl *)iface;
337 ULONG ref = InterlockedIncrement(&This->ref);
339 TRACE("(%p) AddRef to %d\n", This, ref);
341 return ref;
344 /************************************************************************
345 * Storage32BaseImpl_Release (IUnknown)
347 * This method implements the common Release for all IStorage32
348 * implementations contained in this file.
350 * See Windows documentation for more details on IUnknown methods.
352 static ULONG WINAPI StorageBaseImpl_Release(
353 IStorage* iface)
355 StorageBaseImpl *This = (StorageBaseImpl *)iface;
357 ULONG ref = InterlockedDecrement(&This->ref);
359 TRACE("(%p) ReleaseRef to %d\n", This, ref);
361 if (ref == 0)
364 * Since we are using a system of base-classes, we want to call the
365 * destructor of the appropriate derived class. To do this, we are
366 * using virtual functions to implement the destructor.
368 This->v_destructor(This);
371 return ref;
374 /************************************************************************
375 * Storage32BaseImpl_OpenStream (IStorage)
377 * This method will open the specified stream object from the current storage.
379 * See Windows documentation for more details on IStorage methods.
381 static HRESULT WINAPI StorageBaseImpl_OpenStream(
382 IStorage* iface,
383 const OLECHAR* pwcsName, /* [string][in] */
384 void* reserved1, /* [unique][in] */
385 DWORD grfMode, /* [in] */
386 DWORD reserved2, /* [in] */
387 IStream** ppstm) /* [out] */
389 StorageBaseImpl *This = (StorageBaseImpl *)iface;
390 IEnumSTATSTGImpl* propertyEnumeration;
391 StgStreamImpl* newStream;
392 StgProperty currentProperty;
393 ULONG foundPropertyIndex;
394 HRESULT res = STG_E_UNKNOWN;
396 TRACE("(%p, %s, %p, %x, %d, %p)\n",
397 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
399 if ( (pwcsName==NULL) || (ppstm==0) )
401 res = E_INVALIDARG;
402 goto end;
405 *ppstm = NULL;
407 if ( FAILED( validateSTGM(grfMode) ) ||
408 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
410 res = STG_E_INVALIDFLAG;
411 goto end;
415 * As documented.
417 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
419 res = STG_E_INVALIDFUNCTION;
420 goto end;
424 * Check that we're compatible with the parent's storage mode, but
425 * only if we are not in transacted mode
427 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
428 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
430 res = STG_E_ACCESSDENIED;
431 goto end;
436 * Create a property enumeration to search the properties
438 propertyEnumeration = IEnumSTATSTGImpl_Construct(
439 This->ancestorStorage,
440 This->rootPropertySetIndex);
443 * Search the enumeration for the property with the given name
445 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
446 propertyEnumeration,
447 pwcsName,
448 &currentProperty);
451 * Delete the property enumeration since we don't need it anymore
453 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
456 * If it was found, construct the stream object and return a pointer to it.
458 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
459 (currentProperty.propertyType==PROPTYPE_STREAM) )
461 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
463 if (newStream!=0)
465 newStream->grfMode = grfMode;
466 *ppstm = (IStream*)newStream;
468 IStream_AddRef(*ppstm);
470 res = S_OK;
471 goto end;
474 res = E_OUTOFMEMORY;
475 goto end;
478 res = STG_E_FILENOTFOUND;
480 end:
481 if (res == S_OK)
482 TRACE("<-- IStream %p\n", *ppstm);
483 TRACE("<-- %08x\n", res);
484 return res;
487 /************************************************************************
488 * Storage32BaseImpl_OpenStorage (IStorage)
490 * This method will open a new storage object from the current storage.
492 * See Windows documentation for more details on IStorage methods.
494 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
495 IStorage* iface,
496 const OLECHAR* pwcsName, /* [string][unique][in] */
497 IStorage* pstgPriority, /* [unique][in] */
498 DWORD grfMode, /* [in] */
499 SNB snbExclude, /* [unique][in] */
500 DWORD reserved, /* [in] */
501 IStorage** ppstg) /* [out] */
503 StorageBaseImpl *This = (StorageBaseImpl *)iface;
504 StorageInternalImpl* newStorage;
505 IEnumSTATSTGImpl* propertyEnumeration;
506 StgProperty currentProperty;
507 ULONG foundPropertyIndex;
508 HRESULT res = STG_E_UNKNOWN;
510 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
511 iface, debugstr_w(pwcsName), pstgPriority,
512 grfMode, snbExclude, reserved, ppstg);
514 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
516 res = E_INVALIDARG;
517 goto end;
520 /* as documented */
521 if (snbExclude != NULL)
523 res = STG_E_INVALIDPARAMETER;
524 goto end;
527 if ( FAILED( validateSTGM(grfMode) ))
529 res = STG_E_INVALIDFLAG;
530 goto end;
534 * As documented.
536 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
537 (grfMode & STGM_DELETEONRELEASE) ||
538 (grfMode & STGM_PRIORITY) )
540 res = STG_E_INVALIDFUNCTION;
541 goto end;
545 * Check that we're compatible with the parent's storage mode,
546 * but only if we are not transacted
548 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
549 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
551 res = STG_E_ACCESSDENIED;
552 goto end;
556 *ppstg = NULL;
558 propertyEnumeration = IEnumSTATSTGImpl_Construct(
559 This->ancestorStorage,
560 This->rootPropertySetIndex);
562 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
563 propertyEnumeration,
564 pwcsName,
565 &currentProperty);
567 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
569 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
570 (currentProperty.propertyType==PROPTYPE_STORAGE) )
572 newStorage = StorageInternalImpl_Construct(
573 This->ancestorStorage,
574 grfMode,
575 foundPropertyIndex);
577 if (newStorage != 0)
579 *ppstg = (IStorage*)newStorage;
581 StorageBaseImpl_AddRef(*ppstg);
583 res = S_OK;
584 goto end;
587 res = STG_E_INSUFFICIENTMEMORY;
588 goto end;
591 res = STG_E_FILENOTFOUND;
593 end:
594 TRACE("<-- %08x\n", res);
595 return res;
598 /************************************************************************
599 * Storage32BaseImpl_EnumElements (IStorage)
601 * This method will create an enumerator object that can be used to
602 * retrieve information about all the properties in the storage object.
604 * See Windows documentation for more details on IStorage methods.
606 static HRESULT WINAPI StorageBaseImpl_EnumElements(
607 IStorage* iface,
608 DWORD reserved1, /* [in] */
609 void* reserved2, /* [size_is][unique][in] */
610 DWORD reserved3, /* [in] */
611 IEnumSTATSTG** ppenum) /* [out] */
613 StorageBaseImpl *This = (StorageBaseImpl *)iface;
614 IEnumSTATSTGImpl* newEnum;
616 TRACE("(%p, %d, %p, %d, %p)\n",
617 iface, reserved1, reserved2, reserved3, ppenum);
619 if ( (This==0) || (ppenum==0))
620 return E_INVALIDARG;
622 newEnum = IEnumSTATSTGImpl_Construct(
623 This->ancestorStorage,
624 This->rootPropertySetIndex);
626 if (newEnum!=0)
628 *ppenum = (IEnumSTATSTG*)newEnum;
630 IEnumSTATSTG_AddRef(*ppenum);
632 return S_OK;
635 return E_OUTOFMEMORY;
638 /************************************************************************
639 * Storage32BaseImpl_Stat (IStorage)
641 * This method will retrieve information about this storage object.
643 * See Windows documentation for more details on IStorage methods.
645 static HRESULT WINAPI StorageBaseImpl_Stat(
646 IStorage* iface,
647 STATSTG* pstatstg, /* [out] */
648 DWORD grfStatFlag) /* [in] */
650 StorageBaseImpl *This = (StorageBaseImpl *)iface;
651 StgProperty curProperty;
652 BOOL readSuccessful;
653 HRESULT res = STG_E_UNKNOWN;
655 TRACE("(%p, %p, %x)\n",
656 iface, pstatstg, grfStatFlag);
658 if ( (This==0) || (pstatstg==0))
660 res = E_INVALIDARG;
661 goto end;
664 readSuccessful = StorageImpl_ReadProperty(
665 This->ancestorStorage,
666 This->rootPropertySetIndex,
667 &curProperty);
669 if (readSuccessful)
671 StorageUtl_CopyPropertyToSTATSTG(
672 pstatstg,
673 &curProperty,
674 grfStatFlag);
676 pstatstg->grfMode = This->openFlags;
677 pstatstg->grfStateBits = This->stateBits;
679 res = S_OK;
680 goto end;
683 res = E_FAIL;
685 end:
686 if (res == S_OK)
688 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
690 TRACE("<-- %08x\n", res);
691 return res;
694 /************************************************************************
695 * Storage32BaseImpl_RenameElement (IStorage)
697 * This method will rename the specified element.
699 * See Windows documentation for more details on IStorage methods.
701 * Implementation notes: The method used to rename consists of creating a clone
702 * of the deleted StgProperty object setting it with the new name and to
703 * perform a DestroyElement of the old StgProperty.
705 static HRESULT WINAPI StorageBaseImpl_RenameElement(
706 IStorage* iface,
707 const OLECHAR* pwcsOldName, /* [in] */
708 const OLECHAR* pwcsNewName) /* [in] */
710 StorageBaseImpl *This = (StorageBaseImpl *)iface;
711 IEnumSTATSTGImpl* propertyEnumeration;
712 StgProperty currentProperty;
713 ULONG foundPropertyIndex;
715 TRACE("(%p, %s, %s)\n",
716 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
718 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
719 This->rootPropertySetIndex);
721 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
722 pwcsNewName,
723 &currentProperty);
725 if (foundPropertyIndex != PROPERTY_NULL)
728 * There is already a property with the new name
730 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
731 return STG_E_FILEALREADYEXISTS;
734 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
737 * Search the enumeration for the old property name
739 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
740 pwcsOldName,
741 &currentProperty);
743 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
745 if (foundPropertyIndex != PROPERTY_NULL)
747 StgProperty renamedProperty;
748 ULONG renamedPropertyIndex;
751 * Setup a new property for the renamed property
753 renamedProperty.sizeOfNameString =
754 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
756 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
757 return STG_E_INVALIDNAME;
759 strcpyW(renamedProperty.name, pwcsNewName);
761 renamedProperty.propertyType = currentProperty.propertyType;
762 renamedProperty.startingBlock = currentProperty.startingBlock;
763 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
764 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
766 renamedProperty.previousProperty = PROPERTY_NULL;
767 renamedProperty.nextProperty = PROPERTY_NULL;
770 * Bring the dirProperty link in case it is a storage and in which
771 * case the renamed storage elements don't require to be reorganized.
773 renamedProperty.dirProperty = currentProperty.dirProperty;
775 /* call CoFileTime to get the current time
776 renamedProperty.timeStampS1
777 renamedProperty.timeStampD1
778 renamedProperty.timeStampS2
779 renamedProperty.timeStampD2
780 renamedProperty.propertyUniqueID
784 * Obtain a free property in the property chain
786 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
789 * Save the new property into the new property spot
791 StorageImpl_WriteProperty(
792 This->ancestorStorage,
793 renamedPropertyIndex,
794 &renamedProperty);
797 * Find a spot in the property chain for our newly created property.
799 updatePropertyChain(
800 (StorageImpl*)This,
801 renamedPropertyIndex,
802 renamedProperty);
805 * At this point the renamed property has been inserted in the tree,
806 * now, before Destroying the old property we must zero its dirProperty
807 * otherwise the DestroyProperty below will zap it all and we do not want
808 * this to happen.
809 * Also, we fake that the old property is a storage so the DestroyProperty
810 * will not do a SetSize(0) on the stream data.
812 * This means that we need to tweak the StgProperty if it is a stream or a
813 * non empty storage.
815 StorageImpl_ReadProperty(This->ancestorStorage,
816 foundPropertyIndex,
817 &currentProperty);
819 currentProperty.dirProperty = PROPERTY_NULL;
820 currentProperty.propertyType = PROPTYPE_STORAGE;
821 StorageImpl_WriteProperty(
822 This->ancestorStorage,
823 foundPropertyIndex,
824 &currentProperty);
827 * Invoke Destroy to get rid of the ole property and automatically redo
828 * the linking of its previous and next members...
830 IStorage_DestroyElement(iface, pwcsOldName);
833 else
836 * There is no property with the old name
838 return STG_E_FILENOTFOUND;
841 return S_OK;
844 /************************************************************************
845 * Storage32BaseImpl_CreateStream (IStorage)
847 * This method will create a stream object within this storage
849 * See Windows documentation for more details on IStorage methods.
851 static HRESULT WINAPI StorageBaseImpl_CreateStream(
852 IStorage* iface,
853 const OLECHAR* pwcsName, /* [string][in] */
854 DWORD grfMode, /* [in] */
855 DWORD reserved1, /* [in] */
856 DWORD reserved2, /* [in] */
857 IStream** ppstm) /* [out] */
859 StorageBaseImpl *This = (StorageBaseImpl *)iface;
860 IEnumSTATSTGImpl* propertyEnumeration;
861 StgStreamImpl* newStream;
862 StgProperty currentProperty, newStreamProperty;
863 ULONG foundPropertyIndex, newPropertyIndex;
865 TRACE("(%p, %s, %x, %d, %d, %p)\n",
866 iface, debugstr_w(pwcsName), grfMode,
867 reserved1, reserved2, ppstm);
869 if (ppstm == 0)
870 return STG_E_INVALIDPOINTER;
872 if (pwcsName == 0)
873 return STG_E_INVALIDNAME;
875 if (reserved1 || reserved2)
876 return STG_E_INVALIDPARAMETER;
878 if ( FAILED( validateSTGM(grfMode) ))
879 return STG_E_INVALIDFLAG;
881 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
882 return STG_E_INVALIDFLAG;
885 * As documented.
887 if ((grfMode & STGM_DELETEONRELEASE) ||
888 (grfMode & STGM_TRANSACTED))
889 return STG_E_INVALIDFUNCTION;
891 /* Can't create a stream on read-only storage */
892 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
893 return STG_E_ACCESSDENIED;
896 * Check that we're compatible with the parent's storage mode
897 * if not in transacted mode
899 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
900 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
901 return STG_E_ACCESSDENIED;
904 if(This->ancestorStorage->base.openFlags & STGM_SIMPLE)
905 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
907 *ppstm = 0;
909 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
910 This->rootPropertySetIndex);
912 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
913 pwcsName,
914 &currentProperty);
916 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
918 if (foundPropertyIndex != PROPERTY_NULL)
921 * An element with this name already exists
923 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
925 StgStreamImpl *strm;
927 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
929 if (strm->ownerProperty == foundPropertyIndex)
931 TRACE("Stream deleted %p\n", strm);
932 strm->parentStorage = NULL;
933 list_remove(&strm->StrmListEntry);
936 IStorage_DestroyElement(iface, pwcsName);
938 else
939 return STG_E_FILEALREADYEXISTS;
941 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
943 WARN("read-only storage\n");
944 return STG_E_ACCESSDENIED;
948 * memset the empty property
950 memset(&newStreamProperty, 0, sizeof(StgProperty));
952 newStreamProperty.sizeOfNameString =
953 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
955 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
956 return STG_E_INVALIDNAME;
958 strcpyW(newStreamProperty.name, pwcsName);
960 newStreamProperty.propertyType = PROPTYPE_STREAM;
961 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
962 newStreamProperty.size.u.LowPart = 0;
963 newStreamProperty.size.u.HighPart = 0;
965 newStreamProperty.previousProperty = PROPERTY_NULL;
966 newStreamProperty.nextProperty = PROPERTY_NULL;
967 newStreamProperty.dirProperty = PROPERTY_NULL;
969 /* call CoFileTime to get the current time
970 newStreamProperty.timeStampS1
971 newStreamProperty.timeStampD1
972 newStreamProperty.timeStampS2
973 newStreamProperty.timeStampD2
976 /* newStreamProperty.propertyUniqueID */
979 * Get a free property or create a new one
981 newPropertyIndex = getFreeProperty(This->ancestorStorage);
984 * Save the new property into the new property spot
986 StorageImpl_WriteProperty(
987 This->ancestorStorage,
988 newPropertyIndex,
989 &newStreamProperty);
992 * Find a spot in the property chain for our newly created property.
994 updatePropertyChain(
995 (StorageImpl*)This,
996 newPropertyIndex,
997 newStreamProperty);
1000 * Open the stream to return it.
1002 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1004 if (newStream != 0)
1006 *ppstm = (IStream*)newStream;
1008 IStream_AddRef(*ppstm);
1010 else
1012 return STG_E_INSUFFICIENTMEMORY;
1015 return S_OK;
1018 /************************************************************************
1019 * Storage32BaseImpl_SetClass (IStorage)
1021 * This method will write the specified CLSID in the property of this
1022 * storage.
1024 * See Windows documentation for more details on IStorage methods.
1026 static HRESULT WINAPI StorageBaseImpl_SetClass(
1027 IStorage* iface,
1028 REFCLSID clsid) /* [in] */
1030 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1031 HRESULT hRes = E_FAIL;
1032 StgProperty curProperty;
1033 BOOL success;
1035 TRACE("(%p, %p)\n", iface, clsid);
1037 success = StorageImpl_ReadProperty(This->ancestorStorage,
1038 This->rootPropertySetIndex,
1039 &curProperty);
1040 if (success)
1042 curProperty.propertyUniqueID = *clsid;
1044 success = StorageImpl_WriteProperty(This->ancestorStorage,
1045 This->rootPropertySetIndex,
1046 &curProperty);
1047 if (success)
1048 hRes = S_OK;
1051 return hRes;
1054 /************************************************************************
1055 ** Storage32Impl implementation
1058 /************************************************************************
1059 * Storage32Impl_CreateStorage (IStorage)
1061 * This method will create the storage object within the provided storage.
1063 * See Windows documentation for more details on IStorage methods.
1065 static HRESULT WINAPI StorageImpl_CreateStorage(
1066 IStorage* iface,
1067 const OLECHAR *pwcsName, /* [string][in] */
1068 DWORD grfMode, /* [in] */
1069 DWORD reserved1, /* [in] */
1070 DWORD reserved2, /* [in] */
1071 IStorage **ppstg) /* [out] */
1073 StorageImpl* const This=(StorageImpl*)iface;
1075 IEnumSTATSTGImpl *propertyEnumeration;
1076 StgProperty currentProperty;
1077 StgProperty newProperty;
1078 ULONG foundPropertyIndex;
1079 ULONG newPropertyIndex;
1080 HRESULT hr;
1082 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1083 iface, debugstr_w(pwcsName), grfMode,
1084 reserved1, reserved2, ppstg);
1086 if (ppstg == 0)
1087 return STG_E_INVALIDPOINTER;
1089 if (pwcsName == 0)
1090 return STG_E_INVALIDNAME;
1092 *ppstg = NULL;
1094 if ( FAILED( validateSTGM(grfMode) ) ||
1095 (grfMode & STGM_DELETEONRELEASE) )
1097 WARN("bad grfMode: 0x%x\n", grfMode);
1098 return STG_E_INVALIDFLAG;
1102 * Check that we're compatible with the parent's storage mode
1104 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1106 WARN("access denied\n");
1107 return STG_E_ACCESSDENIED;
1111 * Create a property enumeration and search the properties
1113 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1114 This->base.rootPropertySetIndex);
1116 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1117 pwcsName,
1118 &currentProperty);
1119 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1121 if (foundPropertyIndex != PROPERTY_NULL)
1124 * An element with this name already exists
1126 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1127 STGM_ACCESS_MODE(This->base.openFlags) != STGM_READ)
1129 hr = IStorage_DestroyElement(iface, pwcsName);
1130 if (FAILED(hr))
1131 return hr;
1133 else
1135 WARN("file already exists\n");
1136 return STG_E_FILEALREADYEXISTS;
1139 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1141 WARN("read-only storage\n");
1142 return STG_E_ACCESSDENIED;
1146 * memset the empty property
1148 memset(&newProperty, 0, sizeof(StgProperty));
1150 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1152 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1154 FIXME("name too long\n");
1155 return STG_E_INVALIDNAME;
1158 strcpyW(newProperty.name, pwcsName);
1160 newProperty.propertyType = PROPTYPE_STORAGE;
1161 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1162 newProperty.size.u.LowPart = 0;
1163 newProperty.size.u.HighPart = 0;
1165 newProperty.previousProperty = PROPERTY_NULL;
1166 newProperty.nextProperty = PROPERTY_NULL;
1167 newProperty.dirProperty = PROPERTY_NULL;
1169 /* call CoFileTime to get the current time
1170 newProperty.timeStampS1
1171 newProperty.timeStampD1
1172 newProperty.timeStampS2
1173 newProperty.timeStampD2
1176 /* newStorageProperty.propertyUniqueID */
1179 * Obtain a free property in the property chain
1181 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1184 * Save the new property into the new property spot
1186 StorageImpl_WriteProperty(
1187 This->base.ancestorStorage,
1188 newPropertyIndex,
1189 &newProperty);
1192 * Find a spot in the property chain for our newly created property.
1194 updatePropertyChain(
1195 This,
1196 newPropertyIndex,
1197 newProperty);
1200 * Open it to get a pointer to return.
1202 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1204 if( (hr != S_OK) || (*ppstg == NULL))
1206 return hr;
1210 return S_OK;
1214 /***************************************************************************
1216 * Internal Method
1218 * Get a free property or create a new one.
1220 static ULONG getFreeProperty(
1221 StorageImpl *storage)
1223 ULONG currentPropertyIndex = 0;
1224 ULONG newPropertyIndex = PROPERTY_NULL;
1225 BOOL readSuccessful = TRUE;
1226 StgProperty currentProperty;
1231 * Start by reading the root property
1233 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1234 currentPropertyIndex,
1235 &currentProperty);
1236 if (readSuccessful)
1238 if (currentProperty.sizeOfNameString == 0)
1241 * The property existis and is available, we found it.
1243 newPropertyIndex = currentPropertyIndex;
1246 else
1249 * We exhausted the property list, we will create more space below
1251 newPropertyIndex = currentPropertyIndex;
1253 currentPropertyIndex++;
1255 } while (newPropertyIndex == PROPERTY_NULL);
1258 * grow the property chain
1260 if (! readSuccessful)
1262 StgProperty emptyProperty;
1263 ULARGE_INTEGER newSize;
1264 ULONG propertyIndex;
1265 ULONG lastProperty = 0;
1266 ULONG blockCount = 0;
1269 * obtain the new count of property blocks
1271 blockCount = BlockChainStream_GetCount(
1272 storage->base.ancestorStorage->rootBlockChain)+1;
1275 * initialize the size used by the property stream
1277 newSize.u.HighPart = 0;
1278 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1281 * add a property block to the property chain
1283 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1286 * memset the empty property in order to initialize the unused newly
1287 * created property
1289 memset(&emptyProperty, 0, sizeof(StgProperty));
1292 * initialize them
1294 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1296 for(
1297 propertyIndex = newPropertyIndex;
1298 propertyIndex < lastProperty;
1299 propertyIndex++)
1301 StorageImpl_WriteProperty(
1302 storage->base.ancestorStorage,
1303 propertyIndex,
1304 &emptyProperty);
1308 return newPropertyIndex;
1311 /****************************************************************************
1313 * Internal Method
1315 * Case insensitive comparison of StgProperty.name by first considering
1316 * their size.
1318 * Returns <0 when newProperty < currentProperty
1319 * >0 when newProperty > currentProperty
1320 * 0 when newProperty == currentProperty
1322 static LONG propertyNameCmp(
1323 const OLECHAR *newProperty,
1324 const OLECHAR *currentProperty)
1326 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1328 if (diff == 0)
1331 * We compare the string themselves only when they are of the same length
1333 diff = lstrcmpiW( newProperty, currentProperty);
1336 return diff;
1339 /****************************************************************************
1341 * Internal Method
1343 * Properly link this new element in the property chain.
1345 static void updatePropertyChain(
1346 StorageImpl *storage,
1347 ULONG newPropertyIndex,
1348 StgProperty newProperty)
1350 StgProperty currentProperty;
1353 * Read the root property
1355 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1356 storage->base.rootPropertySetIndex,
1357 &currentProperty);
1359 if (currentProperty.dirProperty != PROPERTY_NULL)
1362 * The root storage contains some element, therefore, start the research
1363 * for the appropriate location.
1365 BOOL found = 0;
1366 ULONG current, next, previous, currentPropertyId;
1369 * Keep the StgProperty sequence number of the storage first property
1371 currentPropertyId = currentProperty.dirProperty;
1374 * Read
1376 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1377 currentProperty.dirProperty,
1378 &currentProperty);
1380 previous = currentProperty.previousProperty;
1381 next = currentProperty.nextProperty;
1382 current = currentPropertyId;
1384 while (found == 0)
1386 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1388 if (diff < 0)
1390 if (previous != PROPERTY_NULL)
1392 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1393 previous,
1394 &currentProperty);
1395 current = previous;
1397 else
1399 currentProperty.previousProperty = newPropertyIndex;
1400 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1401 current,
1402 &currentProperty);
1403 found = 1;
1406 else if (diff > 0)
1408 if (next != PROPERTY_NULL)
1410 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1411 next,
1412 &currentProperty);
1413 current = next;
1415 else
1417 currentProperty.nextProperty = newPropertyIndex;
1418 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1419 current,
1420 &currentProperty);
1421 found = 1;
1424 else
1427 * Trying to insert an item with the same name in the
1428 * subtree structure.
1430 assert(FALSE);
1433 previous = currentProperty.previousProperty;
1434 next = currentProperty.nextProperty;
1437 else
1440 * The root storage is empty, link the new property to its dir property
1442 currentProperty.dirProperty = newPropertyIndex;
1443 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1444 storage->base.rootPropertySetIndex,
1445 &currentProperty);
1450 /*************************************************************************
1451 * CopyTo (IStorage)
1453 static HRESULT WINAPI StorageImpl_CopyTo(
1454 IStorage* iface,
1455 DWORD ciidExclude, /* [in] */
1456 const IID* rgiidExclude, /* [size_is][unique][in] */
1457 SNB snbExclude, /* [unique][in] */
1458 IStorage* pstgDest) /* [unique][in] */
1460 IEnumSTATSTG *elements = 0;
1461 STATSTG curElement, strStat;
1462 HRESULT hr;
1463 IStorage *pstgTmp, *pstgChild;
1464 IStream *pstrTmp, *pstrChild;
1465 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1466 int i;
1468 TRACE("(%p, %d, %p, %p, %p)\n",
1469 iface, ciidExclude, rgiidExclude,
1470 snbExclude, pstgDest);
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 );
1489 for(i = 0; i < ciidExclude; ++i)
1491 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1492 skip_storage = TRUE;
1493 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1494 skip_stream = TRUE;
1495 else
1496 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1502 * Obtain the next element
1504 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1506 if ( hr == S_FALSE )
1508 hr = S_OK; /* done, every element has been copied */
1509 break;
1512 if ( snbExclude )
1514 WCHAR **snb = snbExclude;
1515 skip = FALSE;
1516 while ( *snb != NULL && !skip )
1518 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1519 skip = TRUE;
1520 ++snb;
1524 if ( skip )
1525 continue;
1527 if (curElement.type == STGTY_STORAGE)
1529 if(skip_storage)
1530 continue;
1533 * open child source storage
1535 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1536 STGM_READ|STGM_SHARE_EXCLUSIVE,
1537 NULL, 0, &pstgChild );
1539 if (hr != S_OK)
1540 break;
1543 * Check if destination storage is not a child of the source
1544 * storage, which will cause an infinite loop
1546 if (pstgChild == pstgDest)
1548 IEnumSTATSTG_Release(elements);
1550 return STG_E_ACCESSDENIED;
1554 * create a new storage in destination storage
1556 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1557 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1558 0, 0,
1559 &pstgTmp );
1561 * if it already exist, don't create a new one use this one
1563 if (hr == STG_E_FILEALREADYEXISTS)
1565 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1566 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1567 NULL, 0, &pstgTmp );
1570 if (hr != S_OK)
1571 break;
1575 * do the copy recursively
1577 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1578 NULL, pstgTmp );
1580 IStorage_Release( pstgTmp );
1581 IStorage_Release( pstgChild );
1583 else if (curElement.type == STGTY_STREAM)
1585 if(skip_stream)
1586 continue;
1589 * create a new stream in destination storage. If the stream already
1590 * exist, it will be deleted and a new one will be created.
1592 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1593 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1594 0, 0, &pstrTmp );
1596 if (hr != S_OK)
1597 break;
1600 * open child stream storage
1602 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1603 STGM_READ|STGM_SHARE_EXCLUSIVE,
1604 0, &pstrChild );
1606 if (hr != S_OK)
1607 break;
1610 * Get the size of the source stream
1612 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1615 * Set the size of the destination stream.
1617 IStream_SetSize(pstrTmp, strStat.cbSize);
1620 * do the copy
1622 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1623 NULL, NULL );
1625 IStream_Release( pstrTmp );
1626 IStream_Release( pstrChild );
1628 else
1630 WARN("unknown element type: %d\n", curElement.type);
1633 } while (hr == S_OK);
1636 * Clean-up
1638 IEnumSTATSTG_Release(elements);
1640 return hr;
1643 /*************************************************************************
1644 * MoveElementTo (IStorage)
1646 static HRESULT WINAPI StorageImpl_MoveElementTo(
1647 IStorage* iface,
1648 const OLECHAR *pwcsName, /* [string][in] */
1649 IStorage *pstgDest, /* [unique][in] */
1650 const OLECHAR *pwcsNewName,/* [string][in] */
1651 DWORD grfFlags) /* [in] */
1653 FIXME("(%p %s %p %s %u): stub\n", iface,
1654 debugstr_w(pwcsName), pstgDest,
1655 debugstr_w(pwcsNewName), grfFlags);
1656 return E_NOTIMPL;
1659 /*************************************************************************
1660 * Commit (IStorage)
1662 * Ensures that any changes made to a storage object open in transacted mode
1663 * are reflected in the parent storage
1665 * NOTES
1666 * Wine doesn't implement transacted mode, which seems to be a basic
1667 * optimization, so we can ignore this stub for now.
1669 static HRESULT WINAPI StorageImpl_Commit(
1670 IStorage* iface,
1671 DWORD grfCommitFlags)/* [in] */
1673 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1674 return S_OK;
1677 /*************************************************************************
1678 * Revert (IStorage)
1680 * Discard all changes that have been made since the last commit operation
1682 static HRESULT WINAPI StorageImpl_Revert(
1683 IStorage* iface)
1685 FIXME("(%p): stub\n", iface);
1686 return E_NOTIMPL;
1689 /*************************************************************************
1690 * DestroyElement (IStorage)
1692 * Strategy: This implementation is built this way for simplicity not for speed.
1693 * I always delete the topmost element of the enumeration and adjust
1694 * the deleted element pointer all the time. This takes longer to
1695 * do but allow to reinvoke DestroyElement whenever we encounter a
1696 * storage object. The optimisation resides in the usage of another
1697 * enumeration strategy that would give all the leaves of a storage
1698 * first. (postfix order)
1700 static HRESULT WINAPI StorageImpl_DestroyElement(
1701 IStorage* iface,
1702 const OLECHAR *pwcsName)/* [string][in] */
1704 StorageImpl* const This=(StorageImpl*)iface;
1706 IEnumSTATSTGImpl* propertyEnumeration;
1707 HRESULT hr = S_OK;
1708 BOOL res;
1709 StgProperty propertyToDelete;
1710 StgProperty parentProperty;
1711 ULONG foundPropertyIndexToDelete;
1712 ULONG typeOfRelation;
1713 ULONG parentPropertyId = 0;
1715 TRACE("(%p, %s)\n",
1716 iface, debugstr_w(pwcsName));
1718 if (pwcsName==NULL)
1719 return STG_E_INVALIDPOINTER;
1721 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
1722 return STG_E_ACCESSDENIED;
1724 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1725 This->base.ancestorStorage,
1726 This->base.rootPropertySetIndex);
1728 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1729 propertyEnumeration,
1730 pwcsName,
1731 &propertyToDelete);
1733 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1735 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1737 return STG_E_FILENOTFOUND;
1741 * Find the parent property of the property to delete (the one that
1742 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1743 * the parent is This. Otherwise, the parent is one of its sibling...
1747 * First, read This's StgProperty..
1749 res = StorageImpl_ReadProperty(
1750 This->base.ancestorStorage,
1751 This->base.rootPropertySetIndex,
1752 &parentProperty);
1754 assert(res);
1757 * Second, check to see if by any chance the actual storage (This) is not
1758 * the parent of the property to delete... We never know...
1760 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1763 * Set data as it would have been done in the else part...
1765 typeOfRelation = PROPERTY_RELATION_DIR;
1766 parentPropertyId = This->base.rootPropertySetIndex;
1768 else
1771 * Create a property enumeration to search the parent properties, and
1772 * delete it once done.
1774 IEnumSTATSTGImpl* propertyEnumeration2;
1776 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1777 This->base.ancestorStorage,
1778 This->base.rootPropertySetIndex);
1780 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1781 propertyEnumeration2,
1782 foundPropertyIndexToDelete,
1783 &parentProperty,
1784 &parentPropertyId);
1786 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1789 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1791 hr = deleteStorageProperty(
1792 This,
1793 foundPropertyIndexToDelete,
1794 propertyToDelete);
1796 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1798 hr = deleteStreamProperty(
1799 This,
1800 foundPropertyIndexToDelete,
1801 propertyToDelete);
1804 if (hr!=S_OK)
1805 return hr;
1808 * Adjust the property chain
1810 hr = adjustPropertyChain(
1811 This,
1812 propertyToDelete,
1813 parentProperty,
1814 parentPropertyId,
1815 typeOfRelation);
1817 return hr;
1821 /************************************************************************
1822 * StorageImpl_Stat (IStorage)
1824 * This method will retrieve information about this storage object.
1826 * See Windows documentation for more details on IStorage methods.
1828 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1829 STATSTG* pstatstg, /* [out] */
1830 DWORD grfStatFlag) /* [in] */
1832 StorageImpl* const This = (StorageImpl*)iface;
1833 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1835 if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1837 CoTaskMemFree(pstatstg->pwcsName);
1838 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1839 strcpyW(pstatstg->pwcsName, This->pwcsName);
1842 return result;
1845 /******************************************************************************
1846 * Internal stream list handlers
1849 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1851 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1852 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1855 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1857 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1858 list_remove(&(strm->StrmListEntry));
1861 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1863 struct list *cur, *cur2;
1864 StgStreamImpl *strm=NULL;
1866 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1867 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1868 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1869 strm->parentStorage = NULL;
1870 list_remove(cur);
1875 /*********************************************************************
1877 * Internal Method
1879 * Perform the deletion of a complete storage node
1882 static HRESULT deleteStorageProperty(
1883 StorageImpl *parentStorage,
1884 ULONG indexOfPropertyToDelete,
1885 StgProperty propertyToDelete)
1887 IEnumSTATSTG *elements = 0;
1888 IStorage *childStorage = 0;
1889 STATSTG currentElement;
1890 HRESULT hr;
1891 HRESULT destroyHr = S_OK;
1894 * Open the storage and enumerate it
1896 hr = StorageBaseImpl_OpenStorage(
1897 (IStorage*)parentStorage,
1898 propertyToDelete.name,
1900 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1903 &childStorage);
1905 if (hr != S_OK)
1907 return hr;
1911 * Enumerate the elements
1913 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1918 * Obtain the next element
1920 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1921 if (hr==S_OK)
1923 destroyHr = StorageImpl_DestroyElement(childStorage, currentElement.pwcsName);
1925 CoTaskMemFree(currentElement.pwcsName);
1929 * We need to Reset the enumeration every time because we delete elements
1930 * and the enumeration could be invalid
1932 IEnumSTATSTG_Reset(elements);
1934 } while ((hr == S_OK) && (destroyHr == S_OK));
1937 * Invalidate the property by zeroing its name member.
1939 propertyToDelete.sizeOfNameString = 0;
1941 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1942 indexOfPropertyToDelete,
1943 &propertyToDelete);
1945 IStorage_Release(childStorage);
1946 IEnumSTATSTG_Release(elements);
1948 return destroyHr;
1951 /*********************************************************************
1953 * Internal Method
1955 * Perform the deletion of a stream node
1958 static HRESULT deleteStreamProperty(
1959 StorageImpl *parentStorage,
1960 ULONG indexOfPropertyToDelete,
1961 StgProperty propertyToDelete)
1963 IStream *pis;
1964 HRESULT hr;
1965 ULARGE_INTEGER size;
1967 size.u.HighPart = 0;
1968 size.u.LowPart = 0;
1970 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
1971 propertyToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
1973 if (hr!=S_OK)
1975 return(hr);
1979 * Zap the stream
1981 hr = IStream_SetSize(pis, size);
1983 if(hr != S_OK)
1985 return hr;
1989 * Release the stream object.
1991 IStream_Release(pis);
1994 * Invalidate the property by zeroing its name member.
1996 propertyToDelete.sizeOfNameString = 0;
1999 * Here we should re-read the property so we get the updated pointer
2000 * but since we are here to zap it, I don't do it...
2002 StorageImpl_WriteProperty(
2003 parentStorage->base.ancestorStorage,
2004 indexOfPropertyToDelete,
2005 &propertyToDelete);
2007 return S_OK;
2010 /*********************************************************************
2012 * Internal Method
2014 * Finds a placeholder for the StgProperty within the Storage
2017 static HRESULT findPlaceholder(
2018 StorageImpl *storage,
2019 ULONG propertyIndexToStore,
2020 ULONG storePropertyIndex,
2021 INT typeOfRelation)
2023 StgProperty storeProperty;
2024 BOOL res = TRUE;
2027 * Read the storage property
2029 res = StorageImpl_ReadProperty(
2030 storage->base.ancestorStorage,
2031 storePropertyIndex,
2032 &storeProperty);
2034 if(! res)
2036 return E_FAIL;
2039 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2041 if (storeProperty.previousProperty != PROPERTY_NULL)
2043 return findPlaceholder(
2044 storage,
2045 propertyIndexToStore,
2046 storeProperty.previousProperty,
2047 typeOfRelation);
2049 else
2051 storeProperty.previousProperty = propertyIndexToStore;
2054 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2056 if (storeProperty.nextProperty != PROPERTY_NULL)
2058 return findPlaceholder(
2059 storage,
2060 propertyIndexToStore,
2061 storeProperty.nextProperty,
2062 typeOfRelation);
2064 else
2066 storeProperty.nextProperty = propertyIndexToStore;
2069 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2071 if (storeProperty.dirProperty != PROPERTY_NULL)
2073 return findPlaceholder(
2074 storage,
2075 propertyIndexToStore,
2076 storeProperty.dirProperty,
2077 typeOfRelation);
2079 else
2081 storeProperty.dirProperty = propertyIndexToStore;
2085 res = StorageImpl_WriteProperty(
2086 storage->base.ancestorStorage,
2087 storePropertyIndex,
2088 &storeProperty);
2090 if(!res)
2092 return E_FAIL;
2095 return S_OK;
2098 /*************************************************************************
2100 * Internal Method
2102 * This method takes the previous and the next property link of a property
2103 * to be deleted and find them a place in the Storage.
2105 static HRESULT adjustPropertyChain(
2106 StorageImpl *This,
2107 StgProperty propertyToDelete,
2108 StgProperty parentProperty,
2109 ULONG parentPropertyId,
2110 INT typeOfRelation)
2112 ULONG newLinkProperty = PROPERTY_NULL;
2113 BOOL needToFindAPlaceholder = FALSE;
2114 ULONG storeNode = PROPERTY_NULL;
2115 ULONG toStoreNode = PROPERTY_NULL;
2116 INT relationType = 0;
2117 HRESULT hr = S_OK;
2118 BOOL res = TRUE;
2120 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2122 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2125 * Set the parent previous to the property to delete previous
2127 newLinkProperty = propertyToDelete.previousProperty;
2129 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2132 * We also need to find a storage for the other link, setup variables
2133 * to do this at the end...
2135 needToFindAPlaceholder = TRUE;
2136 storeNode = propertyToDelete.previousProperty;
2137 toStoreNode = propertyToDelete.nextProperty;
2138 relationType = PROPERTY_RELATION_NEXT;
2141 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2144 * Set the parent previous to the property to delete next
2146 newLinkProperty = propertyToDelete.nextProperty;
2150 * Link it for real...
2152 parentProperty.previousProperty = newLinkProperty;
2155 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2157 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2160 * Set the parent next to the property to delete next previous
2162 newLinkProperty = propertyToDelete.previousProperty;
2164 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2167 * We also need to find a storage for the other link, setup variables
2168 * to do this at the end...
2170 needToFindAPlaceholder = TRUE;
2171 storeNode = propertyToDelete.previousProperty;
2172 toStoreNode = propertyToDelete.nextProperty;
2173 relationType = PROPERTY_RELATION_NEXT;
2176 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2179 * Set the parent next to the property to delete next
2181 newLinkProperty = propertyToDelete.nextProperty;
2185 * Link it for real...
2187 parentProperty.nextProperty = newLinkProperty;
2189 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2191 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2194 * Set the parent dir to the property to delete previous
2196 newLinkProperty = propertyToDelete.previousProperty;
2198 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2201 * We also need to find a storage for the other link, setup variables
2202 * to do this at the end...
2204 needToFindAPlaceholder = TRUE;
2205 storeNode = propertyToDelete.previousProperty;
2206 toStoreNode = propertyToDelete.nextProperty;
2207 relationType = PROPERTY_RELATION_NEXT;
2210 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2213 * Set the parent dir to the property to delete next
2215 newLinkProperty = propertyToDelete.nextProperty;
2219 * Link it for real...
2221 parentProperty.dirProperty = newLinkProperty;
2225 * Write back the parent property
2227 res = StorageImpl_WriteProperty(
2228 This->base.ancestorStorage,
2229 parentPropertyId,
2230 &parentProperty);
2231 if(! res)
2233 return E_FAIL;
2237 * If a placeholder is required for the other link, then, find one and
2238 * get out of here...
2240 if (needToFindAPlaceholder)
2242 hr = findPlaceholder(
2243 This,
2244 toStoreNode,
2245 storeNode,
2246 relationType);
2249 return hr;
2253 /******************************************************************************
2254 * SetElementTimes (IStorage)
2256 static HRESULT WINAPI StorageImpl_SetElementTimes(
2257 IStorage* iface,
2258 const OLECHAR *pwcsName,/* [string][in] */
2259 const FILETIME *pctime, /* [in] */
2260 const FILETIME *patime, /* [in] */
2261 const FILETIME *pmtime) /* [in] */
2263 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2264 return S_OK;
2267 /******************************************************************************
2268 * SetStateBits (IStorage)
2270 static HRESULT WINAPI StorageImpl_SetStateBits(
2271 IStorage* iface,
2272 DWORD grfStateBits,/* [in] */
2273 DWORD grfMask) /* [in] */
2275 StorageImpl* const This = (StorageImpl*)iface;
2276 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2277 return S_OK;
2281 * Virtual function table for the IStorage32Impl class.
2283 static const IStorageVtbl Storage32Impl_Vtbl =
2285 StorageBaseImpl_QueryInterface,
2286 StorageBaseImpl_AddRef,
2287 StorageBaseImpl_Release,
2288 StorageBaseImpl_CreateStream,
2289 StorageBaseImpl_OpenStream,
2290 StorageImpl_CreateStorage,
2291 StorageBaseImpl_OpenStorage,
2292 StorageImpl_CopyTo,
2293 StorageImpl_MoveElementTo,
2294 StorageImpl_Commit,
2295 StorageImpl_Revert,
2296 StorageBaseImpl_EnumElements,
2297 StorageImpl_DestroyElement,
2298 StorageBaseImpl_RenameElement,
2299 StorageImpl_SetElementTimes,
2300 StorageBaseImpl_SetClass,
2301 StorageImpl_SetStateBits,
2302 StorageImpl_Stat
2305 static HRESULT StorageImpl_Construct(
2306 StorageImpl* This,
2307 HANDLE hFile,
2308 LPCOLESTR pwcsName,
2309 ILockBytes* pLkbyt,
2310 DWORD openFlags,
2311 BOOL fileBased,
2312 BOOL create)
2314 HRESULT hr = S_OK;
2315 StgProperty currentProperty;
2316 BOOL readSuccessful;
2317 ULONG currentPropertyIndex;
2319 if ( FAILED( validateSTGM(openFlags) ))
2320 return STG_E_INVALIDFLAG;
2322 memset(This, 0, sizeof(StorageImpl));
2324 list_init(&This->base.strmHead);
2326 This->base.lpVtbl = &Storage32Impl_Vtbl;
2327 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2328 This->base.v_destructor = StorageImpl_Destroy;
2329 This->base.openFlags = (openFlags & ~STGM_CREATE);
2330 This->create = create;
2333 * This is the top-level storage so initialize the ancestor pointer
2334 * to this.
2336 This->base.ancestorStorage = This;
2338 This->hFile = hFile;
2340 if(pwcsName) {
2341 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2342 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2343 if (!This->pwcsName)
2344 return STG_E_INSUFFICIENTMEMORY;
2345 strcpyW(This->pwcsName, pwcsName);
2349 * Initialize the big block cache.
2351 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2352 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2353 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2354 pLkbyt,
2355 openFlags,
2356 This->bigBlockSize,
2357 fileBased);
2359 if (This->bigBlockFile == 0)
2360 return E_FAIL;
2362 if (create)
2364 ULARGE_INTEGER size;
2365 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2368 * Initialize all header variables:
2369 * - The big block depot consists of one block and it is at block 0
2370 * - The properties start at block 1
2371 * - There is no small block depot
2373 memset( This->bigBlockDepotStart,
2374 BLOCK_UNUSED,
2375 sizeof(This->bigBlockDepotStart));
2377 This->bigBlockDepotCount = 1;
2378 This->bigBlockDepotStart[0] = 0;
2379 This->rootStartBlock = 1;
2380 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2381 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2382 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2383 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2384 This->extBigBlockDepotCount = 0;
2386 StorageImpl_SaveFileHeader(This);
2389 * Add one block for the big block depot and one block for the properties
2391 size.u.HighPart = 0;
2392 size.u.LowPart = This->bigBlockSize * 3;
2393 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2396 * Initialize the big block depot
2398 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2399 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2400 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2401 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2403 else
2406 * Load the header for the file.
2408 hr = StorageImpl_LoadFileHeader(This);
2410 if (FAILED(hr))
2412 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2414 return hr;
2419 * There is no block depot cached yet.
2421 This->indexBlockDepotCached = 0xFFFFFFFF;
2424 * Start searching for free blocks with block 0.
2426 This->prevFreeBlock = 0;
2429 * Create the block chain abstractions.
2431 if(!(This->rootBlockChain =
2432 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2433 return STG_E_READFAULT;
2435 if(!(This->smallBlockDepotChain =
2436 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2437 PROPERTY_NULL)))
2438 return STG_E_READFAULT;
2441 * Write the root property (memory only)
2443 if (create)
2445 StgProperty rootProp;
2447 * Initialize the property chain
2449 memset(&rootProp, 0, sizeof(rootProp));
2450 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2451 sizeof(rootProp.name)/sizeof(WCHAR) );
2452 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2453 rootProp.propertyType = PROPTYPE_ROOT;
2454 rootProp.previousProperty = PROPERTY_NULL;
2455 rootProp.nextProperty = PROPERTY_NULL;
2456 rootProp.dirProperty = PROPERTY_NULL;
2457 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2458 rootProp.size.u.HighPart = 0;
2459 rootProp.size.u.LowPart = 0;
2461 StorageImpl_WriteProperty(This, 0, &rootProp);
2465 * Find the ID of the root in the property sets.
2467 currentPropertyIndex = 0;
2471 readSuccessful = StorageImpl_ReadProperty(
2472 This,
2473 currentPropertyIndex,
2474 &currentProperty);
2476 if (readSuccessful)
2478 if ( (currentProperty.sizeOfNameString != 0 ) &&
2479 (currentProperty.propertyType == PROPTYPE_ROOT) )
2481 This->base.rootPropertySetIndex = currentPropertyIndex;
2485 currentPropertyIndex++;
2487 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2489 if (!readSuccessful)
2491 /* TODO CLEANUP */
2492 return STG_E_READFAULT;
2496 * Create the block chain abstraction for the small block root chain.
2498 if(!(This->smallBlockRootChain =
2499 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2500 return STG_E_READFAULT;
2502 return hr;
2505 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2507 StorageImpl *This = (StorageImpl*) iface;
2508 TRACE("(%p)\n", This);
2510 StorageBaseImpl_DeleteAll(&This->base);
2512 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2514 BlockChainStream_Destroy(This->smallBlockRootChain);
2515 BlockChainStream_Destroy(This->rootBlockChain);
2516 BlockChainStream_Destroy(This->smallBlockDepotChain);
2518 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2519 HeapFree(GetProcessHeap(), 0, This);
2522 /******************************************************************************
2523 * Storage32Impl_GetNextFreeBigBlock
2525 * Returns the index of the next free big block.
2526 * If the big block depot is filled, this method will enlarge it.
2529 static ULONG StorageImpl_GetNextFreeBigBlock(
2530 StorageImpl* This)
2532 ULONG depotBlockIndexPos;
2533 BYTE depotBuffer[BIG_BLOCK_SIZE];
2534 BOOL success;
2535 ULONG depotBlockOffset;
2536 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2537 ULONG nextBlockIndex = BLOCK_SPECIAL;
2538 int depotIndex = 0;
2539 ULONG freeBlock = BLOCK_UNUSED;
2541 depotIndex = This->prevFreeBlock / blocksPerDepot;
2542 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2545 * Scan the entire big block depot until we find a block marked free
2547 while (nextBlockIndex != BLOCK_UNUSED)
2549 if (depotIndex < COUNT_BBDEPOTINHEADER)
2551 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2554 * Grow the primary depot.
2556 if (depotBlockIndexPos == BLOCK_UNUSED)
2558 depotBlockIndexPos = depotIndex*blocksPerDepot;
2561 * Add a block depot.
2563 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2564 This->bigBlockDepotCount++;
2565 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2568 * Flag it as a block depot.
2570 StorageImpl_SetNextBlockInChain(This,
2571 depotBlockIndexPos,
2572 BLOCK_SPECIAL);
2574 /* Save new header information.
2576 StorageImpl_SaveFileHeader(This);
2579 else
2581 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2583 if (depotBlockIndexPos == BLOCK_UNUSED)
2586 * Grow the extended depot.
2588 ULONG extIndex = BLOCK_UNUSED;
2589 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2590 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2592 if (extBlockOffset == 0)
2594 /* We need an extended block.
2596 extIndex = Storage32Impl_AddExtBlockDepot(This);
2597 This->extBigBlockDepotCount++;
2598 depotBlockIndexPos = extIndex + 1;
2600 else
2601 depotBlockIndexPos = depotIndex * blocksPerDepot;
2604 * Add a block depot and mark it in the extended block.
2606 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2607 This->bigBlockDepotCount++;
2608 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2610 /* Flag the block depot.
2612 StorageImpl_SetNextBlockInChain(This,
2613 depotBlockIndexPos,
2614 BLOCK_SPECIAL);
2616 /* If necessary, flag the extended depot block.
2618 if (extIndex != BLOCK_UNUSED)
2619 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2621 /* Save header information.
2623 StorageImpl_SaveFileHeader(This);
2627 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2629 if (success)
2631 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2632 ( nextBlockIndex != BLOCK_UNUSED))
2634 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2636 if (nextBlockIndex == BLOCK_UNUSED)
2638 freeBlock = (depotIndex * blocksPerDepot) +
2639 (depotBlockOffset/sizeof(ULONG));
2642 depotBlockOffset += sizeof(ULONG);
2646 depotIndex++;
2647 depotBlockOffset = 0;
2651 * make sure that the block physically exists before using it
2653 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2655 This->prevFreeBlock = freeBlock;
2657 return freeBlock;
2660 /******************************************************************************
2661 * Storage32Impl_AddBlockDepot
2663 * This will create a depot block, essentially it is a block initialized
2664 * to BLOCK_UNUSEDs.
2666 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2668 BYTE blockBuffer[BIG_BLOCK_SIZE];
2671 * Initialize blocks as free
2673 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2674 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2677 /******************************************************************************
2678 * Storage32Impl_GetExtDepotBlock
2680 * Returns the index of the block that corresponds to the specified depot
2681 * index. This method is only for depot indexes equal or greater than
2682 * COUNT_BBDEPOTINHEADER.
2684 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2686 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2687 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2688 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2689 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2690 ULONG blockIndex = BLOCK_UNUSED;
2691 ULONG extBlockIndex = This->extBigBlockDepotStart;
2693 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2695 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2696 return BLOCK_UNUSED;
2698 while (extBlockCount > 0)
2700 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2701 extBlockCount--;
2704 if (extBlockIndex != BLOCK_UNUSED)
2705 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2706 extBlockOffset * sizeof(ULONG), &blockIndex);
2708 return blockIndex;
2711 /******************************************************************************
2712 * Storage32Impl_SetExtDepotBlock
2714 * Associates the specified block index to the specified depot index.
2715 * This method is only for depot indexes equal or greater than
2716 * COUNT_BBDEPOTINHEADER.
2718 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2720 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2721 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2722 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2723 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2724 ULONG extBlockIndex = This->extBigBlockDepotStart;
2726 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2728 while (extBlockCount > 0)
2730 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2731 extBlockCount--;
2734 if (extBlockIndex != BLOCK_UNUSED)
2736 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2737 extBlockOffset * sizeof(ULONG),
2738 blockIndex);
2742 /******************************************************************************
2743 * Storage32Impl_AddExtBlockDepot
2745 * Creates an extended depot block.
2747 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2749 ULONG numExtBlocks = This->extBigBlockDepotCount;
2750 ULONG nextExtBlock = This->extBigBlockDepotStart;
2751 BYTE depotBuffer[BIG_BLOCK_SIZE];
2752 ULONG index = BLOCK_UNUSED;
2753 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2754 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2755 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2757 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2758 blocksPerDepotBlock;
2760 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2763 * The first extended block.
2765 This->extBigBlockDepotStart = index;
2767 else
2769 unsigned int i;
2771 * Follow the chain to the last one.
2773 for (i = 0; i < (numExtBlocks - 1); i++)
2775 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2779 * Add the new extended block to the chain.
2781 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2782 index);
2786 * Initialize this block.
2788 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2789 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2791 return index;
2794 /******************************************************************************
2795 * Storage32Impl_FreeBigBlock
2797 * This method will flag the specified block as free in the big block depot.
2799 static void StorageImpl_FreeBigBlock(
2800 StorageImpl* This,
2801 ULONG blockIndex)
2803 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2805 if (blockIndex < This->prevFreeBlock)
2806 This->prevFreeBlock = blockIndex;
2809 /************************************************************************
2810 * Storage32Impl_GetNextBlockInChain
2812 * This method will retrieve the block index of the next big block in
2813 * in the chain.
2815 * Params: This - Pointer to the Storage object.
2816 * blockIndex - Index of the block to retrieve the chain
2817 * for.
2818 * nextBlockIndex - receives the return value.
2820 * Returns: This method returns the index of the next block in the chain.
2821 * It will return the constants:
2822 * BLOCK_SPECIAL - If the block given was not part of a
2823 * chain.
2824 * BLOCK_END_OF_CHAIN - If the block given was the last in
2825 * a chain.
2826 * BLOCK_UNUSED - If the block given was not past of a chain
2827 * and is available.
2828 * BLOCK_EXTBBDEPOT - This block is part of the extended
2829 * big block depot.
2831 * See Windows documentation for more details on IStorage methods.
2833 static HRESULT StorageImpl_GetNextBlockInChain(
2834 StorageImpl* This,
2835 ULONG blockIndex,
2836 ULONG* nextBlockIndex)
2838 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2839 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2840 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2841 BYTE depotBuffer[BIG_BLOCK_SIZE];
2842 BOOL success;
2843 ULONG depotBlockIndexPos;
2844 int index;
2846 *nextBlockIndex = BLOCK_SPECIAL;
2848 if(depotBlockCount >= This->bigBlockDepotCount)
2850 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2851 This->bigBlockDepotCount);
2852 return STG_E_READFAULT;
2856 * Cache the currently accessed depot block.
2858 if (depotBlockCount != This->indexBlockDepotCached)
2860 This->indexBlockDepotCached = depotBlockCount;
2862 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2864 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2866 else
2869 * We have to look in the extended depot.
2871 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2874 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2876 if (!success)
2877 return STG_E_READFAULT;
2879 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2881 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2882 This->blockDepotCached[index] = *nextBlockIndex;
2886 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2888 return S_OK;
2891 /******************************************************************************
2892 * Storage32Impl_GetNextExtendedBlock
2894 * Given an extended block this method will return the next extended block.
2896 * NOTES:
2897 * The last ULONG of an extended block is the block index of the next
2898 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2899 * depot.
2901 * Return values:
2902 * - The index of the next extended block
2903 * - BLOCK_UNUSED: there is no next extended block.
2904 * - Any other return values denotes failure.
2906 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2908 ULONG nextBlockIndex = BLOCK_SPECIAL;
2909 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2911 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
2912 &nextBlockIndex);
2914 return nextBlockIndex;
2917 /******************************************************************************
2918 * Storage32Impl_SetNextBlockInChain
2920 * This method will write the index of the specified block's next block
2921 * in the big block depot.
2923 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2924 * do the following
2926 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2927 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2928 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2931 static void StorageImpl_SetNextBlockInChain(
2932 StorageImpl* This,
2933 ULONG blockIndex,
2934 ULONG nextBlock)
2936 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2937 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2938 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2939 ULONG depotBlockIndexPos;
2941 assert(depotBlockCount < This->bigBlockDepotCount);
2942 assert(blockIndex != nextBlock);
2944 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2946 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2948 else
2951 * We have to look in the extended depot.
2953 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2956 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
2957 nextBlock);
2959 * Update the cached block depot, if necessary.
2961 if (depotBlockCount == This->indexBlockDepotCached)
2963 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2967 /******************************************************************************
2968 * Storage32Impl_LoadFileHeader
2970 * This method will read in the file header, i.e. big block index -1.
2972 static HRESULT StorageImpl_LoadFileHeader(
2973 StorageImpl* This)
2975 HRESULT hr = STG_E_FILENOTFOUND;
2976 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2977 BOOL success;
2978 int index;
2980 TRACE("\n");
2982 * Get a pointer to the big block of data containing the header.
2984 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2987 * Extract the information from the header.
2989 if (success)
2992 * Check for the "magic number" signature and return an error if it is not
2993 * found.
2995 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2997 return STG_E_OLDFORMAT;
3000 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3002 return STG_E_INVALIDHEADER;
3005 StorageUtl_ReadWord(
3006 headerBigBlock,
3007 OFFSET_BIGBLOCKSIZEBITS,
3008 &This->bigBlockSizeBits);
3010 StorageUtl_ReadWord(
3011 headerBigBlock,
3012 OFFSET_SMALLBLOCKSIZEBITS,
3013 &This->smallBlockSizeBits);
3015 StorageUtl_ReadDWord(
3016 headerBigBlock,
3017 OFFSET_BBDEPOTCOUNT,
3018 &This->bigBlockDepotCount);
3020 StorageUtl_ReadDWord(
3021 headerBigBlock,
3022 OFFSET_ROOTSTARTBLOCK,
3023 &This->rootStartBlock);
3025 StorageUtl_ReadDWord(
3026 headerBigBlock,
3027 OFFSET_SBDEPOTSTART,
3028 &This->smallBlockDepotStart);
3030 StorageUtl_ReadDWord(
3031 headerBigBlock,
3032 OFFSET_EXTBBDEPOTSTART,
3033 &This->extBigBlockDepotStart);
3035 StorageUtl_ReadDWord(
3036 headerBigBlock,
3037 OFFSET_EXTBBDEPOTCOUNT,
3038 &This->extBigBlockDepotCount);
3040 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3042 StorageUtl_ReadDWord(
3043 headerBigBlock,
3044 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3045 &(This->bigBlockDepotStart[index]));
3049 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3051 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3052 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3055 * Right now, the code is making some assumptions about the size of the
3056 * blocks, just make sure they are what we're expecting.
3058 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3059 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3061 WARN("Broken OLE storage file\n");
3062 hr = STG_E_INVALIDHEADER;
3064 else
3065 hr = S_OK;
3068 return hr;
3071 /******************************************************************************
3072 * Storage32Impl_SaveFileHeader
3074 * This method will save to the file the header, i.e. big block -1.
3076 static void StorageImpl_SaveFileHeader(
3077 StorageImpl* This)
3079 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3080 int index;
3081 BOOL success;
3084 * Get a pointer to the big block of data containing the header.
3086 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3089 * If the block read failed, the file is probably new.
3091 if (!success)
3094 * Initialize for all unknown fields.
3096 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3099 * Initialize the magic number.
3101 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3104 * And a bunch of things we don't know what they mean
3106 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3107 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3108 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3109 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3113 * Write the information to the header.
3115 StorageUtl_WriteWord(
3116 headerBigBlock,
3117 OFFSET_BIGBLOCKSIZEBITS,
3118 This->bigBlockSizeBits);
3120 StorageUtl_WriteWord(
3121 headerBigBlock,
3122 OFFSET_SMALLBLOCKSIZEBITS,
3123 This->smallBlockSizeBits);
3125 StorageUtl_WriteDWord(
3126 headerBigBlock,
3127 OFFSET_BBDEPOTCOUNT,
3128 This->bigBlockDepotCount);
3130 StorageUtl_WriteDWord(
3131 headerBigBlock,
3132 OFFSET_ROOTSTARTBLOCK,
3133 This->rootStartBlock);
3135 StorageUtl_WriteDWord(
3136 headerBigBlock,
3137 OFFSET_SBDEPOTSTART,
3138 This->smallBlockDepotStart);
3140 StorageUtl_WriteDWord(
3141 headerBigBlock,
3142 OFFSET_SBDEPOTCOUNT,
3143 This->smallBlockDepotChain ?
3144 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3146 StorageUtl_WriteDWord(
3147 headerBigBlock,
3148 OFFSET_EXTBBDEPOTSTART,
3149 This->extBigBlockDepotStart);
3151 StorageUtl_WriteDWord(
3152 headerBigBlock,
3153 OFFSET_EXTBBDEPOTCOUNT,
3154 This->extBigBlockDepotCount);
3156 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3158 StorageUtl_WriteDWord(
3159 headerBigBlock,
3160 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3161 (This->bigBlockDepotStart[index]));
3165 * Write the big block back to the file.
3167 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3170 /******************************************************************************
3171 * Storage32Impl_ReadProperty
3173 * This method will read the specified property from the property chain.
3175 BOOL StorageImpl_ReadProperty(
3176 StorageImpl* This,
3177 ULONG index,
3178 StgProperty* buffer)
3180 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3181 ULARGE_INTEGER offsetInPropSet;
3182 HRESULT readRes;
3183 ULONG bytesRead;
3185 offsetInPropSet.u.HighPart = 0;
3186 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3188 readRes = BlockChainStream_ReadAt(
3189 This->rootBlockChain,
3190 offsetInPropSet,
3191 PROPSET_BLOCK_SIZE,
3192 currentProperty,
3193 &bytesRead);
3195 if (SUCCEEDED(readRes))
3197 /* replace the name of root entry (often "Root Entry") by the file name */
3198 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3199 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3201 memset(buffer->name, 0, sizeof(buffer->name));
3202 memcpy(
3203 buffer->name,
3204 propName,
3205 PROPERTY_NAME_BUFFER_LEN );
3206 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3208 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3210 StorageUtl_ReadWord(
3211 currentProperty,
3212 OFFSET_PS_NAMELENGTH,
3213 &buffer->sizeOfNameString);
3215 StorageUtl_ReadDWord(
3216 currentProperty,
3217 OFFSET_PS_PREVIOUSPROP,
3218 &buffer->previousProperty);
3220 StorageUtl_ReadDWord(
3221 currentProperty,
3222 OFFSET_PS_NEXTPROP,
3223 &buffer->nextProperty);
3225 StorageUtl_ReadDWord(
3226 currentProperty,
3227 OFFSET_PS_DIRPROP,
3228 &buffer->dirProperty);
3230 StorageUtl_ReadGUID(
3231 currentProperty,
3232 OFFSET_PS_GUID,
3233 &buffer->propertyUniqueID);
3235 StorageUtl_ReadDWord(
3236 currentProperty,
3237 OFFSET_PS_TSS1,
3238 &buffer->timeStampS1);
3240 StorageUtl_ReadDWord(
3241 currentProperty,
3242 OFFSET_PS_TSD1,
3243 &buffer->timeStampD1);
3245 StorageUtl_ReadDWord(
3246 currentProperty,
3247 OFFSET_PS_TSS2,
3248 &buffer->timeStampS2);
3250 StorageUtl_ReadDWord(
3251 currentProperty,
3252 OFFSET_PS_TSD2,
3253 &buffer->timeStampD2);
3255 StorageUtl_ReadDWord(
3256 currentProperty,
3257 OFFSET_PS_STARTBLOCK,
3258 &buffer->startingBlock);
3260 StorageUtl_ReadDWord(
3261 currentProperty,
3262 OFFSET_PS_SIZE,
3263 &buffer->size.u.LowPart);
3265 buffer->size.u.HighPart = 0;
3268 return SUCCEEDED(readRes) ? TRUE : FALSE;
3271 /*********************************************************************
3272 * Write the specified property into the property chain
3274 BOOL StorageImpl_WriteProperty(
3275 StorageImpl* This,
3276 ULONG index,
3277 const StgProperty* buffer)
3279 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3280 ULARGE_INTEGER offsetInPropSet;
3281 HRESULT writeRes;
3282 ULONG bytesWritten;
3284 offsetInPropSet.u.HighPart = 0;
3285 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3287 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3289 memcpy(
3290 currentProperty + OFFSET_PS_NAME,
3291 buffer->name,
3292 PROPERTY_NAME_BUFFER_LEN );
3294 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3296 StorageUtl_WriteWord(
3297 currentProperty,
3298 OFFSET_PS_NAMELENGTH,
3299 buffer->sizeOfNameString);
3301 StorageUtl_WriteDWord(
3302 currentProperty,
3303 OFFSET_PS_PREVIOUSPROP,
3304 buffer->previousProperty);
3306 StorageUtl_WriteDWord(
3307 currentProperty,
3308 OFFSET_PS_NEXTPROP,
3309 buffer->nextProperty);
3311 StorageUtl_WriteDWord(
3312 currentProperty,
3313 OFFSET_PS_DIRPROP,
3314 buffer->dirProperty);
3316 StorageUtl_WriteGUID(
3317 currentProperty,
3318 OFFSET_PS_GUID,
3319 &buffer->propertyUniqueID);
3321 StorageUtl_WriteDWord(
3322 currentProperty,
3323 OFFSET_PS_TSS1,
3324 buffer->timeStampS1);
3326 StorageUtl_WriteDWord(
3327 currentProperty,
3328 OFFSET_PS_TSD1,
3329 buffer->timeStampD1);
3331 StorageUtl_WriteDWord(
3332 currentProperty,
3333 OFFSET_PS_TSS2,
3334 buffer->timeStampS2);
3336 StorageUtl_WriteDWord(
3337 currentProperty,
3338 OFFSET_PS_TSD2,
3339 buffer->timeStampD2);
3341 StorageUtl_WriteDWord(
3342 currentProperty,
3343 OFFSET_PS_STARTBLOCK,
3344 buffer->startingBlock);
3346 StorageUtl_WriteDWord(
3347 currentProperty,
3348 OFFSET_PS_SIZE,
3349 buffer->size.u.LowPart);
3351 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3352 offsetInPropSet,
3353 PROPSET_BLOCK_SIZE,
3354 currentProperty,
3355 &bytesWritten);
3356 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3359 static BOOL StorageImpl_ReadBigBlock(
3360 StorageImpl* This,
3361 ULONG blockIndex,
3362 void* buffer)
3364 ULARGE_INTEGER ulOffset;
3365 DWORD read;
3367 ulOffset.u.HighPart = 0;
3368 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3370 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3371 return (read == This->bigBlockSize);
3374 static BOOL StorageImpl_ReadDWordFromBigBlock(
3375 StorageImpl* This,
3376 ULONG blockIndex,
3377 ULONG offset,
3378 DWORD* value)
3380 ULARGE_INTEGER ulOffset;
3381 DWORD read;
3382 DWORD tmp;
3384 ulOffset.u.HighPart = 0;
3385 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3386 ulOffset.u.LowPart += offset;
3388 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3389 *value = lendian32toh(tmp);
3390 return (read == sizeof(DWORD));
3393 static BOOL StorageImpl_WriteBigBlock(
3394 StorageImpl* This,
3395 ULONG blockIndex,
3396 const void* buffer)
3398 ULARGE_INTEGER ulOffset;
3399 DWORD wrote;
3401 ulOffset.u.HighPart = 0;
3402 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3404 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3405 return (wrote == This->bigBlockSize);
3408 static BOOL StorageImpl_WriteDWordToBigBlock(
3409 StorageImpl* This,
3410 ULONG blockIndex,
3411 ULONG offset,
3412 DWORD value)
3414 ULARGE_INTEGER ulOffset;
3415 DWORD wrote;
3417 ulOffset.u.HighPart = 0;
3418 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3419 ulOffset.u.LowPart += offset;
3421 value = htole32(value);
3422 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3423 return (wrote == sizeof(DWORD));
3426 /******************************************************************************
3427 * Storage32Impl_SmallBlocksToBigBlocks
3429 * This method will convert a small block chain to a big block chain.
3430 * The small block chain will be destroyed.
3432 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3433 StorageImpl* This,
3434 SmallBlockChainStream** ppsbChain)
3436 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3437 ULARGE_INTEGER size, offset;
3438 ULONG cbRead, cbWritten;
3439 ULARGE_INTEGER cbTotalRead;
3440 ULONG propertyIndex;
3441 HRESULT resWrite = S_OK;
3442 HRESULT resRead;
3443 StgProperty chainProperty;
3444 BYTE *buffer;
3445 BlockChainStream *bbTempChain = NULL;
3446 BlockChainStream *bigBlockChain = NULL;
3449 * Create a temporary big block chain that doesn't have
3450 * an associated property. This temporary chain will be
3451 * used to copy data from small blocks to big blocks.
3453 bbTempChain = BlockChainStream_Construct(This,
3454 &bbHeadOfChain,
3455 PROPERTY_NULL);
3456 if(!bbTempChain) return NULL;
3458 * Grow the big block chain.
3460 size = SmallBlockChainStream_GetSize(*ppsbChain);
3461 BlockChainStream_SetSize(bbTempChain, size);
3464 * Copy the contents of the small block chain to the big block chain
3465 * by small block size increments.
3467 offset.u.LowPart = 0;
3468 offset.u.HighPart = 0;
3469 cbTotalRead.QuadPart = 0;
3471 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3474 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3475 offset,
3476 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3477 buffer,
3478 &cbRead);
3479 if (FAILED(resRead))
3480 break;
3482 if (cbRead > 0)
3484 cbTotalRead.QuadPart += cbRead;
3486 resWrite = BlockChainStream_WriteAt(bbTempChain,
3487 offset,
3488 cbRead,
3489 buffer,
3490 &cbWritten);
3492 if (FAILED(resWrite))
3493 break;
3495 offset.u.LowPart += cbRead;
3497 } while (cbTotalRead.QuadPart < size.QuadPart);
3498 HeapFree(GetProcessHeap(),0,buffer);
3500 size.u.HighPart = 0;
3501 size.u.LowPart = 0;
3503 if (FAILED(resRead) || FAILED(resWrite))
3505 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3506 BlockChainStream_SetSize(bbTempChain, size);
3507 BlockChainStream_Destroy(bbTempChain);
3508 return NULL;
3512 * Destroy the small block chain.
3514 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3515 SmallBlockChainStream_SetSize(*ppsbChain, size);
3516 SmallBlockChainStream_Destroy(*ppsbChain);
3517 *ppsbChain = 0;
3520 * Change the property information. This chain is now a big block chain
3521 * and it doesn't reside in the small blocks chain anymore.
3523 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3525 chainProperty.startingBlock = bbHeadOfChain;
3527 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3530 * Destroy the temporary propertyless big block chain.
3531 * Create a new big block chain associated with this property.
3533 BlockChainStream_Destroy(bbTempChain);
3534 bigBlockChain = BlockChainStream_Construct(This,
3535 NULL,
3536 propertyIndex);
3538 return bigBlockChain;
3541 /******************************************************************************
3542 * Storage32Impl_BigBlocksToSmallBlocks
3544 * This method will convert a big block chain to a small block chain.
3545 * The big block chain will be destroyed on success.
3547 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3548 StorageImpl* This,
3549 BlockChainStream** ppbbChain)
3551 ULARGE_INTEGER size, offset, cbTotalRead;
3552 ULONG cbRead, cbWritten, propertyIndex, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3553 HRESULT resWrite = S_OK, resRead;
3554 StgProperty chainProperty;
3555 BYTE* buffer;
3556 SmallBlockChainStream* sbTempChain;
3558 TRACE("%p %p\n", This, ppbbChain);
3560 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3561 PROPERTY_NULL);
3563 if(!sbTempChain)
3564 return NULL;
3566 size = BlockChainStream_GetSize(*ppbbChain);
3567 SmallBlockChainStream_SetSize(sbTempChain, size);
3569 offset.u.HighPart = 0;
3570 offset.u.LowPart = 0;
3571 cbTotalRead.QuadPart = 0;
3572 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3575 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3576 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3577 buffer, &cbRead);
3579 if(FAILED(resRead))
3580 break;
3582 if(cbRead > 0)
3584 cbTotalRead.QuadPart += cbRead;
3586 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3587 cbRead, buffer, &cbWritten);
3589 if(FAILED(resWrite))
3590 break;
3592 offset.u.LowPart += cbRead;
3594 }while(cbTotalRead.QuadPart < size.QuadPart);
3595 HeapFree(GetProcessHeap(), 0, buffer);
3597 size.u.HighPart = 0;
3598 size.u.LowPart = 0;
3600 if(FAILED(resRead) || FAILED(resWrite))
3602 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3603 SmallBlockChainStream_SetSize(sbTempChain, size);
3604 SmallBlockChainStream_Destroy(sbTempChain);
3605 return NULL;
3608 /* destroy the original big block chain */
3609 propertyIndex = (*ppbbChain)->ownerPropertyIndex;
3610 BlockChainStream_SetSize(*ppbbChain, size);
3611 BlockChainStream_Destroy(*ppbbChain);
3612 *ppbbChain = NULL;
3614 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3615 chainProperty.startingBlock = sbHeadOfChain;
3616 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3618 SmallBlockChainStream_Destroy(sbTempChain);
3619 return SmallBlockChainStream_Construct(This, NULL, propertyIndex);
3622 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3624 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3626 HeapFree(GetProcessHeap(), 0, This);
3629 /******************************************************************************
3631 ** Storage32InternalImpl_Commit
3634 static HRESULT WINAPI StorageInternalImpl_Commit(
3635 IStorage* iface,
3636 DWORD grfCommitFlags) /* [in] */
3638 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
3639 return S_OK;
3642 /******************************************************************************
3644 ** Storage32InternalImpl_Revert
3647 static HRESULT WINAPI StorageInternalImpl_Revert(
3648 IStorage* iface)
3650 FIXME("(%p): stub\n", iface);
3651 return S_OK;
3654 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3656 IStorage_Release((IStorage*)This->parentStorage);
3657 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3658 HeapFree(GetProcessHeap(), 0, This);
3661 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3662 IEnumSTATSTG* iface,
3663 REFIID riid,
3664 void** ppvObject)
3666 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3668 if (ppvObject==0)
3669 return E_INVALIDARG;
3671 *ppvObject = 0;
3673 if (IsEqualGUID(&IID_IUnknown, riid) ||
3674 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3676 *ppvObject = This;
3677 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3678 return S_OK;
3681 return E_NOINTERFACE;
3684 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3685 IEnumSTATSTG* iface)
3687 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3688 return InterlockedIncrement(&This->ref);
3691 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3692 IEnumSTATSTG* iface)
3694 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3696 ULONG newRef;
3698 newRef = InterlockedDecrement(&This->ref);
3700 if (newRef==0)
3702 IEnumSTATSTGImpl_Destroy(This);
3705 return newRef;
3708 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3709 IEnumSTATSTG* iface,
3710 ULONG celt,
3711 STATSTG* rgelt,
3712 ULONG* pceltFetched)
3714 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3716 StgProperty currentProperty;
3717 STATSTG* currentReturnStruct = rgelt;
3718 ULONG objectFetched = 0;
3719 ULONG currentSearchNode;
3721 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3722 return E_INVALIDARG;
3725 * To avoid the special case, get another pointer to a ULONG value if
3726 * the caller didn't supply one.
3728 if (pceltFetched==0)
3729 pceltFetched = &objectFetched;
3732 * Start the iteration, we will iterate until we hit the end of the
3733 * linked list or until we hit the number of items to iterate through
3735 *pceltFetched = 0;
3738 * Start with the node at the top of the stack.
3740 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3742 while ( ( *pceltFetched < celt) &&
3743 ( currentSearchNode!=PROPERTY_NULL) )
3746 * Remove the top node from the stack
3748 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3751 * Read the property from the storage.
3753 StorageImpl_ReadProperty(This->parentStorage,
3754 currentSearchNode,
3755 &currentProperty);
3758 * Copy the information to the return buffer.
3760 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3761 &currentProperty,
3762 STATFLAG_DEFAULT);
3765 * Step to the next item in the iteration
3767 (*pceltFetched)++;
3768 currentReturnStruct++;
3771 * Push the next search node in the search stack.
3773 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3776 * continue the iteration.
3778 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3781 if (*pceltFetched == celt)
3782 return S_OK;
3784 return S_FALSE;
3788 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3789 IEnumSTATSTG* iface,
3790 ULONG celt)
3792 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3794 StgProperty currentProperty;
3795 ULONG objectFetched = 0;
3796 ULONG currentSearchNode;
3799 * Start with the node at the top of the stack.
3801 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3803 while ( (objectFetched < celt) &&
3804 (currentSearchNode!=PROPERTY_NULL) )
3807 * Remove the top node from the stack
3809 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3812 * Read the property from the storage.
3814 StorageImpl_ReadProperty(This->parentStorage,
3815 currentSearchNode,
3816 &currentProperty);
3819 * Step to the next item in the iteration
3821 objectFetched++;
3824 * Push the next search node in the search stack.
3826 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3829 * continue the iteration.
3831 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3834 if (objectFetched == celt)
3835 return S_OK;
3837 return S_FALSE;
3840 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3841 IEnumSTATSTG* iface)
3843 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3845 StgProperty rootProperty;
3846 BOOL readSuccessful;
3849 * Re-initialize the search stack to an empty stack
3851 This->stackSize = 0;
3854 * Read the root property from the storage.
3856 readSuccessful = StorageImpl_ReadProperty(
3857 This->parentStorage,
3858 This->firstPropertyNode,
3859 &rootProperty);
3861 if (readSuccessful)
3863 assert(rootProperty.sizeOfNameString!=0);
3866 * Push the search node in the search stack.
3868 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3871 return S_OK;
3874 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3875 IEnumSTATSTG* iface,
3876 IEnumSTATSTG** ppenum)
3878 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3880 IEnumSTATSTGImpl* newClone;
3883 * Perform a sanity check on the parameters.
3885 if (ppenum==0)
3886 return E_INVALIDARG;
3888 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3889 This->firstPropertyNode);
3893 * The new clone enumeration must point to the same current node as
3894 * the ole one.
3896 newClone->stackSize = This->stackSize ;
3897 newClone->stackMaxSize = This->stackMaxSize ;
3898 newClone->stackToVisit =
3899 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3901 memcpy(
3902 newClone->stackToVisit,
3903 This->stackToVisit,
3904 sizeof(ULONG) * newClone->stackSize);
3906 *ppenum = (IEnumSTATSTG*)newClone;
3909 * Don't forget to nail down a reference to the clone before
3910 * returning it.
3912 IEnumSTATSTGImpl_AddRef(*ppenum);
3914 return S_OK;
3917 static INT IEnumSTATSTGImpl_FindParentProperty(
3918 IEnumSTATSTGImpl *This,
3919 ULONG childProperty,
3920 StgProperty *currentProperty,
3921 ULONG *thisNodeId)
3923 ULONG currentSearchNode;
3924 ULONG foundNode;
3927 * To avoid the special case, get another pointer to a ULONG value if
3928 * the caller didn't supply one.
3930 if (thisNodeId==0)
3931 thisNodeId = &foundNode;
3934 * Start with the node at the top of the stack.
3936 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3939 while (currentSearchNode!=PROPERTY_NULL)
3942 * Store the current node in the returned parameters
3944 *thisNodeId = currentSearchNode;
3947 * Remove the top node from the stack
3949 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3952 * Read the property from the storage.
3954 StorageImpl_ReadProperty(
3955 This->parentStorage,
3956 currentSearchNode,
3957 currentProperty);
3959 if (currentProperty->previousProperty == childProperty)
3960 return PROPERTY_RELATION_PREVIOUS;
3962 else if (currentProperty->nextProperty == childProperty)
3963 return PROPERTY_RELATION_NEXT;
3965 else if (currentProperty->dirProperty == childProperty)
3966 return PROPERTY_RELATION_DIR;
3969 * Push the next search node in the search stack.
3971 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3974 * continue the iteration.
3976 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3979 return PROPERTY_NULL;
3982 static ULONG IEnumSTATSTGImpl_FindProperty(
3983 IEnumSTATSTGImpl* This,
3984 const OLECHAR* lpszPropName,
3985 StgProperty* currentProperty)
3987 ULONG currentSearchNode;
3990 * Start with the node at the top of the stack.
3992 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3994 while (currentSearchNode!=PROPERTY_NULL)
3997 * Remove the top node from the stack
3999 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4002 * Read the property from the storage.
4004 StorageImpl_ReadProperty(This->parentStorage,
4005 currentSearchNode,
4006 currentProperty);
4008 if (propertyNameCmp(currentProperty->name, lpszPropName) == 0)
4009 return currentSearchNode;
4012 * Push the next search node in the search stack.
4014 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4017 * continue the iteration.
4019 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4022 return PROPERTY_NULL;
4025 static void IEnumSTATSTGImpl_PushSearchNode(
4026 IEnumSTATSTGImpl* This,
4027 ULONG nodeToPush)
4029 StgProperty rootProperty;
4030 BOOL readSuccessful;
4033 * First, make sure we're not trying to push an unexisting node.
4035 if (nodeToPush==PROPERTY_NULL)
4036 return;
4039 * First push the node to the stack
4041 if (This->stackSize == This->stackMaxSize)
4043 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4045 This->stackToVisit = HeapReAlloc(
4046 GetProcessHeap(),
4048 This->stackToVisit,
4049 sizeof(ULONG) * This->stackMaxSize);
4052 This->stackToVisit[This->stackSize] = nodeToPush;
4053 This->stackSize++;
4056 * Read the root property from the storage.
4058 readSuccessful = StorageImpl_ReadProperty(
4059 This->parentStorage,
4060 nodeToPush,
4061 &rootProperty);
4063 if (readSuccessful)
4065 assert(rootProperty.sizeOfNameString!=0);
4068 * Push the previous search node in the search stack.
4070 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4074 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4075 IEnumSTATSTGImpl* This,
4076 BOOL remove)
4078 ULONG topNode;
4080 if (This->stackSize == 0)
4081 return PROPERTY_NULL;
4083 topNode = This->stackToVisit[This->stackSize-1];
4085 if (remove)
4086 This->stackSize--;
4088 return topNode;
4092 * Virtual function table for the IEnumSTATSTGImpl class.
4094 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4096 IEnumSTATSTGImpl_QueryInterface,
4097 IEnumSTATSTGImpl_AddRef,
4098 IEnumSTATSTGImpl_Release,
4099 IEnumSTATSTGImpl_Next,
4100 IEnumSTATSTGImpl_Skip,
4101 IEnumSTATSTGImpl_Reset,
4102 IEnumSTATSTGImpl_Clone
4105 /******************************************************************************
4106 ** IEnumSTATSTGImpl implementation
4109 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4110 StorageImpl* parentStorage,
4111 ULONG firstPropertyNode)
4113 IEnumSTATSTGImpl* newEnumeration;
4115 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4117 if (newEnumeration!=0)
4120 * Set-up the virtual function table and reference count.
4122 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4123 newEnumeration->ref = 0;
4126 * We want to nail-down the reference to the storage in case the
4127 * enumeration out-lives the storage in the client application.
4129 newEnumeration->parentStorage = parentStorage;
4130 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4132 newEnumeration->firstPropertyNode = firstPropertyNode;
4135 * Initialize the search stack
4137 newEnumeration->stackSize = 0;
4138 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4139 newEnumeration->stackToVisit =
4140 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4143 * Make sure the current node of the iterator is the first one.
4145 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4148 return newEnumeration;
4152 * Virtual function table for the Storage32InternalImpl class.
4154 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4156 StorageBaseImpl_QueryInterface,
4157 StorageBaseImpl_AddRef,
4158 StorageBaseImpl_Release,
4159 StorageBaseImpl_CreateStream,
4160 StorageBaseImpl_OpenStream,
4161 StorageImpl_CreateStorage,
4162 StorageBaseImpl_OpenStorage,
4163 StorageImpl_CopyTo,
4164 StorageImpl_MoveElementTo,
4165 StorageInternalImpl_Commit,
4166 StorageInternalImpl_Revert,
4167 StorageBaseImpl_EnumElements,
4168 StorageImpl_DestroyElement,
4169 StorageBaseImpl_RenameElement,
4170 StorageImpl_SetElementTimes,
4171 StorageBaseImpl_SetClass,
4172 StorageImpl_SetStateBits,
4173 StorageBaseImpl_Stat
4176 /******************************************************************************
4177 ** Storage32InternalImpl implementation
4180 static StorageInternalImpl* StorageInternalImpl_Construct(
4181 StorageImpl* ancestorStorage,
4182 DWORD openFlags,
4183 ULONG rootPropertyIndex)
4185 StorageInternalImpl* newStorage;
4187 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4189 if (newStorage!=0)
4192 * Initialize the stream list
4194 list_init(&newStorage->base.strmHead);
4197 * Initialize the virtual function table.
4199 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4200 newStorage->base.v_destructor = StorageInternalImpl_Destroy;
4201 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4204 * Keep the ancestor storage pointer but do not nail a reference to it.
4206 newStorage->base.ancestorStorage = ancestorStorage;
4209 * Keep the index of the root property set for this storage,
4211 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4213 return newStorage;
4216 return 0;
4219 /******************************************************************************
4220 ** StorageUtl implementation
4223 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4225 WORD tmp;
4227 memcpy(&tmp, buffer+offset, sizeof(WORD));
4228 *value = lendian16toh(tmp);
4231 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4233 value = htole16(value);
4234 memcpy(buffer+offset, &value, sizeof(WORD));
4237 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4239 DWORD tmp;
4241 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4242 *value = lendian32toh(tmp);
4245 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4247 value = htole32(value);
4248 memcpy(buffer+offset, &value, sizeof(DWORD));
4251 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4252 ULARGE_INTEGER* value)
4254 #ifdef WORDS_BIGENDIAN
4255 ULARGE_INTEGER tmp;
4257 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4258 value->u.LowPart = htole32(tmp.u.HighPart);
4259 value->u.HighPart = htole32(tmp.u.LowPart);
4260 #else
4261 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4262 #endif
4265 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4266 const ULARGE_INTEGER *value)
4268 #ifdef WORDS_BIGENDIAN
4269 ULARGE_INTEGER tmp;
4271 tmp.u.LowPart = htole32(value->u.HighPart);
4272 tmp.u.HighPart = htole32(value->u.LowPart);
4273 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4274 #else
4275 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4276 #endif
4279 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4281 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4282 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4283 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4285 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4288 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4290 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4291 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4292 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4294 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4297 void StorageUtl_CopyPropertyToSTATSTG(
4298 STATSTG* destination,
4299 const StgProperty* source,
4300 int statFlags)
4303 * The copy of the string occurs only when the flag is not set
4305 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4306 (source->name == NULL) ||
4307 (source->name[0] == 0) )
4309 destination->pwcsName = 0;
4311 else
4313 destination->pwcsName =
4314 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4316 strcpyW(destination->pwcsName, source->name);
4319 switch (source->propertyType)
4321 case PROPTYPE_STORAGE:
4322 case PROPTYPE_ROOT:
4323 destination->type = STGTY_STORAGE;
4324 break;
4325 case PROPTYPE_STREAM:
4326 destination->type = STGTY_STREAM;
4327 break;
4328 default:
4329 destination->type = STGTY_STREAM;
4330 break;
4333 destination->cbSize = source->size;
4335 currentReturnStruct->mtime = {0}; TODO
4336 currentReturnStruct->ctime = {0};
4337 currentReturnStruct->atime = {0};
4339 destination->grfMode = 0;
4340 destination->grfLocksSupported = 0;
4341 destination->clsid = source->propertyUniqueID;
4342 destination->grfStateBits = 0;
4343 destination->reserved = 0;
4346 /******************************************************************************
4347 ** BlockChainStream implementation
4350 BlockChainStream* BlockChainStream_Construct(
4351 StorageImpl* parentStorage,
4352 ULONG* headOfStreamPlaceHolder,
4353 ULONG propertyIndex)
4355 BlockChainStream* newStream;
4356 ULONG blockIndex;
4358 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4360 newStream->parentStorage = parentStorage;
4361 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4362 newStream->ownerPropertyIndex = propertyIndex;
4363 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4364 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4365 newStream->numBlocks = 0;
4367 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4369 while (blockIndex != BLOCK_END_OF_CHAIN)
4371 newStream->numBlocks++;
4372 newStream->tailIndex = blockIndex;
4374 if(FAILED(StorageImpl_GetNextBlockInChain(
4375 parentStorage,
4376 blockIndex,
4377 &blockIndex)))
4379 HeapFree(GetProcessHeap(), 0, newStream);
4380 return NULL;
4384 return newStream;
4387 void BlockChainStream_Destroy(BlockChainStream* This)
4389 HeapFree(GetProcessHeap(), 0, This);
4392 /******************************************************************************
4393 * BlockChainStream_GetHeadOfChain
4395 * Returns the head of this stream chain.
4396 * Some special chains don't have properties, their heads are kept in
4397 * This->headOfStreamPlaceHolder.
4400 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4402 StgProperty chainProperty;
4403 BOOL readSuccessful;
4405 if (This->headOfStreamPlaceHolder != 0)
4406 return *(This->headOfStreamPlaceHolder);
4408 if (This->ownerPropertyIndex != PROPERTY_NULL)
4410 readSuccessful = StorageImpl_ReadProperty(
4411 This->parentStorage,
4412 This->ownerPropertyIndex,
4413 &chainProperty);
4415 if (readSuccessful)
4417 return chainProperty.startingBlock;
4421 return BLOCK_END_OF_CHAIN;
4424 /******************************************************************************
4425 * BlockChainStream_GetCount
4427 * Returns the number of blocks that comprises this chain.
4428 * This is not the size of the stream as the last block may not be full!
4431 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4433 ULONG blockIndex;
4434 ULONG count = 0;
4436 blockIndex = BlockChainStream_GetHeadOfChain(This);
4438 while (blockIndex != BLOCK_END_OF_CHAIN)
4440 count++;
4442 if(FAILED(StorageImpl_GetNextBlockInChain(
4443 This->parentStorage,
4444 blockIndex,
4445 &blockIndex)))
4446 return 0;
4449 return count;
4452 /******************************************************************************
4453 * BlockChainStream_ReadAt
4455 * Reads a specified number of bytes from this chain at the specified offset.
4456 * bytesRead may be NULL.
4457 * Failure will be returned if the specified number of bytes has not been read.
4459 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4460 ULARGE_INTEGER offset,
4461 ULONG size,
4462 void* buffer,
4463 ULONG* bytesRead)
4465 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4466 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4467 ULONG bytesToReadInBuffer;
4468 ULONG blockIndex;
4469 BYTE* bufferWalker;
4471 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4474 * Find the first block in the stream that contains part of the buffer.
4476 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4477 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4478 (blockNoInSequence < This->lastBlockNoInSequence) )
4480 blockIndex = BlockChainStream_GetHeadOfChain(This);
4481 This->lastBlockNoInSequence = blockNoInSequence;
4483 else
4485 ULONG temp = blockNoInSequence;
4487 blockIndex = This->lastBlockNoInSequenceIndex;
4488 blockNoInSequence -= This->lastBlockNoInSequence;
4489 This->lastBlockNoInSequence = temp;
4492 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4494 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4495 return STG_E_DOCFILECORRUPT;
4496 blockNoInSequence--;
4499 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4500 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4502 This->lastBlockNoInSequenceIndex = blockIndex;
4505 * Start reading the buffer.
4507 *bytesRead = 0;
4508 bufferWalker = buffer;
4510 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4512 ULARGE_INTEGER ulOffset;
4513 DWORD bytesReadAt;
4515 * Calculate how many bytes we can copy from this big block.
4517 bytesToReadInBuffer =
4518 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4520 TRACE("block %i\n",blockIndex);
4521 ulOffset.u.HighPart = 0;
4522 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4523 offsetInBlock;
4525 StorageImpl_ReadAt(This->parentStorage,
4526 ulOffset,
4527 bufferWalker,
4528 bytesToReadInBuffer,
4529 &bytesReadAt);
4531 * Step to the next big block.
4533 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4534 return STG_E_DOCFILECORRUPT;
4536 bufferWalker += bytesReadAt;
4537 size -= bytesReadAt;
4538 *bytesRead += bytesReadAt;
4539 offsetInBlock = 0; /* There is no offset on the next block */
4541 if (bytesToReadInBuffer != bytesReadAt)
4542 break;
4545 return (size == 0) ? S_OK : STG_E_READFAULT;
4548 /******************************************************************************
4549 * BlockChainStream_WriteAt
4551 * Writes the specified number of bytes to this chain at the specified offset.
4552 * Will fail if not all specified number of bytes have been written.
4554 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4555 ULARGE_INTEGER offset,
4556 ULONG size,
4557 const void* buffer,
4558 ULONG* bytesWritten)
4560 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4561 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4562 ULONG bytesToWrite;
4563 ULONG blockIndex;
4564 const BYTE* bufferWalker;
4567 * Find the first block in the stream that contains part of the buffer.
4569 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4570 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4571 (blockNoInSequence < This->lastBlockNoInSequence) )
4573 blockIndex = BlockChainStream_GetHeadOfChain(This);
4574 This->lastBlockNoInSequence = blockNoInSequence;
4576 else
4578 ULONG temp = blockNoInSequence;
4580 blockIndex = This->lastBlockNoInSequenceIndex;
4581 blockNoInSequence -= This->lastBlockNoInSequence;
4582 This->lastBlockNoInSequence = temp;
4585 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4587 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4588 &blockIndex)))
4589 return STG_E_DOCFILECORRUPT;
4590 blockNoInSequence--;
4593 This->lastBlockNoInSequenceIndex = blockIndex;
4595 /* BlockChainStream_SetSize should have already been called to ensure we have
4596 * enough blocks in the chain to write into */
4597 if (blockIndex == BLOCK_END_OF_CHAIN)
4599 ERR("not enough blocks in chain to write data\n");
4600 return STG_E_DOCFILECORRUPT;
4603 *bytesWritten = 0;
4604 bufferWalker = buffer;
4606 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4608 ULARGE_INTEGER ulOffset;
4609 DWORD bytesWrittenAt;
4611 * Calculate how many bytes we can copy from this big block.
4613 bytesToWrite =
4614 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4616 TRACE("block %i\n",blockIndex);
4617 ulOffset.u.HighPart = 0;
4618 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4619 offsetInBlock;
4621 StorageImpl_WriteAt(This->parentStorage,
4622 ulOffset,
4623 bufferWalker,
4624 bytesToWrite,
4625 &bytesWrittenAt);
4628 * Step to the next big block.
4630 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4631 &blockIndex)))
4632 return STG_E_DOCFILECORRUPT;
4634 bufferWalker += bytesWrittenAt;
4635 size -= bytesWrittenAt;
4636 *bytesWritten += bytesWrittenAt;
4637 offsetInBlock = 0; /* There is no offset on the next block */
4639 if (bytesWrittenAt != bytesToWrite)
4640 break;
4643 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4646 /******************************************************************************
4647 * BlockChainStream_Shrink
4649 * Shrinks this chain in the big block depot.
4651 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4652 ULARGE_INTEGER newSize)
4654 ULONG blockIndex, extraBlock;
4655 ULONG numBlocks;
4656 ULONG count = 1;
4659 * Reset the last accessed block cache.
4661 This->lastBlockNoInSequence = 0xFFFFFFFF;
4662 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4665 * Figure out how many blocks are needed to contain the new size
4667 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4669 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4670 numBlocks++;
4672 blockIndex = BlockChainStream_GetHeadOfChain(This);
4675 * Go to the new end of chain
4677 while (count < numBlocks)
4679 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4680 &blockIndex)))
4681 return FALSE;
4682 count++;
4685 /* Get the next block before marking the new end */
4686 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4687 &extraBlock)))
4688 return FALSE;
4690 /* Mark the new end of chain */
4691 StorageImpl_SetNextBlockInChain(
4692 This->parentStorage,
4693 blockIndex,
4694 BLOCK_END_OF_CHAIN);
4696 This->tailIndex = blockIndex;
4697 This->numBlocks = numBlocks;
4700 * Mark the extra blocks as free
4702 while (extraBlock != BLOCK_END_OF_CHAIN)
4704 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4705 &blockIndex)))
4706 return FALSE;
4707 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4708 extraBlock = blockIndex;
4711 return TRUE;
4714 /******************************************************************************
4715 * BlockChainStream_Enlarge
4717 * Grows this chain in the big block depot.
4719 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4720 ULARGE_INTEGER newSize)
4722 ULONG blockIndex, currentBlock;
4723 ULONG newNumBlocks;
4724 ULONG oldNumBlocks = 0;
4726 blockIndex = BlockChainStream_GetHeadOfChain(This);
4729 * Empty chain. Create the head.
4731 if (blockIndex == BLOCK_END_OF_CHAIN)
4733 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4734 StorageImpl_SetNextBlockInChain(This->parentStorage,
4735 blockIndex,
4736 BLOCK_END_OF_CHAIN);
4738 if (This->headOfStreamPlaceHolder != 0)
4740 *(This->headOfStreamPlaceHolder) = blockIndex;
4742 else
4744 StgProperty chainProp;
4745 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4747 StorageImpl_ReadProperty(
4748 This->parentStorage,
4749 This->ownerPropertyIndex,
4750 &chainProp);
4752 chainProp.startingBlock = blockIndex;
4754 StorageImpl_WriteProperty(
4755 This->parentStorage,
4756 This->ownerPropertyIndex,
4757 &chainProp);
4760 This->tailIndex = blockIndex;
4761 This->numBlocks = 1;
4765 * Figure out how many blocks are needed to contain this stream
4767 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4769 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4770 newNumBlocks++;
4773 * Go to the current end of chain
4775 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4777 currentBlock = blockIndex;
4779 while (blockIndex != BLOCK_END_OF_CHAIN)
4781 This->numBlocks++;
4782 currentBlock = blockIndex;
4784 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4785 &blockIndex)))
4786 return FALSE;
4789 This->tailIndex = currentBlock;
4792 currentBlock = This->tailIndex;
4793 oldNumBlocks = This->numBlocks;
4796 * Add new blocks to the chain
4798 if (oldNumBlocks < newNumBlocks)
4800 while (oldNumBlocks < newNumBlocks)
4802 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4804 StorageImpl_SetNextBlockInChain(
4805 This->parentStorage,
4806 currentBlock,
4807 blockIndex);
4809 StorageImpl_SetNextBlockInChain(
4810 This->parentStorage,
4811 blockIndex,
4812 BLOCK_END_OF_CHAIN);
4814 currentBlock = blockIndex;
4815 oldNumBlocks++;
4818 This->tailIndex = blockIndex;
4819 This->numBlocks = newNumBlocks;
4822 return TRUE;
4825 /******************************************************************************
4826 * BlockChainStream_SetSize
4828 * Sets the size of this stream. The big block depot will be updated.
4829 * The file will grow if we grow the chain.
4831 * TODO: Free the actual blocks in the file when we shrink the chain.
4832 * Currently, the blocks are still in the file. So the file size
4833 * doesn't shrink even if we shrink streams.
4835 BOOL BlockChainStream_SetSize(
4836 BlockChainStream* This,
4837 ULARGE_INTEGER newSize)
4839 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4841 if (newSize.u.LowPart == size.u.LowPart)
4842 return TRUE;
4844 if (newSize.u.LowPart < size.u.LowPart)
4846 BlockChainStream_Shrink(This, newSize);
4848 else
4850 BlockChainStream_Enlarge(This, newSize);
4853 return TRUE;
4856 /******************************************************************************
4857 * BlockChainStream_GetSize
4859 * Returns the size of this chain.
4860 * Will return the block count if this chain doesn't have a property.
4862 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4864 StgProperty chainProperty;
4866 if(This->headOfStreamPlaceHolder == NULL)
4869 * This chain is a data stream read the property and return
4870 * the appropriate size
4872 StorageImpl_ReadProperty(
4873 This->parentStorage,
4874 This->ownerPropertyIndex,
4875 &chainProperty);
4877 return chainProperty.size;
4879 else
4882 * this chain is a chain that does not have a property, figure out the
4883 * size by making the product number of used blocks times the
4884 * size of them
4886 ULARGE_INTEGER result;
4887 result.u.HighPart = 0;
4889 result.u.LowPart =
4890 BlockChainStream_GetCount(This) *
4891 This->parentStorage->bigBlockSize;
4893 return result;
4897 /******************************************************************************
4898 ** SmallBlockChainStream implementation
4901 SmallBlockChainStream* SmallBlockChainStream_Construct(
4902 StorageImpl* parentStorage,
4903 ULONG* headOfStreamPlaceHolder,
4904 ULONG propertyIndex)
4906 SmallBlockChainStream* newStream;
4908 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4910 newStream->parentStorage = parentStorage;
4911 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4912 newStream->ownerPropertyIndex = propertyIndex;
4914 return newStream;
4917 void SmallBlockChainStream_Destroy(
4918 SmallBlockChainStream* This)
4920 HeapFree(GetProcessHeap(), 0, This);
4923 /******************************************************************************
4924 * SmallBlockChainStream_GetHeadOfChain
4926 * Returns the head of this chain of small blocks.
4928 static ULONG SmallBlockChainStream_GetHeadOfChain(
4929 SmallBlockChainStream* This)
4931 StgProperty chainProperty;
4932 BOOL readSuccessful;
4934 if (This->headOfStreamPlaceHolder != NULL)
4935 return *(This->headOfStreamPlaceHolder);
4937 if (This->ownerPropertyIndex)
4939 readSuccessful = StorageImpl_ReadProperty(
4940 This->parentStorage,
4941 This->ownerPropertyIndex,
4942 &chainProperty);
4944 if (readSuccessful)
4946 return chainProperty.startingBlock;
4951 return BLOCK_END_OF_CHAIN;
4954 /******************************************************************************
4955 * SmallBlockChainStream_GetNextBlockInChain
4957 * Returns the index of the next small block in this chain.
4959 * Return Values:
4960 * - BLOCK_END_OF_CHAIN: end of this chain
4961 * - BLOCK_UNUSED: small block 'blockIndex' is free
4963 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
4964 SmallBlockChainStream* This,
4965 ULONG blockIndex,
4966 ULONG* nextBlockInChain)
4968 ULARGE_INTEGER offsetOfBlockInDepot;
4969 DWORD buffer;
4970 ULONG bytesRead;
4971 HRESULT res;
4973 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4975 offsetOfBlockInDepot.u.HighPart = 0;
4976 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4979 * Read those bytes in the buffer from the small block file.
4981 res = BlockChainStream_ReadAt(
4982 This->parentStorage->smallBlockDepotChain,
4983 offsetOfBlockInDepot,
4984 sizeof(DWORD),
4985 &buffer,
4986 &bytesRead);
4988 if (SUCCEEDED(res))
4990 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4991 return S_OK;
4994 return res;
4997 /******************************************************************************
4998 * SmallBlockChainStream_SetNextBlockInChain
5000 * Writes the index of the next block of the specified block in the small
5001 * block depot.
5002 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5003 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5005 static void SmallBlockChainStream_SetNextBlockInChain(
5006 SmallBlockChainStream* This,
5007 ULONG blockIndex,
5008 ULONG nextBlock)
5010 ULARGE_INTEGER offsetOfBlockInDepot;
5011 DWORD buffer;
5012 ULONG bytesWritten;
5014 offsetOfBlockInDepot.u.HighPart = 0;
5015 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5017 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5020 * Read those bytes in the buffer from the small block file.
5022 BlockChainStream_WriteAt(
5023 This->parentStorage->smallBlockDepotChain,
5024 offsetOfBlockInDepot,
5025 sizeof(DWORD),
5026 &buffer,
5027 &bytesWritten);
5030 /******************************************************************************
5031 * SmallBlockChainStream_FreeBlock
5033 * Flag small block 'blockIndex' as free in the small block depot.
5035 static void SmallBlockChainStream_FreeBlock(
5036 SmallBlockChainStream* This,
5037 ULONG blockIndex)
5039 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5042 /******************************************************************************
5043 * SmallBlockChainStream_GetNextFreeBlock
5045 * Returns the index of a free small block. The small block depot will be
5046 * enlarged if necessary. The small block chain will also be enlarged if
5047 * necessary.
5049 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5050 SmallBlockChainStream* This)
5052 ULARGE_INTEGER offsetOfBlockInDepot;
5053 DWORD buffer;
5054 ULONG bytesRead;
5055 ULONG blockIndex = 0;
5056 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5057 HRESULT res = S_OK;
5058 ULONG smallBlocksPerBigBlock;
5060 offsetOfBlockInDepot.u.HighPart = 0;
5063 * Scan the small block depot for a free block
5065 while (nextBlockIndex != BLOCK_UNUSED)
5067 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5069 res = BlockChainStream_ReadAt(
5070 This->parentStorage->smallBlockDepotChain,
5071 offsetOfBlockInDepot,
5072 sizeof(DWORD),
5073 &buffer,
5074 &bytesRead);
5077 * If we run out of space for the small block depot, enlarge it
5079 if (SUCCEEDED(res))
5081 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5083 if (nextBlockIndex != BLOCK_UNUSED)
5084 blockIndex++;
5086 else
5088 ULONG count =
5089 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5091 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5092 ULONG nextBlock, newsbdIndex;
5093 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5095 nextBlock = sbdIndex;
5096 while (nextBlock != BLOCK_END_OF_CHAIN)
5098 sbdIndex = nextBlock;
5099 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5102 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5103 if (sbdIndex != BLOCK_END_OF_CHAIN)
5104 StorageImpl_SetNextBlockInChain(
5105 This->parentStorage,
5106 sbdIndex,
5107 newsbdIndex);
5109 StorageImpl_SetNextBlockInChain(
5110 This->parentStorage,
5111 newsbdIndex,
5112 BLOCK_END_OF_CHAIN);
5115 * Initialize all the small blocks to free
5117 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5118 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5120 if (count == 0)
5123 * We have just created the small block depot.
5125 StgProperty rootProp;
5126 ULONG sbStartIndex;
5129 * Save it in the header
5131 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5132 StorageImpl_SaveFileHeader(This->parentStorage);
5135 * And allocate the first big block that will contain small blocks
5137 sbStartIndex =
5138 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5140 StorageImpl_SetNextBlockInChain(
5141 This->parentStorage,
5142 sbStartIndex,
5143 BLOCK_END_OF_CHAIN);
5145 StorageImpl_ReadProperty(
5146 This->parentStorage,
5147 This->parentStorage->base.rootPropertySetIndex,
5148 &rootProp);
5150 rootProp.startingBlock = sbStartIndex;
5151 rootProp.size.u.HighPart = 0;
5152 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5154 StorageImpl_WriteProperty(
5155 This->parentStorage,
5156 This->parentStorage->base.rootPropertySetIndex,
5157 &rootProp);
5159 else
5160 StorageImpl_SaveFileHeader(This->parentStorage);
5164 smallBlocksPerBigBlock =
5165 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5168 * Verify if we have to allocate big blocks to contain small blocks
5170 if (blockIndex % smallBlocksPerBigBlock == 0)
5172 StgProperty rootProp;
5173 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5175 StorageImpl_ReadProperty(
5176 This->parentStorage,
5177 This->parentStorage->base.rootPropertySetIndex,
5178 &rootProp);
5180 if (rootProp.size.u.LowPart <
5181 (blocksRequired * This->parentStorage->bigBlockSize))
5183 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5185 BlockChainStream_SetSize(
5186 This->parentStorage->smallBlockRootChain,
5187 rootProp.size);
5189 StorageImpl_WriteProperty(
5190 This->parentStorage,
5191 This->parentStorage->base.rootPropertySetIndex,
5192 &rootProp);
5196 return blockIndex;
5199 /******************************************************************************
5200 * SmallBlockChainStream_ReadAt
5202 * Reads a specified number of bytes from this chain at the specified offset.
5203 * bytesRead may be NULL.
5204 * Failure will be returned if the specified number of bytes has not been read.
5206 HRESULT SmallBlockChainStream_ReadAt(
5207 SmallBlockChainStream* This,
5208 ULARGE_INTEGER offset,
5209 ULONG size,
5210 void* buffer,
5211 ULONG* bytesRead)
5213 HRESULT rc = S_OK;
5214 ULARGE_INTEGER offsetInBigBlockFile;
5215 ULONG blockNoInSequence =
5216 offset.u.LowPart / This->parentStorage->smallBlockSize;
5218 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5219 ULONG bytesToReadInBuffer;
5220 ULONG blockIndex;
5221 ULONG bytesReadFromBigBlockFile;
5222 BYTE* bufferWalker;
5225 * This should never happen on a small block file.
5227 assert(offset.u.HighPart==0);
5230 * Find the first block in the stream that contains part of the buffer.
5232 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5234 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5236 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5237 if(FAILED(rc))
5238 return rc;
5239 blockNoInSequence--;
5243 * Start reading the buffer.
5245 *bytesRead = 0;
5246 bufferWalker = buffer;
5248 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5251 * Calculate how many bytes we can copy from this small block.
5253 bytesToReadInBuffer =
5254 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5257 * Calculate the offset of the small block in the small block file.
5259 offsetInBigBlockFile.u.HighPart = 0;
5260 offsetInBigBlockFile.u.LowPart =
5261 blockIndex * This->parentStorage->smallBlockSize;
5263 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5266 * Read those bytes in the buffer from the small block file.
5267 * The small block has already been identified so it shouldn't fail
5268 * unless the file is corrupt.
5270 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5271 offsetInBigBlockFile,
5272 bytesToReadInBuffer,
5273 bufferWalker,
5274 &bytesReadFromBigBlockFile);
5276 if (FAILED(rc))
5277 return rc;
5280 * Step to the next big block.
5282 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5283 if(FAILED(rc))
5284 return STG_E_DOCFILECORRUPT;
5286 bufferWalker += bytesReadFromBigBlockFile;
5287 size -= bytesReadFromBigBlockFile;
5288 *bytesRead += bytesReadFromBigBlockFile;
5289 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5292 return (size == 0) ? S_OK : STG_E_READFAULT;
5295 /******************************************************************************
5296 * SmallBlockChainStream_WriteAt
5298 * Writes the specified number of bytes to this chain at the specified offset.
5299 * Will fail if not all specified number of bytes have been written.
5301 HRESULT SmallBlockChainStream_WriteAt(
5302 SmallBlockChainStream* This,
5303 ULARGE_INTEGER offset,
5304 ULONG size,
5305 const void* buffer,
5306 ULONG* bytesWritten)
5308 ULARGE_INTEGER offsetInBigBlockFile;
5309 ULONG blockNoInSequence =
5310 offset.u.LowPart / This->parentStorage->smallBlockSize;
5312 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5313 ULONG bytesToWriteInBuffer;
5314 ULONG blockIndex;
5315 ULONG bytesWrittenToBigBlockFile;
5316 const BYTE* bufferWalker;
5317 HRESULT res;
5320 * This should never happen on a small block file.
5322 assert(offset.u.HighPart==0);
5325 * Find the first block in the stream that contains part of the buffer.
5327 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5329 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5331 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5332 return STG_E_DOCFILECORRUPT;
5333 blockNoInSequence--;
5337 * Start writing the buffer.
5339 *bytesWritten = 0;
5340 bufferWalker = buffer;
5341 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5344 * Calculate how many bytes we can copy to this small block.
5346 bytesToWriteInBuffer =
5347 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5350 * Calculate the offset of the small block in the small block file.
5352 offsetInBigBlockFile.u.HighPart = 0;
5353 offsetInBigBlockFile.u.LowPart =
5354 blockIndex * This->parentStorage->smallBlockSize;
5356 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5359 * Write those bytes in the buffer to the small block file.
5361 res = BlockChainStream_WriteAt(
5362 This->parentStorage->smallBlockRootChain,
5363 offsetInBigBlockFile,
5364 bytesToWriteInBuffer,
5365 bufferWalker,
5366 &bytesWrittenToBigBlockFile);
5367 if (FAILED(res))
5368 return res;
5371 * Step to the next big block.
5373 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5374 &blockIndex)))
5375 return FALSE;
5376 bufferWalker += bytesWrittenToBigBlockFile;
5377 size -= bytesWrittenToBigBlockFile;
5378 *bytesWritten += bytesWrittenToBigBlockFile;
5379 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5382 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5385 /******************************************************************************
5386 * SmallBlockChainStream_Shrink
5388 * Shrinks this chain in the small block depot.
5390 static BOOL SmallBlockChainStream_Shrink(
5391 SmallBlockChainStream* This,
5392 ULARGE_INTEGER newSize)
5394 ULONG blockIndex, extraBlock;
5395 ULONG numBlocks;
5396 ULONG count = 0;
5398 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5400 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5401 numBlocks++;
5403 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5406 * Go to the new end of chain
5408 while (count < numBlocks)
5410 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5411 &blockIndex)))
5412 return FALSE;
5413 count++;
5417 * If the count is 0, we have a special case, the head of the chain was
5418 * just freed.
5420 if (count == 0)
5422 StgProperty chainProp;
5424 StorageImpl_ReadProperty(This->parentStorage,
5425 This->ownerPropertyIndex,
5426 &chainProp);
5428 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5430 StorageImpl_WriteProperty(This->parentStorage,
5431 This->ownerPropertyIndex,
5432 &chainProp);
5435 * We start freeing the chain at the head block.
5437 extraBlock = blockIndex;
5439 else
5441 /* Get the next block before marking the new end */
5442 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5443 &extraBlock)))
5444 return FALSE;
5446 /* Mark the new end of chain */
5447 SmallBlockChainStream_SetNextBlockInChain(
5448 This,
5449 blockIndex,
5450 BLOCK_END_OF_CHAIN);
5454 * Mark the extra blocks as free
5456 while (extraBlock != BLOCK_END_OF_CHAIN)
5458 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5459 &blockIndex)))
5460 return FALSE;
5461 SmallBlockChainStream_FreeBlock(This, extraBlock);
5462 extraBlock = blockIndex;
5465 return TRUE;
5468 /******************************************************************************
5469 * SmallBlockChainStream_Enlarge
5471 * Grows this chain in the small block depot.
5473 static BOOL SmallBlockChainStream_Enlarge(
5474 SmallBlockChainStream* This,
5475 ULARGE_INTEGER newSize)
5477 ULONG blockIndex, currentBlock;
5478 ULONG newNumBlocks;
5479 ULONG oldNumBlocks = 0;
5481 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5484 * Empty chain. Create the head.
5486 if (blockIndex == BLOCK_END_OF_CHAIN)
5488 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5489 SmallBlockChainStream_SetNextBlockInChain(
5490 This,
5491 blockIndex,
5492 BLOCK_END_OF_CHAIN);
5494 if (This->headOfStreamPlaceHolder != NULL)
5496 *(This->headOfStreamPlaceHolder) = blockIndex;
5498 else
5500 StgProperty chainProp;
5502 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5503 &chainProp);
5505 chainProp.startingBlock = blockIndex;
5507 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5508 &chainProp);
5512 currentBlock = blockIndex;
5515 * Figure out how many blocks are needed to contain this stream
5517 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5519 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5520 newNumBlocks++;
5523 * Go to the current end of chain
5525 while (blockIndex != BLOCK_END_OF_CHAIN)
5527 oldNumBlocks++;
5528 currentBlock = blockIndex;
5529 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5530 return FALSE;
5534 * Add new blocks to the chain
5536 while (oldNumBlocks < newNumBlocks)
5538 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5539 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5541 SmallBlockChainStream_SetNextBlockInChain(
5542 This,
5543 blockIndex,
5544 BLOCK_END_OF_CHAIN);
5546 currentBlock = blockIndex;
5547 oldNumBlocks++;
5550 return TRUE;
5553 /******************************************************************************
5554 * SmallBlockChainStream_SetSize
5556 * Sets the size of this stream.
5557 * The file will grow if we grow the chain.
5559 * TODO: Free the actual blocks in the file when we shrink the chain.
5560 * Currently, the blocks are still in the file. So the file size
5561 * doesn't shrink even if we shrink streams.
5563 BOOL SmallBlockChainStream_SetSize(
5564 SmallBlockChainStream* This,
5565 ULARGE_INTEGER newSize)
5567 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5569 if (newSize.u.LowPart == size.u.LowPart)
5570 return TRUE;
5572 if (newSize.u.LowPart < size.u.LowPart)
5574 SmallBlockChainStream_Shrink(This, newSize);
5576 else
5578 SmallBlockChainStream_Enlarge(This, newSize);
5581 return TRUE;
5584 /******************************************************************************
5585 * SmallBlockChainStream_GetCount
5587 * Returns the number of small blocks that comprises this chain.
5588 * This is not the size of the stream as the last block may not be full!
5591 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5593 ULONG blockIndex;
5594 ULONG count = 0;
5596 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5598 while(blockIndex != BLOCK_END_OF_CHAIN)
5600 count++;
5602 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
5603 blockIndex, &blockIndex)))
5604 return 0;
5607 return count;
5610 /******************************************************************************
5611 * SmallBlockChainStream_GetSize
5613 * Returns the size of this chain.
5615 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5617 StgProperty chainProperty;
5619 if(This->headOfStreamPlaceHolder != NULL)
5621 ULARGE_INTEGER result;
5622 result.u.HighPart = 0;
5624 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
5625 This->parentStorage->smallBlockSize;
5627 return result;
5630 StorageImpl_ReadProperty(
5631 This->parentStorage,
5632 This->ownerPropertyIndex,
5633 &chainProperty);
5635 return chainProperty.size;
5638 /******************************************************************************
5639 * StgCreateDocfile [OLE32.@]
5640 * Creates a new compound file storage object
5642 * PARAMS
5643 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5644 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5645 * reserved [ ?] unused?, usually 0
5646 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5648 * RETURNS
5649 * S_OK if the file was successfully created
5650 * some STG_E_ value if error
5651 * NOTES
5652 * if pwcsName is NULL, create file with new unique name
5653 * the function can returns
5654 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5655 * (unrealized now)
5657 HRESULT WINAPI StgCreateDocfile(
5658 LPCOLESTR pwcsName,
5659 DWORD grfMode,
5660 DWORD reserved,
5661 IStorage **ppstgOpen)
5663 StorageImpl* newStorage = 0;
5664 HANDLE hFile = INVALID_HANDLE_VALUE;
5665 HRESULT hr = STG_E_INVALIDFLAG;
5666 DWORD shareMode;
5667 DWORD accessMode;
5668 DWORD creationMode;
5669 DWORD fileAttributes;
5670 WCHAR tempFileName[MAX_PATH];
5672 TRACE("(%s, %x, %d, %p)\n",
5673 debugstr_w(pwcsName), grfMode,
5674 reserved, ppstgOpen);
5676 if (ppstgOpen == 0)
5677 return STG_E_INVALIDPOINTER;
5678 if (reserved != 0)
5679 return STG_E_INVALIDPARAMETER;
5681 /* if no share mode given then DENY_NONE is the default */
5682 if (STGM_SHARE_MODE(grfMode) == 0)
5683 grfMode |= STGM_SHARE_DENY_NONE;
5685 if ( FAILED( validateSTGM(grfMode) ))
5686 goto end;
5688 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5689 switch(STGM_ACCESS_MODE(grfMode))
5691 case STGM_WRITE:
5692 case STGM_READWRITE:
5693 break;
5694 default:
5695 goto end;
5698 /* in direct mode, can only use SHARE_EXCLUSIVE */
5699 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5700 goto end;
5702 /* but in transacted mode, any share mode is valid */
5705 * Generate a unique name.
5707 if (pwcsName == 0)
5709 WCHAR tempPath[MAX_PATH];
5710 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5712 memset(tempPath, 0, sizeof(tempPath));
5713 memset(tempFileName, 0, sizeof(tempFileName));
5715 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5716 tempPath[0] = '.';
5718 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5719 pwcsName = tempFileName;
5720 else
5722 hr = STG_E_INSUFFICIENTMEMORY;
5723 goto end;
5726 creationMode = TRUNCATE_EXISTING;
5728 else
5730 creationMode = GetCreationModeFromSTGM(grfMode);
5734 * Interpret the STGM value grfMode
5736 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5737 accessMode = GetAccessModeFromSTGM(grfMode);
5739 if (grfMode & STGM_DELETEONRELEASE)
5740 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5741 else
5742 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5744 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5745 FIXME("Storage share mode not implemented.\n");
5747 if (grfMode & STGM_TRANSACTED)
5748 FIXME("Transacted mode not implemented.\n");
5750 *ppstgOpen = 0;
5752 hFile = CreateFileW(pwcsName,
5753 accessMode,
5754 shareMode,
5755 NULL,
5756 creationMode,
5757 fileAttributes,
5760 if (hFile == INVALID_HANDLE_VALUE)
5762 if(GetLastError() == ERROR_FILE_EXISTS)
5763 hr = STG_E_FILEALREADYEXISTS;
5764 else
5765 hr = E_FAIL;
5766 goto end;
5770 * Allocate and initialize the new IStorage32object.
5772 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5774 if (newStorage == 0)
5776 hr = STG_E_INSUFFICIENTMEMORY;
5777 goto end;
5780 hr = StorageImpl_Construct(
5781 newStorage,
5782 hFile,
5783 pwcsName,
5784 NULL,
5785 grfMode,
5786 TRUE,
5787 TRUE);
5789 if (FAILED(hr))
5791 HeapFree(GetProcessHeap(), 0, newStorage);
5792 goto end;
5796 * Get an "out" pointer for the caller.
5798 hr = StorageBaseImpl_QueryInterface(
5799 (IStorage*)newStorage,
5800 &IID_IStorage,
5801 (void**)ppstgOpen);
5802 end:
5803 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5805 return hr;
5808 /******************************************************************************
5809 * StgCreateStorageEx [OLE32.@]
5811 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5813 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5814 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5816 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5818 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5819 return STG_E_INVALIDPARAMETER;
5822 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5824 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5825 return STG_E_INVALIDPARAMETER;
5828 if (stgfmt == STGFMT_FILE)
5830 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5831 return STG_E_INVALIDPARAMETER;
5834 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5836 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5837 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5840 ERR("Invalid stgfmt argument\n");
5841 return STG_E_INVALIDPARAMETER;
5844 /******************************************************************************
5845 * StgCreatePropSetStg [OLE32.@]
5847 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5848 IPropertySetStorage **ppPropSetStg)
5850 HRESULT hr;
5852 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5853 if (reserved)
5854 hr = STG_E_INVALIDPARAMETER;
5855 else
5856 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5857 (void**)ppPropSetStg);
5858 return hr;
5861 /******************************************************************************
5862 * StgOpenStorageEx [OLE32.@]
5864 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5866 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5867 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5869 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5871 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5872 return STG_E_INVALIDPARAMETER;
5875 switch (stgfmt)
5877 case STGFMT_FILE:
5878 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5879 return STG_E_INVALIDPARAMETER;
5881 case STGFMT_STORAGE:
5882 break;
5884 case STGFMT_DOCFILE:
5885 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5887 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5888 return STG_E_INVALIDPARAMETER;
5890 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5891 break;
5893 case STGFMT_ANY:
5894 WARN("STGFMT_ANY assuming storage\n");
5895 break;
5897 default:
5898 return STG_E_INVALIDPARAMETER;
5901 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
5905 /******************************************************************************
5906 * StgOpenStorage [OLE32.@]
5908 HRESULT WINAPI StgOpenStorage(
5909 const OLECHAR *pwcsName,
5910 IStorage *pstgPriority,
5911 DWORD grfMode,
5912 SNB snbExclude,
5913 DWORD reserved,
5914 IStorage **ppstgOpen)
5916 StorageImpl* newStorage = 0;
5917 HRESULT hr = S_OK;
5918 HANDLE hFile = 0;
5919 DWORD shareMode;
5920 DWORD accessMode;
5921 WCHAR fullname[MAX_PATH];
5923 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5924 debugstr_w(pwcsName), pstgPriority, grfMode,
5925 snbExclude, reserved, ppstgOpen);
5927 if (pwcsName == 0)
5929 hr = STG_E_INVALIDNAME;
5930 goto end;
5933 if (ppstgOpen == 0)
5935 hr = STG_E_INVALIDPOINTER;
5936 goto end;
5939 if (reserved)
5941 hr = STG_E_INVALIDPARAMETER;
5942 goto end;
5945 if (grfMode & STGM_PRIORITY)
5947 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5948 return STG_E_INVALIDFLAG;
5949 if (grfMode & STGM_DELETEONRELEASE)
5950 return STG_E_INVALIDFUNCTION;
5951 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5952 return STG_E_INVALIDFLAG;
5953 grfMode &= ~0xf0; /* remove the existing sharing mode */
5954 grfMode |= STGM_SHARE_DENY_NONE;
5956 /* STGM_PRIORITY stops other IStorage objects on the same file from
5957 * committing until the STGM_PRIORITY IStorage is closed. it also
5958 * stops non-transacted mode StgOpenStorage calls with write access from
5959 * succeeding. obviously, both of these cannot be achieved through just
5960 * file share flags */
5961 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5965 * Validate the sharing mode
5967 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5968 switch(STGM_SHARE_MODE(grfMode))
5970 case STGM_SHARE_EXCLUSIVE:
5971 case STGM_SHARE_DENY_WRITE:
5972 break;
5973 default:
5974 hr = STG_E_INVALIDFLAG;
5975 goto end;
5978 if ( FAILED( validateSTGM(grfMode) ) ||
5979 (grfMode&STGM_CREATE))
5981 hr = STG_E_INVALIDFLAG;
5982 goto end;
5985 /* shared reading requires transacted mode */
5986 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5987 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5988 !(grfMode&STGM_TRANSACTED) )
5990 hr = STG_E_INVALIDFLAG;
5991 goto end;
5995 * Interpret the STGM value grfMode
5997 shareMode = GetShareModeFromSTGM(grfMode);
5998 accessMode = GetAccessModeFromSTGM(grfMode);
6000 *ppstgOpen = 0;
6002 hFile = CreateFileW( pwcsName,
6003 accessMode,
6004 shareMode,
6005 NULL,
6006 OPEN_EXISTING,
6007 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6010 if (hFile==INVALID_HANDLE_VALUE)
6012 DWORD last_error = GetLastError();
6014 hr = E_FAIL;
6016 switch (last_error)
6018 case ERROR_FILE_NOT_FOUND:
6019 hr = STG_E_FILENOTFOUND;
6020 break;
6022 case ERROR_PATH_NOT_FOUND:
6023 hr = STG_E_PATHNOTFOUND;
6024 break;
6026 case ERROR_ACCESS_DENIED:
6027 case ERROR_WRITE_PROTECT:
6028 hr = STG_E_ACCESSDENIED;
6029 break;
6031 case ERROR_SHARING_VIOLATION:
6032 hr = STG_E_SHAREVIOLATION;
6033 break;
6035 default:
6036 hr = E_FAIL;
6039 goto end;
6043 * Refuse to open the file if it's too small to be a structured storage file
6044 * FIXME: verify the file when reading instead of here
6046 if (GetFileSize(hFile, NULL) < 0x100)
6048 CloseHandle(hFile);
6049 hr = STG_E_FILEALREADYEXISTS;
6050 goto end;
6054 * Allocate and initialize the new IStorage32object.
6056 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6058 if (newStorage == 0)
6060 hr = STG_E_INSUFFICIENTMEMORY;
6061 goto end;
6064 /* Initialize the storage */
6065 hr = StorageImpl_Construct(
6066 newStorage,
6067 hFile,
6068 pwcsName,
6069 NULL,
6070 grfMode,
6071 TRUE,
6072 FALSE );
6074 if (FAILED(hr))
6076 HeapFree(GetProcessHeap(), 0, newStorage);
6078 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6080 if(hr == STG_E_INVALIDHEADER)
6081 hr = STG_E_FILEALREADYEXISTS;
6082 goto end;
6085 /* prepare the file name string given in lieu of the root property name */
6086 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6087 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6088 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6091 * Get an "out" pointer for the caller.
6093 hr = StorageBaseImpl_QueryInterface(
6094 (IStorage*)newStorage,
6095 &IID_IStorage,
6096 (void**)ppstgOpen);
6098 end:
6099 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6100 return hr;
6103 /******************************************************************************
6104 * StgCreateDocfileOnILockBytes [OLE32.@]
6106 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6107 ILockBytes *plkbyt,
6108 DWORD grfMode,
6109 DWORD reserved,
6110 IStorage** ppstgOpen)
6112 StorageImpl* newStorage = 0;
6113 HRESULT hr = S_OK;
6115 if ((ppstgOpen == 0) || (plkbyt == 0))
6116 return STG_E_INVALIDPOINTER;
6119 * Allocate and initialize the new IStorage object.
6121 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6123 if (newStorage == 0)
6124 return STG_E_INSUFFICIENTMEMORY;
6126 hr = StorageImpl_Construct(
6127 newStorage,
6130 plkbyt,
6131 grfMode,
6132 FALSE,
6133 TRUE);
6135 if (FAILED(hr))
6137 HeapFree(GetProcessHeap(), 0, newStorage);
6138 return hr;
6142 * Get an "out" pointer for the caller.
6144 hr = StorageBaseImpl_QueryInterface(
6145 (IStorage*)newStorage,
6146 &IID_IStorage,
6147 (void**)ppstgOpen);
6149 return hr;
6152 /******************************************************************************
6153 * StgOpenStorageOnILockBytes [OLE32.@]
6155 HRESULT WINAPI StgOpenStorageOnILockBytes(
6156 ILockBytes *plkbyt,
6157 IStorage *pstgPriority,
6158 DWORD grfMode,
6159 SNB snbExclude,
6160 DWORD reserved,
6161 IStorage **ppstgOpen)
6163 StorageImpl* newStorage = 0;
6164 HRESULT hr = S_OK;
6166 if ((plkbyt == 0) || (ppstgOpen == 0))
6167 return STG_E_INVALIDPOINTER;
6169 if ( FAILED( validateSTGM(grfMode) ))
6170 return STG_E_INVALIDFLAG;
6172 *ppstgOpen = 0;
6175 * Allocate and initialize the new IStorage object.
6177 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6179 if (newStorage == 0)
6180 return STG_E_INSUFFICIENTMEMORY;
6182 hr = StorageImpl_Construct(
6183 newStorage,
6186 plkbyt,
6187 grfMode,
6188 FALSE,
6189 FALSE);
6191 if (FAILED(hr))
6193 HeapFree(GetProcessHeap(), 0, newStorage);
6194 return hr;
6198 * Get an "out" pointer for the caller.
6200 hr = StorageBaseImpl_QueryInterface(
6201 (IStorage*)newStorage,
6202 &IID_IStorage,
6203 (void**)ppstgOpen);
6205 return hr;
6208 /******************************************************************************
6209 * StgSetTimes [ole32.@]
6210 * StgSetTimes [OLE32.@]
6214 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6215 FILETIME const *patime, FILETIME const *pmtime)
6217 IStorage *stg = NULL;
6218 HRESULT r;
6220 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6222 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6223 0, 0, &stg);
6224 if( SUCCEEDED(r) )
6226 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6227 IStorage_Release(stg);
6230 return r;
6233 /******************************************************************************
6234 * StgIsStorageILockBytes [OLE32.@]
6236 * Determines if the ILockBytes contains a storage object.
6238 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6240 BYTE sig[8];
6241 ULARGE_INTEGER offset;
6243 offset.u.HighPart = 0;
6244 offset.u.LowPart = 0;
6246 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6248 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6249 return S_OK;
6251 return S_FALSE;
6254 /******************************************************************************
6255 * WriteClassStg [OLE32.@]
6257 * This method will store the specified CLSID in the specified storage object
6259 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6261 HRESULT hRes;
6263 if(!pStg)
6264 return E_INVALIDARG;
6266 if(!rclsid)
6267 return STG_E_INVALIDPOINTER;
6269 hRes = IStorage_SetClass(pStg, rclsid);
6271 return hRes;
6274 /***********************************************************************
6275 * ReadClassStg (OLE32.@)
6277 * This method reads the CLSID previously written to a storage object with
6278 * the WriteClassStg.
6280 * PARAMS
6281 * pstg [I] IStorage pointer
6282 * pclsid [O] Pointer to where the CLSID is written
6284 * RETURNS
6285 * Success: S_OK.
6286 * Failure: HRESULT code.
6288 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6290 STATSTG pstatstg;
6291 HRESULT hRes;
6293 TRACE("(%p, %p)\n", pstg, pclsid);
6295 if(!pstg || !pclsid)
6296 return E_INVALIDARG;
6299 * read a STATSTG structure (contains the clsid) from the storage
6301 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6303 if(SUCCEEDED(hRes))
6304 *pclsid=pstatstg.clsid;
6306 return hRes;
6309 /***********************************************************************
6310 * OleLoadFromStream (OLE32.@)
6312 * This function loads an object from stream
6314 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6316 CLSID clsid;
6317 HRESULT res;
6318 LPPERSISTSTREAM xstm;
6320 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6322 res=ReadClassStm(pStm,&clsid);
6323 if (FAILED(res))
6324 return res;
6325 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6326 if (FAILED(res))
6327 return res;
6328 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6329 if (FAILED(res)) {
6330 IUnknown_Release((IUnknown*)*ppvObj);
6331 return res;
6333 res=IPersistStream_Load(xstm,pStm);
6334 IPersistStream_Release(xstm);
6335 /* FIXME: all refcounts ok at this point? I think they should be:
6336 * pStm : unchanged
6337 * ppvObj : 1
6338 * xstm : 0 (released)
6340 return res;
6343 /***********************************************************************
6344 * OleSaveToStream (OLE32.@)
6346 * This function saves an object with the IPersistStream interface on it
6347 * to the specified stream.
6349 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6352 CLSID clsid;
6353 HRESULT res;
6355 TRACE("(%p,%p)\n",pPStm,pStm);
6357 res=IPersistStream_GetClassID(pPStm,&clsid);
6359 if (SUCCEEDED(res)){
6361 res=WriteClassStm(pStm,&clsid);
6363 if (SUCCEEDED(res))
6365 res=IPersistStream_Save(pPStm,pStm,TRUE);
6368 TRACE("Finished Save\n");
6369 return res;
6372 /****************************************************************************
6373 * This method validate a STGM parameter that can contain the values below
6375 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6376 * The stgm values contained in 0xffff0000 are bitmasks.
6378 * STGM_DIRECT 0x00000000
6379 * STGM_TRANSACTED 0x00010000
6380 * STGM_SIMPLE 0x08000000
6382 * STGM_READ 0x00000000
6383 * STGM_WRITE 0x00000001
6384 * STGM_READWRITE 0x00000002
6386 * STGM_SHARE_DENY_NONE 0x00000040
6387 * STGM_SHARE_DENY_READ 0x00000030
6388 * STGM_SHARE_DENY_WRITE 0x00000020
6389 * STGM_SHARE_EXCLUSIVE 0x00000010
6391 * STGM_PRIORITY 0x00040000
6392 * STGM_DELETEONRELEASE 0x04000000
6394 * STGM_CREATE 0x00001000
6395 * STGM_CONVERT 0x00020000
6396 * STGM_FAILIFTHERE 0x00000000
6398 * STGM_NOSCRATCH 0x00100000
6399 * STGM_NOSNAPSHOT 0x00200000
6401 static HRESULT validateSTGM(DWORD stgm)
6403 DWORD access = STGM_ACCESS_MODE(stgm);
6404 DWORD share = STGM_SHARE_MODE(stgm);
6405 DWORD create = STGM_CREATE_MODE(stgm);
6407 if (stgm&~STGM_KNOWN_FLAGS)
6409 ERR("unknown flags %08x\n", stgm);
6410 return E_FAIL;
6413 switch (access)
6415 case STGM_READ:
6416 case STGM_WRITE:
6417 case STGM_READWRITE:
6418 break;
6419 default:
6420 return E_FAIL;
6423 switch (share)
6425 case STGM_SHARE_DENY_NONE:
6426 case STGM_SHARE_DENY_READ:
6427 case STGM_SHARE_DENY_WRITE:
6428 case STGM_SHARE_EXCLUSIVE:
6429 break;
6430 default:
6431 return E_FAIL;
6434 switch (create)
6436 case STGM_CREATE:
6437 case STGM_FAILIFTHERE:
6438 break;
6439 default:
6440 return E_FAIL;
6444 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6446 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6447 return E_FAIL;
6450 * STGM_CREATE | STGM_CONVERT
6451 * if both are false, STGM_FAILIFTHERE is set to TRUE
6453 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6454 return E_FAIL;
6457 * STGM_NOSCRATCH requires STGM_TRANSACTED
6459 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6460 return E_FAIL;
6463 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6464 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6466 if ( (stgm & STGM_NOSNAPSHOT) &&
6467 (!(stgm & STGM_TRANSACTED) ||
6468 share == STGM_SHARE_EXCLUSIVE ||
6469 share == STGM_SHARE_DENY_WRITE) )
6470 return E_FAIL;
6472 return S_OK;
6475 /****************************************************************************
6476 * GetShareModeFromSTGM
6478 * This method will return a share mode flag from a STGM value.
6479 * The STGM value is assumed valid.
6481 static DWORD GetShareModeFromSTGM(DWORD stgm)
6483 switch (STGM_SHARE_MODE(stgm))
6485 case STGM_SHARE_DENY_NONE:
6486 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6487 case STGM_SHARE_DENY_READ:
6488 return FILE_SHARE_WRITE;
6489 case STGM_SHARE_DENY_WRITE:
6490 return FILE_SHARE_READ;
6491 case STGM_SHARE_EXCLUSIVE:
6492 return 0;
6494 ERR("Invalid share mode!\n");
6495 assert(0);
6496 return 0;
6499 /****************************************************************************
6500 * GetAccessModeFromSTGM
6502 * This method will return an access mode flag from a STGM value.
6503 * The STGM value is assumed valid.
6505 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6507 switch (STGM_ACCESS_MODE(stgm))
6509 case STGM_READ:
6510 return GENERIC_READ;
6511 case STGM_WRITE:
6512 case STGM_READWRITE:
6513 return GENERIC_READ | GENERIC_WRITE;
6515 ERR("Invalid access mode!\n");
6516 assert(0);
6517 return 0;
6520 /****************************************************************************
6521 * GetCreationModeFromSTGM
6523 * This method will return a creation mode flag from a STGM value.
6524 * The STGM value is assumed valid.
6526 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6528 switch(STGM_CREATE_MODE(stgm))
6530 case STGM_CREATE:
6531 return CREATE_ALWAYS;
6532 case STGM_CONVERT:
6533 FIXME("STGM_CONVERT not implemented!\n");
6534 return CREATE_NEW;
6535 case STGM_FAILIFTHERE:
6536 return CREATE_NEW;
6538 ERR("Invalid create mode!\n");
6539 assert(0);
6540 return 0;
6544 /*************************************************************************
6545 * OLECONVERT_LoadOLE10 [Internal]
6547 * Loads the OLE10 STREAM to memory
6549 * PARAMS
6550 * pOleStream [I] The OLESTREAM
6551 * pData [I] Data Structure for the OLESTREAM Data
6553 * RETURNS
6554 * Success: S_OK
6555 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6556 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6558 * NOTES
6559 * This function is used by OleConvertOLESTREAMToIStorage only.
6561 * Memory allocated for pData must be freed by the caller
6563 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6565 DWORD dwSize;
6566 HRESULT hRes = S_OK;
6567 int nTryCnt=0;
6568 int max_try = 6;
6570 pData->pData = NULL;
6571 pData->pstrOleObjFileName = NULL;
6573 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6575 /* Get the OleID */
6576 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6577 if(dwSize != sizeof(pData->dwOleID))
6579 hRes = CONVERT10_E_OLESTREAM_GET;
6581 else if(pData->dwOleID != OLESTREAM_ID)
6583 hRes = CONVERT10_E_OLESTREAM_FMT;
6585 else
6587 hRes = S_OK;
6588 break;
6592 if(hRes == S_OK)
6594 /* Get the TypeID... more info needed for this field */
6595 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6596 if(dwSize != sizeof(pData->dwTypeID))
6598 hRes = CONVERT10_E_OLESTREAM_GET;
6601 if(hRes == S_OK)
6603 if(pData->dwTypeID != 0)
6605 /* Get the length of the OleTypeName */
6606 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6607 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6609 hRes = CONVERT10_E_OLESTREAM_GET;
6612 if(hRes == S_OK)
6614 if(pData->dwOleTypeNameLength > 0)
6616 /* Get the OleTypeName */
6617 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6618 if(dwSize != pData->dwOleTypeNameLength)
6620 hRes = CONVERT10_E_OLESTREAM_GET;
6624 if(bStrem1)
6626 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6627 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6629 hRes = CONVERT10_E_OLESTREAM_GET;
6631 if(hRes == S_OK)
6633 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6634 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6635 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6636 if(pData->pstrOleObjFileName)
6638 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6639 if(dwSize != pData->dwOleObjFileNameLength)
6641 hRes = CONVERT10_E_OLESTREAM_GET;
6644 else
6645 hRes = CONVERT10_E_OLESTREAM_GET;
6648 else
6650 /* Get the Width of the Metafile */
6651 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6652 if(dwSize != sizeof(pData->dwMetaFileWidth))
6654 hRes = CONVERT10_E_OLESTREAM_GET;
6656 if(hRes == S_OK)
6658 /* Get the Height of the Metafile */
6659 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6660 if(dwSize != sizeof(pData->dwMetaFileHeight))
6662 hRes = CONVERT10_E_OLESTREAM_GET;
6666 if(hRes == S_OK)
6668 /* Get the Length of the Data */
6669 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6670 if(dwSize != sizeof(pData->dwDataLength))
6672 hRes = CONVERT10_E_OLESTREAM_GET;
6676 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6678 if(!bStrem1) /* if it is a second OLE stream data */
6680 pData->dwDataLength -= 8;
6681 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6682 if(dwSize != sizeof(pData->strUnknown))
6684 hRes = CONVERT10_E_OLESTREAM_GET;
6688 if(hRes == S_OK)
6690 if(pData->dwDataLength > 0)
6692 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6694 /* Get Data (ex. IStorage, Metafile, or BMP) */
6695 if(pData->pData)
6697 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6698 if(dwSize != pData->dwDataLength)
6700 hRes = CONVERT10_E_OLESTREAM_GET;
6703 else
6705 hRes = CONVERT10_E_OLESTREAM_GET;
6711 return hRes;
6714 /*************************************************************************
6715 * OLECONVERT_SaveOLE10 [Internal]
6717 * Saves the OLE10 STREAM From memory
6719 * PARAMS
6720 * pData [I] Data Structure for the OLESTREAM Data
6721 * pOleStream [I] The OLESTREAM to save
6723 * RETURNS
6724 * Success: S_OK
6725 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6727 * NOTES
6728 * This function is used by OleConvertIStorageToOLESTREAM only.
6731 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6733 DWORD dwSize;
6734 HRESULT hRes = S_OK;
6737 /* Set the OleID */
6738 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6739 if(dwSize != sizeof(pData->dwOleID))
6741 hRes = CONVERT10_E_OLESTREAM_PUT;
6744 if(hRes == S_OK)
6746 /* Set the TypeID */
6747 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6748 if(dwSize != sizeof(pData->dwTypeID))
6750 hRes = CONVERT10_E_OLESTREAM_PUT;
6754 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6756 /* Set the Length of the OleTypeName */
6757 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6758 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6760 hRes = CONVERT10_E_OLESTREAM_PUT;
6763 if(hRes == S_OK)
6765 if(pData->dwOleTypeNameLength > 0)
6767 /* Set the OleTypeName */
6768 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6769 if(dwSize != pData->dwOleTypeNameLength)
6771 hRes = CONVERT10_E_OLESTREAM_PUT;
6776 if(hRes == S_OK)
6778 /* Set the width of the Metafile */
6779 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6780 if(dwSize != sizeof(pData->dwMetaFileWidth))
6782 hRes = CONVERT10_E_OLESTREAM_PUT;
6786 if(hRes == S_OK)
6788 /* Set the height of the Metafile */
6789 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6790 if(dwSize != sizeof(pData->dwMetaFileHeight))
6792 hRes = CONVERT10_E_OLESTREAM_PUT;
6796 if(hRes == S_OK)
6798 /* Set the length of the Data */
6799 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6800 if(dwSize != sizeof(pData->dwDataLength))
6802 hRes = CONVERT10_E_OLESTREAM_PUT;
6806 if(hRes == S_OK)
6808 if(pData->dwDataLength > 0)
6810 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6811 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6812 if(dwSize != pData->dwDataLength)
6814 hRes = CONVERT10_E_OLESTREAM_PUT;
6819 return hRes;
6822 /*************************************************************************
6823 * OLECONVERT_GetOLE20FromOLE10[Internal]
6825 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6826 * opens it, and copies the content to the dest IStorage for
6827 * OleConvertOLESTREAMToIStorage
6830 * PARAMS
6831 * pDestStorage [I] The IStorage to copy the data to
6832 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6833 * nBufferLength [I] The size of the buffer
6835 * RETURNS
6836 * Nothing
6838 * NOTES
6842 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6844 HRESULT hRes;
6845 HANDLE hFile;
6846 IStorage *pTempStorage;
6847 DWORD dwNumOfBytesWritten;
6848 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6849 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6851 /* Create a temp File */
6852 GetTempPathW(MAX_PATH, wstrTempDir);
6853 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6854 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6856 if(hFile != INVALID_HANDLE_VALUE)
6858 /* Write IStorage Data to File */
6859 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6860 CloseHandle(hFile);
6862 /* Open and copy temp storage to the Dest Storage */
6863 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6864 if(hRes == S_OK)
6866 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6867 StorageBaseImpl_Release(pTempStorage);
6869 DeleteFileW(wstrTempFile);
6874 /*************************************************************************
6875 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6877 * Saves the OLE10 STREAM From memory
6879 * PARAMS
6880 * pStorage [I] The Src IStorage to copy
6881 * pData [I] The Dest Memory to write to.
6883 * RETURNS
6884 * The size in bytes allocated for pData
6886 * NOTES
6887 * Memory allocated for pData must be freed by the caller
6889 * Used by OleConvertIStorageToOLESTREAM only.
6892 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6894 HANDLE hFile;
6895 HRESULT hRes;
6896 DWORD nDataLength = 0;
6897 IStorage *pTempStorage;
6898 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6899 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6901 *pData = NULL;
6903 /* Create temp Storage */
6904 GetTempPathW(MAX_PATH, wstrTempDir);
6905 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6906 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6908 if(hRes == S_OK)
6910 /* Copy Src Storage to the Temp Storage */
6911 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6912 StorageBaseImpl_Release(pTempStorage);
6914 /* Open Temp Storage as a file and copy to memory */
6915 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6916 if(hFile != INVALID_HANDLE_VALUE)
6918 nDataLength = GetFileSize(hFile, NULL);
6919 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6920 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6921 CloseHandle(hFile);
6923 DeleteFileW(wstrTempFile);
6925 return nDataLength;
6928 /*************************************************************************
6929 * OLECONVERT_CreateOleStream [Internal]
6931 * Creates the "\001OLE" stream in the IStorage if necessary.
6933 * PARAMS
6934 * pStorage [I] Dest storage to create the stream in
6936 * RETURNS
6937 * Nothing
6939 * NOTES
6940 * This function is used by OleConvertOLESTREAMToIStorage only.
6942 * This stream is still unknown, MS Word seems to have extra data
6943 * but since the data is stored in the OLESTREAM there should be
6944 * no need to recreate the stream. If the stream is manually
6945 * deleted it will create it with this default data.
6948 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6950 HRESULT hRes;
6951 IStream *pStream;
6952 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6953 BYTE pOleStreamHeader [] =
6955 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6956 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6957 0x00, 0x00, 0x00, 0x00
6960 /* Create stream if not present */
6961 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6962 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6964 if(hRes == S_OK)
6966 /* Write default Data */
6967 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6968 IStream_Release(pStream);
6972 /* write a string to a stream, preceded by its length */
6973 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6975 HRESULT r;
6976 LPSTR str;
6977 DWORD len = 0;
6979 if( string )
6980 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6981 r = IStream_Write( stm, &len, sizeof(len), NULL);
6982 if( FAILED( r ) )
6983 return r;
6984 if(len == 0)
6985 return r;
6986 str = CoTaskMemAlloc( len );
6987 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6988 r = IStream_Write( stm, str, len, NULL);
6989 CoTaskMemFree( str );
6990 return r;
6993 /* read a string preceded by its length from a stream */
6994 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6996 HRESULT r;
6997 DWORD len, count = 0;
6998 LPSTR str;
6999 LPWSTR wstr;
7001 r = IStream_Read( stm, &len, sizeof(len), &count );
7002 if( FAILED( r ) )
7003 return r;
7004 if( count != sizeof(len) )
7005 return E_OUTOFMEMORY;
7007 TRACE("%d bytes\n",len);
7009 str = CoTaskMemAlloc( len );
7010 if( !str )
7011 return E_OUTOFMEMORY;
7012 count = 0;
7013 r = IStream_Read( stm, str, len, &count );
7014 if( FAILED( r ) )
7015 return r;
7016 if( count != len )
7018 CoTaskMemFree( str );
7019 return E_OUTOFMEMORY;
7022 TRACE("Read string %s\n",debugstr_an(str,len));
7024 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7025 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7026 if( wstr )
7027 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7028 CoTaskMemFree( str );
7030 *string = wstr;
7032 return r;
7036 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7037 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7039 IStream *pstm;
7040 HRESULT r = S_OK;
7041 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7043 static const BYTE unknown1[12] =
7044 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7045 0xFF, 0xFF, 0xFF, 0xFF};
7046 static const BYTE unknown2[16] =
7047 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7048 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7050 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7051 debugstr_w(lpszUserType), debugstr_w(szClipName),
7052 debugstr_w(szProgIDName));
7054 /* Create a CompObj stream */
7055 r = IStorage_CreateStream(pstg, szwStreamName,
7056 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7057 if( FAILED (r) )
7058 return r;
7060 /* Write CompObj Structure to stream */
7061 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7063 if( SUCCEEDED( r ) )
7064 r = WriteClassStm( pstm, clsid );
7066 if( SUCCEEDED( r ) )
7067 r = STREAM_WriteString( pstm, lpszUserType );
7068 if( SUCCEEDED( r ) )
7069 r = STREAM_WriteString( pstm, szClipName );
7070 if( SUCCEEDED( r ) )
7071 r = STREAM_WriteString( pstm, szProgIDName );
7072 if( SUCCEEDED( r ) )
7073 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7075 IStream_Release( pstm );
7077 return r;
7080 /***********************************************************************
7081 * WriteFmtUserTypeStg (OLE32.@)
7083 HRESULT WINAPI WriteFmtUserTypeStg(
7084 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7086 HRESULT r;
7087 WCHAR szwClipName[0x40];
7088 CLSID clsid = CLSID_NULL;
7089 LPWSTR wstrProgID = NULL;
7090 DWORD n;
7092 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7094 /* get the clipboard format name */
7095 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7096 szwClipName[n]=0;
7098 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7100 /* FIXME: There's room to save a CLSID and its ProgID, but
7101 the CLSID is not looked up in the registry and in all the
7102 tests I wrote it was CLSID_NULL. Where does it come from?
7105 /* get the real program ID. This may fail, but that's fine */
7106 ProgIDFromCLSID(&clsid, &wstrProgID);
7108 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7110 r = STORAGE_WriteCompObj( pstg, &clsid,
7111 lpszUserType, szwClipName, wstrProgID );
7113 CoTaskMemFree(wstrProgID);
7115 return r;
7119 /******************************************************************************
7120 * ReadFmtUserTypeStg [OLE32.@]
7122 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7124 HRESULT r;
7125 IStream *stm = 0;
7126 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7127 unsigned char unknown1[12];
7128 unsigned char unknown2[16];
7129 DWORD count;
7130 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7131 CLSID clsid;
7133 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7135 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7136 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7137 if( FAILED ( r ) )
7139 WARN("Failed to open stream r = %08x\n", r);
7140 return r;
7143 /* read the various parts of the structure */
7144 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7145 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7146 goto end;
7147 r = ReadClassStm( stm, &clsid );
7148 if( FAILED( r ) )
7149 goto end;
7151 r = STREAM_ReadString( stm, &szCLSIDName );
7152 if( FAILED( r ) )
7153 goto end;
7155 r = STREAM_ReadString( stm, &szOleTypeName );
7156 if( FAILED( r ) )
7157 goto end;
7159 r = STREAM_ReadString( stm, &szProgIDName );
7160 if( FAILED( r ) )
7161 goto end;
7163 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7164 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7165 goto end;
7167 /* ok, success... now we just need to store what we found */
7168 if( pcf )
7169 *pcf = RegisterClipboardFormatW( szOleTypeName );
7170 CoTaskMemFree( szOleTypeName );
7172 if( lplpszUserType )
7173 *lplpszUserType = szCLSIDName;
7174 CoTaskMemFree( szProgIDName );
7176 end:
7177 IStream_Release( stm );
7179 return r;
7183 /*************************************************************************
7184 * OLECONVERT_CreateCompObjStream [Internal]
7186 * Creates a "\001CompObj" is the destination IStorage if necessary.
7188 * PARAMS
7189 * pStorage [I] The dest IStorage to create the CompObj Stream
7190 * if necessary.
7191 * strOleTypeName [I] The ProgID
7193 * RETURNS
7194 * Success: S_OK
7195 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7197 * NOTES
7198 * This function is used by OleConvertOLESTREAMToIStorage only.
7200 * The stream data is stored in the OLESTREAM and there should be
7201 * no need to recreate the stream. If the stream is manually
7202 * deleted it will attempt to create it by querying the registry.
7206 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7208 IStream *pStream;
7209 HRESULT hStorageRes, hRes = S_OK;
7210 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7211 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7212 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7214 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7215 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7217 /* Initialize the CompObj structure */
7218 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7219 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7220 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7223 /* Create a CompObj stream if it doesn't exist */
7224 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7225 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7226 if(hStorageRes == S_OK)
7228 /* copy the OleTypeName to the compobj struct */
7229 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7230 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7232 /* copy the OleTypeName to the compobj struct */
7233 /* Note: in the test made, these were Identical */
7234 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7235 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7237 /* Get the CLSID */
7238 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7239 bufferW, OLESTREAM_MAX_STR_LEN );
7240 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7242 if(hRes == S_OK)
7244 HKEY hKey;
7245 LONG hErr;
7246 /* Get the CLSID Default Name from the Registry */
7247 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7248 if(hErr == ERROR_SUCCESS)
7250 char strTemp[OLESTREAM_MAX_STR_LEN];
7251 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7252 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7253 if(hErr == ERROR_SUCCESS)
7255 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7257 RegCloseKey(hKey);
7261 /* Write CompObj Structure to stream */
7262 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7264 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7266 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7267 if(IStorageCompObj.dwCLSIDNameLength > 0)
7269 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7271 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7272 if(IStorageCompObj.dwOleTypeNameLength > 0)
7274 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7276 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7277 if(IStorageCompObj.dwProgIDNameLength > 0)
7279 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7281 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7282 IStream_Release(pStream);
7284 return hRes;
7288 /*************************************************************************
7289 * OLECONVERT_CreateOlePresStream[Internal]
7291 * Creates the "\002OlePres000" Stream with the Metafile data
7293 * PARAMS
7294 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7295 * dwExtentX [I] Width of the Metafile
7296 * dwExtentY [I] Height of the Metafile
7297 * pData [I] Metafile data
7298 * dwDataLength [I] Size of the Metafile data
7300 * RETURNS
7301 * Success: S_OK
7302 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7304 * NOTES
7305 * This function is used by OleConvertOLESTREAMToIStorage only.
7308 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7310 HRESULT hRes;
7311 IStream *pStream;
7312 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7313 BYTE pOlePresStreamHeader [] =
7315 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7316 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7317 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7318 0x00, 0x00, 0x00, 0x00
7321 BYTE pOlePresStreamHeaderEmpty [] =
7323 0x00, 0x00, 0x00, 0x00,
7324 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7325 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7326 0x00, 0x00, 0x00, 0x00
7329 /* Create the OlePres000 Stream */
7330 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7331 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7333 if(hRes == S_OK)
7335 DWORD nHeaderSize;
7336 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7338 memset(&OlePres, 0, sizeof(OlePres));
7339 /* Do we have any metafile data to save */
7340 if(dwDataLength > 0)
7342 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7343 nHeaderSize = sizeof(pOlePresStreamHeader);
7345 else
7347 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7348 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7350 /* Set width and height of the metafile */
7351 OlePres.dwExtentX = dwExtentX;
7352 OlePres.dwExtentY = -dwExtentY;
7354 /* Set Data and Length */
7355 if(dwDataLength > sizeof(METAFILEPICT16))
7357 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7358 OlePres.pData = &(pData[8]);
7360 /* Save OlePres000 Data to Stream */
7361 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7362 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7363 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7364 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7365 if(OlePres.dwSize > 0)
7367 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7369 IStream_Release(pStream);
7373 /*************************************************************************
7374 * OLECONVERT_CreateOle10NativeStream [Internal]
7376 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7378 * PARAMS
7379 * pStorage [I] Dest storage to create the stream in
7380 * pData [I] Ole10 Native Data (ex. bmp)
7381 * dwDataLength [I] Size of the Ole10 Native Data
7383 * RETURNS
7384 * Nothing
7386 * NOTES
7387 * This function is used by OleConvertOLESTREAMToIStorage only.
7389 * Might need to verify the data and return appropriate error message
7392 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7394 HRESULT hRes;
7395 IStream *pStream;
7396 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7398 /* Create the Ole10Native Stream */
7399 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7400 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7402 if(hRes == S_OK)
7404 /* Write info to stream */
7405 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7406 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7407 IStream_Release(pStream);
7412 /*************************************************************************
7413 * OLECONVERT_GetOLE10ProgID [Internal]
7415 * Finds the ProgID (or OleTypeID) from the IStorage
7417 * PARAMS
7418 * pStorage [I] The Src IStorage to get the ProgID
7419 * strProgID [I] the ProgID string to get
7420 * dwSize [I] the size of the string
7422 * RETURNS
7423 * Success: S_OK
7424 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7426 * NOTES
7427 * This function is used by OleConvertIStorageToOLESTREAM only.
7431 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7433 HRESULT hRes;
7434 IStream *pStream;
7435 LARGE_INTEGER iSeekPos;
7436 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7437 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7439 /* Open the CompObj Stream */
7440 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7441 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7442 if(hRes == S_OK)
7445 /*Get the OleType from the CompObj Stream */
7446 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7447 iSeekPos.u.HighPart = 0;
7449 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7450 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7451 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7452 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7453 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7454 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7455 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7457 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7458 if(*dwSize > 0)
7460 IStream_Read(pStream, strProgID, *dwSize, NULL);
7462 IStream_Release(pStream);
7464 else
7466 STATSTG stat;
7467 LPOLESTR wstrProgID;
7469 /* Get the OleType from the registry */
7470 REFCLSID clsid = &(stat.clsid);
7471 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7472 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7473 if(hRes == S_OK)
7475 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7479 return hRes;
7482 /*************************************************************************
7483 * OLECONVERT_GetOle10PresData [Internal]
7485 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7487 * PARAMS
7488 * pStorage [I] Src IStroage
7489 * pOleStream [I] Dest OleStream Mem Struct
7491 * RETURNS
7492 * Nothing
7494 * NOTES
7495 * This function is used by OleConvertIStorageToOLESTREAM only.
7497 * Memory allocated for pData must be freed by the caller
7501 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7504 HRESULT hRes;
7505 IStream *pStream;
7506 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7508 /* Initialize Default data for OLESTREAM */
7509 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7510 pOleStreamData[0].dwTypeID = 2;
7511 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7512 pOleStreamData[1].dwTypeID = 0;
7513 pOleStreamData[0].dwMetaFileWidth = 0;
7514 pOleStreamData[0].dwMetaFileHeight = 0;
7515 pOleStreamData[0].pData = NULL;
7516 pOleStreamData[1].pData = NULL;
7518 /* Open Ole10Native Stream */
7519 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7520 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7521 if(hRes == S_OK)
7524 /* Read Size and Data */
7525 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7526 if(pOleStreamData->dwDataLength > 0)
7528 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7529 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7531 IStream_Release(pStream);
7537 /*************************************************************************
7538 * OLECONVERT_GetOle20PresData[Internal]
7540 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7542 * PARAMS
7543 * pStorage [I] Src IStroage
7544 * pOleStreamData [I] Dest OleStream Mem Struct
7546 * RETURNS
7547 * Nothing
7549 * NOTES
7550 * This function is used by OleConvertIStorageToOLESTREAM only.
7552 * Memory allocated for pData must be freed by the caller
7554 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7556 HRESULT hRes;
7557 IStream *pStream;
7558 OLECONVERT_ISTORAGE_OLEPRES olePress;
7559 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7561 /* Initialize Default data for OLESTREAM */
7562 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7563 pOleStreamData[0].dwTypeID = 2;
7564 pOleStreamData[0].dwMetaFileWidth = 0;
7565 pOleStreamData[0].dwMetaFileHeight = 0;
7566 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7567 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7568 pOleStreamData[1].dwTypeID = 0;
7569 pOleStreamData[1].dwOleTypeNameLength = 0;
7570 pOleStreamData[1].strOleTypeName[0] = 0;
7571 pOleStreamData[1].dwMetaFileWidth = 0;
7572 pOleStreamData[1].dwMetaFileHeight = 0;
7573 pOleStreamData[1].pData = NULL;
7574 pOleStreamData[1].dwDataLength = 0;
7577 /* Open OlePress000 stream */
7578 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7579 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7580 if(hRes == S_OK)
7582 LARGE_INTEGER iSeekPos;
7583 METAFILEPICT16 MetaFilePict;
7584 static const char strMetafilePictName[] = "METAFILEPICT";
7586 /* Set the TypeID for a Metafile */
7587 pOleStreamData[1].dwTypeID = 5;
7589 /* Set the OleTypeName to Metafile */
7590 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7591 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7593 iSeekPos.u.HighPart = 0;
7594 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7596 /* Get Presentation Data */
7597 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7598 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7599 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7600 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7602 /*Set width and Height */
7603 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7604 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7605 if(olePress.dwSize > 0)
7607 /* Set Length */
7608 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7610 /* Set MetaFilePict struct */
7611 MetaFilePict.mm = 8;
7612 MetaFilePict.xExt = olePress.dwExtentX;
7613 MetaFilePict.yExt = olePress.dwExtentY;
7614 MetaFilePict.hMF = 0;
7616 /* Get Metafile Data */
7617 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7618 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7619 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7621 IStream_Release(pStream);
7625 /*************************************************************************
7626 * OleConvertOLESTREAMToIStorage [OLE32.@]
7628 * Read info on MSDN
7630 * TODO
7631 * DVTARGETDEVICE parameter is not handled
7632 * Still unsure of some mem fields for OLE 10 Stream
7633 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7634 * and "\001OLE" streams
7637 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7638 LPOLESTREAM pOleStream,
7639 LPSTORAGE pstg,
7640 const DVTARGETDEVICE* ptd)
7642 int i;
7643 HRESULT hRes=S_OK;
7644 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7646 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7648 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7650 if(ptd != NULL)
7652 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7655 if(pstg == NULL || pOleStream == NULL)
7657 hRes = E_INVALIDARG;
7660 if(hRes == S_OK)
7662 /* Load the OLESTREAM to Memory */
7663 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7666 if(hRes == S_OK)
7668 /* Load the OLESTREAM to Memory (part 2)*/
7669 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7672 if(hRes == S_OK)
7675 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7677 /* Do we have the IStorage Data in the OLESTREAM */
7678 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7680 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7681 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7683 else
7685 /* It must be an original OLE 1.0 source */
7686 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7689 else
7691 /* It must be an original OLE 1.0 source */
7692 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7695 /* Create CompObj Stream if necessary */
7696 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7697 if(hRes == S_OK)
7699 /*Create the Ole Stream if necessary */
7700 OLECONVERT_CreateOleStream(pstg);
7705 /* Free allocated memory */
7706 for(i=0; i < 2; i++)
7708 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7709 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7710 pOleStreamData[i].pstrOleObjFileName = NULL;
7712 return hRes;
7715 /*************************************************************************
7716 * OleConvertIStorageToOLESTREAM [OLE32.@]
7718 * Read info on MSDN
7720 * Read info on MSDN
7722 * TODO
7723 * Still unsure of some mem fields for OLE 10 Stream
7724 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7725 * and "\001OLE" streams.
7728 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7729 LPSTORAGE pstg,
7730 LPOLESTREAM pOleStream)
7732 int i;
7733 HRESULT hRes = S_OK;
7734 IStream *pStream;
7735 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7736 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7738 TRACE("%p %p\n", pstg, pOleStream);
7740 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7742 if(pstg == NULL || pOleStream == NULL)
7744 hRes = E_INVALIDARG;
7746 if(hRes == S_OK)
7748 /* Get the ProgID */
7749 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7750 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7752 if(hRes == S_OK)
7754 /* Was it originally Ole10 */
7755 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7756 if(hRes == S_OK)
7758 IStream_Release(pStream);
7759 /* Get Presentation Data for Ole10Native */
7760 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7762 else
7764 /* Get Presentation Data (OLE20) */
7765 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7768 /* Save OLESTREAM */
7769 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7770 if(hRes == S_OK)
7772 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7777 /* Free allocated memory */
7778 for(i=0; i < 2; i++)
7780 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7783 return hRes;
7786 /***********************************************************************
7787 * GetConvertStg (OLE32.@)
7789 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7790 FIXME("unimplemented stub!\n");
7791 return E_FAIL;
7794 /******************************************************************************
7795 * StgIsStorageFile [OLE32.@]
7796 * Verify if the file contains a storage object
7798 * PARAMS
7799 * fn [ I] Filename
7801 * RETURNS
7802 * S_OK if file has magic bytes as a storage object
7803 * S_FALSE if file is not storage
7805 HRESULT WINAPI
7806 StgIsStorageFile(LPCOLESTR fn)
7808 HANDLE hf;
7809 BYTE magic[8];
7810 DWORD bytes_read;
7812 TRACE("%s\n", debugstr_w(fn));
7813 hf = CreateFileW(fn, GENERIC_READ,
7814 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7815 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7817 if (hf == INVALID_HANDLE_VALUE)
7818 return STG_E_FILENOTFOUND;
7820 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7822 WARN(" unable to read file\n");
7823 CloseHandle(hf);
7824 return S_FALSE;
7827 CloseHandle(hf);
7829 if (bytes_read != 8) {
7830 WARN(" too short\n");
7831 return S_FALSE;
7834 if (!memcmp(magic,STORAGE_magic,8)) {
7835 WARN(" -> YES\n");
7836 return S_OK;
7839 WARN(" -> Invalid header.\n");
7840 return S_FALSE;
7843 /***********************************************************************
7844 * WriteClassStm (OLE32.@)
7846 * Writes a CLSID to a stream.
7848 * PARAMS
7849 * pStm [I] Stream to write to.
7850 * rclsid [I] CLSID to write.
7852 * RETURNS
7853 * Success: S_OK.
7854 * Failure: HRESULT code.
7856 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7858 TRACE("(%p,%p)\n",pStm,rclsid);
7860 if (!pStm || !rclsid)
7861 return E_INVALIDARG;
7863 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7866 /***********************************************************************
7867 * ReadClassStm (OLE32.@)
7869 * Reads a CLSID from a stream.
7871 * PARAMS
7872 * pStm [I] Stream to read from.
7873 * rclsid [O] CLSID to read.
7875 * RETURNS
7876 * Success: S_OK.
7877 * Failure: HRESULT code.
7879 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7881 ULONG nbByte;
7882 HRESULT res;
7884 TRACE("(%p,%p)\n",pStm,pclsid);
7886 if (!pStm || !pclsid)
7887 return E_INVALIDARG;
7889 /* clear the output args */
7890 *pclsid = CLSID_NULL;
7892 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7894 if (FAILED(res))
7895 return res;
7897 if (nbByte != sizeof(CLSID))
7898 return STG_E_READFAULT;
7899 else
7900 return S_OK;