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(
180 DirRef parentStorageIndex,
181 DirRef deletedIndex);
183 /***********************************************************************
184 * Declaration of the functions used to manipulate DirEntry
187 static HRESULT createDirEntry(
188 StorageImpl *storage,
189 const DirEntry *newData,
192 static HRESULT destroyDirEntry(
193 StorageImpl *storage,
196 static HRESULT insertIntoTree(
198 DirRef parentStorageIndex,
199 DirRef newEntryIndex);
201 static LONG entryNameCmp(
202 const OLECHAR *name1,
203 const OLECHAR *name2);
205 static DirRef findElement(
206 StorageImpl *storage,
211 static HRESULT findTreeParent(
212 StorageImpl *storage,
214 const OLECHAR *childName,
215 DirEntry *parentData,
219 /***********************************************************************
220 * Declaration of miscellaneous functions...
222 static HRESULT validateSTGM(DWORD stgmValue);
224 static DWORD GetShareModeFromSTGM(DWORD stgm);
225 static DWORD GetAccessModeFromSTGM(DWORD stgm);
226 static DWORD GetCreationModeFromSTGM(DWORD stgm);
228 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
231 /****************************************************************************
232 * IEnumSTATSTGImpl definitions.
234 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
235 * This class allows iterating through the content of a storage and to find
236 * specific items inside it.
238 struct IEnumSTATSTGImpl
240 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
241 * since we want to cast this in an IEnumSTATSTG pointer */
243 LONG ref; /* Reference count */
244 StorageImpl* parentStorage; /* Reference to the parent storage */
245 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
248 * The current implementation of the IEnumSTATSTGImpl class uses a stack
249 * to walk the directory entries to get the content of a storage. This stack
250 * is implemented by the following 3 data members
254 DirRef* stackToVisit;
256 #define ENUMSTATSGT_SIZE_INCREMENT 10
260 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, DirRef storageDirEntry);
261 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
262 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, DirRef nodeToPush);
263 static DirRef IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
265 /************************************************************************
269 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
271 if (index == 0xffffffff)
276 return index * BIG_BLOCK_SIZE;
279 /************************************************************************
280 ** Storage32BaseImpl implementation
282 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
283 ULARGE_INTEGER offset,
288 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
291 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
292 ULARGE_INTEGER offset,
297 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
300 /************************************************************************
301 * Storage32BaseImpl_QueryInterface (IUnknown)
303 * This method implements the common QueryInterface for all IStorage32
304 * implementations contained in this file.
306 * See Windows documentation for more details on IUnknown methods.
308 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
313 StorageBaseImpl *This = (StorageBaseImpl *)iface;
315 if ( (This==0) || (ppvObject==0) )
320 if (IsEqualGUID(&IID_IUnknown, riid) ||
321 IsEqualGUID(&IID_IStorage, riid))
325 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
327 *ppvObject = &This->pssVtbl;
331 return E_NOINTERFACE;
333 IStorage_AddRef(iface);
338 /************************************************************************
339 * Storage32BaseImpl_AddRef (IUnknown)
341 * This method implements the common AddRef for all IStorage32
342 * implementations contained in this file.
344 * See Windows documentation for more details on IUnknown methods.
346 static ULONG WINAPI StorageBaseImpl_AddRef(
349 StorageBaseImpl *This = (StorageBaseImpl *)iface;
350 ULONG ref = InterlockedIncrement(&This->ref);
352 TRACE("(%p) AddRef to %d\n", This, ref);
357 /************************************************************************
358 * Storage32BaseImpl_Release (IUnknown)
360 * This method implements the common Release for all IStorage32
361 * implementations contained in this file.
363 * See Windows documentation for more details on IUnknown methods.
365 static ULONG WINAPI StorageBaseImpl_Release(
368 StorageBaseImpl *This = (StorageBaseImpl *)iface;
370 ULONG ref = InterlockedDecrement(&This->ref);
372 TRACE("(%p) ReleaseRef to %d\n", This, ref);
377 * Since we are using a system of base-classes, we want to call the
378 * destructor of the appropriate derived class. To do this, we are
379 * using virtual functions to implement the destructor.
381 This->v_destructor(This);
387 /************************************************************************
388 * Storage32BaseImpl_OpenStream (IStorage)
390 * This method will open the specified stream object from the current storage.
392 * See Windows documentation for more details on IStorage methods.
394 static HRESULT WINAPI StorageBaseImpl_OpenStream(
396 const OLECHAR* pwcsName, /* [string][in] */
397 void* reserved1, /* [unique][in] */
398 DWORD grfMode, /* [in] */
399 DWORD reserved2, /* [in] */
400 IStream** ppstm) /* [out] */
402 StorageBaseImpl *This = (StorageBaseImpl *)iface;
403 StgStreamImpl* newStream;
404 DirEntry currentEntry;
405 DirRef streamEntryRef;
406 HRESULT res = STG_E_UNKNOWN;
408 TRACE("(%p, %s, %p, %x, %d, %p)\n",
409 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
411 if ( (pwcsName==NULL) || (ppstm==0) )
419 if ( FAILED( validateSTGM(grfMode) ) ||
420 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
422 res = STG_E_INVALIDFLAG;
429 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
431 res = STG_E_INVALIDFUNCTION;
435 if (!This->ancestorStorage)
437 res = STG_E_REVERTED;
442 * Check that we're compatible with the parent's storage mode, but
443 * only if we are not in transacted mode
445 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
446 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
448 res = STG_E_ACCESSDENIED;
454 * Search for the element with the given name
456 streamEntryRef = findElement(
457 This->ancestorStorage,
458 This->storageDirEntry,
463 * If it was found, construct the stream object and return a pointer to it.
465 if ( (streamEntryRef!=DIRENTRY_NULL) &&
466 (currentEntry.stgType==STGTY_STREAM) )
468 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
470 /* A single stream cannot be opened a second time. */
471 res = STG_E_ACCESSDENIED;
475 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
479 newStream->grfMode = grfMode;
480 *ppstm = (IStream*)newStream;
482 IStream_AddRef(*ppstm);
492 res = STG_E_FILENOTFOUND;
496 TRACE("<-- IStream %p\n", *ppstm);
497 TRACE("<-- %08x\n", res);
501 /************************************************************************
502 * Storage32BaseImpl_OpenStorage (IStorage)
504 * This method will open a new storage object from the current storage.
506 * See Windows documentation for more details on IStorage methods.
508 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
510 const OLECHAR* pwcsName, /* [string][unique][in] */
511 IStorage* pstgPriority, /* [unique][in] */
512 DWORD grfMode, /* [in] */
513 SNB snbExclude, /* [unique][in] */
514 DWORD reserved, /* [in] */
515 IStorage** ppstg) /* [out] */
517 StorageBaseImpl *This = (StorageBaseImpl *)iface;
518 StorageInternalImpl* newStorage;
519 DirEntry currentEntry;
520 DirRef storageEntryRef;
521 HRESULT res = STG_E_UNKNOWN;
523 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
524 iface, debugstr_w(pwcsName), pstgPriority,
525 grfMode, snbExclude, reserved, ppstg);
527 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
534 if (snbExclude != NULL)
536 res = STG_E_INVALIDPARAMETER;
540 if ( FAILED( validateSTGM(grfMode) ))
542 res = STG_E_INVALIDFLAG;
549 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
550 (grfMode & STGM_DELETEONRELEASE) ||
551 (grfMode & STGM_PRIORITY) )
553 res = STG_E_INVALIDFUNCTION;
557 if (!This->ancestorStorage)
558 return STG_E_REVERTED;
561 * Check that we're compatible with the parent's storage mode,
562 * but only if we are not transacted
564 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
565 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
567 res = STG_E_ACCESSDENIED;
574 storageEntryRef = findElement(
575 This->ancestorStorage,
576 This->storageDirEntry,
580 if ( (storageEntryRef!=DIRENTRY_NULL) &&
581 (currentEntry.stgType==STGTY_STORAGE) )
583 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
585 /* A single storage cannot be opened a second time. */
586 res = STG_E_ACCESSDENIED;
590 newStorage = StorageInternalImpl_Construct(
591 This->ancestorStorage,
597 *ppstg = (IStorage*)newStorage;
599 StorageBaseImpl_AddRef(*ppstg);
601 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
607 res = STG_E_INSUFFICIENTMEMORY;
611 res = STG_E_FILENOTFOUND;
614 TRACE("<-- %08x\n", res);
618 /************************************************************************
619 * Storage32BaseImpl_EnumElements (IStorage)
621 * This method will create an enumerator object that can be used to
622 * retrieve information about all the elements in the storage object.
624 * See Windows documentation for more details on IStorage methods.
626 static HRESULT WINAPI StorageBaseImpl_EnumElements(
628 DWORD reserved1, /* [in] */
629 void* reserved2, /* [size_is][unique][in] */
630 DWORD reserved3, /* [in] */
631 IEnumSTATSTG** ppenum) /* [out] */
633 StorageBaseImpl *This = (StorageBaseImpl *)iface;
634 IEnumSTATSTGImpl* newEnum;
636 TRACE("(%p, %d, %p, %d, %p)\n",
637 iface, reserved1, reserved2, reserved3, ppenum);
639 if ( (This==0) || (ppenum==0))
642 if (!This->ancestorStorage)
643 return STG_E_REVERTED;
645 newEnum = IEnumSTATSTGImpl_Construct(
646 This->ancestorStorage,
647 This->storageDirEntry);
651 *ppenum = (IEnumSTATSTG*)newEnum;
653 IEnumSTATSTG_AddRef(*ppenum);
658 return E_OUTOFMEMORY;
661 /************************************************************************
662 * Storage32BaseImpl_Stat (IStorage)
664 * This method will retrieve information about this storage object.
666 * See Windows documentation for more details on IStorage methods.
668 static HRESULT WINAPI StorageBaseImpl_Stat(
670 STATSTG* pstatstg, /* [out] */
671 DWORD grfStatFlag) /* [in] */
673 StorageBaseImpl *This = (StorageBaseImpl *)iface;
674 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 readSuccessful = StorageImpl_ReadDirEntry(
694 This->ancestorStorage,
695 This->storageDirEntry,
700 StorageUtl_CopyDirEntryToSTATSTG(
701 This->ancestorStorage,
706 pstatstg->grfMode = This->openFlags;
707 pstatstg->grfStateBits = This->stateBits;
718 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);
720 TRACE("<-- %08x\n", res);
724 /************************************************************************
725 * Storage32BaseImpl_RenameElement (IStorage)
727 * This method will rename the specified element.
729 * See Windows documentation for more details on IStorage methods.
731 static HRESULT WINAPI StorageBaseImpl_RenameElement(
733 const OLECHAR* pwcsOldName, /* [in] */
734 const OLECHAR* pwcsNewName) /* [in] */
736 StorageBaseImpl *This = (StorageBaseImpl *)iface;
737 DirEntry currentEntry;
738 DirRef currentEntryRef;
740 TRACE("(%p, %s, %s)\n",
741 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
743 if (!This->ancestorStorage)
744 return STG_E_REVERTED;
746 currentEntryRef = findElement(This->ancestorStorage,
747 This->storageDirEntry,
751 if (currentEntryRef != DIRENTRY_NULL)
754 * There is already an element with the new name
756 return STG_E_FILEALREADYEXISTS;
760 * Search for the old element name
762 currentEntryRef = findElement(This->ancestorStorage,
763 This->storageDirEntry,
767 if (currentEntryRef != DIRENTRY_NULL)
769 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
770 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
772 WARN("Element is already open; cannot rename.\n");
773 return STG_E_ACCESSDENIED;
776 /* Remove the element from its current position in the tree */
777 removeFromTree(This->ancestorStorage, This->storageDirEntry,
780 /* Change the name of the element */
781 strcpyW(currentEntry.name, pwcsNewName);
783 StorageImpl_WriteDirEntry(This->ancestorStorage, currentEntryRef,
786 /* Insert the element in a new position in the tree */
787 insertIntoTree(This->ancestorStorage, This->storageDirEntry,
793 * There is no element with the old name
795 return STG_E_FILENOTFOUND;
801 /************************************************************************
802 * Storage32BaseImpl_CreateStream (IStorage)
804 * This method will create a stream object within this storage
806 * See Windows documentation for more details on IStorage methods.
808 static HRESULT WINAPI StorageBaseImpl_CreateStream(
810 const OLECHAR* pwcsName, /* [string][in] */
811 DWORD grfMode, /* [in] */
812 DWORD reserved1, /* [in] */
813 DWORD reserved2, /* [in] */
814 IStream** ppstm) /* [out] */
816 StorageBaseImpl *This = (StorageBaseImpl *)iface;
817 StgStreamImpl* newStream;
818 DirEntry currentEntry, newStreamEntry;
819 DirRef currentEntryRef, newStreamEntryRef;
821 TRACE("(%p, %s, %x, %d, %d, %p)\n",
822 iface, debugstr_w(pwcsName), grfMode,
823 reserved1, reserved2, ppstm);
826 return STG_E_INVALIDPOINTER;
829 return STG_E_INVALIDNAME;
831 if (reserved1 || reserved2)
832 return STG_E_INVALIDPARAMETER;
834 if ( FAILED( validateSTGM(grfMode) ))
835 return STG_E_INVALIDFLAG;
837 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
838 return STG_E_INVALIDFLAG;
840 if (!This->ancestorStorage)
841 return STG_E_REVERTED;
846 if ((grfMode & STGM_DELETEONRELEASE) ||
847 (grfMode & STGM_TRANSACTED))
848 return STG_E_INVALIDFUNCTION;
850 /* Can't create a stream on read-only storage */
851 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
852 return STG_E_ACCESSDENIED;
855 * Check that we're compatible with the parent's storage mode
856 * if not in transacted mode
858 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
859 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
860 return STG_E_ACCESSDENIED;
863 if(This->ancestorStorage->base.openFlags & STGM_SIMPLE)
864 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
868 currentEntryRef = findElement(This->ancestorStorage,
869 This->storageDirEntry,
873 if (currentEntryRef != DIRENTRY_NULL)
876 * An element with this name already exists
878 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
880 IStorage_DestroyElement(iface, pwcsName);
883 return STG_E_FILEALREADYEXISTS;
885 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
887 WARN("read-only storage\n");
888 return STG_E_ACCESSDENIED;
892 * memset the empty entry
894 memset(&newStreamEntry, 0, sizeof(DirEntry));
896 newStreamEntry.sizeOfNameString =
897 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
899 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
900 return STG_E_INVALIDNAME;
902 strcpyW(newStreamEntry.name, pwcsName);
904 newStreamEntry.stgType = STGTY_STREAM;
905 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
906 newStreamEntry.size.u.LowPart = 0;
907 newStreamEntry.size.u.HighPart = 0;
909 newStreamEntry.leftChild = DIRENTRY_NULL;
910 newStreamEntry.rightChild = DIRENTRY_NULL;
911 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
913 /* call CoFileTime to get the current time
918 /* newStreamEntry.clsid */
921 * Create an entry with the new data
923 createDirEntry(This->ancestorStorage, &newStreamEntry, &newStreamEntryRef);
926 * Insert the new entry in the parent storage's tree.
929 This->ancestorStorage,
930 This->storageDirEntry,
934 * Open the stream to return it.
936 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
940 *ppstm = (IStream*)newStream;
942 IStream_AddRef(*ppstm);
946 return STG_E_INSUFFICIENTMEMORY;
952 /************************************************************************
953 * Storage32BaseImpl_SetClass (IStorage)
955 * This method will write the specified CLSID in the directory entry of this
958 * See Windows documentation for more details on IStorage methods.
960 static HRESULT WINAPI StorageBaseImpl_SetClass(
962 REFCLSID clsid) /* [in] */
964 StorageBaseImpl *This = (StorageBaseImpl *)iface;
965 HRESULT hRes = E_FAIL;
966 DirEntry currentEntry;
969 TRACE("(%p, %p)\n", iface, clsid);
971 if (!This->ancestorStorage)
972 return STG_E_REVERTED;
974 success = StorageImpl_ReadDirEntry(This->ancestorStorage,
975 This->storageDirEntry,
979 currentEntry.clsid = *clsid;
981 success = StorageImpl_WriteDirEntry(This->ancestorStorage,
982 This->storageDirEntry,
991 /************************************************************************
992 ** Storage32Impl implementation
995 /************************************************************************
996 * Storage32BaseImpl_CreateStorage (IStorage)
998 * This method will create the storage object within the provided storage.
1000 * See Windows documentation for more details on IStorage methods.
1002 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1004 const OLECHAR *pwcsName, /* [string][in] */
1005 DWORD grfMode, /* [in] */
1006 DWORD reserved1, /* [in] */
1007 DWORD reserved2, /* [in] */
1008 IStorage **ppstg) /* [out] */
1010 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1012 DirEntry currentEntry;
1014 DirRef currentEntryRef;
1018 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1019 iface, debugstr_w(pwcsName), grfMode,
1020 reserved1, reserved2, ppstg);
1023 return STG_E_INVALIDPOINTER;
1026 return STG_E_INVALIDNAME;
1030 if ( FAILED( validateSTGM(grfMode) ) ||
1031 (grfMode & STGM_DELETEONRELEASE) )
1033 WARN("bad grfMode: 0x%x\n", grfMode);
1034 return STG_E_INVALIDFLAG;
1037 if (!This->ancestorStorage)
1038 return STG_E_REVERTED;
1041 * Check that we're compatible with the parent's storage mode
1043 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1045 WARN("access denied\n");
1046 return STG_E_ACCESSDENIED;
1049 currentEntryRef = findElement(This->ancestorStorage,
1050 This->storageDirEntry,
1054 if (currentEntryRef != DIRENTRY_NULL)
1057 * An element with this name already exists
1059 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1060 STGM_ACCESS_MODE(This->openFlags) != STGM_READ)
1062 hr = IStorage_DestroyElement(iface, pwcsName);
1068 WARN("file already exists\n");
1069 return STG_E_FILEALREADYEXISTS;
1072 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1074 WARN("read-only storage\n");
1075 return STG_E_ACCESSDENIED;
1078 memset(&newEntry, 0, sizeof(DirEntry));
1080 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1082 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1084 FIXME("name too long\n");
1085 return STG_E_INVALIDNAME;
1088 strcpyW(newEntry.name, pwcsName);
1090 newEntry.stgType = STGTY_STORAGE;
1091 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1092 newEntry.size.u.LowPart = 0;
1093 newEntry.size.u.HighPart = 0;
1095 newEntry.leftChild = DIRENTRY_NULL;
1096 newEntry.rightChild = DIRENTRY_NULL;
1097 newEntry.dirRootEntry = DIRENTRY_NULL;
1099 /* call CoFileTime to get the current time
1104 /* newEntry.clsid */
1107 * Create a new directory entry for the storage
1109 createDirEntry(This->ancestorStorage, &newEntry, &newEntryRef);
1112 * Insert the new directory entry into the parent storage's tree
1115 This->ancestorStorage,
1116 This->storageDirEntry,
1120 * Open it to get a pointer to return.
1122 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1124 if( (hr != S_OK) || (*ppstg == NULL))
1134 /***************************************************************************
1138 * Reserve a directory entry in the file and initialize it.
1140 static HRESULT createDirEntry(
1141 StorageImpl *storage,
1142 const DirEntry *newData,
1145 ULONG currentEntryIndex = 0;
1146 ULONG newEntryIndex = DIRENTRY_NULL;
1148 BYTE currentData[RAW_DIRENTRY_SIZE];
1149 WORD sizeOfNameString;
1153 hr = StorageImpl_ReadRawDirEntry(storage,
1159 StorageUtl_ReadWord(
1161 OFFSET_PS_NAMELENGTH,
1164 if (sizeOfNameString == 0)
1167 * The entry exists and is available, we found it.
1169 newEntryIndex = currentEntryIndex;
1175 * We exhausted the directory entries, we will create more space below
1177 newEntryIndex = currentEntryIndex;
1179 currentEntryIndex++;
1181 } while (newEntryIndex == DIRENTRY_NULL);
1184 * grow the directory stream
1188 BYTE emptyData[RAW_DIRENTRY_SIZE];
1189 ULARGE_INTEGER newSize;
1191 ULONG lastEntry = 0;
1192 ULONG blockCount = 0;
1195 * obtain the new count of blocks in the directory stream
1197 blockCount = BlockChainStream_GetCount(
1198 storage->rootBlockChain)+1;
1201 * initialize the size used by the directory stream
1203 newSize.u.HighPart = 0;
1204 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1207 * add a block to the directory stream
1209 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1212 * memset the empty entry in order to initialize the unused newly
1215 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1220 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1223 entryIndex = newEntryIndex + 1;
1224 entryIndex < lastEntry;
1227 StorageImpl_WriteRawDirEntry(
1234 UpdateRawDirEntry(currentData, newData);
1236 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1239 *index = newEntryIndex;
1244 /***************************************************************************
1248 * Mark a directory entry in the file as free.
1250 static HRESULT destroyDirEntry(
1251 StorageImpl *storage,
1255 BYTE emptyData[RAW_DIRENTRY_SIZE];
1257 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1259 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1265 /****************************************************************************
1269 * Case insensitive comparison of DirEntry.name by first considering
1272 * Returns <0 when name1 < name2
1273 * >0 when name1 > name2
1274 * 0 when name1 == name2
1276 static LONG entryNameCmp(
1277 const OLECHAR *name1,
1278 const OLECHAR *name2)
1280 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1285 * We compare the string themselves only when they are of the same length
1287 diff = lstrcmpiW( name1, name2);
1293 /****************************************************************************
1297 * Add a directory entry to a storage
1299 static HRESULT insertIntoTree(
1301 DirRef parentStorageIndex,
1302 DirRef newEntryIndex)
1304 DirEntry currentEntry;
1308 * Read the inserted entry
1310 StorageImpl_ReadDirEntry(This,
1315 * Read the storage entry
1317 StorageImpl_ReadDirEntry(This,
1321 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1324 * The root storage contains some element, therefore, start the research
1325 * for the appropriate location.
1328 DirRef current, next, previous, currentEntryId;
1331 * Keep a reference to the root of the storage's element tree
1333 currentEntryId = currentEntry.dirRootEntry;
1338 StorageImpl_ReadDirEntry(This,
1339 currentEntry.dirRootEntry,
1342 previous = currentEntry.leftChild;
1343 next = currentEntry.rightChild;
1344 current = currentEntryId;
1348 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1352 if (previous != DIRENTRY_NULL)
1354 StorageImpl_ReadDirEntry(This,
1361 currentEntry.leftChild = newEntryIndex;
1362 StorageImpl_WriteDirEntry(This,
1370 if (next != DIRENTRY_NULL)
1372 StorageImpl_ReadDirEntry(This,
1379 currentEntry.rightChild = newEntryIndex;
1380 StorageImpl_WriteDirEntry(This,
1389 * Trying to insert an item with the same name in the
1390 * subtree structure.
1392 return STG_E_FILEALREADYEXISTS;
1395 previous = currentEntry.leftChild;
1396 next = currentEntry.rightChild;
1402 * The storage is empty, make the new entry the root of its element tree
1404 currentEntry.dirRootEntry = newEntryIndex;
1405 StorageImpl_WriteDirEntry(This,
1413 /****************************************************************************
1417 * Find and read the element of a storage with the given name.
1419 static DirRef findElement(StorageImpl *storage, DirRef storageEntry,
1420 const OLECHAR *name, DirEntry *data)
1422 DirRef currentEntry;
1424 /* Read the storage entry to find the root of the tree. */
1425 StorageImpl_ReadDirEntry(storage, storageEntry, data);
1427 currentEntry = data->dirRootEntry;
1429 while (currentEntry != DIRENTRY_NULL)
1433 StorageImpl_ReadDirEntry(storage, currentEntry, data);
1435 cmp = entryNameCmp(name, data->name);
1442 currentEntry = data->leftChild;
1445 currentEntry = data->rightChild;
1448 return currentEntry;
1451 /****************************************************************************
1455 * Find and read the binary tree parent of the element with the given name.
1457 * If there is no such element, find a place where it could be inserted and
1458 * return STG_E_FILENOTFOUND.
1460 static HRESULT findTreeParent(StorageImpl *storage, DirRef storageEntry,
1461 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1467 /* Read the storage entry to find the root of the tree. */
1468 StorageImpl_ReadDirEntry(storage, storageEntry, parentData);
1470 *parentEntry = storageEntry;
1471 *relation = DIRENTRY_RELATION_DIR;
1473 childEntry = parentData->dirRootEntry;
1475 while (childEntry != DIRENTRY_NULL)
1479 StorageImpl_ReadDirEntry(storage, childEntry, &childData);
1481 cmp = entryNameCmp(childName, childData.name);
1489 *parentData = childData;
1490 *parentEntry = childEntry;
1491 *relation = DIRENTRY_RELATION_PREVIOUS;
1493 childEntry = parentData->leftChild;
1498 *parentData = childData;
1499 *parentEntry = childEntry;
1500 *relation = DIRENTRY_RELATION_NEXT;
1502 childEntry = parentData->rightChild;
1506 if (childEntry == DIRENTRY_NULL)
1507 return STG_E_FILENOTFOUND;
1513 /*************************************************************************
1516 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1518 DWORD ciidExclude, /* [in] */
1519 const IID* rgiidExclude, /* [size_is][unique][in] */
1520 SNB snbExclude, /* [unique][in] */
1521 IStorage* pstgDest) /* [unique][in] */
1523 IEnumSTATSTG *elements = 0;
1524 STATSTG curElement, strStat;
1526 IStorage *pstgTmp, *pstgChild;
1527 IStream *pstrTmp, *pstrChild;
1528 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1531 TRACE("(%p, %d, %p, %p, %p)\n",
1532 iface, ciidExclude, rgiidExclude,
1533 snbExclude, pstgDest);
1535 if ( pstgDest == 0 )
1536 return STG_E_INVALIDPOINTER;
1539 * Enumerate the elements
1541 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1549 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1550 IStorage_SetClass( pstgDest, &curElement.clsid );
1552 for(i = 0; i < ciidExclude; ++i)
1554 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1555 skip_storage = TRUE;
1556 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1559 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1565 * Obtain the next element
1567 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1569 if ( hr == S_FALSE )
1571 hr = S_OK; /* done, every element has been copied */
1577 WCHAR **snb = snbExclude;
1579 while ( *snb != NULL && !skip )
1581 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1590 if (curElement.type == STGTY_STORAGE)
1596 * open child source storage
1598 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1599 STGM_READ|STGM_SHARE_EXCLUSIVE,
1600 NULL, 0, &pstgChild );
1606 * Check if destination storage is not a child of the source
1607 * storage, which will cause an infinite loop
1609 if (pstgChild == pstgDest)
1611 IEnumSTATSTG_Release(elements);
1613 return STG_E_ACCESSDENIED;
1617 * create a new storage in destination storage
1619 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1620 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1624 * if it already exist, don't create a new one use this one
1626 if (hr == STG_E_FILEALREADYEXISTS)
1628 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1629 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1630 NULL, 0, &pstgTmp );
1638 * do the copy recursively
1640 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1643 IStorage_Release( pstgTmp );
1644 IStorage_Release( pstgChild );
1646 else if (curElement.type == STGTY_STREAM)
1652 * create a new stream in destination storage. If the stream already
1653 * exist, it will be deleted and a new one will be created.
1655 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1656 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1663 * open child stream storage
1665 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1666 STGM_READ|STGM_SHARE_EXCLUSIVE,
1673 * Get the size of the source stream
1675 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1678 * Set the size of the destination stream.
1680 IStream_SetSize(pstrTmp, strStat.cbSize);
1685 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1688 IStream_Release( pstrTmp );
1689 IStream_Release( pstrChild );
1693 WARN("unknown element type: %d\n", curElement.type);
1696 } while (hr == S_OK);
1701 IEnumSTATSTG_Release(elements);
1706 /*************************************************************************
1707 * MoveElementTo (IStorage)
1709 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1711 const OLECHAR *pwcsName, /* [string][in] */
1712 IStorage *pstgDest, /* [unique][in] */
1713 const OLECHAR *pwcsNewName,/* [string][in] */
1714 DWORD grfFlags) /* [in] */
1716 FIXME("(%p %s %p %s %u): stub\n", iface,
1717 debugstr_w(pwcsName), pstgDest,
1718 debugstr_w(pwcsNewName), grfFlags);
1722 /*************************************************************************
1725 * Ensures that any changes made to a storage object open in transacted mode
1726 * are reflected in the parent storage
1729 * Wine doesn't implement transacted mode, which seems to be a basic
1730 * optimization, so we can ignore this stub for now.
1732 static HRESULT WINAPI StorageImpl_Commit(
1734 DWORD grfCommitFlags)/* [in] */
1736 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1740 /*************************************************************************
1743 * Discard all changes that have been made since the last commit operation
1745 static HRESULT WINAPI StorageImpl_Revert(
1748 FIXME("(%p): stub\n", iface);
1752 /*************************************************************************
1753 * DestroyElement (IStorage)
1755 * Strategy: This implementation is built this way for simplicity not for speed.
1756 * I always delete the topmost element of the enumeration and adjust
1757 * the deleted element pointer all the time. This takes longer to
1758 * do but allow to reinvoke DestroyElement whenever we encounter a
1759 * storage object. The optimisation resides in the usage of another
1760 * enumeration strategy that would give all the leaves of a storage
1761 * first. (postfix order)
1763 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1765 const OLECHAR *pwcsName)/* [string][in] */
1767 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1770 DirEntry entryToDelete;
1771 DirRef entryToDeleteRef;
1774 iface, debugstr_w(pwcsName));
1777 return STG_E_INVALIDPOINTER;
1779 if (!This->ancestorStorage)
1780 return STG_E_REVERTED;
1782 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1783 return STG_E_ACCESSDENIED;
1785 entryToDeleteRef = findElement(
1786 This->ancestorStorage,
1787 This->storageDirEntry,
1791 if ( entryToDeleteRef == DIRENTRY_NULL )
1793 return STG_E_FILENOTFOUND;
1796 if ( entryToDelete.stgType == STGTY_STORAGE )
1798 hr = deleteStorageContents(
1803 else if ( entryToDelete.stgType == STGTY_STREAM )
1805 hr = deleteStreamContents(
1815 * Remove the entry from its parent storage
1817 hr = removeFromTree(
1818 This->ancestorStorage,
1819 This->storageDirEntry,
1823 * Invalidate the entry
1826 destroyDirEntry(This->ancestorStorage,
1833 /************************************************************************
1834 * StorageImpl_Stat (IStorage)
1836 * This method will retrieve information about this storage object.
1838 * See Windows documentation for more details on IStorage methods.
1840 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1841 STATSTG* pstatstg, /* [out] */
1842 DWORD grfStatFlag) /* [in] */
1844 StorageImpl* const This = (StorageImpl*)iface;
1845 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1847 if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1849 CoTaskMemFree(pstatstg->pwcsName);
1850 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1851 strcpyW(pstatstg->pwcsName, This->pwcsName);
1857 /******************************************************************************
1858 * Internal stream list handlers
1861 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1863 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1864 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1867 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1869 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1870 list_remove(&(strm->StrmListEntry));
1873 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1875 StgStreamImpl *strm;
1877 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1879 if (strm->dirEntry == streamEntry)
1888 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1890 StorageInternalImpl *childstg;
1892 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1894 if (childstg->base.storageDirEntry == storageEntry)
1903 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1905 struct list *cur, *cur2;
1906 StgStreamImpl *strm=NULL;
1907 StorageInternalImpl *childstg=NULL;
1909 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1910 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1911 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1912 strm->parentStorage = NULL;
1916 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1917 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1918 StorageInternalImpl_Invalidate( childstg );
1923 /*********************************************************************
1927 * Delete the contents of a storage entry.
1930 static HRESULT deleteStorageContents(
1931 StorageBaseImpl *parentStorage,
1932 DirRef indexToDelete,
1933 DirEntry entryDataToDelete)
1935 IEnumSTATSTG *elements = 0;
1936 IStorage *childStorage = 0;
1937 STATSTG currentElement;
1939 HRESULT destroyHr = S_OK;
1940 StorageInternalImpl *stg, *stg2;
1942 /* Invalidate any open storage objects. */
1943 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
1945 if (stg->base.storageDirEntry == indexToDelete)
1947 StorageInternalImpl_Invalidate(stg);
1952 * Open the storage and enumerate it
1954 hr = StorageBaseImpl_OpenStorage(
1955 (IStorage*)parentStorage,
1956 entryDataToDelete.name,
1958 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1969 * Enumerate the elements
1971 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1976 * Obtain the next element
1978 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1981 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
1983 CoTaskMemFree(currentElement.pwcsName);
1987 * We need to Reset the enumeration every time because we delete elements
1988 * and the enumeration could be invalid
1990 IEnumSTATSTG_Reset(elements);
1992 } while ((hr == S_OK) && (destroyHr == S_OK));
1994 IStorage_Release(childStorage);
1995 IEnumSTATSTG_Release(elements);
2000 /*********************************************************************
2004 * Perform the deletion of a stream's data
2007 static HRESULT deleteStreamContents(
2008 StorageBaseImpl *parentStorage,
2009 DirRef indexToDelete,
2010 DirEntry entryDataToDelete)
2014 ULARGE_INTEGER size;
2015 StgStreamImpl *strm, *strm2;
2017 /* Invalidate any open stream objects. */
2018 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2020 if (strm->dirEntry == indexToDelete)
2022 TRACE("Stream deleted %p\n", strm);
2023 strm->parentStorage = NULL;
2024 list_remove(&strm->StrmListEntry);
2028 size.u.HighPart = 0;
2031 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2032 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2042 hr = IStream_SetSize(pis, size);
2050 * Release the stream object.
2052 IStream_Release(pis);
2057 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2061 case DIRENTRY_RELATION_PREVIOUS:
2062 entry->leftChild = new_target;
2064 case DIRENTRY_RELATION_NEXT:
2065 entry->rightChild = new_target;
2067 case DIRENTRY_RELATION_DIR:
2068 entry->dirRootEntry = new_target;
2075 /*************************************************************************
2079 * This method removes a directory entry from its parent storage tree without
2080 * freeing any resources attached to it.
2082 static HRESULT removeFromTree(
2084 DirRef parentStorageIndex,
2085 DirRef deletedIndex)
2089 DirEntry entryToDelete;
2090 DirEntry parentEntry;
2091 DirRef parentEntryRef;
2092 ULONG typeOfRelation;
2094 res = StorageImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2097 * Find the element that links to the one we want to delete.
2099 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2100 &parentEntry, &parentEntryRef, &typeOfRelation);
2105 if (entryToDelete.leftChild != DIRENTRY_NULL)
2108 * Replace the deleted entry with its left child
2110 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2112 res = StorageImpl_WriteDirEntry(
2121 if (entryToDelete.rightChild != DIRENTRY_NULL)
2124 * We need to reinsert the right child somewhere. We already know it and
2125 * its children are greater than everything in the left tree, so we
2126 * insert it at the rightmost point in the left tree.
2128 DirRef newRightChildParent = entryToDelete.leftChild;
2129 DirEntry newRightChildParentEntry;
2133 res = StorageImpl_ReadDirEntry(
2135 newRightChildParent,
2136 &newRightChildParentEntry);
2142 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2143 newRightChildParent = newRightChildParentEntry.rightChild;
2144 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2146 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2148 res = StorageImpl_WriteDirEntry(
2150 newRightChildParent,
2151 &newRightChildParentEntry);
2161 * Replace the deleted entry with its right child
2163 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2165 res = StorageImpl_WriteDirEntry(
2179 /******************************************************************************
2180 * SetElementTimes (IStorage)
2182 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2184 const OLECHAR *pwcsName,/* [string][in] */
2185 const FILETIME *pctime, /* [in] */
2186 const FILETIME *patime, /* [in] */
2187 const FILETIME *pmtime) /* [in] */
2189 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2193 /******************************************************************************
2194 * SetStateBits (IStorage)
2196 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2198 DWORD grfStateBits,/* [in] */
2199 DWORD grfMask) /* [in] */
2201 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2203 if (!This->ancestorStorage)
2204 return STG_E_REVERTED;
2206 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2211 * Virtual function table for the IStorage32Impl class.
2213 static const IStorageVtbl Storage32Impl_Vtbl =
2215 StorageBaseImpl_QueryInterface,
2216 StorageBaseImpl_AddRef,
2217 StorageBaseImpl_Release,
2218 StorageBaseImpl_CreateStream,
2219 StorageBaseImpl_OpenStream,
2220 StorageBaseImpl_CreateStorage,
2221 StorageBaseImpl_OpenStorage,
2222 StorageBaseImpl_CopyTo,
2223 StorageBaseImpl_MoveElementTo,
2226 StorageBaseImpl_EnumElements,
2227 StorageBaseImpl_DestroyElement,
2228 StorageBaseImpl_RenameElement,
2229 StorageBaseImpl_SetElementTimes,
2230 StorageBaseImpl_SetClass,
2231 StorageBaseImpl_SetStateBits,
2235 static HRESULT StorageImpl_Construct(
2242 StorageImpl** result)
2246 DirEntry currentEntry;
2247 BOOL readSuccessful;
2248 DirRef currentEntryRef;
2250 if ( FAILED( validateSTGM(openFlags) ))
2251 return STG_E_INVALIDFLAG;
2253 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2255 return E_OUTOFMEMORY;
2257 memset(This, 0, sizeof(StorageImpl));
2259 list_init(&This->base.strmHead);
2261 list_init(&This->base.storageHead);
2263 This->base.lpVtbl = &Storage32Impl_Vtbl;
2264 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2265 This->base.v_destructor = StorageImpl_Destroy;
2266 This->base.openFlags = (openFlags & ~STGM_CREATE);
2268 This->create = create;
2271 * This is the top-level storage so initialize the ancestor pointer
2274 This->base.ancestorStorage = This;
2276 This->hFile = hFile;
2279 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2280 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2281 if (!This->pwcsName)
2283 hr = STG_E_INSUFFICIENTMEMORY;
2286 strcpyW(This->pwcsName, pwcsName);
2290 * Initialize the big block cache.
2292 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2293 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2294 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2300 if (This->bigBlockFile == 0)
2308 ULARGE_INTEGER size;
2309 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2312 * Initialize all header variables:
2313 * - The big block depot consists of one block and it is at block 0
2314 * - The directory table starts at block 1
2315 * - There is no small block depot
2317 memset( This->bigBlockDepotStart,
2319 sizeof(This->bigBlockDepotStart));
2321 This->bigBlockDepotCount = 1;
2322 This->bigBlockDepotStart[0] = 0;
2323 This->rootStartBlock = 1;
2324 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2325 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2326 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2327 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2328 This->extBigBlockDepotCount = 0;
2330 StorageImpl_SaveFileHeader(This);
2333 * Add one block for the big block depot and one block for the directory table
2335 size.u.HighPart = 0;
2336 size.u.LowPart = This->bigBlockSize * 3;
2337 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2340 * Initialize the big block depot
2342 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2343 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2344 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2345 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2350 * Load the header for the file.
2352 hr = StorageImpl_LoadFileHeader(This);
2361 * There is no block depot cached yet.
2363 This->indexBlockDepotCached = 0xFFFFFFFF;
2366 * Start searching for free blocks with block 0.
2368 This->prevFreeBlock = 0;
2371 * Create the block chain abstractions.
2373 if(!(This->rootBlockChain =
2374 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2376 hr = STG_E_READFAULT;
2380 if(!(This->smallBlockDepotChain =
2381 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2384 hr = STG_E_READFAULT;
2389 * Write the root storage entry (memory only)
2395 * Initialize the directory table
2397 memset(&rootEntry, 0, sizeof(rootEntry));
2398 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2399 sizeof(rootEntry.name)/sizeof(WCHAR) );
2400 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2401 rootEntry.stgType = STGTY_ROOT;
2402 rootEntry.leftChild = DIRENTRY_NULL;
2403 rootEntry.rightChild = DIRENTRY_NULL;
2404 rootEntry.dirRootEntry = DIRENTRY_NULL;
2405 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2406 rootEntry.size.u.HighPart = 0;
2407 rootEntry.size.u.LowPart = 0;
2409 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2413 * Find the ID of the root storage.
2415 currentEntryRef = 0;
2419 readSuccessful = StorageImpl_ReadDirEntry(
2426 if ( (currentEntry.sizeOfNameString != 0 ) &&
2427 (currentEntry.stgType == STGTY_ROOT) )
2429 This->base.storageDirEntry = currentEntryRef;
2435 } while (readSuccessful && (This->base.storageDirEntry == DIRENTRY_NULL) );
2437 if (!readSuccessful)
2439 hr = STG_E_READFAULT;
2444 * Create the block chain abstraction for the small block root chain.
2446 if(!(This->smallBlockRootChain =
2447 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2449 hr = STG_E_READFAULT;
2455 IStorage_Release((IStorage*)This);
2464 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2466 StorageImpl *This = (StorageImpl*) iface;
2467 TRACE("(%p)\n", This);
2469 StorageBaseImpl_DeleteAll(&This->base);
2471 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2473 BlockChainStream_Destroy(This->smallBlockRootChain);
2474 BlockChainStream_Destroy(This->rootBlockChain);
2475 BlockChainStream_Destroy(This->smallBlockDepotChain);
2477 if (This->bigBlockFile)
2478 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2479 HeapFree(GetProcessHeap(), 0, This);
2482 /******************************************************************************
2483 * Storage32Impl_GetNextFreeBigBlock
2485 * Returns the index of the next free big block.
2486 * If the big block depot is filled, this method will enlarge it.
2489 static ULONG StorageImpl_GetNextFreeBigBlock(
2492 ULONG depotBlockIndexPos;
2493 BYTE depotBuffer[BIG_BLOCK_SIZE];
2495 ULONG depotBlockOffset;
2496 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2497 ULONG nextBlockIndex = BLOCK_SPECIAL;
2499 ULONG freeBlock = BLOCK_UNUSED;
2501 depotIndex = This->prevFreeBlock / blocksPerDepot;
2502 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2505 * Scan the entire big block depot until we find a block marked free
2507 while (nextBlockIndex != BLOCK_UNUSED)
2509 if (depotIndex < COUNT_BBDEPOTINHEADER)
2511 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2514 * Grow the primary depot.
2516 if (depotBlockIndexPos == BLOCK_UNUSED)
2518 depotBlockIndexPos = depotIndex*blocksPerDepot;
2521 * Add a block depot.
2523 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2524 This->bigBlockDepotCount++;
2525 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2528 * Flag it as a block depot.
2530 StorageImpl_SetNextBlockInChain(This,
2534 /* Save new header information.
2536 StorageImpl_SaveFileHeader(This);
2541 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2543 if (depotBlockIndexPos == BLOCK_UNUSED)
2546 * Grow the extended depot.
2548 ULONG extIndex = BLOCK_UNUSED;
2549 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2550 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2552 if (extBlockOffset == 0)
2554 /* We need an extended block.
2556 extIndex = Storage32Impl_AddExtBlockDepot(This);
2557 This->extBigBlockDepotCount++;
2558 depotBlockIndexPos = extIndex + 1;
2561 depotBlockIndexPos = depotIndex * blocksPerDepot;
2564 * Add a block depot and mark it in the extended block.
2566 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2567 This->bigBlockDepotCount++;
2568 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2570 /* Flag the block depot.
2572 StorageImpl_SetNextBlockInChain(This,
2576 /* If necessary, flag the extended depot block.
2578 if (extIndex != BLOCK_UNUSED)
2579 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2581 /* Save header information.
2583 StorageImpl_SaveFileHeader(This);
2587 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2591 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2592 ( nextBlockIndex != BLOCK_UNUSED))
2594 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2596 if (nextBlockIndex == BLOCK_UNUSED)
2598 freeBlock = (depotIndex * blocksPerDepot) +
2599 (depotBlockOffset/sizeof(ULONG));
2602 depotBlockOffset += sizeof(ULONG);
2607 depotBlockOffset = 0;
2611 * make sure that the block physically exists before using it
2613 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2615 This->prevFreeBlock = freeBlock;
2620 /******************************************************************************
2621 * Storage32Impl_AddBlockDepot
2623 * This will create a depot block, essentially it is a block initialized
2626 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2628 BYTE blockBuffer[BIG_BLOCK_SIZE];
2631 * Initialize blocks as free
2633 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2634 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2637 /******************************************************************************
2638 * Storage32Impl_GetExtDepotBlock
2640 * Returns the index of the block that corresponds to the specified depot
2641 * index. This method is only for depot indexes equal or greater than
2642 * COUNT_BBDEPOTINHEADER.
2644 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2646 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2647 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2648 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2649 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2650 ULONG blockIndex = BLOCK_UNUSED;
2651 ULONG extBlockIndex = This->extBigBlockDepotStart;
2653 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2655 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2656 return BLOCK_UNUSED;
2658 while (extBlockCount > 0)
2660 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2664 if (extBlockIndex != BLOCK_UNUSED)
2665 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2666 extBlockOffset * sizeof(ULONG), &blockIndex);
2671 /******************************************************************************
2672 * Storage32Impl_SetExtDepotBlock
2674 * Associates the specified block index to the specified depot index.
2675 * This method is only for depot indexes equal or greater than
2676 * COUNT_BBDEPOTINHEADER.
2678 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2680 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2681 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2682 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2683 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2684 ULONG extBlockIndex = This->extBigBlockDepotStart;
2686 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2688 while (extBlockCount > 0)
2690 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2694 if (extBlockIndex != BLOCK_UNUSED)
2696 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2697 extBlockOffset * sizeof(ULONG),
2702 /******************************************************************************
2703 * Storage32Impl_AddExtBlockDepot
2705 * Creates an extended depot block.
2707 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2709 ULONG numExtBlocks = This->extBigBlockDepotCount;
2710 ULONG nextExtBlock = This->extBigBlockDepotStart;
2711 BYTE depotBuffer[BIG_BLOCK_SIZE];
2712 ULONG index = BLOCK_UNUSED;
2713 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2714 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2715 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2717 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2718 blocksPerDepotBlock;
2720 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2723 * The first extended block.
2725 This->extBigBlockDepotStart = index;
2731 * Follow the chain to the last one.
2733 for (i = 0; i < (numExtBlocks - 1); i++)
2735 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2739 * Add the new extended block to the chain.
2741 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2746 * Initialize this block.
2748 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2749 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2754 /******************************************************************************
2755 * Storage32Impl_FreeBigBlock
2757 * This method will flag the specified block as free in the big block depot.
2759 static void StorageImpl_FreeBigBlock(
2763 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2765 if (blockIndex < This->prevFreeBlock)
2766 This->prevFreeBlock = blockIndex;
2769 /************************************************************************
2770 * Storage32Impl_GetNextBlockInChain
2772 * This method will retrieve the block index of the next big block in
2775 * Params: This - Pointer to the Storage object.
2776 * blockIndex - Index of the block to retrieve the chain
2778 * nextBlockIndex - receives the return value.
2780 * Returns: This method returns the index of the next block in the chain.
2781 * It will return the constants:
2782 * BLOCK_SPECIAL - If the block given was not part of a
2784 * BLOCK_END_OF_CHAIN - If the block given was the last in
2786 * BLOCK_UNUSED - If the block given was not past of a chain
2788 * BLOCK_EXTBBDEPOT - This block is part of the extended
2791 * See Windows documentation for more details on IStorage methods.
2793 static HRESULT StorageImpl_GetNextBlockInChain(
2796 ULONG* nextBlockIndex)
2798 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2799 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2800 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2801 BYTE depotBuffer[BIG_BLOCK_SIZE];
2803 ULONG depotBlockIndexPos;
2806 *nextBlockIndex = BLOCK_SPECIAL;
2808 if(depotBlockCount >= This->bigBlockDepotCount)
2810 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2811 This->bigBlockDepotCount);
2812 return STG_E_READFAULT;
2816 * Cache the currently accessed depot block.
2818 if (depotBlockCount != This->indexBlockDepotCached)
2820 This->indexBlockDepotCached = depotBlockCount;
2822 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2824 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2829 * We have to look in the extended depot.
2831 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2834 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2837 return STG_E_READFAULT;
2839 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2841 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2842 This->blockDepotCached[index] = *nextBlockIndex;
2846 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2851 /******************************************************************************
2852 * Storage32Impl_GetNextExtendedBlock
2854 * Given an extended block this method will return the next extended block.
2857 * The last ULONG of an extended block is the block index of the next
2858 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2862 * - The index of the next extended block
2863 * - BLOCK_UNUSED: there is no next extended block.
2864 * - Any other return values denotes failure.
2866 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2868 ULONG nextBlockIndex = BLOCK_SPECIAL;
2869 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2871 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
2874 return nextBlockIndex;
2877 /******************************************************************************
2878 * Storage32Impl_SetNextBlockInChain
2880 * This method will write the index of the specified block's next block
2881 * in the big block depot.
2883 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2886 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2887 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2888 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2891 static void StorageImpl_SetNextBlockInChain(
2896 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2897 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2898 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2899 ULONG depotBlockIndexPos;
2901 assert(depotBlockCount < This->bigBlockDepotCount);
2902 assert(blockIndex != nextBlock);
2904 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2906 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2911 * We have to look in the extended depot.
2913 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2916 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
2919 * Update the cached block depot, if necessary.
2921 if (depotBlockCount == This->indexBlockDepotCached)
2923 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2927 /******************************************************************************
2928 * Storage32Impl_LoadFileHeader
2930 * This method will read in the file header, i.e. big block index -1.
2932 static HRESULT StorageImpl_LoadFileHeader(
2935 HRESULT hr = STG_E_FILENOTFOUND;
2936 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2942 * Get a pointer to the big block of data containing the header.
2944 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2947 * Extract the information from the header.
2952 * Check for the "magic number" signature and return an error if it is not
2955 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2957 return STG_E_OLDFORMAT;
2960 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2962 return STG_E_INVALIDHEADER;
2965 StorageUtl_ReadWord(
2967 OFFSET_BIGBLOCKSIZEBITS,
2968 &This->bigBlockSizeBits);
2970 StorageUtl_ReadWord(
2972 OFFSET_SMALLBLOCKSIZEBITS,
2973 &This->smallBlockSizeBits);
2975 StorageUtl_ReadDWord(
2977 OFFSET_BBDEPOTCOUNT,
2978 &This->bigBlockDepotCount);
2980 StorageUtl_ReadDWord(
2982 OFFSET_ROOTSTARTBLOCK,
2983 &This->rootStartBlock);
2985 StorageUtl_ReadDWord(
2987 OFFSET_SBDEPOTSTART,
2988 &This->smallBlockDepotStart);
2990 StorageUtl_ReadDWord(
2992 OFFSET_EXTBBDEPOTSTART,
2993 &This->extBigBlockDepotStart);
2995 StorageUtl_ReadDWord(
2997 OFFSET_EXTBBDEPOTCOUNT,
2998 &This->extBigBlockDepotCount);
3000 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3002 StorageUtl_ReadDWord(
3004 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3005 &(This->bigBlockDepotStart[index]));
3009 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3011 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3012 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3015 * Right now, the code is making some assumptions about the size of the
3016 * blocks, just make sure they are what we're expecting.
3018 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3019 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3021 WARN("Broken OLE storage file\n");
3022 hr = STG_E_INVALIDHEADER;
3031 /******************************************************************************
3032 * Storage32Impl_SaveFileHeader
3034 * This method will save to the file the header, i.e. big block -1.
3036 static void StorageImpl_SaveFileHeader(
3039 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3044 * Get a pointer to the big block of data containing the header.
3046 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3049 * If the block read failed, the file is probably new.
3054 * Initialize for all unknown fields.
3056 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3059 * Initialize the magic number.
3061 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3064 * And a bunch of things we don't know what they mean
3066 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3067 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3068 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3069 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3073 * Write the information to the header.
3075 StorageUtl_WriteWord(
3077 OFFSET_BIGBLOCKSIZEBITS,
3078 This->bigBlockSizeBits);
3080 StorageUtl_WriteWord(
3082 OFFSET_SMALLBLOCKSIZEBITS,
3083 This->smallBlockSizeBits);
3085 StorageUtl_WriteDWord(
3087 OFFSET_BBDEPOTCOUNT,
3088 This->bigBlockDepotCount);
3090 StorageUtl_WriteDWord(
3092 OFFSET_ROOTSTARTBLOCK,
3093 This->rootStartBlock);
3095 StorageUtl_WriteDWord(
3097 OFFSET_SBDEPOTSTART,
3098 This->smallBlockDepotStart);
3100 StorageUtl_WriteDWord(
3102 OFFSET_SBDEPOTCOUNT,
3103 This->smallBlockDepotChain ?
3104 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3106 StorageUtl_WriteDWord(
3108 OFFSET_EXTBBDEPOTSTART,
3109 This->extBigBlockDepotStart);
3111 StorageUtl_WriteDWord(
3113 OFFSET_EXTBBDEPOTCOUNT,
3114 This->extBigBlockDepotCount);
3116 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3118 StorageUtl_WriteDWord(
3120 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3121 (This->bigBlockDepotStart[index]));
3125 * Write the big block back to the file.
3127 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3130 /******************************************************************************
3131 * StorageImpl_ReadRawDirEntry
3133 * This method will read the raw data from a directory entry in the file.
3135 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3137 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3139 ULARGE_INTEGER offset;
3143 offset.u.HighPart = 0;
3144 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3146 hr = BlockChainStream_ReadAt(
3147 This->rootBlockChain,
3156 /******************************************************************************
3157 * StorageImpl_WriteRawDirEntry
3159 * This method will write the raw data from a directory entry in the file.
3161 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3163 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3165 ULARGE_INTEGER offset;
3169 offset.u.HighPart = 0;
3170 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3172 hr = BlockChainStream_WriteAt(
3173 This->rootBlockChain,
3182 /******************************************************************************
3185 * Update raw directory entry data from the fields in newData.
3187 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3189 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3191 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3194 buffer + OFFSET_PS_NAME,
3196 DIRENTRY_NAME_BUFFER_LEN );
3198 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3200 StorageUtl_WriteWord(
3202 OFFSET_PS_NAMELENGTH,
3203 newData->sizeOfNameString);
3205 StorageUtl_WriteDWord(
3207 OFFSET_PS_LEFTCHILD,
3208 newData->leftChild);
3210 StorageUtl_WriteDWord(
3212 OFFSET_PS_RIGHTCHILD,
3213 newData->rightChild);
3215 StorageUtl_WriteDWord(
3218 newData->dirRootEntry);
3220 StorageUtl_WriteGUID(
3225 StorageUtl_WriteDWord(
3228 newData->ctime.dwLowDateTime);
3230 StorageUtl_WriteDWord(
3232 OFFSET_PS_CTIMEHIGH,
3233 newData->ctime.dwHighDateTime);
3235 StorageUtl_WriteDWord(
3238 newData->mtime.dwLowDateTime);
3240 StorageUtl_WriteDWord(
3242 OFFSET_PS_MTIMEHIGH,
3243 newData->ctime.dwHighDateTime);
3245 StorageUtl_WriteDWord(
3247 OFFSET_PS_STARTBLOCK,
3248 newData->startingBlock);
3250 StorageUtl_WriteDWord(
3253 newData->size.u.LowPart);
3256 /******************************************************************************
3257 * Storage32Impl_ReadDirEntry
3259 * This method will read the specified directory entry.
3261 BOOL StorageImpl_ReadDirEntry(
3266 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3269 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3271 if (SUCCEEDED(readRes))
3273 memset(buffer->name, 0, sizeof(buffer->name));
3276 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3277 DIRENTRY_NAME_BUFFER_LEN );
3278 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3280 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3282 StorageUtl_ReadWord(
3284 OFFSET_PS_NAMELENGTH,
3285 &buffer->sizeOfNameString);
3287 StorageUtl_ReadDWord(
3289 OFFSET_PS_LEFTCHILD,
3290 &buffer->leftChild);
3292 StorageUtl_ReadDWord(
3294 OFFSET_PS_RIGHTCHILD,
3295 &buffer->rightChild);
3297 StorageUtl_ReadDWord(
3300 &buffer->dirRootEntry);
3302 StorageUtl_ReadGUID(
3307 StorageUtl_ReadDWord(
3310 &buffer->ctime.dwLowDateTime);
3312 StorageUtl_ReadDWord(
3314 OFFSET_PS_CTIMEHIGH,
3315 &buffer->ctime.dwHighDateTime);
3317 StorageUtl_ReadDWord(
3320 &buffer->mtime.dwLowDateTime);
3322 StorageUtl_ReadDWord(
3324 OFFSET_PS_MTIMEHIGH,
3325 &buffer->mtime.dwHighDateTime);
3327 StorageUtl_ReadDWord(
3329 OFFSET_PS_STARTBLOCK,
3330 &buffer->startingBlock);
3332 StorageUtl_ReadDWord(
3335 &buffer->size.u.LowPart);
3337 buffer->size.u.HighPart = 0;
3340 return SUCCEEDED(readRes) ? TRUE : FALSE;
3343 /*********************************************************************
3344 * Write the specified directory entry to the file
3346 BOOL StorageImpl_WriteDirEntry(
3349 const DirEntry* buffer)
3351 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3354 UpdateRawDirEntry(currentEntry, buffer);
3356 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3357 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3360 static BOOL StorageImpl_ReadBigBlock(
3365 ULARGE_INTEGER ulOffset;
3368 ulOffset.u.HighPart = 0;
3369 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3371 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3372 return (read == This->bigBlockSize);
3375 static BOOL StorageImpl_ReadDWordFromBigBlock(
3381 ULARGE_INTEGER ulOffset;
3385 ulOffset.u.HighPart = 0;
3386 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3387 ulOffset.u.LowPart += offset;
3389 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3390 *value = lendian32toh(tmp);
3391 return (read == sizeof(DWORD));
3394 static BOOL StorageImpl_WriteBigBlock(
3399 ULARGE_INTEGER ulOffset;
3402 ulOffset.u.HighPart = 0;
3403 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3405 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3406 return (wrote == This->bigBlockSize);
3409 static BOOL StorageImpl_WriteDWordToBigBlock(
3415 ULARGE_INTEGER ulOffset;
3418 ulOffset.u.HighPart = 0;
3419 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3420 ulOffset.u.LowPart += offset;
3422 value = htole32(value);
3423 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3424 return (wrote == sizeof(DWORD));
3427 /******************************************************************************
3428 * Storage32Impl_SmallBlocksToBigBlocks
3430 * This method will convert a small block chain to a big block chain.
3431 * The small block chain will be destroyed.
3433 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3435 SmallBlockChainStream** ppsbChain)
3437 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3438 ULARGE_INTEGER size, offset;
3439 ULONG cbRead, cbWritten;
3440 ULARGE_INTEGER cbTotalRead;
3441 DirRef streamEntryRef;
3442 HRESULT resWrite = S_OK;
3444 DirEntry streamEntry;
3446 BlockChainStream *bbTempChain = NULL;
3447 BlockChainStream *bigBlockChain = NULL;
3450 * Create a temporary big block chain that doesn't have
3451 * an associated directory entry. This temporary chain will be
3452 * used to copy data from small blocks to big blocks.
3454 bbTempChain = BlockChainStream_Construct(This,
3457 if(!bbTempChain) return NULL;
3459 * Grow the big block chain.
3461 size = SmallBlockChainStream_GetSize(*ppsbChain);
3462 BlockChainStream_SetSize(bbTempChain, size);
3465 * Copy the contents of the small block chain to the big block chain
3466 * by small block size increments.
3468 offset.u.LowPart = 0;
3469 offset.u.HighPart = 0;
3470 cbTotalRead.QuadPart = 0;
3472 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3475 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3477 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3480 if (FAILED(resRead))
3485 cbTotalRead.QuadPart += cbRead;
3487 resWrite = BlockChainStream_WriteAt(bbTempChain,
3493 if (FAILED(resWrite))
3496 offset.u.LowPart += cbRead;
3498 } while (cbTotalRead.QuadPart < size.QuadPart);
3499 HeapFree(GetProcessHeap(),0,buffer);
3501 size.u.HighPart = 0;
3504 if (FAILED(resRead) || FAILED(resWrite))
3506 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3507 BlockChainStream_SetSize(bbTempChain, size);
3508 BlockChainStream_Destroy(bbTempChain);
3513 * Destroy the small block chain.
3515 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3516 SmallBlockChainStream_SetSize(*ppsbChain, size);
3517 SmallBlockChainStream_Destroy(*ppsbChain);
3521 * Change the directory entry. This chain is now a big block chain
3522 * and it doesn't reside in the small blocks chain anymore.
3524 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3526 streamEntry.startingBlock = bbHeadOfChain;
3528 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3531 * Destroy the temporary entryless big block chain.
3532 * Create a new big block chain associated with this entry.
3534 BlockChainStream_Destroy(bbTempChain);
3535 bigBlockChain = BlockChainStream_Construct(This,
3539 return bigBlockChain;
3542 /******************************************************************************
3543 * Storage32Impl_BigBlocksToSmallBlocks
3545 * This method will convert a big block chain to a small block chain.
3546 * The big block chain will be destroyed on success.
3548 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3550 BlockChainStream** ppbbChain)
3552 ULARGE_INTEGER size, offset, cbTotalRead;
3553 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3554 DirRef streamEntryRef;
3555 HRESULT resWrite = S_OK, resRead;
3556 DirEntry streamEntry;
3558 SmallBlockChainStream* sbTempChain;
3560 TRACE("%p %p\n", This, ppbbChain);
3562 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3568 size = BlockChainStream_GetSize(*ppbbChain);
3569 SmallBlockChainStream_SetSize(sbTempChain, size);
3571 offset.u.HighPart = 0;
3572 offset.u.LowPart = 0;
3573 cbTotalRead.QuadPart = 0;
3574 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3577 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3578 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3586 cbTotalRead.QuadPart += cbRead;
3588 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3589 cbRead, buffer, &cbWritten);
3591 if(FAILED(resWrite))
3594 offset.u.LowPart += cbRead;
3596 }while(cbTotalRead.QuadPart < size.QuadPart);
3597 HeapFree(GetProcessHeap(), 0, buffer);
3599 size.u.HighPart = 0;
3602 if(FAILED(resRead) || FAILED(resWrite))
3604 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3605 SmallBlockChainStream_SetSize(sbTempChain, size);
3606 SmallBlockChainStream_Destroy(sbTempChain);
3610 /* destroy the original big block chain */
3611 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3612 BlockChainStream_SetSize(*ppbbChain, size);
3613 BlockChainStream_Destroy(*ppbbChain);
3616 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3617 streamEntry.startingBlock = sbHeadOfChain;
3618 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3620 SmallBlockChainStream_Destroy(sbTempChain);
3621 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3624 static void StorageInternalImpl_Invalidate( StorageInternalImpl *This )
3626 if (This->base.ancestorStorage)
3628 TRACE("Storage invalidated (stg=%p)\n", This);
3630 This->base.ancestorStorage = NULL;
3632 StorageBaseImpl_DeleteAll(&This->base);
3634 list_remove(&This->ParentListEntry);
3638 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3640 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3642 StorageInternalImpl_Invalidate(This);
3644 HeapFree(GetProcessHeap(), 0, This);
3647 /******************************************************************************
3649 ** Storage32InternalImpl_Commit
3652 static HRESULT WINAPI StorageInternalImpl_Commit(
3654 DWORD grfCommitFlags) /* [in] */
3656 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
3660 /******************************************************************************
3662 ** Storage32InternalImpl_Revert
3665 static HRESULT WINAPI StorageInternalImpl_Revert(
3668 FIXME("(%p): stub\n", iface);
3672 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3674 IStorage_Release((IStorage*)This->parentStorage);
3675 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3676 HeapFree(GetProcessHeap(), 0, This);
3679 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3680 IEnumSTATSTG* iface,
3684 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3687 return E_INVALIDARG;
3691 if (IsEqualGUID(&IID_IUnknown, riid) ||
3692 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3695 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3699 return E_NOINTERFACE;
3702 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3703 IEnumSTATSTG* iface)
3705 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3706 return InterlockedIncrement(&This->ref);
3709 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3710 IEnumSTATSTG* iface)
3712 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3716 newRef = InterlockedDecrement(&This->ref);
3720 IEnumSTATSTGImpl_Destroy(This);
3726 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3727 IEnumSTATSTG* iface,
3730 ULONG* pceltFetched)
3732 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3734 DirEntry currentEntry;
3735 STATSTG* currentReturnStruct = rgelt;
3736 ULONG objectFetched = 0;
3737 DirRef currentSearchNode;
3739 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3740 return E_INVALIDARG;
3743 * To avoid the special case, get another pointer to a ULONG value if
3744 * the caller didn't supply one.
3746 if (pceltFetched==0)
3747 pceltFetched = &objectFetched;
3750 * Start the iteration, we will iterate until we hit the end of the
3751 * linked list or until we hit the number of items to iterate through
3756 * Start with the node at the top of the stack.
3758 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3760 while ( ( *pceltFetched < celt) &&
3761 ( currentSearchNode!=DIRENTRY_NULL) )
3764 * Remove the top node from the stack
3766 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3769 * Read the entry from the storage.
3771 StorageImpl_ReadDirEntry(This->parentStorage,
3776 * Copy the information to the return buffer.
3778 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
3779 currentReturnStruct,
3784 * Step to the next item in the iteration
3787 currentReturnStruct++;
3790 * Push the next search node in the search stack.
3792 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
3795 * continue the iteration.
3797 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3800 if (*pceltFetched == celt)
3807 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3808 IEnumSTATSTG* iface,
3811 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3813 DirEntry currentEntry;
3814 ULONG objectFetched = 0;
3815 DirRef currentSearchNode;
3818 * Start with the node at the top of the stack.
3820 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3822 while ( (objectFetched < celt) &&
3823 (currentSearchNode!=DIRENTRY_NULL) )
3826 * Remove the top node from the stack
3828 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3831 * Read the entry from the storage.
3833 StorageImpl_ReadDirEntry(This->parentStorage,
3838 * Step to the next item in the iteration
3843 * Push the next search node in the search stack.
3845 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
3848 * continue the iteration.
3850 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3853 if (objectFetched == celt)
3859 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3860 IEnumSTATSTG* iface)
3862 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3864 DirEntry storageEntry;
3865 BOOL readSuccessful;
3868 * Re-initialize the search stack to an empty stack
3870 This->stackSize = 0;
3873 * Read the storage entry from the top-level storage.
3875 readSuccessful = StorageImpl_ReadDirEntry(
3876 This->parentStorage,
3877 This->storageDirEntry,
3882 assert(storageEntry.sizeOfNameString!=0);
3885 * Push the search node in the search stack.
3887 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.dirRootEntry);
3893 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3894 IEnumSTATSTG* iface,
3895 IEnumSTATSTG** ppenum)
3897 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3899 IEnumSTATSTGImpl* newClone;
3902 * Perform a sanity check on the parameters.
3905 return E_INVALIDARG;
3907 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3908 This->storageDirEntry);
3912 * The new clone enumeration must point to the same current node as
3915 newClone->stackSize = This->stackSize ;
3916 newClone->stackMaxSize = This->stackMaxSize ;
3917 newClone->stackToVisit =
3918 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3921 newClone->stackToVisit,
3923 sizeof(DirRef) * newClone->stackSize);
3925 *ppenum = (IEnumSTATSTG*)newClone;
3928 * Don't forget to nail down a reference to the clone before
3931 IEnumSTATSTGImpl_AddRef(*ppenum);
3936 static void IEnumSTATSTGImpl_PushSearchNode(
3937 IEnumSTATSTGImpl* This,
3940 DirEntry storageEntry;
3941 BOOL readSuccessful;
3944 * First, make sure we're not trying to push an unexisting node.
3946 if (nodeToPush==DIRENTRY_NULL)
3950 * First push the node to the stack
3952 if (This->stackSize == This->stackMaxSize)
3954 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3956 This->stackToVisit = HeapReAlloc(
3960 sizeof(DirRef) * This->stackMaxSize);
3963 This->stackToVisit[This->stackSize] = nodeToPush;
3967 * Read the storage entry from the top-level storage.
3969 readSuccessful = StorageImpl_ReadDirEntry(
3970 This->parentStorage,
3976 assert(storageEntry.sizeOfNameString!=0);
3979 * Push the previous search node in the search stack.
3981 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.leftChild);
3985 static DirRef IEnumSTATSTGImpl_PopSearchNode(
3986 IEnumSTATSTGImpl* This,
3991 if (This->stackSize == 0)
3992 return DIRENTRY_NULL;
3994 topNode = This->stackToVisit[This->stackSize-1];
4003 * Virtual function table for the IEnumSTATSTGImpl class.
4005 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4007 IEnumSTATSTGImpl_QueryInterface,
4008 IEnumSTATSTGImpl_AddRef,
4009 IEnumSTATSTGImpl_Release,
4010 IEnumSTATSTGImpl_Next,
4011 IEnumSTATSTGImpl_Skip,
4012 IEnumSTATSTGImpl_Reset,
4013 IEnumSTATSTGImpl_Clone
4016 /******************************************************************************
4017 ** IEnumSTATSTGImpl implementation
4020 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4021 StorageImpl* parentStorage,
4022 DirRef storageDirEntry)
4024 IEnumSTATSTGImpl* newEnumeration;
4026 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4028 if (newEnumeration!=0)
4031 * Set-up the virtual function table and reference count.
4033 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4034 newEnumeration->ref = 0;
4037 * We want to nail-down the reference to the storage in case the
4038 * enumeration out-lives the storage in the client application.
4040 newEnumeration->parentStorage = parentStorage;
4041 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4043 newEnumeration->storageDirEntry = storageDirEntry;
4046 * Initialize the search stack
4048 newEnumeration->stackSize = 0;
4049 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4050 newEnumeration->stackToVisit =
4051 HeapAlloc(GetProcessHeap(), 0, sizeof(DirRef)*ENUMSTATSGT_SIZE_INCREMENT);
4054 * Make sure the current node of the iterator is the first one.
4056 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4059 return newEnumeration;
4063 * Virtual function table for the Storage32InternalImpl class.
4065 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4067 StorageBaseImpl_QueryInterface,
4068 StorageBaseImpl_AddRef,
4069 StorageBaseImpl_Release,
4070 StorageBaseImpl_CreateStream,
4071 StorageBaseImpl_OpenStream,
4072 StorageBaseImpl_CreateStorage,
4073 StorageBaseImpl_OpenStorage,
4074 StorageBaseImpl_CopyTo,
4075 StorageBaseImpl_MoveElementTo,
4076 StorageInternalImpl_Commit,
4077 StorageInternalImpl_Revert,
4078 StorageBaseImpl_EnumElements,
4079 StorageBaseImpl_DestroyElement,
4080 StorageBaseImpl_RenameElement,
4081 StorageBaseImpl_SetElementTimes,
4082 StorageBaseImpl_SetClass,
4083 StorageBaseImpl_SetStateBits,
4084 StorageBaseImpl_Stat
4087 /******************************************************************************
4088 ** Storage32InternalImpl implementation
4091 static StorageInternalImpl* StorageInternalImpl_Construct(
4092 StorageImpl* ancestorStorage,
4094 DirRef storageDirEntry)
4096 StorageInternalImpl* newStorage;
4098 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4102 list_init(&newStorage->base.strmHead);
4104 list_init(&newStorage->base.storageHead);
4107 * Initialize the virtual function table.
4109 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4110 newStorage->base.v_destructor = StorageInternalImpl_Destroy;
4111 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4114 * Keep the ancestor storage pointer but do not nail a reference to it.
4116 newStorage->base.ancestorStorage = ancestorStorage;
4119 * Keep a reference to the directory entry of this storage
4121 newStorage->base.storageDirEntry = storageDirEntry;
4129 /******************************************************************************
4130 ** StorageUtl implementation
4133 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4137 memcpy(&tmp, buffer+offset, sizeof(WORD));
4138 *value = lendian16toh(tmp);
4141 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4143 value = htole16(value);
4144 memcpy(buffer+offset, &value, sizeof(WORD));
4147 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4151 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4152 *value = lendian32toh(tmp);
4155 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4157 value = htole32(value);
4158 memcpy(buffer+offset, &value, sizeof(DWORD));
4161 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4162 ULARGE_INTEGER* value)
4164 #ifdef WORDS_BIGENDIAN
4167 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4168 value->u.LowPart = htole32(tmp.u.HighPart);
4169 value->u.HighPart = htole32(tmp.u.LowPart);
4171 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4175 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4176 const ULARGE_INTEGER *value)
4178 #ifdef WORDS_BIGENDIAN
4181 tmp.u.LowPart = htole32(value->u.HighPart);
4182 tmp.u.HighPart = htole32(value->u.LowPart);
4183 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4185 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4189 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4191 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4192 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4193 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4195 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4198 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4200 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4201 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4202 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4204 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4207 void StorageUtl_CopyDirEntryToSTATSTG(
4208 StorageImpl* storage,
4209 STATSTG* destination,
4210 const DirEntry* source,
4215 if (source->stgType == STGTY_ROOT)
4217 /* replace the name of root entry (often "Root Entry") by the file name */
4218 entryName = storage->filename;
4222 entryName = source->name;
4226 * The copy of the string occurs only when the flag is not set
4228 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4229 (entryName == NULL) ||
4230 (entryName[0] == 0) )
4232 destination->pwcsName = 0;
4236 destination->pwcsName =
4237 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
4239 strcpyW(destination->pwcsName, entryName);
4242 switch (source->stgType)
4246 destination->type = STGTY_STORAGE;
4249 destination->type = STGTY_STREAM;
4252 destination->type = STGTY_STREAM;
4256 destination->cbSize = source->size;
4258 currentReturnStruct->mtime = {0}; TODO
4259 currentReturnStruct->ctime = {0};
4260 currentReturnStruct->atime = {0};
4262 destination->grfMode = 0;
4263 destination->grfLocksSupported = 0;
4264 destination->clsid = source->clsid;
4265 destination->grfStateBits = 0;
4266 destination->reserved = 0;
4269 /******************************************************************************
4270 ** BlockChainStream implementation
4273 BlockChainStream* BlockChainStream_Construct(
4274 StorageImpl* parentStorage,
4275 ULONG* headOfStreamPlaceHolder,
4278 BlockChainStream* newStream;
4281 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4283 newStream->parentStorage = parentStorage;
4284 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4285 newStream->ownerDirEntry = dirEntry;
4286 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4287 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4288 newStream->numBlocks = 0;
4290 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4292 while (blockIndex != BLOCK_END_OF_CHAIN)
4294 newStream->numBlocks++;
4295 newStream->tailIndex = blockIndex;
4297 if(FAILED(StorageImpl_GetNextBlockInChain(
4302 HeapFree(GetProcessHeap(), 0, newStream);
4310 void BlockChainStream_Destroy(BlockChainStream* This)
4312 HeapFree(GetProcessHeap(), 0, This);
4315 /******************************************************************************
4316 * BlockChainStream_GetHeadOfChain
4318 * Returns the head of this stream chain.
4319 * Some special chains don't have directory entries, their heads are kept in
4320 * This->headOfStreamPlaceHolder.
4323 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4325 DirEntry chainEntry;
4326 BOOL readSuccessful;
4328 if (This->headOfStreamPlaceHolder != 0)
4329 return *(This->headOfStreamPlaceHolder);
4331 if (This->ownerDirEntry != DIRENTRY_NULL)
4333 readSuccessful = StorageImpl_ReadDirEntry(
4334 This->parentStorage,
4335 This->ownerDirEntry,
4340 return chainEntry.startingBlock;
4344 return BLOCK_END_OF_CHAIN;
4347 /******************************************************************************
4348 * BlockChainStream_GetCount
4350 * Returns the number of blocks that comprises this chain.
4351 * This is not the size of the stream as the last block may not be full!
4354 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4359 blockIndex = BlockChainStream_GetHeadOfChain(This);
4361 while (blockIndex != BLOCK_END_OF_CHAIN)
4365 if(FAILED(StorageImpl_GetNextBlockInChain(
4366 This->parentStorage,
4375 /******************************************************************************
4376 * BlockChainStream_ReadAt
4378 * Reads a specified number of bytes from this chain at the specified offset.
4379 * bytesRead may be NULL.
4380 * Failure will be returned if the specified number of bytes has not been read.
4382 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4383 ULARGE_INTEGER offset,
4388 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4389 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4390 ULONG bytesToReadInBuffer;
4394 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4397 * Find the first block in the stream that contains part of the buffer.
4399 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4400 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4401 (blockNoInSequence < This->lastBlockNoInSequence) )
4403 blockIndex = BlockChainStream_GetHeadOfChain(This);
4404 This->lastBlockNoInSequence = blockNoInSequence;
4408 ULONG temp = blockNoInSequence;
4410 blockIndex = This->lastBlockNoInSequenceIndex;
4411 blockNoInSequence -= This->lastBlockNoInSequence;
4412 This->lastBlockNoInSequence = temp;
4415 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4417 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4418 return STG_E_DOCFILECORRUPT;
4419 blockNoInSequence--;
4422 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4423 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4425 This->lastBlockNoInSequenceIndex = blockIndex;
4428 * Start reading the buffer.
4431 bufferWalker = buffer;
4433 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4435 ULARGE_INTEGER ulOffset;
4438 * Calculate how many bytes we can copy from this big block.
4440 bytesToReadInBuffer =
4441 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4443 TRACE("block %i\n",blockIndex);
4444 ulOffset.u.HighPart = 0;
4445 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4448 StorageImpl_ReadAt(This->parentStorage,
4451 bytesToReadInBuffer,
4454 * Step to the next big block.
4456 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4457 return STG_E_DOCFILECORRUPT;
4459 bufferWalker += bytesReadAt;
4460 size -= bytesReadAt;
4461 *bytesRead += bytesReadAt;
4462 offsetInBlock = 0; /* There is no offset on the next block */
4464 if (bytesToReadInBuffer != bytesReadAt)
4468 return (size == 0) ? S_OK : STG_E_READFAULT;
4471 /******************************************************************************
4472 * BlockChainStream_WriteAt
4474 * Writes the specified number of bytes to this chain at the specified offset.
4475 * Will fail if not all specified number of bytes have been written.
4477 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4478 ULARGE_INTEGER offset,
4481 ULONG* bytesWritten)
4483 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4484 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4487 const BYTE* bufferWalker;
4490 * Find the first block in the stream that contains part of the buffer.
4492 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4493 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4494 (blockNoInSequence < This->lastBlockNoInSequence) )
4496 blockIndex = BlockChainStream_GetHeadOfChain(This);
4497 This->lastBlockNoInSequence = blockNoInSequence;
4501 ULONG temp = blockNoInSequence;
4503 blockIndex = This->lastBlockNoInSequenceIndex;
4504 blockNoInSequence -= This->lastBlockNoInSequence;
4505 This->lastBlockNoInSequence = temp;
4508 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4510 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4512 return STG_E_DOCFILECORRUPT;
4513 blockNoInSequence--;
4516 This->lastBlockNoInSequenceIndex = blockIndex;
4518 /* BlockChainStream_SetSize should have already been called to ensure we have
4519 * enough blocks in the chain to write into */
4520 if (blockIndex == BLOCK_END_OF_CHAIN)
4522 ERR("not enough blocks in chain to write data\n");
4523 return STG_E_DOCFILECORRUPT;
4527 bufferWalker = buffer;
4529 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4531 ULARGE_INTEGER ulOffset;
4532 DWORD bytesWrittenAt;
4534 * Calculate how many bytes we can copy from this big block.
4537 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4539 TRACE("block %i\n",blockIndex);
4540 ulOffset.u.HighPart = 0;
4541 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4544 StorageImpl_WriteAt(This->parentStorage,
4551 * Step to the next big block.
4553 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4555 return STG_E_DOCFILECORRUPT;
4557 bufferWalker += bytesWrittenAt;
4558 size -= bytesWrittenAt;
4559 *bytesWritten += bytesWrittenAt;
4560 offsetInBlock = 0; /* There is no offset on the next block */
4562 if (bytesWrittenAt != bytesToWrite)
4566 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4569 /******************************************************************************
4570 * BlockChainStream_Shrink
4572 * Shrinks this chain in the big block depot.
4574 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4575 ULARGE_INTEGER newSize)
4577 ULONG blockIndex, extraBlock;
4582 * Reset the last accessed block cache.
4584 This->lastBlockNoInSequence = 0xFFFFFFFF;
4585 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4588 * Figure out how many blocks are needed to contain the new size
4590 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4592 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4595 blockIndex = BlockChainStream_GetHeadOfChain(This);
4598 * Go to the new end of chain
4600 while (count < numBlocks)
4602 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4608 /* Get the next block before marking the new end */
4609 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4613 /* Mark the new end of chain */
4614 StorageImpl_SetNextBlockInChain(
4615 This->parentStorage,
4617 BLOCK_END_OF_CHAIN);
4619 This->tailIndex = blockIndex;
4620 This->numBlocks = numBlocks;
4623 * Mark the extra blocks as free
4625 while (extraBlock != BLOCK_END_OF_CHAIN)
4627 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4630 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4631 extraBlock = blockIndex;
4637 /******************************************************************************
4638 * BlockChainStream_Enlarge
4640 * Grows this chain in the big block depot.
4642 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4643 ULARGE_INTEGER newSize)
4645 ULONG blockIndex, currentBlock;
4647 ULONG oldNumBlocks = 0;
4649 blockIndex = BlockChainStream_GetHeadOfChain(This);
4652 * Empty chain. Create the head.
4654 if (blockIndex == BLOCK_END_OF_CHAIN)
4656 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4657 StorageImpl_SetNextBlockInChain(This->parentStorage,
4659 BLOCK_END_OF_CHAIN);
4661 if (This->headOfStreamPlaceHolder != 0)
4663 *(This->headOfStreamPlaceHolder) = blockIndex;
4667 DirEntry chainEntry;
4668 assert(This->ownerDirEntry != DIRENTRY_NULL);
4670 StorageImpl_ReadDirEntry(
4671 This->parentStorage,
4672 This->ownerDirEntry,
4675 chainEntry.startingBlock = blockIndex;
4677 StorageImpl_WriteDirEntry(
4678 This->parentStorage,
4679 This->ownerDirEntry,
4683 This->tailIndex = blockIndex;
4684 This->numBlocks = 1;
4688 * Figure out how many blocks are needed to contain this stream
4690 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4692 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4696 * Go to the current end of chain
4698 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4700 currentBlock = blockIndex;
4702 while (blockIndex != BLOCK_END_OF_CHAIN)
4705 currentBlock = blockIndex;
4707 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4712 This->tailIndex = currentBlock;
4715 currentBlock = This->tailIndex;
4716 oldNumBlocks = This->numBlocks;
4719 * Add new blocks to the chain
4721 if (oldNumBlocks < newNumBlocks)
4723 while (oldNumBlocks < newNumBlocks)
4725 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4727 StorageImpl_SetNextBlockInChain(
4728 This->parentStorage,
4732 StorageImpl_SetNextBlockInChain(
4733 This->parentStorage,
4735 BLOCK_END_OF_CHAIN);
4737 currentBlock = blockIndex;
4741 This->tailIndex = blockIndex;
4742 This->numBlocks = newNumBlocks;
4748 /******************************************************************************
4749 * BlockChainStream_SetSize
4751 * Sets the size of this stream. The big block depot will be updated.
4752 * The file will grow if we grow the chain.
4754 * TODO: Free the actual blocks in the file when we shrink the chain.
4755 * Currently, the blocks are still in the file. So the file size
4756 * doesn't shrink even if we shrink streams.
4758 BOOL BlockChainStream_SetSize(
4759 BlockChainStream* This,
4760 ULARGE_INTEGER newSize)
4762 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4764 if (newSize.u.LowPart == size.u.LowPart)
4767 if (newSize.u.LowPart < size.u.LowPart)
4769 BlockChainStream_Shrink(This, newSize);
4773 BlockChainStream_Enlarge(This, newSize);
4779 /******************************************************************************
4780 * BlockChainStream_GetSize
4782 * Returns the size of this chain.
4783 * Will return the block count if this chain doesn't have a directory entry.
4785 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4787 DirEntry chainEntry;
4789 if(This->headOfStreamPlaceHolder == NULL)
4792 * This chain has a directory entry so use the size value from there.
4794 StorageImpl_ReadDirEntry(
4795 This->parentStorage,
4796 This->ownerDirEntry,
4799 return chainEntry.size;
4804 * this chain is a chain that does not have a directory entry, figure out the
4805 * size by making the product number of used blocks times the
4808 ULARGE_INTEGER result;
4809 result.u.HighPart = 0;
4812 BlockChainStream_GetCount(This) *
4813 This->parentStorage->bigBlockSize;
4819 /******************************************************************************
4820 ** SmallBlockChainStream implementation
4823 SmallBlockChainStream* SmallBlockChainStream_Construct(
4824 StorageImpl* parentStorage,
4825 ULONG* headOfStreamPlaceHolder,
4828 SmallBlockChainStream* newStream;
4830 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4832 newStream->parentStorage = parentStorage;
4833 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4834 newStream->ownerDirEntry = dirEntry;
4839 void SmallBlockChainStream_Destroy(
4840 SmallBlockChainStream* This)
4842 HeapFree(GetProcessHeap(), 0, This);
4845 /******************************************************************************
4846 * SmallBlockChainStream_GetHeadOfChain
4848 * Returns the head of this chain of small blocks.
4850 static ULONG SmallBlockChainStream_GetHeadOfChain(
4851 SmallBlockChainStream* This)
4853 DirEntry chainEntry;
4854 BOOL readSuccessful;
4856 if (This->headOfStreamPlaceHolder != NULL)
4857 return *(This->headOfStreamPlaceHolder);
4859 if (This->ownerDirEntry)
4861 readSuccessful = StorageImpl_ReadDirEntry(
4862 This->parentStorage,
4863 This->ownerDirEntry,
4868 return chainEntry.startingBlock;
4873 return BLOCK_END_OF_CHAIN;
4876 /******************************************************************************
4877 * SmallBlockChainStream_GetNextBlockInChain
4879 * Returns the index of the next small block in this chain.
4882 * - BLOCK_END_OF_CHAIN: end of this chain
4883 * - BLOCK_UNUSED: small block 'blockIndex' is free
4885 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
4886 SmallBlockChainStream* This,
4888 ULONG* nextBlockInChain)
4890 ULARGE_INTEGER offsetOfBlockInDepot;
4895 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4897 offsetOfBlockInDepot.u.HighPart = 0;
4898 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4901 * Read those bytes in the buffer from the small block file.
4903 res = BlockChainStream_ReadAt(
4904 This->parentStorage->smallBlockDepotChain,
4905 offsetOfBlockInDepot,
4912 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4919 /******************************************************************************
4920 * SmallBlockChainStream_SetNextBlockInChain
4922 * Writes the index of the next block of the specified block in the small
4924 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4925 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4927 static void SmallBlockChainStream_SetNextBlockInChain(
4928 SmallBlockChainStream* This,
4932 ULARGE_INTEGER offsetOfBlockInDepot;
4936 offsetOfBlockInDepot.u.HighPart = 0;
4937 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4939 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4942 * Read those bytes in the buffer from the small block file.
4944 BlockChainStream_WriteAt(
4945 This->parentStorage->smallBlockDepotChain,
4946 offsetOfBlockInDepot,
4952 /******************************************************************************
4953 * SmallBlockChainStream_FreeBlock
4955 * Flag small block 'blockIndex' as free in the small block depot.
4957 static void SmallBlockChainStream_FreeBlock(
4958 SmallBlockChainStream* This,
4961 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4964 /******************************************************************************
4965 * SmallBlockChainStream_GetNextFreeBlock
4967 * Returns the index of a free small block. The small block depot will be
4968 * enlarged if necessary. The small block chain will also be enlarged if
4971 static ULONG SmallBlockChainStream_GetNextFreeBlock(
4972 SmallBlockChainStream* This)
4974 ULARGE_INTEGER offsetOfBlockInDepot;
4977 ULONG blockIndex = 0;
4978 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4980 ULONG smallBlocksPerBigBlock;
4982 offsetOfBlockInDepot.u.HighPart = 0;
4985 * Scan the small block depot for a free block
4987 while (nextBlockIndex != BLOCK_UNUSED)
4989 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4991 res = BlockChainStream_ReadAt(
4992 This->parentStorage->smallBlockDepotChain,
4993 offsetOfBlockInDepot,
4999 * If we run out of space for the small block depot, enlarge it
5003 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5005 if (nextBlockIndex != BLOCK_UNUSED)
5011 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5013 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5014 ULONG nextBlock, newsbdIndex;
5015 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5017 nextBlock = sbdIndex;
5018 while (nextBlock != BLOCK_END_OF_CHAIN)
5020 sbdIndex = nextBlock;
5021 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5024 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5025 if (sbdIndex != BLOCK_END_OF_CHAIN)
5026 StorageImpl_SetNextBlockInChain(
5027 This->parentStorage,
5031 StorageImpl_SetNextBlockInChain(
5032 This->parentStorage,
5034 BLOCK_END_OF_CHAIN);
5037 * Initialize all the small blocks to free
5039 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5040 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5045 * We have just created the small block depot.
5051 * Save it in the header
5053 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5054 StorageImpl_SaveFileHeader(This->parentStorage);
5057 * And allocate the first big block that will contain small blocks
5060 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5062 StorageImpl_SetNextBlockInChain(
5063 This->parentStorage,
5065 BLOCK_END_OF_CHAIN);
5067 StorageImpl_ReadDirEntry(
5068 This->parentStorage,
5069 This->parentStorage->base.storageDirEntry,
5072 rootEntry.startingBlock = sbStartIndex;
5073 rootEntry.size.u.HighPart = 0;
5074 rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
5076 StorageImpl_WriteDirEntry(
5077 This->parentStorage,
5078 This->parentStorage->base.storageDirEntry,
5082 StorageImpl_SaveFileHeader(This->parentStorage);
5086 smallBlocksPerBigBlock =
5087 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5090 * Verify if we have to allocate big blocks to contain small blocks
5092 if (blockIndex % smallBlocksPerBigBlock == 0)
5095 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5097 StorageImpl_ReadDirEntry(
5098 This->parentStorage,
5099 This->parentStorage->base.storageDirEntry,
5102 if (rootEntry.size.u.LowPart <
5103 (blocksRequired * This->parentStorage->bigBlockSize))
5105 rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5107 BlockChainStream_SetSize(
5108 This->parentStorage->smallBlockRootChain,
5111 StorageImpl_WriteDirEntry(
5112 This->parentStorage,
5113 This->parentStorage->base.storageDirEntry,
5121 /******************************************************************************
5122 * SmallBlockChainStream_ReadAt
5124 * Reads a specified number of bytes from this chain at the specified offset.
5125 * bytesRead may be NULL.
5126 * Failure will be returned if the specified number of bytes has not been read.
5128 HRESULT SmallBlockChainStream_ReadAt(
5129 SmallBlockChainStream* This,
5130 ULARGE_INTEGER offset,
5136 ULARGE_INTEGER offsetInBigBlockFile;
5137 ULONG blockNoInSequence =
5138 offset.u.LowPart / This->parentStorage->smallBlockSize;
5140 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5141 ULONG bytesToReadInBuffer;
5143 ULONG bytesReadFromBigBlockFile;
5147 * This should never happen on a small block file.
5149 assert(offset.u.HighPart==0);
5152 * Find the first block in the stream that contains part of the buffer.
5154 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5156 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5158 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5161 blockNoInSequence--;
5165 * Start reading the buffer.
5168 bufferWalker = buffer;
5170 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5173 * Calculate how many bytes we can copy from this small block.
5175 bytesToReadInBuffer =
5176 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5179 * Calculate the offset of the small block in the small block file.
5181 offsetInBigBlockFile.u.HighPart = 0;
5182 offsetInBigBlockFile.u.LowPart =
5183 blockIndex * This->parentStorage->smallBlockSize;
5185 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5188 * Read those bytes in the buffer from the small block file.
5189 * The small block has already been identified so it shouldn't fail
5190 * unless the file is corrupt.
5192 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5193 offsetInBigBlockFile,
5194 bytesToReadInBuffer,
5196 &bytesReadFromBigBlockFile);
5202 * Step to the next big block.
5204 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5206 return STG_E_DOCFILECORRUPT;
5208 bufferWalker += bytesReadFromBigBlockFile;
5209 size -= bytesReadFromBigBlockFile;
5210 *bytesRead += bytesReadFromBigBlockFile;
5211 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5214 return (size == 0) ? S_OK : STG_E_READFAULT;
5217 /******************************************************************************
5218 * SmallBlockChainStream_WriteAt
5220 * Writes the specified number of bytes to this chain at the specified offset.
5221 * Will fail if not all specified number of bytes have been written.
5223 HRESULT SmallBlockChainStream_WriteAt(
5224 SmallBlockChainStream* This,
5225 ULARGE_INTEGER offset,
5228 ULONG* bytesWritten)
5230 ULARGE_INTEGER offsetInBigBlockFile;
5231 ULONG blockNoInSequence =
5232 offset.u.LowPart / This->parentStorage->smallBlockSize;
5234 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5235 ULONG bytesToWriteInBuffer;
5237 ULONG bytesWrittenToBigBlockFile;
5238 const BYTE* bufferWalker;
5242 * This should never happen on a small block file.
5244 assert(offset.u.HighPart==0);
5247 * Find the first block in the stream that contains part of the buffer.
5249 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5251 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5253 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5254 return STG_E_DOCFILECORRUPT;
5255 blockNoInSequence--;
5259 * Start writing the buffer.
5262 bufferWalker = buffer;
5263 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5266 * Calculate how many bytes we can copy to this small block.
5268 bytesToWriteInBuffer =
5269 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5272 * Calculate the offset of the small block in the small block file.
5274 offsetInBigBlockFile.u.HighPart = 0;
5275 offsetInBigBlockFile.u.LowPart =
5276 blockIndex * This->parentStorage->smallBlockSize;
5278 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5281 * Write those bytes in the buffer to the small block file.
5283 res = BlockChainStream_WriteAt(
5284 This->parentStorage->smallBlockRootChain,
5285 offsetInBigBlockFile,
5286 bytesToWriteInBuffer,
5288 &bytesWrittenToBigBlockFile);
5293 * Step to the next big block.
5295 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5298 bufferWalker += bytesWrittenToBigBlockFile;
5299 size -= bytesWrittenToBigBlockFile;
5300 *bytesWritten += bytesWrittenToBigBlockFile;
5301 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5304 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5307 /******************************************************************************
5308 * SmallBlockChainStream_Shrink
5310 * Shrinks this chain in the small block depot.
5312 static BOOL SmallBlockChainStream_Shrink(
5313 SmallBlockChainStream* This,
5314 ULARGE_INTEGER newSize)
5316 ULONG blockIndex, extraBlock;
5320 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5322 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5325 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5328 * Go to the new end of chain
5330 while (count < numBlocks)
5332 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5339 * If the count is 0, we have a special case, the head of the chain was
5344 DirEntry chainEntry;
5346 StorageImpl_ReadDirEntry(This->parentStorage,
5347 This->ownerDirEntry,
5350 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
5352 StorageImpl_WriteDirEntry(This->parentStorage,
5353 This->ownerDirEntry,
5357 * We start freeing the chain at the head block.
5359 extraBlock = blockIndex;
5363 /* Get the next block before marking the new end */
5364 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5368 /* Mark the new end of chain */
5369 SmallBlockChainStream_SetNextBlockInChain(
5372 BLOCK_END_OF_CHAIN);
5376 * Mark the extra blocks as free
5378 while (extraBlock != BLOCK_END_OF_CHAIN)
5380 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5383 SmallBlockChainStream_FreeBlock(This, extraBlock);
5384 extraBlock = blockIndex;
5390 /******************************************************************************
5391 * SmallBlockChainStream_Enlarge
5393 * Grows this chain in the small block depot.
5395 static BOOL SmallBlockChainStream_Enlarge(
5396 SmallBlockChainStream* This,
5397 ULARGE_INTEGER newSize)
5399 ULONG blockIndex, currentBlock;
5401 ULONG oldNumBlocks = 0;
5403 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5406 * Empty chain. Create the head.
5408 if (blockIndex == BLOCK_END_OF_CHAIN)
5410 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5411 SmallBlockChainStream_SetNextBlockInChain(
5414 BLOCK_END_OF_CHAIN);
5416 if (This->headOfStreamPlaceHolder != NULL)
5418 *(This->headOfStreamPlaceHolder) = blockIndex;
5422 DirEntry chainEntry;
5424 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
5427 chainEntry.startingBlock = blockIndex;
5429 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
5434 currentBlock = blockIndex;
5437 * Figure out how many blocks are needed to contain this stream
5439 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5441 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5445 * Go to the current end of chain
5447 while (blockIndex != BLOCK_END_OF_CHAIN)
5450 currentBlock = blockIndex;
5451 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5456 * Add new blocks to the chain
5458 while (oldNumBlocks < newNumBlocks)
5460 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5461 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5463 SmallBlockChainStream_SetNextBlockInChain(
5466 BLOCK_END_OF_CHAIN);
5468 currentBlock = blockIndex;
5475 /******************************************************************************
5476 * SmallBlockChainStream_SetSize
5478 * Sets the size of this stream.
5479 * The file will grow if we grow the chain.
5481 * TODO: Free the actual blocks in the file when we shrink the chain.
5482 * Currently, the blocks are still in the file. So the file size
5483 * doesn't shrink even if we shrink streams.
5485 BOOL SmallBlockChainStream_SetSize(
5486 SmallBlockChainStream* This,
5487 ULARGE_INTEGER newSize)
5489 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5491 if (newSize.u.LowPart == size.u.LowPart)
5494 if (newSize.u.LowPart < size.u.LowPart)
5496 SmallBlockChainStream_Shrink(This, newSize);
5500 SmallBlockChainStream_Enlarge(This, newSize);
5506 /******************************************************************************
5507 * SmallBlockChainStream_GetCount
5509 * Returns the number of small blocks that comprises this chain.
5510 * This is not the size of the stream as the last block may not be full!
5513 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5518 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5520 while(blockIndex != BLOCK_END_OF_CHAIN)
5524 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
5525 blockIndex, &blockIndex)))
5532 /******************************************************************************
5533 * SmallBlockChainStream_GetSize
5535 * Returns the size of this chain.
5537 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5539 DirEntry chainEntry;
5541 if(This->headOfStreamPlaceHolder != NULL)
5543 ULARGE_INTEGER result;
5544 result.u.HighPart = 0;
5546 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
5547 This->parentStorage->smallBlockSize;
5552 StorageImpl_ReadDirEntry(
5553 This->parentStorage,
5554 This->ownerDirEntry,
5557 return chainEntry.size;
5560 /******************************************************************************
5561 * StgCreateDocfile [OLE32.@]
5562 * Creates a new compound file storage object
5565 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5566 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5567 * reserved [ ?] unused?, usually 0
5568 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5571 * S_OK if the file was successfully created
5572 * some STG_E_ value if error
5574 * if pwcsName is NULL, create file with new unique name
5575 * the function can returns
5576 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5579 HRESULT WINAPI StgCreateDocfile(
5583 IStorage **ppstgOpen)
5585 StorageImpl* newStorage = 0;
5586 HANDLE hFile = INVALID_HANDLE_VALUE;
5587 HRESULT hr = STG_E_INVALIDFLAG;
5591 DWORD fileAttributes;
5592 WCHAR tempFileName[MAX_PATH];
5594 TRACE("(%s, %x, %d, %p)\n",
5595 debugstr_w(pwcsName), grfMode,
5596 reserved, ppstgOpen);
5599 return STG_E_INVALIDPOINTER;
5601 return STG_E_INVALIDPARAMETER;
5603 /* if no share mode given then DENY_NONE is the default */
5604 if (STGM_SHARE_MODE(grfMode) == 0)
5605 grfMode |= STGM_SHARE_DENY_NONE;
5607 if ( FAILED( validateSTGM(grfMode) ))
5610 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5611 switch(STGM_ACCESS_MODE(grfMode))
5614 case STGM_READWRITE:
5620 /* in direct mode, can only use SHARE_EXCLUSIVE */
5621 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5624 /* but in transacted mode, any share mode is valid */
5627 * Generate a unique name.
5631 WCHAR tempPath[MAX_PATH];
5632 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5634 memset(tempPath, 0, sizeof(tempPath));
5635 memset(tempFileName, 0, sizeof(tempFileName));
5637 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5640 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5641 pwcsName = tempFileName;
5644 hr = STG_E_INSUFFICIENTMEMORY;
5648 creationMode = TRUNCATE_EXISTING;
5652 creationMode = GetCreationModeFromSTGM(grfMode);
5656 * Interpret the STGM value grfMode
5658 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5659 accessMode = GetAccessModeFromSTGM(grfMode);
5661 if (grfMode & STGM_DELETEONRELEASE)
5662 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5664 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5666 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5667 FIXME("Storage share mode not implemented.\n");
5669 if (grfMode & STGM_TRANSACTED)
5670 FIXME("Transacted mode not implemented.\n");
5674 hFile = CreateFileW(pwcsName,
5682 if (hFile == INVALID_HANDLE_VALUE)
5684 if(GetLastError() == ERROR_FILE_EXISTS)
5685 hr = STG_E_FILEALREADYEXISTS;
5692 * Allocate and initialize the new IStorage32object.
5694 hr = StorageImpl_Construct(
5709 * Get an "out" pointer for the caller.
5711 *ppstgOpen = (IStorage*)newStorage;
5714 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5719 /******************************************************************************
5720 * StgCreateStorageEx [OLE32.@]
5722 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5724 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5725 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5727 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5729 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5730 return STG_E_INVALIDPARAMETER;
5733 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5735 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5736 return STG_E_INVALIDPARAMETER;
5739 if (stgfmt == STGFMT_FILE)
5741 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5742 return STG_E_INVALIDPARAMETER;
5745 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5747 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5748 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5751 ERR("Invalid stgfmt argument\n");
5752 return STG_E_INVALIDPARAMETER;
5755 /******************************************************************************
5756 * StgCreatePropSetStg [OLE32.@]
5758 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5759 IPropertySetStorage **ppPropSetStg)
5763 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5765 hr = STG_E_INVALIDPARAMETER;
5767 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5768 (void**)ppPropSetStg);
5772 /******************************************************************************
5773 * StgOpenStorageEx [OLE32.@]
5775 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5777 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5778 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5780 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5782 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5783 return STG_E_INVALIDPARAMETER;
5789 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5790 return STG_E_INVALIDPARAMETER;
5792 case STGFMT_STORAGE:
5795 case STGFMT_DOCFILE:
5796 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5798 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5799 return STG_E_INVALIDPARAMETER;
5801 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5805 WARN("STGFMT_ANY assuming storage\n");
5809 return STG_E_INVALIDPARAMETER;
5812 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
5816 /******************************************************************************
5817 * StgOpenStorage [OLE32.@]
5819 HRESULT WINAPI StgOpenStorage(
5820 const OLECHAR *pwcsName,
5821 IStorage *pstgPriority,
5825 IStorage **ppstgOpen)
5827 StorageImpl* newStorage = 0;
5832 WCHAR fullname[MAX_PATH];
5834 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5835 debugstr_w(pwcsName), pstgPriority, grfMode,
5836 snbExclude, reserved, ppstgOpen);
5840 hr = STG_E_INVALIDNAME;
5846 hr = STG_E_INVALIDPOINTER;
5852 hr = STG_E_INVALIDPARAMETER;
5856 if (grfMode & STGM_PRIORITY)
5858 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5859 return STG_E_INVALIDFLAG;
5860 if (grfMode & STGM_DELETEONRELEASE)
5861 return STG_E_INVALIDFUNCTION;
5862 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5863 return STG_E_INVALIDFLAG;
5864 grfMode &= ~0xf0; /* remove the existing sharing mode */
5865 grfMode |= STGM_SHARE_DENY_NONE;
5867 /* STGM_PRIORITY stops other IStorage objects on the same file from
5868 * committing until the STGM_PRIORITY IStorage is closed. it also
5869 * stops non-transacted mode StgOpenStorage calls with write access from
5870 * succeeding. obviously, both of these cannot be achieved through just
5871 * file share flags */
5872 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5876 * Validate the sharing mode
5878 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5879 switch(STGM_SHARE_MODE(grfMode))
5881 case STGM_SHARE_EXCLUSIVE:
5882 case STGM_SHARE_DENY_WRITE:
5885 hr = STG_E_INVALIDFLAG;
5889 if ( FAILED( validateSTGM(grfMode) ) ||
5890 (grfMode&STGM_CREATE))
5892 hr = STG_E_INVALIDFLAG;
5896 /* shared reading requires transacted mode */
5897 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5898 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5899 !(grfMode&STGM_TRANSACTED) )
5901 hr = STG_E_INVALIDFLAG;
5906 * Interpret the STGM value grfMode
5908 shareMode = GetShareModeFromSTGM(grfMode);
5909 accessMode = GetAccessModeFromSTGM(grfMode);
5913 hFile = CreateFileW( pwcsName,
5918 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5921 if (hFile==INVALID_HANDLE_VALUE)
5923 DWORD last_error = GetLastError();
5929 case ERROR_FILE_NOT_FOUND:
5930 hr = STG_E_FILENOTFOUND;
5933 case ERROR_PATH_NOT_FOUND:
5934 hr = STG_E_PATHNOTFOUND;
5937 case ERROR_ACCESS_DENIED:
5938 case ERROR_WRITE_PROTECT:
5939 hr = STG_E_ACCESSDENIED;
5942 case ERROR_SHARING_VIOLATION:
5943 hr = STG_E_SHAREVIOLATION;
5954 * Refuse to open the file if it's too small to be a structured storage file
5955 * FIXME: verify the file when reading instead of here
5957 if (GetFileSize(hFile, NULL) < 0x100)
5960 hr = STG_E_FILEALREADYEXISTS;
5965 * Allocate and initialize the new IStorage32object.
5967 hr = StorageImpl_Construct(
5979 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5981 if(hr == STG_E_INVALIDHEADER)
5982 hr = STG_E_FILEALREADYEXISTS;
5986 /* prepare the file name string given in lieu of the root property name */
5987 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5988 memcpy(newStorage->filename, fullname, DIRENTRY_NAME_BUFFER_LEN);
5989 newStorage->filename[DIRENTRY_NAME_BUFFER_LEN-1] = '\0';
5992 * Get an "out" pointer for the caller.
5994 *ppstgOpen = (IStorage*)newStorage;
5997 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6001 /******************************************************************************
6002 * StgCreateDocfileOnILockBytes [OLE32.@]
6004 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6008 IStorage** ppstgOpen)
6010 StorageImpl* newStorage = 0;
6013 if ((ppstgOpen == 0) || (plkbyt == 0))
6014 return STG_E_INVALIDPOINTER;
6017 * Allocate and initialize the new IStorage object.
6019 hr = StorageImpl_Construct(
6034 * Get an "out" pointer for the caller.
6036 *ppstgOpen = (IStorage*)newStorage;
6041 /******************************************************************************
6042 * StgOpenStorageOnILockBytes [OLE32.@]
6044 HRESULT WINAPI StgOpenStorageOnILockBytes(
6046 IStorage *pstgPriority,
6050 IStorage **ppstgOpen)
6052 StorageImpl* newStorage = 0;
6055 if ((plkbyt == 0) || (ppstgOpen == 0))
6056 return STG_E_INVALIDPOINTER;
6058 if ( FAILED( validateSTGM(grfMode) ))
6059 return STG_E_INVALIDFLAG;
6064 * Allocate and initialize the new IStorage object.
6066 hr = StorageImpl_Construct(
6081 * Get an "out" pointer for the caller.
6083 *ppstgOpen = (IStorage*)newStorage;
6088 /******************************************************************************
6089 * StgSetTimes [ole32.@]
6090 * StgSetTimes [OLE32.@]
6094 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6095 FILETIME const *patime, FILETIME const *pmtime)
6097 IStorage *stg = NULL;
6100 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6102 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6106 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6107 IStorage_Release(stg);
6113 /******************************************************************************
6114 * StgIsStorageILockBytes [OLE32.@]
6116 * Determines if the ILockBytes contains a storage object.
6118 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6121 ULARGE_INTEGER offset;
6123 offset.u.HighPart = 0;
6124 offset.u.LowPart = 0;
6126 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6128 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6134 /******************************************************************************
6135 * WriteClassStg [OLE32.@]
6137 * This method will store the specified CLSID in the specified storage object
6139 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6144 return E_INVALIDARG;
6147 return STG_E_INVALIDPOINTER;
6149 hRes = IStorage_SetClass(pStg, rclsid);
6154 /***********************************************************************
6155 * ReadClassStg (OLE32.@)
6157 * This method reads the CLSID previously written to a storage object with
6158 * the WriteClassStg.
6161 * pstg [I] IStorage pointer
6162 * pclsid [O] Pointer to where the CLSID is written
6166 * Failure: HRESULT code.
6168 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6173 TRACE("(%p, %p)\n", pstg, pclsid);
6175 if(!pstg || !pclsid)
6176 return E_INVALIDARG;
6179 * read a STATSTG structure (contains the clsid) from the storage
6181 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6184 *pclsid=pstatstg.clsid;
6189 /***********************************************************************
6190 * OleLoadFromStream (OLE32.@)
6192 * This function loads an object from stream
6194 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6198 LPPERSISTSTREAM xstm;
6200 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6202 res=ReadClassStm(pStm,&clsid);
6205 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6208 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6210 IUnknown_Release((IUnknown*)*ppvObj);
6213 res=IPersistStream_Load(xstm,pStm);
6214 IPersistStream_Release(xstm);
6215 /* FIXME: all refcounts ok at this point? I think they should be:
6218 * xstm : 0 (released)
6223 /***********************************************************************
6224 * OleSaveToStream (OLE32.@)
6226 * This function saves an object with the IPersistStream interface on it
6227 * to the specified stream.
6229 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6235 TRACE("(%p,%p)\n",pPStm,pStm);
6237 res=IPersistStream_GetClassID(pPStm,&clsid);
6239 if (SUCCEEDED(res)){
6241 res=WriteClassStm(pStm,&clsid);
6245 res=IPersistStream_Save(pPStm,pStm,TRUE);
6248 TRACE("Finished Save\n");
6252 /****************************************************************************
6253 * This method validate a STGM parameter that can contain the values below
6255 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6256 * The stgm values contained in 0xffff0000 are bitmasks.
6258 * STGM_DIRECT 0x00000000
6259 * STGM_TRANSACTED 0x00010000
6260 * STGM_SIMPLE 0x08000000
6262 * STGM_READ 0x00000000
6263 * STGM_WRITE 0x00000001
6264 * STGM_READWRITE 0x00000002
6266 * STGM_SHARE_DENY_NONE 0x00000040
6267 * STGM_SHARE_DENY_READ 0x00000030
6268 * STGM_SHARE_DENY_WRITE 0x00000020
6269 * STGM_SHARE_EXCLUSIVE 0x00000010
6271 * STGM_PRIORITY 0x00040000
6272 * STGM_DELETEONRELEASE 0x04000000
6274 * STGM_CREATE 0x00001000
6275 * STGM_CONVERT 0x00020000
6276 * STGM_FAILIFTHERE 0x00000000
6278 * STGM_NOSCRATCH 0x00100000
6279 * STGM_NOSNAPSHOT 0x00200000
6281 static HRESULT validateSTGM(DWORD stgm)
6283 DWORD access = STGM_ACCESS_MODE(stgm);
6284 DWORD share = STGM_SHARE_MODE(stgm);
6285 DWORD create = STGM_CREATE_MODE(stgm);
6287 if (stgm&~STGM_KNOWN_FLAGS)
6289 ERR("unknown flags %08x\n", stgm);
6297 case STGM_READWRITE:
6305 case STGM_SHARE_DENY_NONE:
6306 case STGM_SHARE_DENY_READ:
6307 case STGM_SHARE_DENY_WRITE:
6308 case STGM_SHARE_EXCLUSIVE:
6317 case STGM_FAILIFTHERE:
6324 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6326 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6330 * STGM_CREATE | STGM_CONVERT
6331 * if both are false, STGM_FAILIFTHERE is set to TRUE
6333 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6337 * STGM_NOSCRATCH requires STGM_TRANSACTED
6339 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6343 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6344 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6346 if ( (stgm & STGM_NOSNAPSHOT) &&
6347 (!(stgm & STGM_TRANSACTED) ||
6348 share == STGM_SHARE_EXCLUSIVE ||
6349 share == STGM_SHARE_DENY_WRITE) )
6355 /****************************************************************************
6356 * GetShareModeFromSTGM
6358 * This method will return a share mode flag from a STGM value.
6359 * The STGM value is assumed valid.
6361 static DWORD GetShareModeFromSTGM(DWORD stgm)
6363 switch (STGM_SHARE_MODE(stgm))
6365 case STGM_SHARE_DENY_NONE:
6366 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6367 case STGM_SHARE_DENY_READ:
6368 return FILE_SHARE_WRITE;
6369 case STGM_SHARE_DENY_WRITE:
6370 return FILE_SHARE_READ;
6371 case STGM_SHARE_EXCLUSIVE:
6374 ERR("Invalid share mode!\n");
6379 /****************************************************************************
6380 * GetAccessModeFromSTGM
6382 * This method will return an access mode flag from a STGM value.
6383 * The STGM value is assumed valid.
6385 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6387 switch (STGM_ACCESS_MODE(stgm))
6390 return GENERIC_READ;
6392 case STGM_READWRITE:
6393 return GENERIC_READ | GENERIC_WRITE;
6395 ERR("Invalid access mode!\n");
6400 /****************************************************************************
6401 * GetCreationModeFromSTGM
6403 * This method will return a creation mode flag from a STGM value.
6404 * The STGM value is assumed valid.
6406 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6408 switch(STGM_CREATE_MODE(stgm))
6411 return CREATE_ALWAYS;
6413 FIXME("STGM_CONVERT not implemented!\n");
6415 case STGM_FAILIFTHERE:
6418 ERR("Invalid create mode!\n");
6424 /*************************************************************************
6425 * OLECONVERT_LoadOLE10 [Internal]
6427 * Loads the OLE10 STREAM to memory
6430 * pOleStream [I] The OLESTREAM
6431 * pData [I] Data Structure for the OLESTREAM Data
6435 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6436 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6439 * This function is used by OleConvertOLESTREAMToIStorage only.
6441 * Memory allocated for pData must be freed by the caller
6443 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6446 HRESULT hRes = S_OK;
6450 pData->pData = NULL;
6451 pData->pstrOleObjFileName = NULL;
6453 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6456 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6457 if(dwSize != sizeof(pData->dwOleID))
6459 hRes = CONVERT10_E_OLESTREAM_GET;
6461 else if(pData->dwOleID != OLESTREAM_ID)
6463 hRes = CONVERT10_E_OLESTREAM_FMT;
6474 /* Get the TypeID... more info needed for this field */
6475 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6476 if(dwSize != sizeof(pData->dwTypeID))
6478 hRes = CONVERT10_E_OLESTREAM_GET;
6483 if(pData->dwTypeID != 0)
6485 /* Get the length of the OleTypeName */
6486 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6487 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6489 hRes = CONVERT10_E_OLESTREAM_GET;
6494 if(pData->dwOleTypeNameLength > 0)
6496 /* Get the OleTypeName */
6497 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6498 if(dwSize != pData->dwOleTypeNameLength)
6500 hRes = CONVERT10_E_OLESTREAM_GET;
6506 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6507 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6509 hRes = CONVERT10_E_OLESTREAM_GET;
6513 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6514 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6515 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6516 if(pData->pstrOleObjFileName)
6518 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6519 if(dwSize != pData->dwOleObjFileNameLength)
6521 hRes = CONVERT10_E_OLESTREAM_GET;
6525 hRes = CONVERT10_E_OLESTREAM_GET;
6530 /* Get the Width of the Metafile */
6531 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6532 if(dwSize != sizeof(pData->dwMetaFileWidth))
6534 hRes = CONVERT10_E_OLESTREAM_GET;
6538 /* Get the Height of the Metafile */
6539 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6540 if(dwSize != sizeof(pData->dwMetaFileHeight))
6542 hRes = CONVERT10_E_OLESTREAM_GET;
6548 /* Get the Length of the Data */
6549 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6550 if(dwSize != sizeof(pData->dwDataLength))
6552 hRes = CONVERT10_E_OLESTREAM_GET;
6556 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6558 if(!bStrem1) /* if it is a second OLE stream data */
6560 pData->dwDataLength -= 8;
6561 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6562 if(dwSize != sizeof(pData->strUnknown))
6564 hRes = CONVERT10_E_OLESTREAM_GET;
6570 if(pData->dwDataLength > 0)
6572 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6574 /* Get Data (ex. IStorage, Metafile, or BMP) */
6577 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6578 if(dwSize != pData->dwDataLength)
6580 hRes = CONVERT10_E_OLESTREAM_GET;
6585 hRes = CONVERT10_E_OLESTREAM_GET;
6594 /*************************************************************************
6595 * OLECONVERT_SaveOLE10 [Internal]
6597 * Saves the OLE10 STREAM From memory
6600 * pData [I] Data Structure for the OLESTREAM Data
6601 * pOleStream [I] The OLESTREAM to save
6605 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6608 * This function is used by OleConvertIStorageToOLESTREAM only.
6611 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6614 HRESULT hRes = S_OK;
6618 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6619 if(dwSize != sizeof(pData->dwOleID))
6621 hRes = CONVERT10_E_OLESTREAM_PUT;
6626 /* Set the TypeID */
6627 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6628 if(dwSize != sizeof(pData->dwTypeID))
6630 hRes = CONVERT10_E_OLESTREAM_PUT;
6634 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6636 /* Set the Length of the OleTypeName */
6637 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6638 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6640 hRes = CONVERT10_E_OLESTREAM_PUT;
6645 if(pData->dwOleTypeNameLength > 0)
6647 /* Set the OleTypeName */
6648 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6649 if(dwSize != pData->dwOleTypeNameLength)
6651 hRes = CONVERT10_E_OLESTREAM_PUT;
6658 /* Set the width of the Metafile */
6659 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6660 if(dwSize != sizeof(pData->dwMetaFileWidth))
6662 hRes = CONVERT10_E_OLESTREAM_PUT;
6668 /* Set the height of the Metafile */
6669 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6670 if(dwSize != sizeof(pData->dwMetaFileHeight))
6672 hRes = CONVERT10_E_OLESTREAM_PUT;
6678 /* Set the length of the Data */
6679 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6680 if(dwSize != sizeof(pData->dwDataLength))
6682 hRes = CONVERT10_E_OLESTREAM_PUT;
6688 if(pData->dwDataLength > 0)
6690 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6691 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6692 if(dwSize != pData->dwDataLength)
6694 hRes = CONVERT10_E_OLESTREAM_PUT;
6702 /*************************************************************************
6703 * OLECONVERT_GetOLE20FromOLE10[Internal]
6705 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6706 * opens it, and copies the content to the dest IStorage for
6707 * OleConvertOLESTREAMToIStorage
6711 * pDestStorage [I] The IStorage to copy the data to
6712 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6713 * nBufferLength [I] The size of the buffer
6722 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6726 IStorage *pTempStorage;
6727 DWORD dwNumOfBytesWritten;
6728 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6729 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6731 /* Create a temp File */
6732 GetTempPathW(MAX_PATH, wstrTempDir);
6733 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6734 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6736 if(hFile != INVALID_HANDLE_VALUE)
6738 /* Write IStorage Data to File */
6739 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6742 /* Open and copy temp storage to the Dest Storage */
6743 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6746 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6747 IStorage_Release(pTempStorage);
6749 DeleteFileW(wstrTempFile);
6754 /*************************************************************************
6755 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6757 * Saves the OLE10 STREAM From memory
6760 * pStorage [I] The Src IStorage to copy
6761 * pData [I] The Dest Memory to write to.
6764 * The size in bytes allocated for pData
6767 * Memory allocated for pData must be freed by the caller
6769 * Used by OleConvertIStorageToOLESTREAM only.
6772 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6776 DWORD nDataLength = 0;
6777 IStorage *pTempStorage;
6778 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6779 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6783 /* Create temp Storage */
6784 GetTempPathW(MAX_PATH, wstrTempDir);
6785 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6786 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6790 /* Copy Src Storage to the Temp Storage */
6791 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6792 IStorage_Release(pTempStorage);
6794 /* Open Temp Storage as a file and copy to memory */
6795 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6796 if(hFile != INVALID_HANDLE_VALUE)
6798 nDataLength = GetFileSize(hFile, NULL);
6799 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6800 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6803 DeleteFileW(wstrTempFile);
6808 /*************************************************************************
6809 * OLECONVERT_CreateOleStream [Internal]
6811 * Creates the "\001OLE" stream in the IStorage if necessary.
6814 * pStorage [I] Dest storage to create the stream in
6820 * This function is used by OleConvertOLESTREAMToIStorage only.
6822 * This stream is still unknown, MS Word seems to have extra data
6823 * but since the data is stored in the OLESTREAM there should be
6824 * no need to recreate the stream. If the stream is manually
6825 * deleted it will create it with this default data.
6828 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6832 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6833 BYTE pOleStreamHeader [] =
6835 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6836 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6837 0x00, 0x00, 0x00, 0x00
6840 /* Create stream if not present */
6841 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6842 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6846 /* Write default Data */
6847 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6848 IStream_Release(pStream);
6852 /* write a string to a stream, preceded by its length */
6853 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6860 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6861 r = IStream_Write( stm, &len, sizeof(len), NULL);
6866 str = CoTaskMemAlloc( len );
6867 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6868 r = IStream_Write( stm, str, len, NULL);
6869 CoTaskMemFree( str );
6873 /* read a string preceded by its length from a stream */
6874 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6877 DWORD len, count = 0;
6881 r = IStream_Read( stm, &len, sizeof(len), &count );
6884 if( count != sizeof(len) )
6885 return E_OUTOFMEMORY;
6887 TRACE("%d bytes\n",len);
6889 str = CoTaskMemAlloc( len );
6891 return E_OUTOFMEMORY;
6893 r = IStream_Read( stm, str, len, &count );
6898 CoTaskMemFree( str );
6899 return E_OUTOFMEMORY;
6902 TRACE("Read string %s\n",debugstr_an(str,len));
6904 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6905 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6907 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6908 CoTaskMemFree( str );
6916 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6917 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6921 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6923 static const BYTE unknown1[12] =
6924 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6925 0xFF, 0xFF, 0xFF, 0xFF};
6926 static const BYTE unknown2[16] =
6927 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6928 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6930 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6931 debugstr_w(lpszUserType), debugstr_w(szClipName),
6932 debugstr_w(szProgIDName));
6934 /* Create a CompObj stream */
6935 r = IStorage_CreateStream(pstg, szwStreamName,
6936 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6940 /* Write CompObj Structure to stream */
6941 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6943 if( SUCCEEDED( r ) )
6944 r = WriteClassStm( pstm, clsid );
6946 if( SUCCEEDED( r ) )
6947 r = STREAM_WriteString( pstm, lpszUserType );
6948 if( SUCCEEDED( r ) )
6949 r = STREAM_WriteString( pstm, szClipName );
6950 if( SUCCEEDED( r ) )
6951 r = STREAM_WriteString( pstm, szProgIDName );
6952 if( SUCCEEDED( r ) )
6953 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6955 IStream_Release( pstm );
6960 /***********************************************************************
6961 * WriteFmtUserTypeStg (OLE32.@)
6963 HRESULT WINAPI WriteFmtUserTypeStg(
6964 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6967 WCHAR szwClipName[0x40];
6968 CLSID clsid = CLSID_NULL;
6969 LPWSTR wstrProgID = NULL;
6972 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6974 /* get the clipboard format name */
6975 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
6978 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6980 /* FIXME: There's room to save a CLSID and its ProgID, but
6981 the CLSID is not looked up in the registry and in all the
6982 tests I wrote it was CLSID_NULL. Where does it come from?
6985 /* get the real program ID. This may fail, but that's fine */
6986 ProgIDFromCLSID(&clsid, &wstrProgID);
6988 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6990 r = STORAGE_WriteCompObj( pstg, &clsid,
6991 lpszUserType, szwClipName, wstrProgID );
6993 CoTaskMemFree(wstrProgID);
6999 /******************************************************************************
7000 * ReadFmtUserTypeStg [OLE32.@]
7002 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7006 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7007 unsigned char unknown1[12];
7008 unsigned char unknown2[16];
7010 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7013 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7015 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7016 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7019 WARN("Failed to open stream r = %08x\n", r);
7023 /* read the various parts of the structure */
7024 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7025 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7027 r = ReadClassStm( stm, &clsid );
7031 r = STREAM_ReadString( stm, &szCLSIDName );
7035 r = STREAM_ReadString( stm, &szOleTypeName );
7039 r = STREAM_ReadString( stm, &szProgIDName );
7043 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7044 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7047 /* ok, success... now we just need to store what we found */
7049 *pcf = RegisterClipboardFormatW( szOleTypeName );
7050 CoTaskMemFree( szOleTypeName );
7052 if( lplpszUserType )
7053 *lplpszUserType = szCLSIDName;
7054 CoTaskMemFree( szProgIDName );
7057 IStream_Release( stm );
7063 /*************************************************************************
7064 * OLECONVERT_CreateCompObjStream [Internal]
7066 * Creates a "\001CompObj" is the destination IStorage if necessary.
7069 * pStorage [I] The dest IStorage to create the CompObj Stream
7071 * strOleTypeName [I] The ProgID
7075 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7078 * This function is used by OleConvertOLESTREAMToIStorage only.
7080 * The stream data is stored in the OLESTREAM and there should be
7081 * no need to recreate the stream. If the stream is manually
7082 * deleted it will attempt to create it by querying the registry.
7086 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7089 HRESULT hStorageRes, hRes = S_OK;
7090 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7091 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7092 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7094 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7095 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7097 /* Initialize the CompObj structure */
7098 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7099 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7100 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7103 /* Create a CompObj stream if it doesn't exist */
7104 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7105 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7106 if(hStorageRes == S_OK)
7108 /* copy the OleTypeName to the compobj struct */
7109 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7110 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7112 /* copy the OleTypeName to the compobj struct */
7113 /* Note: in the test made, these were Identical */
7114 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7115 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7118 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7119 bufferW, OLESTREAM_MAX_STR_LEN );
7120 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7126 /* Get the CLSID Default Name from the Registry */
7127 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7128 if(hErr == ERROR_SUCCESS)
7130 char strTemp[OLESTREAM_MAX_STR_LEN];
7131 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7132 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7133 if(hErr == ERROR_SUCCESS)
7135 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7141 /* Write CompObj Structure to stream */
7142 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7144 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7146 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7147 if(IStorageCompObj.dwCLSIDNameLength > 0)
7149 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7151 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7152 if(IStorageCompObj.dwOleTypeNameLength > 0)
7154 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7156 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7157 if(IStorageCompObj.dwProgIDNameLength > 0)
7159 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7161 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7162 IStream_Release(pStream);
7168 /*************************************************************************
7169 * OLECONVERT_CreateOlePresStream[Internal]
7171 * Creates the "\002OlePres000" Stream with the Metafile data
7174 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7175 * dwExtentX [I] Width of the Metafile
7176 * dwExtentY [I] Height of the Metafile
7177 * pData [I] Metafile data
7178 * dwDataLength [I] Size of the Metafile data
7182 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7185 * This function is used by OleConvertOLESTREAMToIStorage only.
7188 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7192 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7193 BYTE pOlePresStreamHeader [] =
7195 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7196 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7197 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7198 0x00, 0x00, 0x00, 0x00
7201 BYTE pOlePresStreamHeaderEmpty [] =
7203 0x00, 0x00, 0x00, 0x00,
7204 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7205 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7206 0x00, 0x00, 0x00, 0x00
7209 /* Create the OlePres000 Stream */
7210 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7211 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7216 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7218 memset(&OlePres, 0, sizeof(OlePres));
7219 /* Do we have any metafile data to save */
7220 if(dwDataLength > 0)
7222 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7223 nHeaderSize = sizeof(pOlePresStreamHeader);
7227 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7228 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7230 /* Set width and height of the metafile */
7231 OlePres.dwExtentX = dwExtentX;
7232 OlePres.dwExtentY = -dwExtentY;
7234 /* Set Data and Length */
7235 if(dwDataLength > sizeof(METAFILEPICT16))
7237 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7238 OlePres.pData = &(pData[8]);
7240 /* Save OlePres000 Data to Stream */
7241 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7242 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7243 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7244 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7245 if(OlePres.dwSize > 0)
7247 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7249 IStream_Release(pStream);
7253 /*************************************************************************
7254 * OLECONVERT_CreateOle10NativeStream [Internal]
7256 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7259 * pStorage [I] Dest storage to create the stream in
7260 * pData [I] Ole10 Native Data (ex. bmp)
7261 * dwDataLength [I] Size of the Ole10 Native Data
7267 * This function is used by OleConvertOLESTREAMToIStorage only.
7269 * Might need to verify the data and return appropriate error message
7272 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7276 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7278 /* Create the Ole10Native Stream */
7279 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7280 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7284 /* Write info to stream */
7285 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7286 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7287 IStream_Release(pStream);
7292 /*************************************************************************
7293 * OLECONVERT_GetOLE10ProgID [Internal]
7295 * Finds the ProgID (or OleTypeID) from the IStorage
7298 * pStorage [I] The Src IStorage to get the ProgID
7299 * strProgID [I] the ProgID string to get
7300 * dwSize [I] the size of the string
7304 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7307 * This function is used by OleConvertIStorageToOLESTREAM only.
7311 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7315 LARGE_INTEGER iSeekPos;
7316 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7317 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7319 /* Open the CompObj Stream */
7320 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7321 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7325 /*Get the OleType from the CompObj Stream */
7326 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7327 iSeekPos.u.HighPart = 0;
7329 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7330 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7331 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7332 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7333 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7334 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7335 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7337 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7340 IStream_Read(pStream, strProgID, *dwSize, NULL);
7342 IStream_Release(pStream);
7347 LPOLESTR wstrProgID;
7349 /* Get the OleType from the registry */
7350 REFCLSID clsid = &(stat.clsid);
7351 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7352 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7355 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7362 /*************************************************************************
7363 * OLECONVERT_GetOle10PresData [Internal]
7365 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7368 * pStorage [I] Src IStroage
7369 * pOleStream [I] Dest OleStream Mem Struct
7375 * This function is used by OleConvertIStorageToOLESTREAM only.
7377 * Memory allocated for pData must be freed by the caller
7381 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7386 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7388 /* Initialize Default data for OLESTREAM */
7389 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7390 pOleStreamData[0].dwTypeID = 2;
7391 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7392 pOleStreamData[1].dwTypeID = 0;
7393 pOleStreamData[0].dwMetaFileWidth = 0;
7394 pOleStreamData[0].dwMetaFileHeight = 0;
7395 pOleStreamData[0].pData = NULL;
7396 pOleStreamData[1].pData = NULL;
7398 /* Open Ole10Native Stream */
7399 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7400 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7404 /* Read Size and Data */
7405 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7406 if(pOleStreamData->dwDataLength > 0)
7408 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7409 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7411 IStream_Release(pStream);
7417 /*************************************************************************
7418 * OLECONVERT_GetOle20PresData[Internal]
7420 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7423 * pStorage [I] Src IStroage
7424 * pOleStreamData [I] Dest OleStream Mem Struct
7430 * This function is used by OleConvertIStorageToOLESTREAM only.
7432 * Memory allocated for pData must be freed by the caller
7434 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7438 OLECONVERT_ISTORAGE_OLEPRES olePress;
7439 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7441 /* Initialize Default data for OLESTREAM */
7442 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7443 pOleStreamData[0].dwTypeID = 2;
7444 pOleStreamData[0].dwMetaFileWidth = 0;
7445 pOleStreamData[0].dwMetaFileHeight = 0;
7446 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7447 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7448 pOleStreamData[1].dwTypeID = 0;
7449 pOleStreamData[1].dwOleTypeNameLength = 0;
7450 pOleStreamData[1].strOleTypeName[0] = 0;
7451 pOleStreamData[1].dwMetaFileWidth = 0;
7452 pOleStreamData[1].dwMetaFileHeight = 0;
7453 pOleStreamData[1].pData = NULL;
7454 pOleStreamData[1].dwDataLength = 0;
7457 /* Open OlePress000 stream */
7458 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7459 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7462 LARGE_INTEGER iSeekPos;
7463 METAFILEPICT16 MetaFilePict;
7464 static const char strMetafilePictName[] = "METAFILEPICT";
7466 /* Set the TypeID for a Metafile */
7467 pOleStreamData[1].dwTypeID = 5;
7469 /* Set the OleTypeName to Metafile */
7470 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7471 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7473 iSeekPos.u.HighPart = 0;
7474 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7476 /* Get Presentation Data */
7477 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7478 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7479 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7480 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7482 /*Set width and Height */
7483 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7484 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7485 if(olePress.dwSize > 0)
7488 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7490 /* Set MetaFilePict struct */
7491 MetaFilePict.mm = 8;
7492 MetaFilePict.xExt = olePress.dwExtentX;
7493 MetaFilePict.yExt = olePress.dwExtentY;
7494 MetaFilePict.hMF = 0;
7496 /* Get Metafile Data */
7497 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7498 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7499 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7501 IStream_Release(pStream);
7505 /*************************************************************************
7506 * OleConvertOLESTREAMToIStorage [OLE32.@]
7511 * DVTARGETDEVICE parameter is not handled
7512 * Still unsure of some mem fields for OLE 10 Stream
7513 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7514 * and "\001OLE" streams
7517 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7518 LPOLESTREAM pOleStream,
7520 const DVTARGETDEVICE* ptd)
7524 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7526 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7528 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7532 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7535 if(pstg == NULL || pOleStream == NULL)
7537 hRes = E_INVALIDARG;
7542 /* Load the OLESTREAM to Memory */
7543 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7548 /* Load the OLESTREAM to Memory (part 2)*/
7549 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7555 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7557 /* Do we have the IStorage Data in the OLESTREAM */
7558 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7560 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7561 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7565 /* It must be an original OLE 1.0 source */
7566 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7571 /* It must be an original OLE 1.0 source */
7572 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7575 /* Create CompObj Stream if necessary */
7576 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7579 /*Create the Ole Stream if necessary */
7580 OLECONVERT_CreateOleStream(pstg);
7585 /* Free allocated memory */
7586 for(i=0; i < 2; i++)
7588 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7589 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7590 pOleStreamData[i].pstrOleObjFileName = NULL;
7595 /*************************************************************************
7596 * OleConvertIStorageToOLESTREAM [OLE32.@]
7603 * Still unsure of some mem fields for OLE 10 Stream
7604 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7605 * and "\001OLE" streams.
7608 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7610 LPOLESTREAM pOleStream)
7613 HRESULT hRes = S_OK;
7615 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7616 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7618 TRACE("%p %p\n", pstg, pOleStream);
7620 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7622 if(pstg == NULL || pOleStream == NULL)
7624 hRes = E_INVALIDARG;
7628 /* Get the ProgID */
7629 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7630 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7634 /* Was it originally Ole10 */
7635 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7638 IStream_Release(pStream);
7639 /* Get Presentation Data for Ole10Native */
7640 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7644 /* Get Presentation Data (OLE20) */
7645 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7648 /* Save OLESTREAM */
7649 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7652 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7657 /* Free allocated memory */
7658 for(i=0; i < 2; i++)
7660 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7666 /***********************************************************************
7667 * GetConvertStg (OLE32.@)
7669 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7670 FIXME("unimplemented stub!\n");
7674 /******************************************************************************
7675 * StgIsStorageFile [OLE32.@]
7676 * Verify if the file contains a storage object
7682 * S_OK if file has magic bytes as a storage object
7683 * S_FALSE if file is not storage
7686 StgIsStorageFile(LPCOLESTR fn)
7692 TRACE("%s\n", debugstr_w(fn));
7693 hf = CreateFileW(fn, GENERIC_READ,
7694 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7695 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7697 if (hf == INVALID_HANDLE_VALUE)
7698 return STG_E_FILENOTFOUND;
7700 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7702 WARN(" unable to read file\n");
7709 if (bytes_read != 8) {
7710 WARN(" too short\n");
7714 if (!memcmp(magic,STORAGE_magic,8)) {
7719 WARN(" -> Invalid header.\n");
7723 /***********************************************************************
7724 * WriteClassStm (OLE32.@)
7726 * Writes a CLSID to a stream.
7729 * pStm [I] Stream to write to.
7730 * rclsid [I] CLSID to write.
7734 * Failure: HRESULT code.
7736 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7738 TRACE("(%p,%p)\n",pStm,rclsid);
7740 if (!pStm || !rclsid)
7741 return E_INVALIDARG;
7743 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7746 /***********************************************************************
7747 * ReadClassStm (OLE32.@)
7749 * Reads a CLSID from a stream.
7752 * pStm [I] Stream to read from.
7753 * rclsid [O] CLSID to read.
7757 * Failure: HRESULT code.
7759 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7764 TRACE("(%p,%p)\n",pStm,pclsid);
7766 if (!pStm || !pclsid)
7767 return E_INVALIDARG;
7769 /* clear the output args */
7770 *pclsid = CLSID_NULL;
7772 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7777 if (nbByte != sizeof(CLSID))
7778 return STG_E_READFAULT;