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
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.
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
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 rootEntryName[] = "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;
82 * Entry in the parent's stream tracking list
84 struct list ParentListEntry;
86 typedef struct StorageInternalImpl StorageInternalImpl;
88 /* Method definitions for the Storage32InternalImpl class. */
89 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
90 DWORD openFlags, DirRef storageDirEntry);
91 static void StorageImpl_Destroy(StorageBaseImpl* iface);
92 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
93 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
94 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
95 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
96 static void StorageImpl_SaveFileHeader(StorageImpl* This);
98 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
99 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
100 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
101 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
102 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
104 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
105 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
106 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
108 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
109 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
110 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
111 ULONG blockIndex, ULONG offset, DWORD value);
112 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
113 ULONG blockIndex, ULONG offset, DWORD* value);
115 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
116 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
117 static void StorageInternalImpl_Invalidate( StorageInternalImpl *This );
119 /* OLESTREAM memory structure to use for Get and Put Routines */
120 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
125 DWORD dwOleTypeNameLength;
126 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
127 CHAR *pstrOleObjFileName;
128 DWORD dwOleObjFileNameLength;
129 DWORD dwMetaFileWidth;
130 DWORD dwMetaFileHeight;
131 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
134 }OLECONVERT_OLESTREAM_DATA;
136 /* CompObj Stream structure */
137 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
142 DWORD dwCLSIDNameLength;
143 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
144 DWORD dwOleTypeNameLength;
145 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
146 DWORD dwProgIDNameLength;
147 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
149 }OLECONVERT_ISTORAGE_COMPOBJ;
152 /* Ole Presentation Stream structure */
153 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
161 }OLECONVERT_ISTORAGE_OLEPRES;
165 /***********************************************************************
166 * Forward declaration of internal functions used by the method DestroyElement
168 static HRESULT deleteStorageContents(
169 StorageBaseImpl *parentStorage,
170 DirRef indexToDelete,
171 DirEntry entryDataToDelete);
173 static HRESULT deleteStreamContents(
174 StorageBaseImpl *parentStorage,
175 DirRef indexToDelete,
176 DirEntry entryDataToDelete);
178 static HRESULT removeFromTree(
179 StorageBaseImpl *This,
180 DirRef parentStorageIndex,
181 DirRef deletedIndex);
183 /***********************************************************************
184 * Declaration of the functions used to manipulate DirEntry
187 static HRESULT destroyDirEntry(
188 StorageImpl *storage,
191 static HRESULT insertIntoTree(
192 StorageBaseImpl *This,
193 DirRef parentStorageIndex,
194 DirRef newEntryIndex);
196 static LONG entryNameCmp(
197 const OLECHAR *name1,
198 const OLECHAR *name2);
200 static DirRef findElement(
201 StorageImpl *storage,
206 static HRESULT findTreeParent(
207 StorageBaseImpl *storage,
209 const OLECHAR *childName,
210 DirEntry *parentData,
214 /***********************************************************************
215 * Declaration of miscellaneous functions...
217 static HRESULT validateSTGM(DWORD stgmValue);
219 static DWORD GetShareModeFromSTGM(DWORD stgm);
220 static DWORD GetAccessModeFromSTGM(DWORD stgm);
221 static DWORD GetCreationModeFromSTGM(DWORD stgm);
223 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
226 /****************************************************************************
227 * IEnumSTATSTGImpl definitions.
229 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
230 * This class allows iterating through the content of a storage and to find
231 * specific items inside it.
233 struct IEnumSTATSTGImpl
235 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
236 * since we want to cast this in an IEnumSTATSTG pointer */
238 LONG ref; /* Reference count */
239 StorageImpl* parentStorage; /* Reference to the parent storage */
240 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
243 * The current implementation of the IEnumSTATSTGImpl class uses a stack
244 * to walk the directory entries to get the content of a storage. This stack
245 * is implemented by the following 3 data members
249 DirRef* stackToVisit;
251 #define ENUMSTATSGT_SIZE_INCREMENT 10
255 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, DirRef storageDirEntry);
256 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
257 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, DirRef nodeToPush);
258 static DirRef IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
260 /************************************************************************
264 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
266 if (index == 0xffffffff)
271 return index * BIG_BLOCK_SIZE;
274 /************************************************************************
275 ** Storage32BaseImpl implementation
277 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
278 ULARGE_INTEGER offset,
283 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
286 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
287 ULARGE_INTEGER offset,
292 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
295 /************************************************************************
296 * Storage32BaseImpl_QueryInterface (IUnknown)
298 * This method implements the common QueryInterface for all IStorage32
299 * implementations contained in this file.
301 * See Windows documentation for more details on IUnknown methods.
303 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
308 StorageBaseImpl *This = (StorageBaseImpl *)iface;
310 if ( (This==0) || (ppvObject==0) )
315 if (IsEqualGUID(&IID_IUnknown, riid) ||
316 IsEqualGUID(&IID_IStorage, riid))
320 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
322 *ppvObject = &This->pssVtbl;
326 return E_NOINTERFACE;
328 IStorage_AddRef(iface);
333 /************************************************************************
334 * Storage32BaseImpl_AddRef (IUnknown)
336 * This method implements the common AddRef for all IStorage32
337 * implementations contained in this file.
339 * See Windows documentation for more details on IUnknown methods.
341 static ULONG WINAPI StorageBaseImpl_AddRef(
344 StorageBaseImpl *This = (StorageBaseImpl *)iface;
345 ULONG ref = InterlockedIncrement(&This->ref);
347 TRACE("(%p) AddRef to %d\n", This, ref);
352 /************************************************************************
353 * Storage32BaseImpl_Release (IUnknown)
355 * This method implements the common Release for all IStorage32
356 * implementations contained in this file.
358 * See Windows documentation for more details on IUnknown methods.
360 static ULONG WINAPI StorageBaseImpl_Release(
363 StorageBaseImpl *This = (StorageBaseImpl *)iface;
365 ULONG ref = InterlockedDecrement(&This->ref);
367 TRACE("(%p) ReleaseRef to %d\n", This, ref);
372 * Since we are using a system of base-classes, we want to call the
373 * destructor of the appropriate derived class. To do this, we are
374 * using virtual functions to implement the destructor.
376 StorageBaseImpl_Destroy(This);
382 /************************************************************************
383 * Storage32BaseImpl_OpenStream (IStorage)
385 * This method will open the specified stream object from the current storage.
387 * See Windows documentation for more details on IStorage methods.
389 static HRESULT WINAPI StorageBaseImpl_OpenStream(
391 const OLECHAR* pwcsName, /* [string][in] */
392 void* reserved1, /* [unique][in] */
393 DWORD grfMode, /* [in] */
394 DWORD reserved2, /* [in] */
395 IStream** ppstm) /* [out] */
397 StorageBaseImpl *This = (StorageBaseImpl *)iface;
398 StgStreamImpl* newStream;
399 DirEntry currentEntry;
400 DirRef streamEntryRef;
401 HRESULT res = STG_E_UNKNOWN;
403 TRACE("(%p, %s, %p, %x, %d, %p)\n",
404 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
406 if ( (pwcsName==NULL) || (ppstm==0) )
414 if ( FAILED( validateSTGM(grfMode) ) ||
415 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
417 res = STG_E_INVALIDFLAG;
424 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
426 res = STG_E_INVALIDFUNCTION;
430 if (!This->ancestorStorage)
432 res = STG_E_REVERTED;
437 * Check that we're compatible with the parent's storage mode, but
438 * only if we are not in transacted mode
440 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
441 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
443 res = STG_E_ACCESSDENIED;
449 * Search for the element with the given name
451 streamEntryRef = findElement(
452 This->ancestorStorage,
453 This->storageDirEntry,
458 * If it was found, construct the stream object and return a pointer to it.
460 if ( (streamEntryRef!=DIRENTRY_NULL) &&
461 (currentEntry.stgType==STGTY_STREAM) )
463 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
465 /* A single stream cannot be opened a second time. */
466 res = STG_E_ACCESSDENIED;
470 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
474 newStream->grfMode = grfMode;
475 *ppstm = (IStream*)newStream;
477 IStream_AddRef(*ppstm);
487 res = STG_E_FILENOTFOUND;
491 TRACE("<-- IStream %p\n", *ppstm);
492 TRACE("<-- %08x\n", res);
496 /************************************************************************
497 * Storage32BaseImpl_OpenStorage (IStorage)
499 * This method will open a new storage object from the current storage.
501 * See Windows documentation for more details on IStorage methods.
503 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
505 const OLECHAR* pwcsName, /* [string][unique][in] */
506 IStorage* pstgPriority, /* [unique][in] */
507 DWORD grfMode, /* [in] */
508 SNB snbExclude, /* [unique][in] */
509 DWORD reserved, /* [in] */
510 IStorage** ppstg) /* [out] */
512 StorageBaseImpl *This = (StorageBaseImpl *)iface;
513 StorageInternalImpl* newStorage;
514 DirEntry currentEntry;
515 DirRef storageEntryRef;
516 HRESULT res = STG_E_UNKNOWN;
518 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
519 iface, debugstr_w(pwcsName), pstgPriority,
520 grfMode, snbExclude, reserved, ppstg);
522 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
528 if (This->openFlags & STGM_SIMPLE)
530 res = STG_E_INVALIDFUNCTION;
535 if (snbExclude != NULL)
537 res = STG_E_INVALIDPARAMETER;
541 if ( FAILED( validateSTGM(grfMode) ))
543 res = STG_E_INVALIDFLAG;
550 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
551 (grfMode & STGM_DELETEONRELEASE) ||
552 (grfMode & STGM_PRIORITY) )
554 res = STG_E_INVALIDFUNCTION;
558 if (!This->ancestorStorage)
559 return STG_E_REVERTED;
562 * Check that we're compatible with the parent's storage mode,
563 * but only if we are not transacted
565 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
566 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
568 res = STG_E_ACCESSDENIED;
575 storageEntryRef = findElement(
576 This->ancestorStorage,
577 This->storageDirEntry,
581 if ( (storageEntryRef!=DIRENTRY_NULL) &&
582 (currentEntry.stgType==STGTY_STORAGE) )
584 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
586 /* A single storage cannot be opened a second time. */
587 res = STG_E_ACCESSDENIED;
591 newStorage = StorageInternalImpl_Construct(
592 This->ancestorStorage,
598 *ppstg = (IStorage*)newStorage;
600 StorageBaseImpl_AddRef(*ppstg);
602 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
608 res = STG_E_INSUFFICIENTMEMORY;
612 res = STG_E_FILENOTFOUND;
615 TRACE("<-- %08x\n", res);
619 /************************************************************************
620 * Storage32BaseImpl_EnumElements (IStorage)
622 * This method will create an enumerator object that can be used to
623 * retrieve information about all the elements in the storage object.
625 * See Windows documentation for more details on IStorage methods.
627 static HRESULT WINAPI StorageBaseImpl_EnumElements(
629 DWORD reserved1, /* [in] */
630 void* reserved2, /* [size_is][unique][in] */
631 DWORD reserved3, /* [in] */
632 IEnumSTATSTG** ppenum) /* [out] */
634 StorageBaseImpl *This = (StorageBaseImpl *)iface;
635 IEnumSTATSTGImpl* newEnum;
637 TRACE("(%p, %d, %p, %d, %p)\n",
638 iface, reserved1, reserved2, reserved3, ppenum);
640 if ( (This==0) || (ppenum==0))
643 if (!This->ancestorStorage)
644 return STG_E_REVERTED;
646 newEnum = IEnumSTATSTGImpl_Construct(
647 This->ancestorStorage,
648 This->storageDirEntry);
652 *ppenum = (IEnumSTATSTG*)newEnum;
654 IEnumSTATSTG_AddRef(*ppenum);
659 return E_OUTOFMEMORY;
662 /************************************************************************
663 * Storage32BaseImpl_Stat (IStorage)
665 * This method will retrieve information about this storage object.
667 * See Windows documentation for more details on IStorage methods.
669 static HRESULT WINAPI StorageBaseImpl_Stat(
671 STATSTG* pstatstg, /* [out] */
672 DWORD grfStatFlag) /* [in] */
674 StorageBaseImpl *This = (StorageBaseImpl *)iface;
675 DirEntry currentEntry;
676 HRESULT res = STG_E_UNKNOWN;
678 TRACE("(%p, %p, %x)\n",
679 iface, pstatstg, grfStatFlag);
681 if ( (This==0) || (pstatstg==0))
687 if (!This->ancestorStorage)
689 res = STG_E_REVERTED;
693 res = StorageBaseImpl_ReadDirEntry(
695 This->storageDirEntry,
700 StorageUtl_CopyDirEntryToSTATSTG(
706 pstatstg->grfMode = This->openFlags;
707 pstatstg->grfStateBits = This->stateBits;
713 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);
715 TRACE("<-- %08x\n", res);
719 /************************************************************************
720 * Storage32BaseImpl_RenameElement (IStorage)
722 * This method will rename the specified element.
724 * See Windows documentation for more details on IStorage methods.
726 static HRESULT WINAPI StorageBaseImpl_RenameElement(
728 const OLECHAR* pwcsOldName, /* [in] */
729 const OLECHAR* pwcsNewName) /* [in] */
731 StorageBaseImpl *This = (StorageBaseImpl *)iface;
732 DirEntry currentEntry;
733 DirRef currentEntryRef;
735 TRACE("(%p, %s, %s)\n",
736 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
738 if (!This->ancestorStorage)
739 return STG_E_REVERTED;
741 currentEntryRef = findElement(This->ancestorStorage,
742 This->storageDirEntry,
746 if (currentEntryRef != DIRENTRY_NULL)
749 * There is already an element with the new name
751 return STG_E_FILEALREADYEXISTS;
755 * Search for the old element name
757 currentEntryRef = findElement(This->ancestorStorage,
758 This->storageDirEntry,
762 if (currentEntryRef != DIRENTRY_NULL)
764 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
765 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
767 WARN("Element is already open; cannot rename.\n");
768 return STG_E_ACCESSDENIED;
771 /* Remove the element from its current position in the tree */
772 removeFromTree(This, This->storageDirEntry,
775 /* Change the name of the element */
776 strcpyW(currentEntry.name, pwcsNewName);
778 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
781 /* Insert the element in a new position in the tree */
782 insertIntoTree(This, This->storageDirEntry,
788 * There is no element with the old name
790 return STG_E_FILENOTFOUND;
796 /************************************************************************
797 * Storage32BaseImpl_CreateStream (IStorage)
799 * This method will create a stream object within this storage
801 * See Windows documentation for more details on IStorage methods.
803 static HRESULT WINAPI StorageBaseImpl_CreateStream(
805 const OLECHAR* pwcsName, /* [string][in] */
806 DWORD grfMode, /* [in] */
807 DWORD reserved1, /* [in] */
808 DWORD reserved2, /* [in] */
809 IStream** ppstm) /* [out] */
811 StorageBaseImpl *This = (StorageBaseImpl *)iface;
812 StgStreamImpl* newStream;
813 DirEntry currentEntry, newStreamEntry;
814 DirRef currentEntryRef, newStreamEntryRef;
816 TRACE("(%p, %s, %x, %d, %d, %p)\n",
817 iface, debugstr_w(pwcsName), grfMode,
818 reserved1, reserved2, ppstm);
821 return STG_E_INVALIDPOINTER;
824 return STG_E_INVALIDNAME;
826 if (reserved1 || reserved2)
827 return STG_E_INVALIDPARAMETER;
829 if ( FAILED( validateSTGM(grfMode) ))
830 return STG_E_INVALIDFLAG;
832 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
833 return STG_E_INVALIDFLAG;
835 if (!This->ancestorStorage)
836 return STG_E_REVERTED;
841 if ((grfMode & STGM_DELETEONRELEASE) ||
842 (grfMode & STGM_TRANSACTED))
843 return STG_E_INVALIDFUNCTION;
845 /* Can't create a stream on read-only storage */
846 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
847 return STG_E_ACCESSDENIED;
850 * Check that we're compatible with the parent's storage mode
851 * if not in transacted mode
853 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
854 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
855 return STG_E_ACCESSDENIED;
858 if(This->openFlags & STGM_SIMPLE)
859 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
863 currentEntryRef = findElement(This->ancestorStorage,
864 This->storageDirEntry,
868 if (currentEntryRef != DIRENTRY_NULL)
871 * An element with this name already exists
873 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
875 IStorage_DestroyElement(iface, pwcsName);
878 return STG_E_FILEALREADYEXISTS;
880 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
882 WARN("read-only storage\n");
883 return STG_E_ACCESSDENIED;
887 * memset the empty entry
889 memset(&newStreamEntry, 0, sizeof(DirEntry));
891 newStreamEntry.sizeOfNameString =
892 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
894 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
895 return STG_E_INVALIDNAME;
897 strcpyW(newStreamEntry.name, pwcsName);
899 newStreamEntry.stgType = STGTY_STREAM;
900 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
901 newStreamEntry.size.u.LowPart = 0;
902 newStreamEntry.size.u.HighPart = 0;
904 newStreamEntry.leftChild = DIRENTRY_NULL;
905 newStreamEntry.rightChild = DIRENTRY_NULL;
906 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
908 /* call CoFileTime to get the current time
913 /* newStreamEntry.clsid */
916 * Create an entry with the new data
918 StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
920 * Insert the new entry in the parent storage's tree.
924 This->storageDirEntry,
928 * Open the stream to return it.
930 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
934 *ppstm = (IStream*)newStream;
936 IStream_AddRef(*ppstm);
940 return STG_E_INSUFFICIENTMEMORY;
946 /************************************************************************
947 * Storage32BaseImpl_SetClass (IStorage)
949 * This method will write the specified CLSID in the directory entry of this
952 * See Windows documentation for more details on IStorage methods.
954 static HRESULT WINAPI StorageBaseImpl_SetClass(
956 REFCLSID clsid) /* [in] */
958 StorageBaseImpl *This = (StorageBaseImpl *)iface;
960 DirEntry currentEntry;
962 TRACE("(%p, %p)\n", iface, clsid);
964 if (!This->ancestorStorage)
965 return STG_E_REVERTED;
967 hRes = StorageBaseImpl_ReadDirEntry(This,
968 This->storageDirEntry,
972 currentEntry.clsid = *clsid;
974 hRes = StorageBaseImpl_WriteDirEntry(This,
975 This->storageDirEntry,
982 /************************************************************************
983 ** Storage32Impl implementation
986 /************************************************************************
987 * Storage32BaseImpl_CreateStorage (IStorage)
989 * This method will create the storage object within the provided storage.
991 * See Windows documentation for more details on IStorage methods.
993 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
995 const OLECHAR *pwcsName, /* [string][in] */
996 DWORD grfMode, /* [in] */
997 DWORD reserved1, /* [in] */
998 DWORD reserved2, /* [in] */
999 IStorage **ppstg) /* [out] */
1001 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1003 DirEntry currentEntry;
1005 DirRef currentEntryRef;
1009 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1010 iface, debugstr_w(pwcsName), grfMode,
1011 reserved1, reserved2, ppstg);
1014 return STG_E_INVALIDPOINTER;
1016 if (This->openFlags & STGM_SIMPLE)
1018 return STG_E_INVALIDFUNCTION;
1022 return STG_E_INVALIDNAME;
1026 if ( FAILED( validateSTGM(grfMode) ) ||
1027 (grfMode & STGM_DELETEONRELEASE) )
1029 WARN("bad grfMode: 0x%x\n", grfMode);
1030 return STG_E_INVALIDFLAG;
1033 if (!This->ancestorStorage)
1034 return STG_E_REVERTED;
1037 * Check that we're compatible with the parent's storage mode
1039 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1041 WARN("access denied\n");
1042 return STG_E_ACCESSDENIED;
1045 currentEntryRef = findElement(This->ancestorStorage,
1046 This->storageDirEntry,
1050 if (currentEntryRef != DIRENTRY_NULL)
1053 * An element with this name already exists
1055 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1056 STGM_ACCESS_MODE(This->openFlags) != STGM_READ)
1058 hr = IStorage_DestroyElement(iface, pwcsName);
1064 WARN("file already exists\n");
1065 return STG_E_FILEALREADYEXISTS;
1068 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1070 WARN("read-only storage\n");
1071 return STG_E_ACCESSDENIED;
1074 memset(&newEntry, 0, sizeof(DirEntry));
1076 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1078 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1080 FIXME("name too long\n");
1081 return STG_E_INVALIDNAME;
1084 strcpyW(newEntry.name, pwcsName);
1086 newEntry.stgType = STGTY_STORAGE;
1087 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1088 newEntry.size.u.LowPart = 0;
1089 newEntry.size.u.HighPart = 0;
1091 newEntry.leftChild = DIRENTRY_NULL;
1092 newEntry.rightChild = DIRENTRY_NULL;
1093 newEntry.dirRootEntry = DIRENTRY_NULL;
1095 /* call CoFileTime to get the current time
1100 /* newEntry.clsid */
1103 * Create a new directory entry for the storage
1105 StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1108 * Insert the new directory entry into the parent storage's tree
1112 This->storageDirEntry,
1116 * Open it to get a pointer to return.
1118 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1120 if( (hr != S_OK) || (*ppstg == NULL))
1130 /***************************************************************************
1134 * Reserve a directory entry in the file and initialize it.
1136 static HRESULT StorageImpl_CreateDirEntry(
1137 StorageBaseImpl *base,
1138 const DirEntry *newData,
1141 StorageImpl *storage = (StorageImpl*)base;
1142 ULONG currentEntryIndex = 0;
1143 ULONG newEntryIndex = DIRENTRY_NULL;
1145 BYTE currentData[RAW_DIRENTRY_SIZE];
1146 WORD sizeOfNameString;
1150 hr = StorageImpl_ReadRawDirEntry(storage,
1156 StorageUtl_ReadWord(
1158 OFFSET_PS_NAMELENGTH,
1161 if (sizeOfNameString == 0)
1164 * The entry exists and is available, we found it.
1166 newEntryIndex = currentEntryIndex;
1172 * We exhausted the directory entries, we will create more space below
1174 newEntryIndex = currentEntryIndex;
1176 currentEntryIndex++;
1178 } while (newEntryIndex == DIRENTRY_NULL);
1181 * grow the directory stream
1185 BYTE emptyData[RAW_DIRENTRY_SIZE];
1186 ULARGE_INTEGER newSize;
1188 ULONG lastEntry = 0;
1189 ULONG blockCount = 0;
1192 * obtain the new count of blocks in the directory stream
1194 blockCount = BlockChainStream_GetCount(
1195 storage->rootBlockChain)+1;
1198 * initialize the size used by the directory stream
1200 newSize.u.HighPart = 0;
1201 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1204 * add a block to the directory stream
1206 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1209 * memset the empty entry in order to initialize the unused newly
1212 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1217 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1220 entryIndex = newEntryIndex + 1;
1221 entryIndex < lastEntry;
1224 StorageImpl_WriteRawDirEntry(
1231 UpdateRawDirEntry(currentData, newData);
1233 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1236 *index = newEntryIndex;
1241 /***************************************************************************
1245 * Mark a directory entry in the file as free.
1247 static HRESULT destroyDirEntry(
1248 StorageImpl *storage,
1252 BYTE emptyData[RAW_DIRENTRY_SIZE];
1254 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1256 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1262 /****************************************************************************
1266 * Case insensitive comparison of DirEntry.name by first considering
1269 * Returns <0 when name1 < name2
1270 * >0 when name1 > name2
1271 * 0 when name1 == name2
1273 static LONG entryNameCmp(
1274 const OLECHAR *name1,
1275 const OLECHAR *name2)
1277 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1282 * We compare the string themselves only when they are of the same length
1284 diff = lstrcmpiW( name1, name2);
1290 /****************************************************************************
1294 * Add a directory entry to a storage
1296 static HRESULT insertIntoTree(
1297 StorageBaseImpl *This,
1298 DirRef parentStorageIndex,
1299 DirRef newEntryIndex)
1301 DirEntry currentEntry;
1305 * Read the inserted entry
1307 StorageBaseImpl_ReadDirEntry(This,
1312 * Read the storage entry
1314 StorageBaseImpl_ReadDirEntry(This,
1318 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1321 * The root storage contains some element, therefore, start the research
1322 * for the appropriate location.
1325 DirRef current, next, previous, currentEntryId;
1328 * Keep a reference to the root of the storage's element tree
1330 currentEntryId = currentEntry.dirRootEntry;
1335 StorageBaseImpl_ReadDirEntry(This,
1336 currentEntry.dirRootEntry,
1339 previous = currentEntry.leftChild;
1340 next = currentEntry.rightChild;
1341 current = currentEntryId;
1345 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1349 if (previous != DIRENTRY_NULL)
1351 StorageBaseImpl_ReadDirEntry(This,
1358 currentEntry.leftChild = newEntryIndex;
1359 StorageBaseImpl_WriteDirEntry(This,
1367 if (next != DIRENTRY_NULL)
1369 StorageBaseImpl_ReadDirEntry(This,
1376 currentEntry.rightChild = newEntryIndex;
1377 StorageBaseImpl_WriteDirEntry(This,
1386 * Trying to insert an item with the same name in the
1387 * subtree structure.
1389 return STG_E_FILEALREADYEXISTS;
1392 previous = currentEntry.leftChild;
1393 next = currentEntry.rightChild;
1399 * The storage is empty, make the new entry the root of its element tree
1401 currentEntry.dirRootEntry = newEntryIndex;
1402 StorageBaseImpl_WriteDirEntry(This,
1410 /****************************************************************************
1414 * Find and read the element of a storage with the given name.
1416 static DirRef findElement(StorageImpl *storage, DirRef storageEntry,
1417 const OLECHAR *name, DirEntry *data)
1419 DirRef currentEntry;
1421 /* Read the storage entry to find the root of the tree. */
1422 StorageImpl_ReadDirEntry(storage, storageEntry, data);
1424 currentEntry = data->dirRootEntry;
1426 while (currentEntry != DIRENTRY_NULL)
1430 StorageImpl_ReadDirEntry(storage, currentEntry, data);
1432 cmp = entryNameCmp(name, data->name);
1439 currentEntry = data->leftChild;
1442 currentEntry = data->rightChild;
1445 return currentEntry;
1448 /****************************************************************************
1452 * Find and read the binary tree parent of the element with the given name.
1454 * If there is no such element, find a place where it could be inserted and
1455 * return STG_E_FILENOTFOUND.
1457 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1458 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1464 /* Read the storage entry to find the root of the tree. */
1465 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1467 *parentEntry = storageEntry;
1468 *relation = DIRENTRY_RELATION_DIR;
1470 childEntry = parentData->dirRootEntry;
1472 while (childEntry != DIRENTRY_NULL)
1476 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1478 cmp = entryNameCmp(childName, childData.name);
1486 *parentData = childData;
1487 *parentEntry = childEntry;
1488 *relation = DIRENTRY_RELATION_PREVIOUS;
1490 childEntry = parentData->leftChild;
1495 *parentData = childData;
1496 *parentEntry = childEntry;
1497 *relation = DIRENTRY_RELATION_NEXT;
1499 childEntry = parentData->rightChild;
1503 if (childEntry == DIRENTRY_NULL)
1504 return STG_E_FILENOTFOUND;
1510 /*************************************************************************
1513 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1515 DWORD ciidExclude, /* [in] */
1516 const IID* rgiidExclude, /* [size_is][unique][in] */
1517 SNB snbExclude, /* [unique][in] */
1518 IStorage* pstgDest) /* [unique][in] */
1520 IEnumSTATSTG *elements = 0;
1521 STATSTG curElement, strStat;
1523 IStorage *pstgTmp, *pstgChild;
1524 IStream *pstrTmp, *pstrChild;
1525 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1528 TRACE("(%p, %d, %p, %p, %p)\n",
1529 iface, ciidExclude, rgiidExclude,
1530 snbExclude, pstgDest);
1532 if ( pstgDest == 0 )
1533 return STG_E_INVALIDPOINTER;
1536 * Enumerate the elements
1538 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1546 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1547 IStorage_SetClass( pstgDest, &curElement.clsid );
1549 for(i = 0; i < ciidExclude; ++i)
1551 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1552 skip_storage = TRUE;
1553 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1556 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1562 * Obtain the next element
1564 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1566 if ( hr == S_FALSE )
1568 hr = S_OK; /* done, every element has been copied */
1574 WCHAR **snb = snbExclude;
1576 while ( *snb != NULL && !skip )
1578 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1587 if (curElement.type == STGTY_STORAGE)
1593 * open child source storage
1595 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1596 STGM_READ|STGM_SHARE_EXCLUSIVE,
1597 NULL, 0, &pstgChild );
1603 * Check if destination storage is not a child of the source
1604 * storage, which will cause an infinite loop
1606 if (pstgChild == pstgDest)
1608 IEnumSTATSTG_Release(elements);
1610 return STG_E_ACCESSDENIED;
1614 * create a new storage in destination storage
1616 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1617 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1621 * if it already exist, don't create a new one use this one
1623 if (hr == STG_E_FILEALREADYEXISTS)
1625 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1626 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1627 NULL, 0, &pstgTmp );
1635 * do the copy recursively
1637 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1640 IStorage_Release( pstgTmp );
1641 IStorage_Release( pstgChild );
1643 else if (curElement.type == STGTY_STREAM)
1649 * create a new stream in destination storage. If the stream already
1650 * exist, it will be deleted and a new one will be created.
1652 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1653 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1660 * open child stream storage
1662 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1663 STGM_READ|STGM_SHARE_EXCLUSIVE,
1670 * Get the size of the source stream
1672 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1675 * Set the size of the destination stream.
1677 IStream_SetSize(pstrTmp, strStat.cbSize);
1682 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1685 IStream_Release( pstrTmp );
1686 IStream_Release( pstrChild );
1690 WARN("unknown element type: %d\n", curElement.type);
1693 } while (hr == S_OK);
1698 IEnumSTATSTG_Release(elements);
1703 /*************************************************************************
1704 * MoveElementTo (IStorage)
1706 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1708 const OLECHAR *pwcsName, /* [string][in] */
1709 IStorage *pstgDest, /* [unique][in] */
1710 const OLECHAR *pwcsNewName,/* [string][in] */
1711 DWORD grfFlags) /* [in] */
1713 FIXME("(%p %s %p %s %u): stub\n", iface,
1714 debugstr_w(pwcsName), pstgDest,
1715 debugstr_w(pwcsNewName), grfFlags);
1719 /*************************************************************************
1722 * Ensures that any changes made to a storage object open in transacted mode
1723 * are reflected in the parent storage
1726 * Wine doesn't implement transacted mode, which seems to be a basic
1727 * optimization, so we can ignore this stub for now.
1729 static HRESULT WINAPI StorageImpl_Commit(
1731 DWORD grfCommitFlags)/* [in] */
1733 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1737 /*************************************************************************
1740 * Discard all changes that have been made since the last commit operation
1742 static HRESULT WINAPI StorageImpl_Revert(
1745 FIXME("(%p): stub\n", iface);
1749 /*************************************************************************
1750 * DestroyElement (IStorage)
1752 * Strategy: This implementation is built this way for simplicity not for speed.
1753 * I always delete the topmost element of the enumeration and adjust
1754 * the deleted element pointer all the time. This takes longer to
1755 * do but allow to reinvoke DestroyElement whenever we encounter a
1756 * storage object. The optimisation resides in the usage of another
1757 * enumeration strategy that would give all the leaves of a storage
1758 * first. (postfix order)
1760 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1762 const OLECHAR *pwcsName)/* [string][in] */
1764 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1767 DirEntry entryToDelete;
1768 DirRef entryToDeleteRef;
1771 iface, debugstr_w(pwcsName));
1774 return STG_E_INVALIDPOINTER;
1776 if (!This->ancestorStorage)
1777 return STG_E_REVERTED;
1779 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1780 return STG_E_ACCESSDENIED;
1782 entryToDeleteRef = findElement(
1783 This->ancestorStorage,
1784 This->storageDirEntry,
1788 if ( entryToDeleteRef == DIRENTRY_NULL )
1790 return STG_E_FILENOTFOUND;
1793 if ( entryToDelete.stgType == STGTY_STORAGE )
1795 hr = deleteStorageContents(
1800 else if ( entryToDelete.stgType == STGTY_STREAM )
1802 hr = deleteStreamContents(
1812 * Remove the entry from its parent storage
1814 hr = removeFromTree(
1816 This->storageDirEntry,
1820 * Invalidate the entry
1823 destroyDirEntry(This->ancestorStorage,
1830 /******************************************************************************
1831 * Internal stream list handlers
1834 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1836 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1837 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1840 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1842 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1843 list_remove(&(strm->StrmListEntry));
1846 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1848 StgStreamImpl *strm;
1850 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1852 if (strm->dirEntry == streamEntry)
1861 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1863 StorageInternalImpl *childstg;
1865 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1867 if (childstg->base.storageDirEntry == storageEntry)
1876 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1878 struct list *cur, *cur2;
1879 StgStreamImpl *strm=NULL;
1880 StorageInternalImpl *childstg=NULL;
1882 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1883 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1884 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1885 strm->parentStorage = NULL;
1889 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1890 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1891 StorageInternalImpl_Invalidate( childstg );
1896 /*********************************************************************
1900 * Delete the contents of a storage entry.
1903 static HRESULT deleteStorageContents(
1904 StorageBaseImpl *parentStorage,
1905 DirRef indexToDelete,
1906 DirEntry entryDataToDelete)
1908 IEnumSTATSTG *elements = 0;
1909 IStorage *childStorage = 0;
1910 STATSTG currentElement;
1912 HRESULT destroyHr = S_OK;
1913 StorageInternalImpl *stg, *stg2;
1915 /* Invalidate any open storage objects. */
1916 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
1918 if (stg->base.storageDirEntry == indexToDelete)
1920 StorageInternalImpl_Invalidate(stg);
1925 * Open the storage and enumerate it
1927 hr = StorageBaseImpl_OpenStorage(
1928 (IStorage*)parentStorage,
1929 entryDataToDelete.name,
1931 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1942 * Enumerate the elements
1944 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1949 * Obtain the next element
1951 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1954 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
1956 CoTaskMemFree(currentElement.pwcsName);
1960 * We need to Reset the enumeration every time because we delete elements
1961 * and the enumeration could be invalid
1963 IEnumSTATSTG_Reset(elements);
1965 } while ((hr == S_OK) && (destroyHr == S_OK));
1967 IStorage_Release(childStorage);
1968 IEnumSTATSTG_Release(elements);
1973 /*********************************************************************
1977 * Perform the deletion of a stream's data
1980 static HRESULT deleteStreamContents(
1981 StorageBaseImpl *parentStorage,
1982 DirRef indexToDelete,
1983 DirEntry entryDataToDelete)
1987 ULARGE_INTEGER size;
1988 StgStreamImpl *strm, *strm2;
1990 /* Invalidate any open stream objects. */
1991 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
1993 if (strm->dirEntry == indexToDelete)
1995 TRACE("Stream deleted %p\n", strm);
1996 strm->parentStorage = NULL;
1997 list_remove(&strm->StrmListEntry);
2001 size.u.HighPart = 0;
2004 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2005 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2015 hr = IStream_SetSize(pis, size);
2023 * Release the stream object.
2025 IStream_Release(pis);
2030 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2034 case DIRENTRY_RELATION_PREVIOUS:
2035 entry->leftChild = new_target;
2037 case DIRENTRY_RELATION_NEXT:
2038 entry->rightChild = new_target;
2040 case DIRENTRY_RELATION_DIR:
2041 entry->dirRootEntry = new_target;
2048 /*************************************************************************
2052 * This method removes a directory entry from its parent storage tree without
2053 * freeing any resources attached to it.
2055 static HRESULT removeFromTree(
2056 StorageBaseImpl *This,
2057 DirRef parentStorageIndex,
2058 DirRef deletedIndex)
2061 DirEntry entryToDelete;
2062 DirEntry parentEntry;
2063 DirRef parentEntryRef;
2064 ULONG typeOfRelation;
2066 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2072 * Find the element that links to the one we want to delete.
2074 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2075 &parentEntry, &parentEntryRef, &typeOfRelation);
2080 if (entryToDelete.leftChild != DIRENTRY_NULL)
2083 * Replace the deleted entry with its left child
2085 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2087 hr = StorageBaseImpl_WriteDirEntry(
2096 if (entryToDelete.rightChild != DIRENTRY_NULL)
2099 * We need to reinsert the right child somewhere. We already know it and
2100 * its children are greater than everything in the left tree, so we
2101 * insert it at the rightmost point in the left tree.
2103 DirRef newRightChildParent = entryToDelete.leftChild;
2104 DirEntry newRightChildParentEntry;
2108 hr = StorageBaseImpl_ReadDirEntry(
2110 newRightChildParent,
2111 &newRightChildParentEntry);
2117 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2118 newRightChildParent = newRightChildParentEntry.rightChild;
2119 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2121 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2123 hr = StorageBaseImpl_WriteDirEntry(
2125 newRightChildParent,
2126 &newRightChildParentEntry);
2136 * Replace the deleted entry with its right child
2138 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2140 hr = StorageBaseImpl_WriteDirEntry(
2154 /******************************************************************************
2155 * SetElementTimes (IStorage)
2157 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2159 const OLECHAR *pwcsName,/* [string][in] */
2160 const FILETIME *pctime, /* [in] */
2161 const FILETIME *patime, /* [in] */
2162 const FILETIME *pmtime) /* [in] */
2164 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2168 /******************************************************************************
2169 * SetStateBits (IStorage)
2171 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2173 DWORD grfStateBits,/* [in] */
2174 DWORD grfMask) /* [in] */
2176 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2178 if (!This->ancestorStorage)
2179 return STG_E_REVERTED;
2181 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2185 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2186 DirRef index, const DirEntry *data)
2188 StorageImpl *This = (StorageImpl*)base;
2189 return StorageImpl_WriteDirEntry(This, index, data);
2192 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2193 DirRef index, DirEntry *data)
2195 StorageImpl *This = (StorageImpl*)base;
2196 return StorageImpl_ReadDirEntry(This, index, data);
2200 * Virtual function table for the IStorage32Impl class.
2202 static const IStorageVtbl Storage32Impl_Vtbl =
2204 StorageBaseImpl_QueryInterface,
2205 StorageBaseImpl_AddRef,
2206 StorageBaseImpl_Release,
2207 StorageBaseImpl_CreateStream,
2208 StorageBaseImpl_OpenStream,
2209 StorageBaseImpl_CreateStorage,
2210 StorageBaseImpl_OpenStorage,
2211 StorageBaseImpl_CopyTo,
2212 StorageBaseImpl_MoveElementTo,
2215 StorageBaseImpl_EnumElements,
2216 StorageBaseImpl_DestroyElement,
2217 StorageBaseImpl_RenameElement,
2218 StorageBaseImpl_SetElementTimes,
2219 StorageBaseImpl_SetClass,
2220 StorageBaseImpl_SetStateBits,
2221 StorageBaseImpl_Stat
2224 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2226 StorageImpl_Destroy,
2227 StorageImpl_CreateDirEntry,
2228 StorageImpl_BaseWriteDirEntry,
2229 StorageImpl_BaseReadDirEntry,
2232 static HRESULT StorageImpl_Construct(
2239 StorageImpl** result)
2243 DirEntry currentEntry;
2244 DirRef currentEntryRef;
2245 WCHAR fullpath[MAX_PATH];
2247 if ( FAILED( validateSTGM(openFlags) ))
2248 return STG_E_INVALIDFLAG;
2250 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2252 return E_OUTOFMEMORY;
2254 memset(This, 0, sizeof(StorageImpl));
2256 list_init(&This->base.strmHead);
2258 list_init(&This->base.storageHead);
2260 This->base.lpVtbl = &Storage32Impl_Vtbl;
2261 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2262 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2263 This->base.openFlags = (openFlags & ~STGM_CREATE);
2265 This->base.create = create;
2268 * This is the top-level storage so initialize the ancestor pointer
2271 This->base.ancestorStorage = This;
2273 This->hFile = hFile;
2276 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2278 lstrcpynW(fullpath, pwcsName, MAX_PATH);
2280 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2281 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2282 if (!This->pwcsName)
2284 hr = STG_E_INSUFFICIENTMEMORY;
2287 strcpyW(This->pwcsName, fullpath);
2288 This->base.filename = This->pwcsName;
2292 * Initialize the big block cache.
2294 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2295 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2296 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2302 if (This->bigBlockFile == 0)
2310 ULARGE_INTEGER size;
2311 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2314 * Initialize all header variables:
2315 * - The big block depot consists of one block and it is at block 0
2316 * - The directory table starts at block 1
2317 * - There is no small block depot
2319 memset( This->bigBlockDepotStart,
2321 sizeof(This->bigBlockDepotStart));
2323 This->bigBlockDepotCount = 1;
2324 This->bigBlockDepotStart[0] = 0;
2325 This->rootStartBlock = 1;
2326 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2327 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2328 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2329 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2330 This->extBigBlockDepotCount = 0;
2332 StorageImpl_SaveFileHeader(This);
2335 * Add one block for the big block depot and one block for the directory table
2337 size.u.HighPart = 0;
2338 size.u.LowPart = This->bigBlockSize * 3;
2339 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2342 * Initialize the big block depot
2344 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2345 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2346 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2347 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2352 * Load the header for the file.
2354 hr = StorageImpl_LoadFileHeader(This);
2363 * There is no block depot cached yet.
2365 This->indexBlockDepotCached = 0xFFFFFFFF;
2368 * Start searching for free blocks with block 0.
2370 This->prevFreeBlock = 0;
2373 * Create the block chain abstractions.
2375 if(!(This->rootBlockChain =
2376 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2378 hr = STG_E_READFAULT;
2382 if(!(This->smallBlockDepotChain =
2383 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2386 hr = STG_E_READFAULT;
2391 * Write the root storage entry (memory only)
2397 * Initialize the directory table
2399 memset(&rootEntry, 0, sizeof(rootEntry));
2400 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2401 sizeof(rootEntry.name)/sizeof(WCHAR) );
2402 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2403 rootEntry.stgType = STGTY_ROOT;
2404 rootEntry.leftChild = DIRENTRY_NULL;
2405 rootEntry.rightChild = DIRENTRY_NULL;
2406 rootEntry.dirRootEntry = DIRENTRY_NULL;
2407 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2408 rootEntry.size.u.HighPart = 0;
2409 rootEntry.size.u.LowPart = 0;
2411 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2415 * Find the ID of the root storage.
2417 currentEntryRef = 0;
2421 hr = StorageImpl_ReadDirEntry(
2428 if ( (currentEntry.sizeOfNameString != 0 ) &&
2429 (currentEntry.stgType == STGTY_ROOT) )
2431 This->base.storageDirEntry = currentEntryRef;
2437 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2441 hr = STG_E_READFAULT;
2446 * Create the block chain abstraction for the small block root chain.
2448 if(!(This->smallBlockRootChain =
2449 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2451 hr = STG_E_READFAULT;
2457 IStorage_Release((IStorage*)This);
2466 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2468 StorageImpl *This = (StorageImpl*) iface;
2469 TRACE("(%p)\n", This);
2471 StorageBaseImpl_DeleteAll(&This->base);
2473 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2475 BlockChainStream_Destroy(This->smallBlockRootChain);
2476 BlockChainStream_Destroy(This->rootBlockChain);
2477 BlockChainStream_Destroy(This->smallBlockDepotChain);
2479 if (This->bigBlockFile)
2480 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2481 HeapFree(GetProcessHeap(), 0, This);
2484 /******************************************************************************
2485 * Storage32Impl_GetNextFreeBigBlock
2487 * Returns the index of the next free big block.
2488 * If the big block depot is filled, this method will enlarge it.
2491 static ULONG StorageImpl_GetNextFreeBigBlock(
2494 ULONG depotBlockIndexPos;
2495 BYTE depotBuffer[BIG_BLOCK_SIZE];
2497 ULONG depotBlockOffset;
2498 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2499 ULONG nextBlockIndex = BLOCK_SPECIAL;
2501 ULONG freeBlock = BLOCK_UNUSED;
2503 depotIndex = This->prevFreeBlock / blocksPerDepot;
2504 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2507 * Scan the entire big block depot until we find a block marked free
2509 while (nextBlockIndex != BLOCK_UNUSED)
2511 if (depotIndex < COUNT_BBDEPOTINHEADER)
2513 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2516 * Grow the primary depot.
2518 if (depotBlockIndexPos == BLOCK_UNUSED)
2520 depotBlockIndexPos = depotIndex*blocksPerDepot;
2523 * Add a block depot.
2525 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2526 This->bigBlockDepotCount++;
2527 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2530 * Flag it as a block depot.
2532 StorageImpl_SetNextBlockInChain(This,
2536 /* Save new header information.
2538 StorageImpl_SaveFileHeader(This);
2543 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2545 if (depotBlockIndexPos == BLOCK_UNUSED)
2548 * Grow the extended depot.
2550 ULONG extIndex = BLOCK_UNUSED;
2551 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2552 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2554 if (extBlockOffset == 0)
2556 /* We need an extended block.
2558 extIndex = Storage32Impl_AddExtBlockDepot(This);
2559 This->extBigBlockDepotCount++;
2560 depotBlockIndexPos = extIndex + 1;
2563 depotBlockIndexPos = depotIndex * blocksPerDepot;
2566 * Add a block depot and mark it in the extended block.
2568 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2569 This->bigBlockDepotCount++;
2570 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2572 /* Flag the block depot.
2574 StorageImpl_SetNextBlockInChain(This,
2578 /* If necessary, flag the extended depot block.
2580 if (extIndex != BLOCK_UNUSED)
2581 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2583 /* Save header information.
2585 StorageImpl_SaveFileHeader(This);
2589 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2593 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2594 ( nextBlockIndex != BLOCK_UNUSED))
2596 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2598 if (nextBlockIndex == BLOCK_UNUSED)
2600 freeBlock = (depotIndex * blocksPerDepot) +
2601 (depotBlockOffset/sizeof(ULONG));
2604 depotBlockOffset += sizeof(ULONG);
2609 depotBlockOffset = 0;
2613 * make sure that the block physically exists before using it
2615 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2617 This->prevFreeBlock = freeBlock;
2622 /******************************************************************************
2623 * Storage32Impl_AddBlockDepot
2625 * This will create a depot block, essentially it is a block initialized
2628 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2630 BYTE blockBuffer[BIG_BLOCK_SIZE];
2633 * Initialize blocks as free
2635 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2636 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2639 /******************************************************************************
2640 * Storage32Impl_GetExtDepotBlock
2642 * Returns the index of the block that corresponds to the specified depot
2643 * index. This method is only for depot indexes equal or greater than
2644 * COUNT_BBDEPOTINHEADER.
2646 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2648 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2649 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2650 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2651 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2652 ULONG blockIndex = BLOCK_UNUSED;
2653 ULONG extBlockIndex = This->extBigBlockDepotStart;
2655 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2657 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2658 return BLOCK_UNUSED;
2660 while (extBlockCount > 0)
2662 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2666 if (extBlockIndex != BLOCK_UNUSED)
2667 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2668 extBlockOffset * sizeof(ULONG), &blockIndex);
2673 /******************************************************************************
2674 * Storage32Impl_SetExtDepotBlock
2676 * Associates the specified block index to the specified depot index.
2677 * This method is only for depot indexes equal or greater than
2678 * COUNT_BBDEPOTINHEADER.
2680 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2682 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2683 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2684 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2685 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2686 ULONG extBlockIndex = This->extBigBlockDepotStart;
2688 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2690 while (extBlockCount > 0)
2692 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2696 if (extBlockIndex != BLOCK_UNUSED)
2698 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2699 extBlockOffset * sizeof(ULONG),
2704 /******************************************************************************
2705 * Storage32Impl_AddExtBlockDepot
2707 * Creates an extended depot block.
2709 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2711 ULONG numExtBlocks = This->extBigBlockDepotCount;
2712 ULONG nextExtBlock = This->extBigBlockDepotStart;
2713 BYTE depotBuffer[BIG_BLOCK_SIZE];
2714 ULONG index = BLOCK_UNUSED;
2715 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2716 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2717 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2719 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2720 blocksPerDepotBlock;
2722 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2725 * The first extended block.
2727 This->extBigBlockDepotStart = index;
2733 * Follow the chain to the last one.
2735 for (i = 0; i < (numExtBlocks - 1); i++)
2737 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2741 * Add the new extended block to the chain.
2743 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2748 * Initialize this block.
2750 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2751 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2756 /******************************************************************************
2757 * Storage32Impl_FreeBigBlock
2759 * This method will flag the specified block as free in the big block depot.
2761 static void StorageImpl_FreeBigBlock(
2765 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2767 if (blockIndex < This->prevFreeBlock)
2768 This->prevFreeBlock = blockIndex;
2771 /************************************************************************
2772 * Storage32Impl_GetNextBlockInChain
2774 * This method will retrieve the block index of the next big block in
2777 * Params: This - Pointer to the Storage object.
2778 * blockIndex - Index of the block to retrieve the chain
2780 * nextBlockIndex - receives the return value.
2782 * Returns: This method returns the index of the next block in the chain.
2783 * It will return the constants:
2784 * BLOCK_SPECIAL - If the block given was not part of a
2786 * BLOCK_END_OF_CHAIN - If the block given was the last in
2788 * BLOCK_UNUSED - If the block given was not past of a chain
2790 * BLOCK_EXTBBDEPOT - This block is part of the extended
2793 * See Windows documentation for more details on IStorage methods.
2795 static HRESULT StorageImpl_GetNextBlockInChain(
2798 ULONG* nextBlockIndex)
2800 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2801 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2802 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2803 BYTE depotBuffer[BIG_BLOCK_SIZE];
2805 ULONG depotBlockIndexPos;
2808 *nextBlockIndex = BLOCK_SPECIAL;
2810 if(depotBlockCount >= This->bigBlockDepotCount)
2812 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2813 This->bigBlockDepotCount);
2814 return STG_E_READFAULT;
2818 * Cache the currently accessed depot block.
2820 if (depotBlockCount != This->indexBlockDepotCached)
2822 This->indexBlockDepotCached = depotBlockCount;
2824 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2826 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2831 * We have to look in the extended depot.
2833 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2836 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2839 return STG_E_READFAULT;
2841 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2843 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2844 This->blockDepotCached[index] = *nextBlockIndex;
2848 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2853 /******************************************************************************
2854 * Storage32Impl_GetNextExtendedBlock
2856 * Given an extended block this method will return the next extended block.
2859 * The last ULONG of an extended block is the block index of the next
2860 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2864 * - The index of the next extended block
2865 * - BLOCK_UNUSED: there is no next extended block.
2866 * - Any other return values denotes failure.
2868 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2870 ULONG nextBlockIndex = BLOCK_SPECIAL;
2871 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2873 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
2876 return nextBlockIndex;
2879 /******************************************************************************
2880 * Storage32Impl_SetNextBlockInChain
2882 * This method will write the index of the specified block's next block
2883 * in the big block depot.
2885 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2888 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2889 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2890 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2893 static void StorageImpl_SetNextBlockInChain(
2898 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2899 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2900 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2901 ULONG depotBlockIndexPos;
2903 assert(depotBlockCount < This->bigBlockDepotCount);
2904 assert(blockIndex != nextBlock);
2906 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2908 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2913 * We have to look in the extended depot.
2915 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2918 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
2921 * Update the cached block depot, if necessary.
2923 if (depotBlockCount == This->indexBlockDepotCached)
2925 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2929 /******************************************************************************
2930 * Storage32Impl_LoadFileHeader
2932 * This method will read in the file header, i.e. big block index -1.
2934 static HRESULT StorageImpl_LoadFileHeader(
2937 HRESULT hr = STG_E_FILENOTFOUND;
2938 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2944 * Get a pointer to the big block of data containing the header.
2946 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2949 * Extract the information from the header.
2954 * Check for the "magic number" signature and return an error if it is not
2957 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2959 return STG_E_OLDFORMAT;
2962 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2964 return STG_E_INVALIDHEADER;
2967 StorageUtl_ReadWord(
2969 OFFSET_BIGBLOCKSIZEBITS,
2970 &This->bigBlockSizeBits);
2972 StorageUtl_ReadWord(
2974 OFFSET_SMALLBLOCKSIZEBITS,
2975 &This->smallBlockSizeBits);
2977 StorageUtl_ReadDWord(
2979 OFFSET_BBDEPOTCOUNT,
2980 &This->bigBlockDepotCount);
2982 StorageUtl_ReadDWord(
2984 OFFSET_ROOTSTARTBLOCK,
2985 &This->rootStartBlock);
2987 StorageUtl_ReadDWord(
2989 OFFSET_SBDEPOTSTART,
2990 &This->smallBlockDepotStart);
2992 StorageUtl_ReadDWord(
2994 OFFSET_EXTBBDEPOTSTART,
2995 &This->extBigBlockDepotStart);
2997 StorageUtl_ReadDWord(
2999 OFFSET_EXTBBDEPOTCOUNT,
3000 &This->extBigBlockDepotCount);
3002 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3004 StorageUtl_ReadDWord(
3006 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3007 &(This->bigBlockDepotStart[index]));
3011 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3013 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3014 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3017 * Right now, the code is making some assumptions about the size of the
3018 * blocks, just make sure they are what we're expecting.
3020 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3021 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3023 WARN("Broken OLE storage file\n");
3024 hr = STG_E_INVALIDHEADER;
3033 /******************************************************************************
3034 * Storage32Impl_SaveFileHeader
3036 * This method will save to the file the header, i.e. big block -1.
3038 static void StorageImpl_SaveFileHeader(
3041 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3046 * Get a pointer to the big block of data containing the header.
3048 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3051 * If the block read failed, the file is probably new.
3056 * Initialize for all unknown fields.
3058 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3061 * Initialize the magic number.
3063 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3066 * And a bunch of things we don't know what they mean
3068 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3069 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3070 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3071 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3075 * Write the information to the header.
3077 StorageUtl_WriteWord(
3079 OFFSET_BIGBLOCKSIZEBITS,
3080 This->bigBlockSizeBits);
3082 StorageUtl_WriteWord(
3084 OFFSET_SMALLBLOCKSIZEBITS,
3085 This->smallBlockSizeBits);
3087 StorageUtl_WriteDWord(
3089 OFFSET_BBDEPOTCOUNT,
3090 This->bigBlockDepotCount);
3092 StorageUtl_WriteDWord(
3094 OFFSET_ROOTSTARTBLOCK,
3095 This->rootStartBlock);
3097 StorageUtl_WriteDWord(
3099 OFFSET_SBDEPOTSTART,
3100 This->smallBlockDepotStart);
3102 StorageUtl_WriteDWord(
3104 OFFSET_SBDEPOTCOUNT,
3105 This->smallBlockDepotChain ?
3106 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3108 StorageUtl_WriteDWord(
3110 OFFSET_EXTBBDEPOTSTART,
3111 This->extBigBlockDepotStart);
3113 StorageUtl_WriteDWord(
3115 OFFSET_EXTBBDEPOTCOUNT,
3116 This->extBigBlockDepotCount);
3118 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3120 StorageUtl_WriteDWord(
3122 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3123 (This->bigBlockDepotStart[index]));
3127 * Write the big block back to the file.
3129 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3132 /******************************************************************************
3133 * StorageImpl_ReadRawDirEntry
3135 * This method will read the raw data from a directory entry in the file.
3137 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3139 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3141 ULARGE_INTEGER offset;
3145 offset.u.HighPart = 0;
3146 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3148 hr = BlockChainStream_ReadAt(
3149 This->rootBlockChain,
3158 /******************************************************************************
3159 * StorageImpl_WriteRawDirEntry
3161 * This method will write the raw data from a directory entry in the file.
3163 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3165 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3167 ULARGE_INTEGER offset;
3171 offset.u.HighPart = 0;
3172 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3174 hr = BlockChainStream_WriteAt(
3175 This->rootBlockChain,
3184 /******************************************************************************
3187 * Update raw directory entry data from the fields in newData.
3189 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3191 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3193 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3196 buffer + OFFSET_PS_NAME,
3198 DIRENTRY_NAME_BUFFER_LEN );
3200 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3202 StorageUtl_WriteWord(
3204 OFFSET_PS_NAMELENGTH,
3205 newData->sizeOfNameString);
3207 StorageUtl_WriteDWord(
3209 OFFSET_PS_LEFTCHILD,
3210 newData->leftChild);
3212 StorageUtl_WriteDWord(
3214 OFFSET_PS_RIGHTCHILD,
3215 newData->rightChild);
3217 StorageUtl_WriteDWord(
3220 newData->dirRootEntry);
3222 StorageUtl_WriteGUID(
3227 StorageUtl_WriteDWord(
3230 newData->ctime.dwLowDateTime);
3232 StorageUtl_WriteDWord(
3234 OFFSET_PS_CTIMEHIGH,
3235 newData->ctime.dwHighDateTime);
3237 StorageUtl_WriteDWord(
3240 newData->mtime.dwLowDateTime);
3242 StorageUtl_WriteDWord(
3244 OFFSET_PS_MTIMEHIGH,
3245 newData->ctime.dwHighDateTime);
3247 StorageUtl_WriteDWord(
3249 OFFSET_PS_STARTBLOCK,
3250 newData->startingBlock);
3252 StorageUtl_WriteDWord(
3255 newData->size.u.LowPart);
3258 /******************************************************************************
3259 * Storage32Impl_ReadDirEntry
3261 * This method will read the specified directory entry.
3263 HRESULT StorageImpl_ReadDirEntry(
3268 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3271 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3273 if (SUCCEEDED(readRes))
3275 memset(buffer->name, 0, sizeof(buffer->name));
3278 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3279 DIRENTRY_NAME_BUFFER_LEN );
3280 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3282 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3284 StorageUtl_ReadWord(
3286 OFFSET_PS_NAMELENGTH,
3287 &buffer->sizeOfNameString);
3289 StorageUtl_ReadDWord(
3291 OFFSET_PS_LEFTCHILD,
3292 &buffer->leftChild);
3294 StorageUtl_ReadDWord(
3296 OFFSET_PS_RIGHTCHILD,
3297 &buffer->rightChild);
3299 StorageUtl_ReadDWord(
3302 &buffer->dirRootEntry);
3304 StorageUtl_ReadGUID(
3309 StorageUtl_ReadDWord(
3312 &buffer->ctime.dwLowDateTime);
3314 StorageUtl_ReadDWord(
3316 OFFSET_PS_CTIMEHIGH,
3317 &buffer->ctime.dwHighDateTime);
3319 StorageUtl_ReadDWord(
3322 &buffer->mtime.dwLowDateTime);
3324 StorageUtl_ReadDWord(
3326 OFFSET_PS_MTIMEHIGH,
3327 &buffer->mtime.dwHighDateTime);
3329 StorageUtl_ReadDWord(
3331 OFFSET_PS_STARTBLOCK,
3332 &buffer->startingBlock);
3334 StorageUtl_ReadDWord(
3337 &buffer->size.u.LowPart);
3339 buffer->size.u.HighPart = 0;
3345 /*********************************************************************
3346 * Write the specified directory entry to the file
3348 HRESULT StorageImpl_WriteDirEntry(
3351 const DirEntry* buffer)
3353 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3356 UpdateRawDirEntry(currentEntry, buffer);
3358 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3362 static BOOL StorageImpl_ReadBigBlock(
3367 ULARGE_INTEGER ulOffset;
3370 ulOffset.u.HighPart = 0;
3371 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3373 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3374 return (read == This->bigBlockSize);
3377 static BOOL StorageImpl_ReadDWordFromBigBlock(
3383 ULARGE_INTEGER ulOffset;
3387 ulOffset.u.HighPart = 0;
3388 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3389 ulOffset.u.LowPart += offset;
3391 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3392 *value = lendian32toh(tmp);
3393 return (read == sizeof(DWORD));
3396 static BOOL StorageImpl_WriteBigBlock(
3401 ULARGE_INTEGER ulOffset;
3404 ulOffset.u.HighPart = 0;
3405 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3407 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3408 return (wrote == This->bigBlockSize);
3411 static BOOL StorageImpl_WriteDWordToBigBlock(
3417 ULARGE_INTEGER ulOffset;
3420 ulOffset.u.HighPart = 0;
3421 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3422 ulOffset.u.LowPart += offset;
3424 value = htole32(value);
3425 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3426 return (wrote == sizeof(DWORD));
3429 /******************************************************************************
3430 * Storage32Impl_SmallBlocksToBigBlocks
3432 * This method will convert a small block chain to a big block chain.
3433 * The small block chain will be destroyed.
3435 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3437 SmallBlockChainStream** ppsbChain)
3439 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3440 ULARGE_INTEGER size, offset;
3441 ULONG cbRead, cbWritten;
3442 ULARGE_INTEGER cbTotalRead;
3443 DirRef streamEntryRef;
3444 HRESULT resWrite = S_OK;
3446 DirEntry streamEntry;
3448 BlockChainStream *bbTempChain = NULL;
3449 BlockChainStream *bigBlockChain = NULL;
3452 * Create a temporary big block chain that doesn't have
3453 * an associated directory entry. This temporary chain will be
3454 * used to copy data from small blocks to big blocks.
3456 bbTempChain = BlockChainStream_Construct(This,
3459 if(!bbTempChain) return NULL;
3461 * Grow the big block chain.
3463 size = SmallBlockChainStream_GetSize(*ppsbChain);
3464 BlockChainStream_SetSize(bbTempChain, size);
3467 * Copy the contents of the small block chain to the big block chain
3468 * by small block size increments.
3470 offset.u.LowPart = 0;
3471 offset.u.HighPart = 0;
3472 cbTotalRead.QuadPart = 0;
3474 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3477 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3479 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3482 if (FAILED(resRead))
3487 cbTotalRead.QuadPart += cbRead;
3489 resWrite = BlockChainStream_WriteAt(bbTempChain,
3495 if (FAILED(resWrite))
3498 offset.u.LowPart += cbRead;
3500 } while (cbTotalRead.QuadPart < size.QuadPart);
3501 HeapFree(GetProcessHeap(),0,buffer);
3503 size.u.HighPart = 0;
3506 if (FAILED(resRead) || FAILED(resWrite))
3508 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3509 BlockChainStream_SetSize(bbTempChain, size);
3510 BlockChainStream_Destroy(bbTempChain);
3515 * Destroy the small block chain.
3517 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3518 SmallBlockChainStream_SetSize(*ppsbChain, size);
3519 SmallBlockChainStream_Destroy(*ppsbChain);
3523 * Change the directory entry. This chain is now a big block chain
3524 * and it doesn't reside in the small blocks chain anymore.
3526 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3528 streamEntry.startingBlock = bbHeadOfChain;
3530 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3533 * Destroy the temporary entryless big block chain.
3534 * Create a new big block chain associated with this entry.
3536 BlockChainStream_Destroy(bbTempChain);
3537 bigBlockChain = BlockChainStream_Construct(This,
3541 return bigBlockChain;
3544 /******************************************************************************
3545 * Storage32Impl_BigBlocksToSmallBlocks
3547 * This method will convert a big block chain to a small block chain.
3548 * The big block chain will be destroyed on success.
3550 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3552 BlockChainStream** ppbbChain)
3554 ULARGE_INTEGER size, offset, cbTotalRead;
3555 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3556 DirRef streamEntryRef;
3557 HRESULT resWrite = S_OK, resRead;
3558 DirEntry streamEntry;
3560 SmallBlockChainStream* sbTempChain;
3562 TRACE("%p %p\n", This, ppbbChain);
3564 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3570 size = BlockChainStream_GetSize(*ppbbChain);
3571 SmallBlockChainStream_SetSize(sbTempChain, size);
3573 offset.u.HighPart = 0;
3574 offset.u.LowPart = 0;
3575 cbTotalRead.QuadPart = 0;
3576 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3579 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3580 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3588 cbTotalRead.QuadPart += cbRead;
3590 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3591 cbRead, buffer, &cbWritten);
3593 if(FAILED(resWrite))
3596 offset.u.LowPart += cbRead;
3598 }while(cbTotalRead.QuadPart < size.QuadPart);
3599 HeapFree(GetProcessHeap(), 0, buffer);
3601 size.u.HighPart = 0;
3604 if(FAILED(resRead) || FAILED(resWrite))
3606 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3607 SmallBlockChainStream_SetSize(sbTempChain, size);
3608 SmallBlockChainStream_Destroy(sbTempChain);
3612 /* destroy the original big block chain */
3613 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3614 BlockChainStream_SetSize(*ppbbChain, size);
3615 BlockChainStream_Destroy(*ppbbChain);
3618 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3619 streamEntry.startingBlock = sbHeadOfChain;
3620 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3622 SmallBlockChainStream_Destroy(sbTempChain);
3623 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3626 static void StorageInternalImpl_Invalidate( StorageInternalImpl *This )
3628 if (This->base.ancestorStorage)
3630 TRACE("Storage invalidated (stg=%p)\n", This);
3632 This->base.ancestorStorage = NULL;
3634 StorageBaseImpl_DeleteAll(&This->base);
3636 list_remove(&This->ParentListEntry);
3640 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3642 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3644 StorageInternalImpl_Invalidate(This);
3646 HeapFree(GetProcessHeap(), 0, This);
3649 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
3650 const DirEntry *newData, DirRef *index)
3652 return StorageBaseImpl_CreateDirEntry(&base->ancestorStorage->base,
3656 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
3657 DirRef index, const DirEntry *data)
3659 return StorageBaseImpl_WriteDirEntry(&base->ancestorStorage->base,
3663 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
3664 DirRef index, DirEntry *data)
3666 return StorageBaseImpl_ReadDirEntry(&base->ancestorStorage->base,
3670 /******************************************************************************
3672 ** Storage32InternalImpl_Commit
3675 static HRESULT WINAPI StorageInternalImpl_Commit(
3677 DWORD grfCommitFlags) /* [in] */
3679 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
3683 /******************************************************************************
3685 ** Storage32InternalImpl_Revert
3688 static HRESULT WINAPI StorageInternalImpl_Revert(
3691 FIXME("(%p): stub\n", iface);
3695 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3697 IStorage_Release((IStorage*)This->parentStorage);
3698 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3699 HeapFree(GetProcessHeap(), 0, This);
3702 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3703 IEnumSTATSTG* iface,
3707 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3710 return E_INVALIDARG;
3714 if (IsEqualGUID(&IID_IUnknown, riid) ||
3715 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3718 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3722 return E_NOINTERFACE;
3725 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3726 IEnumSTATSTG* iface)
3728 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3729 return InterlockedIncrement(&This->ref);
3732 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3733 IEnumSTATSTG* iface)
3735 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3739 newRef = InterlockedDecrement(&This->ref);
3743 IEnumSTATSTGImpl_Destroy(This);
3749 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3750 IEnumSTATSTG* iface,
3753 ULONG* pceltFetched)
3755 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3757 DirEntry currentEntry;
3758 STATSTG* currentReturnStruct = rgelt;
3759 ULONG objectFetched = 0;
3760 DirRef currentSearchNode;
3762 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3763 return E_INVALIDARG;
3766 * To avoid the special case, get another pointer to a ULONG value if
3767 * the caller didn't supply one.
3769 if (pceltFetched==0)
3770 pceltFetched = &objectFetched;
3773 * Start the iteration, we will iterate until we hit the end of the
3774 * linked list or until we hit the number of items to iterate through
3779 * Start with the node at the top of the stack.
3781 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3783 while ( ( *pceltFetched < celt) &&
3784 ( currentSearchNode!=DIRENTRY_NULL) )
3787 * Remove the top node from the stack
3789 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3792 * Read the entry from the storage.
3794 StorageImpl_ReadDirEntry(This->parentStorage,
3799 * Copy the information to the return buffer.
3801 StorageUtl_CopyDirEntryToSTATSTG(&This->parentStorage->base,
3802 currentReturnStruct,
3807 * Step to the next item in the iteration
3810 currentReturnStruct++;
3813 * Push the next search node in the search stack.
3815 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
3818 * continue the iteration.
3820 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3823 if (*pceltFetched == celt)
3830 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3831 IEnumSTATSTG* iface,
3834 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3836 DirEntry currentEntry;
3837 ULONG objectFetched = 0;
3838 DirRef currentSearchNode;
3841 * Start with the node at the top of the stack.
3843 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3845 while ( (objectFetched < celt) &&
3846 (currentSearchNode!=DIRENTRY_NULL) )
3849 * Remove the top node from the stack
3851 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3854 * Read the entry from the storage.
3856 StorageImpl_ReadDirEntry(This->parentStorage,
3861 * Step to the next item in the iteration
3866 * Push the next search node in the search stack.
3868 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
3871 * continue the iteration.
3873 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3876 if (objectFetched == celt)
3882 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3883 IEnumSTATSTG* iface)
3885 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3887 DirEntry storageEntry;
3891 * Re-initialize the search stack to an empty stack
3893 This->stackSize = 0;
3896 * Read the storage entry from the top-level storage.
3898 hr = StorageImpl_ReadDirEntry(
3899 This->parentStorage,
3900 This->storageDirEntry,
3905 assert(storageEntry.sizeOfNameString!=0);
3908 * Push the search node in the search stack.
3910 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.dirRootEntry);
3916 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3917 IEnumSTATSTG* iface,
3918 IEnumSTATSTG** ppenum)
3920 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3922 IEnumSTATSTGImpl* newClone;
3925 * Perform a sanity check on the parameters.
3928 return E_INVALIDARG;
3930 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3931 This->storageDirEntry);
3935 * The new clone enumeration must point to the same current node as
3938 newClone->stackSize = This->stackSize ;
3939 newClone->stackMaxSize = This->stackMaxSize ;
3940 newClone->stackToVisit =
3941 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3944 newClone->stackToVisit,
3946 sizeof(DirRef) * newClone->stackSize);
3948 *ppenum = (IEnumSTATSTG*)newClone;
3951 * Don't forget to nail down a reference to the clone before
3954 IEnumSTATSTGImpl_AddRef(*ppenum);
3959 static void IEnumSTATSTGImpl_PushSearchNode(
3960 IEnumSTATSTGImpl* This,
3963 DirEntry storageEntry;
3967 * First, make sure we're not trying to push an unexisting node.
3969 if (nodeToPush==DIRENTRY_NULL)
3973 * First push the node to the stack
3975 if (This->stackSize == This->stackMaxSize)
3977 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3979 This->stackToVisit = HeapReAlloc(
3983 sizeof(DirRef) * This->stackMaxSize);
3986 This->stackToVisit[This->stackSize] = nodeToPush;
3990 * Read the storage entry from the top-level storage.
3992 hr = StorageImpl_ReadDirEntry(
3993 This->parentStorage,
3999 assert(storageEntry.sizeOfNameString!=0);
4002 * Push the previous search node in the search stack.
4004 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.leftChild);
4008 static DirRef IEnumSTATSTGImpl_PopSearchNode(
4009 IEnumSTATSTGImpl* This,
4014 if (This->stackSize == 0)
4015 return DIRENTRY_NULL;
4017 topNode = This->stackToVisit[This->stackSize-1];
4026 * Virtual function table for the IEnumSTATSTGImpl class.
4028 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4030 IEnumSTATSTGImpl_QueryInterface,
4031 IEnumSTATSTGImpl_AddRef,
4032 IEnumSTATSTGImpl_Release,
4033 IEnumSTATSTGImpl_Next,
4034 IEnumSTATSTGImpl_Skip,
4035 IEnumSTATSTGImpl_Reset,
4036 IEnumSTATSTGImpl_Clone
4039 /******************************************************************************
4040 ** IEnumSTATSTGImpl implementation
4043 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4044 StorageImpl* parentStorage,
4045 DirRef storageDirEntry)
4047 IEnumSTATSTGImpl* newEnumeration;
4049 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4051 if (newEnumeration!=0)
4054 * Set-up the virtual function table and reference count.
4056 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4057 newEnumeration->ref = 0;
4060 * We want to nail-down the reference to the storage in case the
4061 * enumeration out-lives the storage in the client application.
4063 newEnumeration->parentStorage = parentStorage;
4064 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4066 newEnumeration->storageDirEntry = storageDirEntry;
4069 * Initialize the search stack
4071 newEnumeration->stackSize = 0;
4072 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4073 newEnumeration->stackToVisit =
4074 HeapAlloc(GetProcessHeap(), 0, sizeof(DirRef)*ENUMSTATSGT_SIZE_INCREMENT);
4077 * Make sure the current node of the iterator is the first one.
4079 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4082 return newEnumeration;
4086 * Virtual function table for the Storage32InternalImpl class.
4088 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4090 StorageBaseImpl_QueryInterface,
4091 StorageBaseImpl_AddRef,
4092 StorageBaseImpl_Release,
4093 StorageBaseImpl_CreateStream,
4094 StorageBaseImpl_OpenStream,
4095 StorageBaseImpl_CreateStorage,
4096 StorageBaseImpl_OpenStorage,
4097 StorageBaseImpl_CopyTo,
4098 StorageBaseImpl_MoveElementTo,
4099 StorageInternalImpl_Commit,
4100 StorageInternalImpl_Revert,
4101 StorageBaseImpl_EnumElements,
4102 StorageBaseImpl_DestroyElement,
4103 StorageBaseImpl_RenameElement,
4104 StorageBaseImpl_SetElementTimes,
4105 StorageBaseImpl_SetClass,
4106 StorageBaseImpl_SetStateBits,
4107 StorageBaseImpl_Stat
4110 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
4112 StorageInternalImpl_Destroy,
4113 StorageInternalImpl_CreateDirEntry,
4114 StorageInternalImpl_WriteDirEntry,
4115 StorageInternalImpl_ReadDirEntry
4118 /******************************************************************************
4119 ** Storage32InternalImpl implementation
4122 static StorageInternalImpl* StorageInternalImpl_Construct(
4123 StorageImpl* ancestorStorage,
4125 DirRef storageDirEntry)
4127 StorageInternalImpl* newStorage;
4129 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4133 list_init(&newStorage->base.strmHead);
4135 list_init(&newStorage->base.storageHead);
4138 * Initialize the virtual function table.
4140 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4141 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
4142 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4145 * Keep the ancestor storage pointer but do not nail a reference to it.
4147 newStorage->base.ancestorStorage = ancestorStorage;
4150 * Keep a reference to the directory entry of this storage
4152 newStorage->base.storageDirEntry = storageDirEntry;
4154 newStorage->base.create = 0;
4162 /******************************************************************************
4163 ** StorageUtl implementation
4166 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4170 memcpy(&tmp, buffer+offset, sizeof(WORD));
4171 *value = lendian16toh(tmp);
4174 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4176 value = htole16(value);
4177 memcpy(buffer+offset, &value, sizeof(WORD));
4180 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4184 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4185 *value = lendian32toh(tmp);
4188 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4190 value = htole32(value);
4191 memcpy(buffer+offset, &value, sizeof(DWORD));
4194 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4195 ULARGE_INTEGER* value)
4197 #ifdef WORDS_BIGENDIAN
4200 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4201 value->u.LowPart = htole32(tmp.u.HighPart);
4202 value->u.HighPart = htole32(tmp.u.LowPart);
4204 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4208 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4209 const ULARGE_INTEGER *value)
4211 #ifdef WORDS_BIGENDIAN
4214 tmp.u.LowPart = htole32(value->u.HighPart);
4215 tmp.u.HighPart = htole32(value->u.LowPart);
4216 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4218 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4222 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4224 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4225 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4226 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4228 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4231 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4233 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4234 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4235 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4237 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4240 void StorageUtl_CopyDirEntryToSTATSTG(
4241 StorageBaseImpl* storage,
4242 STATSTG* destination,
4243 const DirEntry* source,
4248 if (source->stgType == STGTY_ROOT)
4250 /* replace the name of root entry (often "Root Entry") by the file name */
4251 entryName = storage->filename;
4255 entryName = source->name;
4259 * The copy of the string occurs only when the flag is not set
4261 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4262 (entryName == NULL) ||
4263 (entryName[0] == 0) )
4265 destination->pwcsName = 0;
4269 destination->pwcsName =
4270 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
4272 strcpyW(destination->pwcsName, entryName);
4275 switch (source->stgType)
4279 destination->type = STGTY_STORAGE;
4282 destination->type = STGTY_STREAM;
4285 destination->type = STGTY_STREAM;
4289 destination->cbSize = source->size;
4291 currentReturnStruct->mtime = {0}; TODO
4292 currentReturnStruct->ctime = {0};
4293 currentReturnStruct->atime = {0};
4295 destination->grfMode = 0;
4296 destination->grfLocksSupported = 0;
4297 destination->clsid = source->clsid;
4298 destination->grfStateBits = 0;
4299 destination->reserved = 0;
4302 /******************************************************************************
4303 ** BlockChainStream implementation
4306 BlockChainStream* BlockChainStream_Construct(
4307 StorageImpl* parentStorage,
4308 ULONG* headOfStreamPlaceHolder,
4311 BlockChainStream* newStream;
4314 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4316 newStream->parentStorage = parentStorage;
4317 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4318 newStream->ownerDirEntry = dirEntry;
4319 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4320 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4321 newStream->numBlocks = 0;
4323 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4325 while (blockIndex != BLOCK_END_OF_CHAIN)
4327 newStream->numBlocks++;
4328 newStream->tailIndex = blockIndex;
4330 if(FAILED(StorageImpl_GetNextBlockInChain(
4335 HeapFree(GetProcessHeap(), 0, newStream);
4343 void BlockChainStream_Destroy(BlockChainStream* This)
4345 HeapFree(GetProcessHeap(), 0, This);
4348 /******************************************************************************
4349 * BlockChainStream_GetHeadOfChain
4351 * Returns the head of this stream chain.
4352 * Some special chains don't have directory entries, their heads are kept in
4353 * This->headOfStreamPlaceHolder.
4356 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4358 DirEntry chainEntry;
4361 if (This->headOfStreamPlaceHolder != 0)
4362 return *(This->headOfStreamPlaceHolder);
4364 if (This->ownerDirEntry != DIRENTRY_NULL)
4366 hr = StorageImpl_ReadDirEntry(
4367 This->parentStorage,
4368 This->ownerDirEntry,
4373 return chainEntry.startingBlock;
4377 return BLOCK_END_OF_CHAIN;
4380 /******************************************************************************
4381 * BlockChainStream_GetCount
4383 * Returns the number of blocks that comprises this chain.
4384 * This is not the size of the stream as the last block may not be full!
4387 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4392 blockIndex = BlockChainStream_GetHeadOfChain(This);
4394 while (blockIndex != BLOCK_END_OF_CHAIN)
4398 if(FAILED(StorageImpl_GetNextBlockInChain(
4399 This->parentStorage,
4408 /******************************************************************************
4409 * BlockChainStream_ReadAt
4411 * Reads a specified number of bytes from this chain at the specified offset.
4412 * bytesRead may be NULL.
4413 * Failure will be returned if the specified number of bytes has not been read.
4415 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4416 ULARGE_INTEGER offset,
4421 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4422 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4423 ULONG bytesToReadInBuffer;
4427 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4430 * Find the first block in the stream that contains part of the buffer.
4432 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4433 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4434 (blockNoInSequence < This->lastBlockNoInSequence) )
4436 blockIndex = BlockChainStream_GetHeadOfChain(This);
4437 This->lastBlockNoInSequence = blockNoInSequence;
4441 ULONG temp = blockNoInSequence;
4443 blockIndex = This->lastBlockNoInSequenceIndex;
4444 blockNoInSequence -= This->lastBlockNoInSequence;
4445 This->lastBlockNoInSequence = temp;
4448 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4450 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4451 return STG_E_DOCFILECORRUPT;
4452 blockNoInSequence--;
4455 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4456 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4458 This->lastBlockNoInSequenceIndex = blockIndex;
4461 * Start reading the buffer.
4464 bufferWalker = buffer;
4466 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4468 ULARGE_INTEGER ulOffset;
4471 * Calculate how many bytes we can copy from this big block.
4473 bytesToReadInBuffer =
4474 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4476 TRACE("block %i\n",blockIndex);
4477 ulOffset.u.HighPart = 0;
4478 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4481 StorageImpl_ReadAt(This->parentStorage,
4484 bytesToReadInBuffer,
4487 * Step to the next big block.
4489 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4490 return STG_E_DOCFILECORRUPT;
4492 bufferWalker += bytesReadAt;
4493 size -= bytesReadAt;
4494 *bytesRead += bytesReadAt;
4495 offsetInBlock = 0; /* There is no offset on the next block */
4497 if (bytesToReadInBuffer != bytesReadAt)
4501 return (size == 0) ? S_OK : STG_E_READFAULT;
4504 /******************************************************************************
4505 * BlockChainStream_WriteAt
4507 * Writes the specified number of bytes to this chain at the specified offset.
4508 * Will fail if not all specified number of bytes have been written.
4510 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4511 ULARGE_INTEGER offset,
4514 ULONG* bytesWritten)
4516 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4517 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4520 const BYTE* bufferWalker;
4523 * Find the first block in the stream that contains part of the buffer.
4525 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4526 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4527 (blockNoInSequence < This->lastBlockNoInSequence) )
4529 blockIndex = BlockChainStream_GetHeadOfChain(This);
4530 This->lastBlockNoInSequence = blockNoInSequence;
4534 ULONG temp = blockNoInSequence;
4536 blockIndex = This->lastBlockNoInSequenceIndex;
4537 blockNoInSequence -= This->lastBlockNoInSequence;
4538 This->lastBlockNoInSequence = temp;
4541 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4543 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4545 return STG_E_DOCFILECORRUPT;
4546 blockNoInSequence--;
4549 This->lastBlockNoInSequenceIndex = blockIndex;
4551 /* BlockChainStream_SetSize should have already been called to ensure we have
4552 * enough blocks in the chain to write into */
4553 if (blockIndex == BLOCK_END_OF_CHAIN)
4555 ERR("not enough blocks in chain to write data\n");
4556 return STG_E_DOCFILECORRUPT;
4560 bufferWalker = buffer;
4562 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4564 ULARGE_INTEGER ulOffset;
4565 DWORD bytesWrittenAt;
4567 * Calculate how many bytes we can copy from this big block.
4570 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4572 TRACE("block %i\n",blockIndex);
4573 ulOffset.u.HighPart = 0;
4574 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4577 StorageImpl_WriteAt(This->parentStorage,
4584 * Step to the next big block.
4586 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4588 return STG_E_DOCFILECORRUPT;
4590 bufferWalker += bytesWrittenAt;
4591 size -= bytesWrittenAt;
4592 *bytesWritten += bytesWrittenAt;
4593 offsetInBlock = 0; /* There is no offset on the next block */
4595 if (bytesWrittenAt != bytesToWrite)
4599 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4602 /******************************************************************************
4603 * BlockChainStream_Shrink
4605 * Shrinks this chain in the big block depot.
4607 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4608 ULARGE_INTEGER newSize)
4610 ULONG blockIndex, extraBlock;
4615 * Reset the last accessed block cache.
4617 This->lastBlockNoInSequence = 0xFFFFFFFF;
4618 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4621 * Figure out how many blocks are needed to contain the new size
4623 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4625 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4628 blockIndex = BlockChainStream_GetHeadOfChain(This);
4631 * Go to the new end of chain
4633 while (count < numBlocks)
4635 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4641 /* Get the next block before marking the new end */
4642 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4646 /* Mark the new end of chain */
4647 StorageImpl_SetNextBlockInChain(
4648 This->parentStorage,
4650 BLOCK_END_OF_CHAIN);
4652 This->tailIndex = blockIndex;
4653 This->numBlocks = numBlocks;
4656 * Mark the extra blocks as free
4658 while (extraBlock != BLOCK_END_OF_CHAIN)
4660 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4663 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4664 extraBlock = blockIndex;
4670 /******************************************************************************
4671 * BlockChainStream_Enlarge
4673 * Grows this chain in the big block depot.
4675 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4676 ULARGE_INTEGER newSize)
4678 ULONG blockIndex, currentBlock;
4680 ULONG oldNumBlocks = 0;
4682 blockIndex = BlockChainStream_GetHeadOfChain(This);
4685 * Empty chain. Create the head.
4687 if (blockIndex == BLOCK_END_OF_CHAIN)
4689 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4690 StorageImpl_SetNextBlockInChain(This->parentStorage,
4692 BLOCK_END_OF_CHAIN);
4694 if (This->headOfStreamPlaceHolder != 0)
4696 *(This->headOfStreamPlaceHolder) = blockIndex;
4700 DirEntry chainEntry;
4701 assert(This->ownerDirEntry != DIRENTRY_NULL);
4703 StorageImpl_ReadDirEntry(
4704 This->parentStorage,
4705 This->ownerDirEntry,
4708 chainEntry.startingBlock = blockIndex;
4710 StorageImpl_WriteDirEntry(
4711 This->parentStorage,
4712 This->ownerDirEntry,
4716 This->tailIndex = blockIndex;
4717 This->numBlocks = 1;
4721 * Figure out how many blocks are needed to contain this stream
4723 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4725 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4729 * Go to the current end of chain
4731 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4733 currentBlock = blockIndex;
4735 while (blockIndex != BLOCK_END_OF_CHAIN)
4738 currentBlock = blockIndex;
4740 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4745 This->tailIndex = currentBlock;
4748 currentBlock = This->tailIndex;
4749 oldNumBlocks = This->numBlocks;
4752 * Add new blocks to the chain
4754 if (oldNumBlocks < newNumBlocks)
4756 while (oldNumBlocks < newNumBlocks)
4758 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4760 StorageImpl_SetNextBlockInChain(
4761 This->parentStorage,
4765 StorageImpl_SetNextBlockInChain(
4766 This->parentStorage,
4768 BLOCK_END_OF_CHAIN);
4770 currentBlock = blockIndex;
4774 This->tailIndex = blockIndex;
4775 This->numBlocks = newNumBlocks;
4781 /******************************************************************************
4782 * BlockChainStream_SetSize
4784 * Sets the size of this stream. The big block depot will be updated.
4785 * The file will grow if we grow the chain.
4787 * TODO: Free the actual blocks in the file when we shrink the chain.
4788 * Currently, the blocks are still in the file. So the file size
4789 * doesn't shrink even if we shrink streams.
4791 BOOL BlockChainStream_SetSize(
4792 BlockChainStream* This,
4793 ULARGE_INTEGER newSize)
4795 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4797 if (newSize.u.LowPart == size.u.LowPart)
4800 if (newSize.u.LowPart < size.u.LowPart)
4802 BlockChainStream_Shrink(This, newSize);
4806 BlockChainStream_Enlarge(This, newSize);
4812 /******************************************************************************
4813 * BlockChainStream_GetSize
4815 * Returns the size of this chain.
4816 * Will return the block count if this chain doesn't have a directory entry.
4818 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4820 DirEntry chainEntry;
4822 if(This->headOfStreamPlaceHolder == NULL)
4825 * This chain has a directory entry so use the size value from there.
4827 StorageImpl_ReadDirEntry(
4828 This->parentStorage,
4829 This->ownerDirEntry,
4832 return chainEntry.size;
4837 * this chain is a chain that does not have a directory entry, figure out the
4838 * size by making the product number of used blocks times the
4841 ULARGE_INTEGER result;
4842 result.u.HighPart = 0;
4845 BlockChainStream_GetCount(This) *
4846 This->parentStorage->bigBlockSize;
4852 /******************************************************************************
4853 ** SmallBlockChainStream implementation
4856 SmallBlockChainStream* SmallBlockChainStream_Construct(
4857 StorageImpl* parentStorage,
4858 ULONG* headOfStreamPlaceHolder,
4861 SmallBlockChainStream* newStream;
4863 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4865 newStream->parentStorage = parentStorage;
4866 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4867 newStream->ownerDirEntry = dirEntry;
4872 void SmallBlockChainStream_Destroy(
4873 SmallBlockChainStream* This)
4875 HeapFree(GetProcessHeap(), 0, This);
4878 /******************************************************************************
4879 * SmallBlockChainStream_GetHeadOfChain
4881 * Returns the head of this chain of small blocks.
4883 static ULONG SmallBlockChainStream_GetHeadOfChain(
4884 SmallBlockChainStream* This)
4886 DirEntry chainEntry;
4889 if (This->headOfStreamPlaceHolder != NULL)
4890 return *(This->headOfStreamPlaceHolder);
4892 if (This->ownerDirEntry)
4894 hr = StorageImpl_ReadDirEntry(
4895 This->parentStorage,
4896 This->ownerDirEntry,
4901 return chainEntry.startingBlock;
4906 return BLOCK_END_OF_CHAIN;
4909 /******************************************************************************
4910 * SmallBlockChainStream_GetNextBlockInChain
4912 * Returns the index of the next small block in this chain.
4915 * - BLOCK_END_OF_CHAIN: end of this chain
4916 * - BLOCK_UNUSED: small block 'blockIndex' is free
4918 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
4919 SmallBlockChainStream* This,
4921 ULONG* nextBlockInChain)
4923 ULARGE_INTEGER offsetOfBlockInDepot;
4928 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4930 offsetOfBlockInDepot.u.HighPart = 0;
4931 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4934 * Read those bytes in the buffer from the small block file.
4936 res = BlockChainStream_ReadAt(
4937 This->parentStorage->smallBlockDepotChain,
4938 offsetOfBlockInDepot,
4945 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4952 /******************************************************************************
4953 * SmallBlockChainStream_SetNextBlockInChain
4955 * Writes the index of the next block of the specified block in the small
4957 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4958 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4960 static void SmallBlockChainStream_SetNextBlockInChain(
4961 SmallBlockChainStream* This,
4965 ULARGE_INTEGER offsetOfBlockInDepot;
4969 offsetOfBlockInDepot.u.HighPart = 0;
4970 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4972 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4975 * Read those bytes in the buffer from the small block file.
4977 BlockChainStream_WriteAt(
4978 This->parentStorage->smallBlockDepotChain,
4979 offsetOfBlockInDepot,
4985 /******************************************************************************
4986 * SmallBlockChainStream_FreeBlock
4988 * Flag small block 'blockIndex' as free in the small block depot.
4990 static void SmallBlockChainStream_FreeBlock(
4991 SmallBlockChainStream* This,
4994 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4997 /******************************************************************************
4998 * SmallBlockChainStream_GetNextFreeBlock
5000 * Returns the index of a free small block. The small block depot will be
5001 * enlarged if necessary. The small block chain will also be enlarged if
5004 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5005 SmallBlockChainStream* This)
5007 ULARGE_INTEGER offsetOfBlockInDepot;
5010 ULONG blockIndex = 0;
5011 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5013 ULONG smallBlocksPerBigBlock;
5015 offsetOfBlockInDepot.u.HighPart = 0;
5018 * Scan the small block depot for a free block
5020 while (nextBlockIndex != BLOCK_UNUSED)
5022 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5024 res = BlockChainStream_ReadAt(
5025 This->parentStorage->smallBlockDepotChain,
5026 offsetOfBlockInDepot,
5032 * If we run out of space for the small block depot, enlarge it
5036 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5038 if (nextBlockIndex != BLOCK_UNUSED)
5044 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5046 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5047 ULONG nextBlock, newsbdIndex;
5048 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5050 nextBlock = sbdIndex;
5051 while (nextBlock != BLOCK_END_OF_CHAIN)
5053 sbdIndex = nextBlock;
5054 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5057 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5058 if (sbdIndex != BLOCK_END_OF_CHAIN)
5059 StorageImpl_SetNextBlockInChain(
5060 This->parentStorage,
5064 StorageImpl_SetNextBlockInChain(
5065 This->parentStorage,
5067 BLOCK_END_OF_CHAIN);
5070 * Initialize all the small blocks to free
5072 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5073 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5078 * We have just created the small block depot.
5084 * Save it in the header
5086 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5087 StorageImpl_SaveFileHeader(This->parentStorage);
5090 * And allocate the first big block that will contain small blocks
5093 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5095 StorageImpl_SetNextBlockInChain(
5096 This->parentStorage,
5098 BLOCK_END_OF_CHAIN);
5100 StorageImpl_ReadDirEntry(
5101 This->parentStorage,
5102 This->parentStorage->base.storageDirEntry,
5105 rootEntry.startingBlock = sbStartIndex;
5106 rootEntry.size.u.HighPart = 0;
5107 rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
5109 StorageImpl_WriteDirEntry(
5110 This->parentStorage,
5111 This->parentStorage->base.storageDirEntry,
5115 StorageImpl_SaveFileHeader(This->parentStorage);
5119 smallBlocksPerBigBlock =
5120 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5123 * Verify if we have to allocate big blocks to contain small blocks
5125 if (blockIndex % smallBlocksPerBigBlock == 0)
5128 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5130 StorageImpl_ReadDirEntry(
5131 This->parentStorage,
5132 This->parentStorage->base.storageDirEntry,
5135 if (rootEntry.size.u.LowPart <
5136 (blocksRequired * This->parentStorage->bigBlockSize))
5138 rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5140 BlockChainStream_SetSize(
5141 This->parentStorage->smallBlockRootChain,
5144 StorageImpl_WriteDirEntry(
5145 This->parentStorage,
5146 This->parentStorage->base.storageDirEntry,
5154 /******************************************************************************
5155 * SmallBlockChainStream_ReadAt
5157 * Reads a specified number of bytes from this chain at the specified offset.
5158 * bytesRead may be NULL.
5159 * Failure will be returned if the specified number of bytes has not been read.
5161 HRESULT SmallBlockChainStream_ReadAt(
5162 SmallBlockChainStream* This,
5163 ULARGE_INTEGER offset,
5169 ULARGE_INTEGER offsetInBigBlockFile;
5170 ULONG blockNoInSequence =
5171 offset.u.LowPart / This->parentStorage->smallBlockSize;
5173 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5174 ULONG bytesToReadInBuffer;
5176 ULONG bytesReadFromBigBlockFile;
5180 * This should never happen on a small block file.
5182 assert(offset.u.HighPart==0);
5185 * Find the first block in the stream that contains part of the buffer.
5187 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5189 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5191 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5194 blockNoInSequence--;
5198 * Start reading the buffer.
5201 bufferWalker = buffer;
5203 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5206 * Calculate how many bytes we can copy from this small block.
5208 bytesToReadInBuffer =
5209 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5212 * Calculate the offset of the small block in the small block file.
5214 offsetInBigBlockFile.u.HighPart = 0;
5215 offsetInBigBlockFile.u.LowPart =
5216 blockIndex * This->parentStorage->smallBlockSize;
5218 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5221 * Read those bytes in the buffer from the small block file.
5222 * The small block has already been identified so it shouldn't fail
5223 * unless the file is corrupt.
5225 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5226 offsetInBigBlockFile,
5227 bytesToReadInBuffer,
5229 &bytesReadFromBigBlockFile);
5235 * Step to the next big block.
5237 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5239 return STG_E_DOCFILECORRUPT;
5241 bufferWalker += bytesReadFromBigBlockFile;
5242 size -= bytesReadFromBigBlockFile;
5243 *bytesRead += bytesReadFromBigBlockFile;
5244 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5247 return (size == 0) ? S_OK : STG_E_READFAULT;
5250 /******************************************************************************
5251 * SmallBlockChainStream_WriteAt
5253 * Writes the specified number of bytes to this chain at the specified offset.
5254 * Will fail if not all specified number of bytes have been written.
5256 HRESULT SmallBlockChainStream_WriteAt(
5257 SmallBlockChainStream* This,
5258 ULARGE_INTEGER offset,
5261 ULONG* bytesWritten)
5263 ULARGE_INTEGER offsetInBigBlockFile;
5264 ULONG blockNoInSequence =
5265 offset.u.LowPart / This->parentStorage->smallBlockSize;
5267 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5268 ULONG bytesToWriteInBuffer;
5270 ULONG bytesWrittenToBigBlockFile;
5271 const BYTE* bufferWalker;
5275 * This should never happen on a small block file.
5277 assert(offset.u.HighPart==0);
5280 * Find the first block in the stream that contains part of the buffer.
5282 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5284 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5286 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5287 return STG_E_DOCFILECORRUPT;
5288 blockNoInSequence--;
5292 * Start writing the buffer.
5295 bufferWalker = buffer;
5296 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5299 * Calculate how many bytes we can copy to this small block.
5301 bytesToWriteInBuffer =
5302 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5305 * Calculate the offset of the small block in the small block file.
5307 offsetInBigBlockFile.u.HighPart = 0;
5308 offsetInBigBlockFile.u.LowPart =
5309 blockIndex * This->parentStorage->smallBlockSize;
5311 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5314 * Write those bytes in the buffer to the small block file.
5316 res = BlockChainStream_WriteAt(
5317 This->parentStorage->smallBlockRootChain,
5318 offsetInBigBlockFile,
5319 bytesToWriteInBuffer,
5321 &bytesWrittenToBigBlockFile);
5326 * Step to the next big block.
5328 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5331 bufferWalker += bytesWrittenToBigBlockFile;
5332 size -= bytesWrittenToBigBlockFile;
5333 *bytesWritten += bytesWrittenToBigBlockFile;
5334 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5337 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5340 /******************************************************************************
5341 * SmallBlockChainStream_Shrink
5343 * Shrinks this chain in the small block depot.
5345 static BOOL SmallBlockChainStream_Shrink(
5346 SmallBlockChainStream* This,
5347 ULARGE_INTEGER newSize)
5349 ULONG blockIndex, extraBlock;
5353 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5355 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5358 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5361 * Go to the new end of chain
5363 while (count < numBlocks)
5365 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5372 * If the count is 0, we have a special case, the head of the chain was
5377 DirEntry chainEntry;
5379 StorageImpl_ReadDirEntry(This->parentStorage,
5380 This->ownerDirEntry,
5383 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
5385 StorageImpl_WriteDirEntry(This->parentStorage,
5386 This->ownerDirEntry,
5390 * We start freeing the chain at the head block.
5392 extraBlock = blockIndex;
5396 /* Get the next block before marking the new end */
5397 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5401 /* Mark the new end of chain */
5402 SmallBlockChainStream_SetNextBlockInChain(
5405 BLOCK_END_OF_CHAIN);
5409 * Mark the extra blocks as free
5411 while (extraBlock != BLOCK_END_OF_CHAIN)
5413 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5416 SmallBlockChainStream_FreeBlock(This, extraBlock);
5417 extraBlock = blockIndex;
5423 /******************************************************************************
5424 * SmallBlockChainStream_Enlarge
5426 * Grows this chain in the small block depot.
5428 static BOOL SmallBlockChainStream_Enlarge(
5429 SmallBlockChainStream* This,
5430 ULARGE_INTEGER newSize)
5432 ULONG blockIndex, currentBlock;
5434 ULONG oldNumBlocks = 0;
5436 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5439 * Empty chain. Create the head.
5441 if (blockIndex == BLOCK_END_OF_CHAIN)
5443 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5444 SmallBlockChainStream_SetNextBlockInChain(
5447 BLOCK_END_OF_CHAIN);
5449 if (This->headOfStreamPlaceHolder != NULL)
5451 *(This->headOfStreamPlaceHolder) = blockIndex;
5455 DirEntry chainEntry;
5457 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
5460 chainEntry.startingBlock = blockIndex;
5462 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
5467 currentBlock = blockIndex;
5470 * Figure out how many blocks are needed to contain this stream
5472 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5474 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5478 * Go to the current end of chain
5480 while (blockIndex != BLOCK_END_OF_CHAIN)
5483 currentBlock = blockIndex;
5484 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5489 * Add new blocks to the chain
5491 while (oldNumBlocks < newNumBlocks)
5493 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5494 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5496 SmallBlockChainStream_SetNextBlockInChain(
5499 BLOCK_END_OF_CHAIN);
5501 currentBlock = blockIndex;
5508 /******************************************************************************
5509 * SmallBlockChainStream_SetSize
5511 * Sets the size of this stream.
5512 * The file will grow if we grow the chain.
5514 * TODO: Free the actual blocks in the file when we shrink the chain.
5515 * Currently, the blocks are still in the file. So the file size
5516 * doesn't shrink even if we shrink streams.
5518 BOOL SmallBlockChainStream_SetSize(
5519 SmallBlockChainStream* This,
5520 ULARGE_INTEGER newSize)
5522 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5524 if (newSize.u.LowPart == size.u.LowPart)
5527 if (newSize.u.LowPart < size.u.LowPart)
5529 SmallBlockChainStream_Shrink(This, newSize);
5533 SmallBlockChainStream_Enlarge(This, newSize);
5539 /******************************************************************************
5540 * SmallBlockChainStream_GetCount
5542 * Returns the number of small blocks that comprises this chain.
5543 * This is not the size of the stream as the last block may not be full!
5546 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5551 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5553 while(blockIndex != BLOCK_END_OF_CHAIN)
5557 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
5558 blockIndex, &blockIndex)))
5565 /******************************************************************************
5566 * SmallBlockChainStream_GetSize
5568 * Returns the size of this chain.
5570 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5572 DirEntry chainEntry;
5574 if(This->headOfStreamPlaceHolder != NULL)
5576 ULARGE_INTEGER result;
5577 result.u.HighPart = 0;
5579 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
5580 This->parentStorage->smallBlockSize;
5585 StorageImpl_ReadDirEntry(
5586 This->parentStorage,
5587 This->ownerDirEntry,
5590 return chainEntry.size;
5593 /******************************************************************************
5594 * StgCreateDocfile [OLE32.@]
5595 * Creates a new compound file storage object
5598 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5599 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5600 * reserved [ ?] unused?, usually 0
5601 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5604 * S_OK if the file was successfully created
5605 * some STG_E_ value if error
5607 * if pwcsName is NULL, create file with new unique name
5608 * the function can returns
5609 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5612 HRESULT WINAPI StgCreateDocfile(
5616 IStorage **ppstgOpen)
5618 StorageImpl* newStorage = 0;
5619 HANDLE hFile = INVALID_HANDLE_VALUE;
5620 HRESULT hr = STG_E_INVALIDFLAG;
5624 DWORD fileAttributes;
5625 WCHAR tempFileName[MAX_PATH];
5627 TRACE("(%s, %x, %d, %p)\n",
5628 debugstr_w(pwcsName), grfMode,
5629 reserved, ppstgOpen);
5632 return STG_E_INVALIDPOINTER;
5634 return STG_E_INVALIDPARAMETER;
5636 /* if no share mode given then DENY_NONE is the default */
5637 if (STGM_SHARE_MODE(grfMode) == 0)
5638 grfMode |= STGM_SHARE_DENY_NONE;
5640 if ( FAILED( validateSTGM(grfMode) ))
5643 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5644 switch(STGM_ACCESS_MODE(grfMode))
5647 case STGM_READWRITE:
5653 /* in direct mode, can only use SHARE_EXCLUSIVE */
5654 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5657 /* but in transacted mode, any share mode is valid */
5660 * Generate a unique name.
5664 WCHAR tempPath[MAX_PATH];
5665 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5667 memset(tempPath, 0, sizeof(tempPath));
5668 memset(tempFileName, 0, sizeof(tempFileName));
5670 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5673 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5674 pwcsName = tempFileName;
5677 hr = STG_E_INSUFFICIENTMEMORY;
5681 creationMode = TRUNCATE_EXISTING;
5685 creationMode = GetCreationModeFromSTGM(grfMode);
5689 * Interpret the STGM value grfMode
5691 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5692 accessMode = GetAccessModeFromSTGM(grfMode);
5694 if (grfMode & STGM_DELETEONRELEASE)
5695 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5697 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5699 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5700 FIXME("Storage share mode not implemented.\n");
5702 if (grfMode & STGM_TRANSACTED)
5703 FIXME("Transacted mode not implemented.\n");
5707 hFile = CreateFileW(pwcsName,
5715 if (hFile == INVALID_HANDLE_VALUE)
5717 if(GetLastError() == ERROR_FILE_EXISTS)
5718 hr = STG_E_FILEALREADYEXISTS;
5725 * Allocate and initialize the new IStorage32object.
5727 hr = StorageImpl_Construct(
5742 * Get an "out" pointer for the caller.
5744 *ppstgOpen = (IStorage*)newStorage;
5747 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5752 /******************************************************************************
5753 * StgCreateStorageEx [OLE32.@]
5755 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5757 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5758 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5760 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5762 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5763 return STG_E_INVALIDPARAMETER;
5766 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5768 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5769 return STG_E_INVALIDPARAMETER;
5772 if (stgfmt == STGFMT_FILE)
5774 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5775 return STG_E_INVALIDPARAMETER;
5778 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5780 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5781 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5784 ERR("Invalid stgfmt argument\n");
5785 return STG_E_INVALIDPARAMETER;
5788 /******************************************************************************
5789 * StgCreatePropSetStg [OLE32.@]
5791 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5792 IPropertySetStorage **ppPropSetStg)
5796 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5798 hr = STG_E_INVALIDPARAMETER;
5800 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5801 (void**)ppPropSetStg);
5805 /******************************************************************************
5806 * StgOpenStorageEx [OLE32.@]
5808 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5810 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5811 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5813 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5815 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5816 return STG_E_INVALIDPARAMETER;
5822 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5823 return STG_E_INVALIDPARAMETER;
5825 case STGFMT_STORAGE:
5828 case STGFMT_DOCFILE:
5829 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5831 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5832 return STG_E_INVALIDPARAMETER;
5834 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5838 WARN("STGFMT_ANY assuming storage\n");
5842 return STG_E_INVALIDPARAMETER;
5845 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
5849 /******************************************************************************
5850 * StgOpenStorage [OLE32.@]
5852 HRESULT WINAPI StgOpenStorage(
5853 const OLECHAR *pwcsName,
5854 IStorage *pstgPriority,
5858 IStorage **ppstgOpen)
5860 StorageImpl* newStorage = 0;
5866 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5867 debugstr_w(pwcsName), pstgPriority, grfMode,
5868 snbExclude, reserved, ppstgOpen);
5872 hr = STG_E_INVALIDNAME;
5878 hr = STG_E_INVALIDPOINTER;
5884 hr = STG_E_INVALIDPARAMETER;
5888 if (grfMode & STGM_PRIORITY)
5890 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5891 return STG_E_INVALIDFLAG;
5892 if (grfMode & STGM_DELETEONRELEASE)
5893 return STG_E_INVALIDFUNCTION;
5894 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5895 return STG_E_INVALIDFLAG;
5896 grfMode &= ~0xf0; /* remove the existing sharing mode */
5897 grfMode |= STGM_SHARE_DENY_NONE;
5899 /* STGM_PRIORITY stops other IStorage objects on the same file from
5900 * committing until the STGM_PRIORITY IStorage is closed. it also
5901 * stops non-transacted mode StgOpenStorage calls with write access from
5902 * succeeding. obviously, both of these cannot be achieved through just
5903 * file share flags */
5904 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5908 * Validate the sharing mode
5910 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5911 switch(STGM_SHARE_MODE(grfMode))
5913 case STGM_SHARE_EXCLUSIVE:
5914 case STGM_SHARE_DENY_WRITE:
5917 hr = STG_E_INVALIDFLAG;
5921 if ( FAILED( validateSTGM(grfMode) ) ||
5922 (grfMode&STGM_CREATE))
5924 hr = STG_E_INVALIDFLAG;
5928 /* shared reading requires transacted mode */
5929 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5930 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5931 !(grfMode&STGM_TRANSACTED) )
5933 hr = STG_E_INVALIDFLAG;
5938 * Interpret the STGM value grfMode
5940 shareMode = GetShareModeFromSTGM(grfMode);
5941 accessMode = GetAccessModeFromSTGM(grfMode);
5945 hFile = CreateFileW( pwcsName,
5950 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5953 if (hFile==INVALID_HANDLE_VALUE)
5955 DWORD last_error = GetLastError();
5961 case ERROR_FILE_NOT_FOUND:
5962 hr = STG_E_FILENOTFOUND;
5965 case ERROR_PATH_NOT_FOUND:
5966 hr = STG_E_PATHNOTFOUND;
5969 case ERROR_ACCESS_DENIED:
5970 case ERROR_WRITE_PROTECT:
5971 hr = STG_E_ACCESSDENIED;
5974 case ERROR_SHARING_VIOLATION:
5975 hr = STG_E_SHAREVIOLATION;
5986 * Refuse to open the file if it's too small to be a structured storage file
5987 * FIXME: verify the file when reading instead of here
5989 if (GetFileSize(hFile, NULL) < 0x100)
5992 hr = STG_E_FILEALREADYEXISTS;
5997 * Allocate and initialize the new IStorage32object.
5999 hr = StorageImpl_Construct(
6011 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6013 if(hr == STG_E_INVALIDHEADER)
6014 hr = STG_E_FILEALREADYEXISTS;
6019 * Get an "out" pointer for the caller.
6021 *ppstgOpen = (IStorage*)newStorage;
6024 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6028 /******************************************************************************
6029 * StgCreateDocfileOnILockBytes [OLE32.@]
6031 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6035 IStorage** ppstgOpen)
6037 StorageImpl* newStorage = 0;
6040 if ((ppstgOpen == 0) || (plkbyt == 0))
6041 return STG_E_INVALIDPOINTER;
6044 * Allocate and initialize the new IStorage object.
6046 hr = StorageImpl_Construct(
6061 * Get an "out" pointer for the caller.
6063 *ppstgOpen = (IStorage*)newStorage;
6068 /******************************************************************************
6069 * StgOpenStorageOnILockBytes [OLE32.@]
6071 HRESULT WINAPI StgOpenStorageOnILockBytes(
6073 IStorage *pstgPriority,
6077 IStorage **ppstgOpen)
6079 StorageImpl* newStorage = 0;
6082 if ((plkbyt == 0) || (ppstgOpen == 0))
6083 return STG_E_INVALIDPOINTER;
6085 if ( FAILED( validateSTGM(grfMode) ))
6086 return STG_E_INVALIDFLAG;
6091 * Allocate and initialize the new IStorage object.
6093 hr = StorageImpl_Construct(
6108 * Get an "out" pointer for the caller.
6110 *ppstgOpen = (IStorage*)newStorage;
6115 /******************************************************************************
6116 * StgSetTimes [ole32.@]
6117 * StgSetTimes [OLE32.@]
6121 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6122 FILETIME const *patime, FILETIME const *pmtime)
6124 IStorage *stg = NULL;
6127 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6129 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6133 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6134 IStorage_Release(stg);
6140 /******************************************************************************
6141 * StgIsStorageILockBytes [OLE32.@]
6143 * Determines if the ILockBytes contains a storage object.
6145 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6148 ULARGE_INTEGER offset;
6150 offset.u.HighPart = 0;
6151 offset.u.LowPart = 0;
6153 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6155 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6161 /******************************************************************************
6162 * WriteClassStg [OLE32.@]
6164 * This method will store the specified CLSID in the specified storage object
6166 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6171 return E_INVALIDARG;
6174 return STG_E_INVALIDPOINTER;
6176 hRes = IStorage_SetClass(pStg, rclsid);
6181 /***********************************************************************
6182 * ReadClassStg (OLE32.@)
6184 * This method reads the CLSID previously written to a storage object with
6185 * the WriteClassStg.
6188 * pstg [I] IStorage pointer
6189 * pclsid [O] Pointer to where the CLSID is written
6193 * Failure: HRESULT code.
6195 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6200 TRACE("(%p, %p)\n", pstg, pclsid);
6202 if(!pstg || !pclsid)
6203 return E_INVALIDARG;
6206 * read a STATSTG structure (contains the clsid) from the storage
6208 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6211 *pclsid=pstatstg.clsid;
6216 /***********************************************************************
6217 * OleLoadFromStream (OLE32.@)
6219 * This function loads an object from stream
6221 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6225 LPPERSISTSTREAM xstm;
6227 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6229 res=ReadClassStm(pStm,&clsid);
6232 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6235 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6237 IUnknown_Release((IUnknown*)*ppvObj);
6240 res=IPersistStream_Load(xstm,pStm);
6241 IPersistStream_Release(xstm);
6242 /* FIXME: all refcounts ok at this point? I think they should be:
6245 * xstm : 0 (released)
6250 /***********************************************************************
6251 * OleSaveToStream (OLE32.@)
6253 * This function saves an object with the IPersistStream interface on it
6254 * to the specified stream.
6256 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6262 TRACE("(%p,%p)\n",pPStm,pStm);
6264 res=IPersistStream_GetClassID(pPStm,&clsid);
6266 if (SUCCEEDED(res)){
6268 res=WriteClassStm(pStm,&clsid);
6272 res=IPersistStream_Save(pPStm,pStm,TRUE);
6275 TRACE("Finished Save\n");
6279 /****************************************************************************
6280 * This method validate a STGM parameter that can contain the values below
6282 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6283 * The stgm values contained in 0xffff0000 are bitmasks.
6285 * STGM_DIRECT 0x00000000
6286 * STGM_TRANSACTED 0x00010000
6287 * STGM_SIMPLE 0x08000000
6289 * STGM_READ 0x00000000
6290 * STGM_WRITE 0x00000001
6291 * STGM_READWRITE 0x00000002
6293 * STGM_SHARE_DENY_NONE 0x00000040
6294 * STGM_SHARE_DENY_READ 0x00000030
6295 * STGM_SHARE_DENY_WRITE 0x00000020
6296 * STGM_SHARE_EXCLUSIVE 0x00000010
6298 * STGM_PRIORITY 0x00040000
6299 * STGM_DELETEONRELEASE 0x04000000
6301 * STGM_CREATE 0x00001000
6302 * STGM_CONVERT 0x00020000
6303 * STGM_FAILIFTHERE 0x00000000
6305 * STGM_NOSCRATCH 0x00100000
6306 * STGM_NOSNAPSHOT 0x00200000
6308 static HRESULT validateSTGM(DWORD stgm)
6310 DWORD access = STGM_ACCESS_MODE(stgm);
6311 DWORD share = STGM_SHARE_MODE(stgm);
6312 DWORD create = STGM_CREATE_MODE(stgm);
6314 if (stgm&~STGM_KNOWN_FLAGS)
6316 ERR("unknown flags %08x\n", stgm);
6324 case STGM_READWRITE:
6332 case STGM_SHARE_DENY_NONE:
6333 case STGM_SHARE_DENY_READ:
6334 case STGM_SHARE_DENY_WRITE:
6335 case STGM_SHARE_EXCLUSIVE:
6344 case STGM_FAILIFTHERE:
6351 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6353 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6357 * STGM_CREATE | STGM_CONVERT
6358 * if both are false, STGM_FAILIFTHERE is set to TRUE
6360 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6364 * STGM_NOSCRATCH requires STGM_TRANSACTED
6366 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6370 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6371 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6373 if ( (stgm & STGM_NOSNAPSHOT) &&
6374 (!(stgm & STGM_TRANSACTED) ||
6375 share == STGM_SHARE_EXCLUSIVE ||
6376 share == STGM_SHARE_DENY_WRITE) )
6382 /****************************************************************************
6383 * GetShareModeFromSTGM
6385 * This method will return a share mode flag from a STGM value.
6386 * The STGM value is assumed valid.
6388 static DWORD GetShareModeFromSTGM(DWORD stgm)
6390 switch (STGM_SHARE_MODE(stgm))
6392 case STGM_SHARE_DENY_NONE:
6393 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6394 case STGM_SHARE_DENY_READ:
6395 return FILE_SHARE_WRITE;
6396 case STGM_SHARE_DENY_WRITE:
6397 return FILE_SHARE_READ;
6398 case STGM_SHARE_EXCLUSIVE:
6401 ERR("Invalid share mode!\n");
6406 /****************************************************************************
6407 * GetAccessModeFromSTGM
6409 * This method will return an access mode flag from a STGM value.
6410 * The STGM value is assumed valid.
6412 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6414 switch (STGM_ACCESS_MODE(stgm))
6417 return GENERIC_READ;
6419 case STGM_READWRITE:
6420 return GENERIC_READ | GENERIC_WRITE;
6422 ERR("Invalid access mode!\n");
6427 /****************************************************************************
6428 * GetCreationModeFromSTGM
6430 * This method will return a creation mode flag from a STGM value.
6431 * The STGM value is assumed valid.
6433 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6435 switch(STGM_CREATE_MODE(stgm))
6438 return CREATE_ALWAYS;
6440 FIXME("STGM_CONVERT not implemented!\n");
6442 case STGM_FAILIFTHERE:
6445 ERR("Invalid create mode!\n");
6451 /*************************************************************************
6452 * OLECONVERT_LoadOLE10 [Internal]
6454 * Loads the OLE10 STREAM to memory
6457 * pOleStream [I] The OLESTREAM
6458 * pData [I] Data Structure for the OLESTREAM Data
6462 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6463 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6466 * This function is used by OleConvertOLESTREAMToIStorage only.
6468 * Memory allocated for pData must be freed by the caller
6470 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6473 HRESULT hRes = S_OK;
6477 pData->pData = NULL;
6478 pData->pstrOleObjFileName = NULL;
6480 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6483 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6484 if(dwSize != sizeof(pData->dwOleID))
6486 hRes = CONVERT10_E_OLESTREAM_GET;
6488 else if(pData->dwOleID != OLESTREAM_ID)
6490 hRes = CONVERT10_E_OLESTREAM_FMT;
6501 /* Get the TypeID... more info needed for this field */
6502 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6503 if(dwSize != sizeof(pData->dwTypeID))
6505 hRes = CONVERT10_E_OLESTREAM_GET;
6510 if(pData->dwTypeID != 0)
6512 /* Get the length of the OleTypeName */
6513 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6514 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6516 hRes = CONVERT10_E_OLESTREAM_GET;
6521 if(pData->dwOleTypeNameLength > 0)
6523 /* Get the OleTypeName */
6524 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6525 if(dwSize != pData->dwOleTypeNameLength)
6527 hRes = CONVERT10_E_OLESTREAM_GET;
6533 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6534 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6536 hRes = CONVERT10_E_OLESTREAM_GET;
6540 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6541 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6542 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6543 if(pData->pstrOleObjFileName)
6545 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6546 if(dwSize != pData->dwOleObjFileNameLength)
6548 hRes = CONVERT10_E_OLESTREAM_GET;
6552 hRes = CONVERT10_E_OLESTREAM_GET;
6557 /* Get the Width of the Metafile */
6558 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6559 if(dwSize != sizeof(pData->dwMetaFileWidth))
6561 hRes = CONVERT10_E_OLESTREAM_GET;
6565 /* Get the Height of the Metafile */
6566 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6567 if(dwSize != sizeof(pData->dwMetaFileHeight))
6569 hRes = CONVERT10_E_OLESTREAM_GET;
6575 /* Get the Length of the Data */
6576 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6577 if(dwSize != sizeof(pData->dwDataLength))
6579 hRes = CONVERT10_E_OLESTREAM_GET;
6583 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6585 if(!bStrem1) /* if it is a second OLE stream data */
6587 pData->dwDataLength -= 8;
6588 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6589 if(dwSize != sizeof(pData->strUnknown))
6591 hRes = CONVERT10_E_OLESTREAM_GET;
6597 if(pData->dwDataLength > 0)
6599 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6601 /* Get Data (ex. IStorage, Metafile, or BMP) */
6604 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6605 if(dwSize != pData->dwDataLength)
6607 hRes = CONVERT10_E_OLESTREAM_GET;
6612 hRes = CONVERT10_E_OLESTREAM_GET;
6621 /*************************************************************************
6622 * OLECONVERT_SaveOLE10 [Internal]
6624 * Saves the OLE10 STREAM From memory
6627 * pData [I] Data Structure for the OLESTREAM Data
6628 * pOleStream [I] The OLESTREAM to save
6632 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6635 * This function is used by OleConvertIStorageToOLESTREAM only.
6638 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6641 HRESULT hRes = S_OK;
6645 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6646 if(dwSize != sizeof(pData->dwOleID))
6648 hRes = CONVERT10_E_OLESTREAM_PUT;
6653 /* Set the TypeID */
6654 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6655 if(dwSize != sizeof(pData->dwTypeID))
6657 hRes = CONVERT10_E_OLESTREAM_PUT;
6661 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6663 /* Set the Length of the OleTypeName */
6664 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6665 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6667 hRes = CONVERT10_E_OLESTREAM_PUT;
6672 if(pData->dwOleTypeNameLength > 0)
6674 /* Set the OleTypeName */
6675 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6676 if(dwSize != pData->dwOleTypeNameLength)
6678 hRes = CONVERT10_E_OLESTREAM_PUT;
6685 /* Set the width of the Metafile */
6686 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6687 if(dwSize != sizeof(pData->dwMetaFileWidth))
6689 hRes = CONVERT10_E_OLESTREAM_PUT;
6695 /* Set the height of the Metafile */
6696 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6697 if(dwSize != sizeof(pData->dwMetaFileHeight))
6699 hRes = CONVERT10_E_OLESTREAM_PUT;
6705 /* Set the length of the Data */
6706 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6707 if(dwSize != sizeof(pData->dwDataLength))
6709 hRes = CONVERT10_E_OLESTREAM_PUT;
6715 if(pData->dwDataLength > 0)
6717 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6718 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6719 if(dwSize != pData->dwDataLength)
6721 hRes = CONVERT10_E_OLESTREAM_PUT;
6729 /*************************************************************************
6730 * OLECONVERT_GetOLE20FromOLE10[Internal]
6732 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6733 * opens it, and copies the content to the dest IStorage for
6734 * OleConvertOLESTREAMToIStorage
6738 * pDestStorage [I] The IStorage to copy the data to
6739 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6740 * nBufferLength [I] The size of the buffer
6749 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6753 IStorage *pTempStorage;
6754 DWORD dwNumOfBytesWritten;
6755 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6756 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6758 /* Create a temp File */
6759 GetTempPathW(MAX_PATH, wstrTempDir);
6760 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6761 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6763 if(hFile != INVALID_HANDLE_VALUE)
6765 /* Write IStorage Data to File */
6766 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6769 /* Open and copy temp storage to the Dest Storage */
6770 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6773 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6774 IStorage_Release(pTempStorage);
6776 DeleteFileW(wstrTempFile);
6781 /*************************************************************************
6782 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6784 * Saves the OLE10 STREAM From memory
6787 * pStorage [I] The Src IStorage to copy
6788 * pData [I] The Dest Memory to write to.
6791 * The size in bytes allocated for pData
6794 * Memory allocated for pData must be freed by the caller
6796 * Used by OleConvertIStorageToOLESTREAM only.
6799 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6803 DWORD nDataLength = 0;
6804 IStorage *pTempStorage;
6805 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6806 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6810 /* Create temp Storage */
6811 GetTempPathW(MAX_PATH, wstrTempDir);
6812 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6813 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6817 /* Copy Src Storage to the Temp Storage */
6818 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6819 IStorage_Release(pTempStorage);
6821 /* Open Temp Storage as a file and copy to memory */
6822 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6823 if(hFile != INVALID_HANDLE_VALUE)
6825 nDataLength = GetFileSize(hFile, NULL);
6826 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6827 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6830 DeleteFileW(wstrTempFile);
6835 /*************************************************************************
6836 * OLECONVERT_CreateOleStream [Internal]
6838 * Creates the "\001OLE" stream in the IStorage if necessary.
6841 * pStorage [I] Dest storage to create the stream in
6847 * This function is used by OleConvertOLESTREAMToIStorage only.
6849 * This stream is still unknown, MS Word seems to have extra data
6850 * but since the data is stored in the OLESTREAM there should be
6851 * no need to recreate the stream. If the stream is manually
6852 * deleted it will create it with this default data.
6855 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6859 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6860 BYTE pOleStreamHeader [] =
6862 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6863 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6864 0x00, 0x00, 0x00, 0x00
6867 /* Create stream if not present */
6868 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6869 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6873 /* Write default Data */
6874 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6875 IStream_Release(pStream);
6879 /* write a string to a stream, preceded by its length */
6880 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6887 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6888 r = IStream_Write( stm, &len, sizeof(len), NULL);
6893 str = CoTaskMemAlloc( len );
6894 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6895 r = IStream_Write( stm, str, len, NULL);
6896 CoTaskMemFree( str );
6900 /* read a string preceded by its length from a stream */
6901 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6904 DWORD len, count = 0;
6908 r = IStream_Read( stm, &len, sizeof(len), &count );
6911 if( count != sizeof(len) )
6912 return E_OUTOFMEMORY;
6914 TRACE("%d bytes\n",len);
6916 str = CoTaskMemAlloc( len );
6918 return E_OUTOFMEMORY;
6920 r = IStream_Read( stm, str, len, &count );
6925 CoTaskMemFree( str );
6926 return E_OUTOFMEMORY;
6929 TRACE("Read string %s\n",debugstr_an(str,len));
6931 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6932 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6934 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6935 CoTaskMemFree( str );
6943 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6944 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6948 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6950 static const BYTE unknown1[12] =
6951 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6952 0xFF, 0xFF, 0xFF, 0xFF};
6953 static const BYTE unknown2[16] =
6954 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6955 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6957 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6958 debugstr_w(lpszUserType), debugstr_w(szClipName),
6959 debugstr_w(szProgIDName));
6961 /* Create a CompObj stream */
6962 r = IStorage_CreateStream(pstg, szwStreamName,
6963 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6967 /* Write CompObj Structure to stream */
6968 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6970 if( SUCCEEDED( r ) )
6971 r = WriteClassStm( pstm, clsid );
6973 if( SUCCEEDED( r ) )
6974 r = STREAM_WriteString( pstm, lpszUserType );
6975 if( SUCCEEDED( r ) )
6976 r = STREAM_WriteString( pstm, szClipName );
6977 if( SUCCEEDED( r ) )
6978 r = STREAM_WriteString( pstm, szProgIDName );
6979 if( SUCCEEDED( r ) )
6980 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6982 IStream_Release( pstm );
6987 /***********************************************************************
6988 * WriteFmtUserTypeStg (OLE32.@)
6990 HRESULT WINAPI WriteFmtUserTypeStg(
6991 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6994 WCHAR szwClipName[0x40];
6995 CLSID clsid = CLSID_NULL;
6996 LPWSTR wstrProgID = NULL;
6999 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7001 /* get the clipboard format name */
7002 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7005 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7007 /* FIXME: There's room to save a CLSID and its ProgID, but
7008 the CLSID is not looked up in the registry and in all the
7009 tests I wrote it was CLSID_NULL. Where does it come from?
7012 /* get the real program ID. This may fail, but that's fine */
7013 ProgIDFromCLSID(&clsid, &wstrProgID);
7015 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7017 r = STORAGE_WriteCompObj( pstg, &clsid,
7018 lpszUserType, szwClipName, wstrProgID );
7020 CoTaskMemFree(wstrProgID);
7026 /******************************************************************************
7027 * ReadFmtUserTypeStg [OLE32.@]
7029 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7033 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7034 unsigned char unknown1[12];
7035 unsigned char unknown2[16];
7037 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7040 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7042 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7043 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7046 WARN("Failed to open stream r = %08x\n", r);
7050 /* read the various parts of the structure */
7051 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7052 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7054 r = ReadClassStm( stm, &clsid );
7058 r = STREAM_ReadString( stm, &szCLSIDName );
7062 r = STREAM_ReadString( stm, &szOleTypeName );
7066 r = STREAM_ReadString( stm, &szProgIDName );
7070 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7071 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7074 /* ok, success... now we just need to store what we found */
7076 *pcf = RegisterClipboardFormatW( szOleTypeName );
7077 CoTaskMemFree( szOleTypeName );
7079 if( lplpszUserType )
7080 *lplpszUserType = szCLSIDName;
7081 CoTaskMemFree( szProgIDName );
7084 IStream_Release( stm );
7090 /*************************************************************************
7091 * OLECONVERT_CreateCompObjStream [Internal]
7093 * Creates a "\001CompObj" is the destination IStorage if necessary.
7096 * pStorage [I] The dest IStorage to create the CompObj Stream
7098 * strOleTypeName [I] The ProgID
7102 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7105 * This function is used by OleConvertOLESTREAMToIStorage only.
7107 * The stream data is stored in the OLESTREAM and there should be
7108 * no need to recreate the stream. If the stream is manually
7109 * deleted it will attempt to create it by querying the registry.
7113 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7116 HRESULT hStorageRes, hRes = S_OK;
7117 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7118 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7119 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7121 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7122 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7124 /* Initialize the CompObj structure */
7125 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7126 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7127 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7130 /* Create a CompObj stream if it doesn't exist */
7131 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7132 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7133 if(hStorageRes == S_OK)
7135 /* copy the OleTypeName to the compobj struct */
7136 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7137 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7139 /* copy the OleTypeName to the compobj struct */
7140 /* Note: in the test made, these were Identical */
7141 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7142 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7145 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7146 bufferW, OLESTREAM_MAX_STR_LEN );
7147 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7153 /* Get the CLSID Default Name from the Registry */
7154 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7155 if(hErr == ERROR_SUCCESS)
7157 char strTemp[OLESTREAM_MAX_STR_LEN];
7158 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7159 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7160 if(hErr == ERROR_SUCCESS)
7162 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7168 /* Write CompObj Structure to stream */
7169 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7171 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7173 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7174 if(IStorageCompObj.dwCLSIDNameLength > 0)
7176 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7178 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7179 if(IStorageCompObj.dwOleTypeNameLength > 0)
7181 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7183 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7184 if(IStorageCompObj.dwProgIDNameLength > 0)
7186 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7188 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7189 IStream_Release(pStream);
7195 /*************************************************************************
7196 * OLECONVERT_CreateOlePresStream[Internal]
7198 * Creates the "\002OlePres000" Stream with the Metafile data
7201 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7202 * dwExtentX [I] Width of the Metafile
7203 * dwExtentY [I] Height of the Metafile
7204 * pData [I] Metafile data
7205 * dwDataLength [I] Size of the Metafile data
7209 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7212 * This function is used by OleConvertOLESTREAMToIStorage only.
7215 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7219 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7220 BYTE pOlePresStreamHeader [] =
7222 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7223 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7224 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7225 0x00, 0x00, 0x00, 0x00
7228 BYTE pOlePresStreamHeaderEmpty [] =
7230 0x00, 0x00, 0x00, 0x00,
7231 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7232 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7233 0x00, 0x00, 0x00, 0x00
7236 /* Create the OlePres000 Stream */
7237 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7238 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7243 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7245 memset(&OlePres, 0, sizeof(OlePres));
7246 /* Do we have any metafile data to save */
7247 if(dwDataLength > 0)
7249 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7250 nHeaderSize = sizeof(pOlePresStreamHeader);
7254 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7255 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7257 /* Set width and height of the metafile */
7258 OlePres.dwExtentX = dwExtentX;
7259 OlePres.dwExtentY = -dwExtentY;
7261 /* Set Data and Length */
7262 if(dwDataLength > sizeof(METAFILEPICT16))
7264 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7265 OlePres.pData = &(pData[8]);
7267 /* Save OlePres000 Data to Stream */
7268 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7269 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7270 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7271 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7272 if(OlePres.dwSize > 0)
7274 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7276 IStream_Release(pStream);
7280 /*************************************************************************
7281 * OLECONVERT_CreateOle10NativeStream [Internal]
7283 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7286 * pStorage [I] Dest storage to create the stream in
7287 * pData [I] Ole10 Native Data (ex. bmp)
7288 * dwDataLength [I] Size of the Ole10 Native Data
7294 * This function is used by OleConvertOLESTREAMToIStorage only.
7296 * Might need to verify the data and return appropriate error message
7299 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7303 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7305 /* Create the Ole10Native Stream */
7306 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7307 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7311 /* Write info to stream */
7312 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7313 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7314 IStream_Release(pStream);
7319 /*************************************************************************
7320 * OLECONVERT_GetOLE10ProgID [Internal]
7322 * Finds the ProgID (or OleTypeID) from the IStorage
7325 * pStorage [I] The Src IStorage to get the ProgID
7326 * strProgID [I] the ProgID string to get
7327 * dwSize [I] the size of the string
7331 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7334 * This function is used by OleConvertIStorageToOLESTREAM only.
7338 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7342 LARGE_INTEGER iSeekPos;
7343 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7344 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7346 /* Open the CompObj Stream */
7347 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7348 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7352 /*Get the OleType from the CompObj Stream */
7353 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7354 iSeekPos.u.HighPart = 0;
7356 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7357 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7358 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7359 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7360 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7361 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7362 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7364 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7367 IStream_Read(pStream, strProgID, *dwSize, NULL);
7369 IStream_Release(pStream);
7374 LPOLESTR wstrProgID;
7376 /* Get the OleType from the registry */
7377 REFCLSID clsid = &(stat.clsid);
7378 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7379 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7382 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7389 /*************************************************************************
7390 * OLECONVERT_GetOle10PresData [Internal]
7392 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7395 * pStorage [I] Src IStroage
7396 * pOleStream [I] Dest OleStream Mem Struct
7402 * This function is used by OleConvertIStorageToOLESTREAM only.
7404 * Memory allocated for pData must be freed by the caller
7408 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7413 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7415 /* Initialize Default data for OLESTREAM */
7416 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7417 pOleStreamData[0].dwTypeID = 2;
7418 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7419 pOleStreamData[1].dwTypeID = 0;
7420 pOleStreamData[0].dwMetaFileWidth = 0;
7421 pOleStreamData[0].dwMetaFileHeight = 0;
7422 pOleStreamData[0].pData = NULL;
7423 pOleStreamData[1].pData = NULL;
7425 /* Open Ole10Native Stream */
7426 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7427 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7431 /* Read Size and Data */
7432 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7433 if(pOleStreamData->dwDataLength > 0)
7435 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7436 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7438 IStream_Release(pStream);
7444 /*************************************************************************
7445 * OLECONVERT_GetOle20PresData[Internal]
7447 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7450 * pStorage [I] Src IStroage
7451 * pOleStreamData [I] Dest OleStream Mem Struct
7457 * This function is used by OleConvertIStorageToOLESTREAM only.
7459 * Memory allocated for pData must be freed by the caller
7461 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7465 OLECONVERT_ISTORAGE_OLEPRES olePress;
7466 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7468 /* Initialize Default data for OLESTREAM */
7469 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7470 pOleStreamData[0].dwTypeID = 2;
7471 pOleStreamData[0].dwMetaFileWidth = 0;
7472 pOleStreamData[0].dwMetaFileHeight = 0;
7473 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7474 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7475 pOleStreamData[1].dwTypeID = 0;
7476 pOleStreamData[1].dwOleTypeNameLength = 0;
7477 pOleStreamData[1].strOleTypeName[0] = 0;
7478 pOleStreamData[1].dwMetaFileWidth = 0;
7479 pOleStreamData[1].dwMetaFileHeight = 0;
7480 pOleStreamData[1].pData = NULL;
7481 pOleStreamData[1].dwDataLength = 0;
7484 /* Open OlePress000 stream */
7485 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7486 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7489 LARGE_INTEGER iSeekPos;
7490 METAFILEPICT16 MetaFilePict;
7491 static const char strMetafilePictName[] = "METAFILEPICT";
7493 /* Set the TypeID for a Metafile */
7494 pOleStreamData[1].dwTypeID = 5;
7496 /* Set the OleTypeName to Metafile */
7497 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7498 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7500 iSeekPos.u.HighPart = 0;
7501 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7503 /* Get Presentation Data */
7504 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7505 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7506 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7507 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7509 /*Set width and Height */
7510 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7511 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7512 if(olePress.dwSize > 0)
7515 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7517 /* Set MetaFilePict struct */
7518 MetaFilePict.mm = 8;
7519 MetaFilePict.xExt = olePress.dwExtentX;
7520 MetaFilePict.yExt = olePress.dwExtentY;
7521 MetaFilePict.hMF = 0;
7523 /* Get Metafile Data */
7524 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7525 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7526 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7528 IStream_Release(pStream);
7532 /*************************************************************************
7533 * OleConvertOLESTREAMToIStorage [OLE32.@]
7538 * DVTARGETDEVICE parameter is not handled
7539 * Still unsure of some mem fields for OLE 10 Stream
7540 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7541 * and "\001OLE" streams
7544 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7545 LPOLESTREAM pOleStream,
7547 const DVTARGETDEVICE* ptd)
7551 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7553 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7555 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7559 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7562 if(pstg == NULL || pOleStream == NULL)
7564 hRes = E_INVALIDARG;
7569 /* Load the OLESTREAM to Memory */
7570 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7575 /* Load the OLESTREAM to Memory (part 2)*/
7576 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7582 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7584 /* Do we have the IStorage Data in the OLESTREAM */
7585 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7587 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7588 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7592 /* It must be an original OLE 1.0 source */
7593 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7598 /* It must be an original OLE 1.0 source */
7599 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7602 /* Create CompObj Stream if necessary */
7603 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7606 /*Create the Ole Stream if necessary */
7607 OLECONVERT_CreateOleStream(pstg);
7612 /* Free allocated memory */
7613 for(i=0; i < 2; i++)
7615 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7616 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7617 pOleStreamData[i].pstrOleObjFileName = NULL;
7622 /*************************************************************************
7623 * OleConvertIStorageToOLESTREAM [OLE32.@]
7630 * Still unsure of some mem fields for OLE 10 Stream
7631 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7632 * and "\001OLE" streams.
7635 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7637 LPOLESTREAM pOleStream)
7640 HRESULT hRes = S_OK;
7642 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7643 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7645 TRACE("%p %p\n", pstg, pOleStream);
7647 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7649 if(pstg == NULL || pOleStream == NULL)
7651 hRes = E_INVALIDARG;
7655 /* Get the ProgID */
7656 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7657 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7661 /* Was it originally Ole10 */
7662 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7665 IStream_Release(pStream);
7666 /* Get Presentation Data for Ole10Native */
7667 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7671 /* Get Presentation Data (OLE20) */
7672 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7675 /* Save OLESTREAM */
7676 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7679 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7684 /* Free allocated memory */
7685 for(i=0; i < 2; i++)
7687 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7693 /***********************************************************************
7694 * GetConvertStg (OLE32.@)
7696 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7697 FIXME("unimplemented stub!\n");
7701 /******************************************************************************
7702 * StgIsStorageFile [OLE32.@]
7703 * Verify if the file contains a storage object
7709 * S_OK if file has magic bytes as a storage object
7710 * S_FALSE if file is not storage
7713 StgIsStorageFile(LPCOLESTR fn)
7719 TRACE("%s\n", debugstr_w(fn));
7720 hf = CreateFileW(fn, GENERIC_READ,
7721 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7722 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7724 if (hf == INVALID_HANDLE_VALUE)
7725 return STG_E_FILENOTFOUND;
7727 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7729 WARN(" unable to read file\n");
7736 if (bytes_read != 8) {
7737 WARN(" too short\n");
7741 if (!memcmp(magic,STORAGE_magic,8)) {
7746 WARN(" -> Invalid header.\n");
7750 /***********************************************************************
7751 * WriteClassStm (OLE32.@)
7753 * Writes a CLSID to a stream.
7756 * pStm [I] Stream to write to.
7757 * rclsid [I] CLSID to write.
7761 * Failure: HRESULT code.
7763 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7765 TRACE("(%p,%p)\n",pStm,rclsid);
7767 if (!pStm || !rclsid)
7768 return E_INVALIDARG;
7770 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7773 /***********************************************************************
7774 * ReadClassStm (OLE32.@)
7776 * Reads a CLSID from a stream.
7779 * pStm [I] Stream to read from.
7780 * rclsid [O] CLSID to read.
7784 * Failure: HRESULT code.
7786 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7791 TRACE("(%p,%p)\n",pStm,pclsid);
7793 if (!pStm || !pclsid)
7794 return E_INVALIDARG;
7796 /* clear the output args */
7797 *pclsid = CLSID_NULL;
7799 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7804 if (nbByte != sizeof(CLSID))
7805 return STG_E_READFAULT;