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 StorageBaseImpl *parentStorage;
88 typedef struct StorageInternalImpl StorageInternalImpl;
90 /* Method definitions for the Storage32InternalImpl class. */
91 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
92 DWORD openFlags, DirRef storageDirEntry);
93 static void StorageImpl_Destroy(StorageBaseImpl* iface);
94 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
95 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
96 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
97 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
98 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
99 static void StorageImpl_SaveFileHeader(StorageImpl* This);
101 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
102 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
103 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
104 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
105 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
107 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
108 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
109 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
111 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
112 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
113 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
114 ULONG blockIndex, ULONG offset, DWORD value);
115 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
116 ULONG blockIndex, ULONG offset, DWORD* value);
118 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
119 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
121 /****************************************************************************
122 * Transacted storage object that reads/writes a snapshot file.
124 typedef struct TransactedSnapshotImpl
126 struct StorageBaseImpl base;
129 * Changes are temporarily saved to the snapshot.
131 StorageBaseImpl *snapshot;
134 * Changes are committed to the transacted parent.
136 StorageBaseImpl *transactedParent;
137 } TransactedSnapshotImpl;
139 /* Generic function to create a transacted wrapper for a direct storage object. */
140 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
142 /* OLESTREAM memory structure to use for Get and Put Routines */
143 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
148 DWORD dwOleTypeNameLength;
149 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
150 CHAR *pstrOleObjFileName;
151 DWORD dwOleObjFileNameLength;
152 DWORD dwMetaFileWidth;
153 DWORD dwMetaFileHeight;
154 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
157 }OLECONVERT_OLESTREAM_DATA;
159 /* CompObj Stream structure */
160 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
165 DWORD dwCLSIDNameLength;
166 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
167 DWORD dwOleTypeNameLength;
168 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
169 DWORD dwProgIDNameLength;
170 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
172 }OLECONVERT_ISTORAGE_COMPOBJ;
175 /* Ole Presentation Stream structure */
176 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
184 }OLECONVERT_ISTORAGE_OLEPRES;
188 /***********************************************************************
189 * Forward declaration of internal functions used by the method DestroyElement
191 static HRESULT deleteStorageContents(
192 StorageBaseImpl *parentStorage,
193 DirRef indexToDelete,
194 DirEntry entryDataToDelete);
196 static HRESULT deleteStreamContents(
197 StorageBaseImpl *parentStorage,
198 DirRef indexToDelete,
199 DirEntry entryDataToDelete);
201 static HRESULT removeFromTree(
202 StorageBaseImpl *This,
203 DirRef parentStorageIndex,
204 DirRef deletedIndex);
206 /***********************************************************************
207 * Declaration of the functions used to manipulate DirEntry
210 static HRESULT insertIntoTree(
211 StorageBaseImpl *This,
212 DirRef parentStorageIndex,
213 DirRef newEntryIndex);
215 static LONG entryNameCmp(
216 const OLECHAR *name1,
217 const OLECHAR *name2);
219 static DirRef findElement(
220 StorageBaseImpl *storage,
225 static HRESULT findTreeParent(
226 StorageBaseImpl *storage,
228 const OLECHAR *childName,
229 DirEntry *parentData,
233 /***********************************************************************
234 * Declaration of miscellaneous functions...
236 static HRESULT validateSTGM(DWORD stgmValue);
238 static DWORD GetShareModeFromSTGM(DWORD stgm);
239 static DWORD GetAccessModeFromSTGM(DWORD stgm);
240 static DWORD GetCreationModeFromSTGM(DWORD stgm);
242 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
245 /****************************************************************************
246 * IEnumSTATSTGImpl definitions.
248 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
249 * This class allows iterating through the content of a storage and to find
250 * specific items inside it.
252 struct IEnumSTATSTGImpl
254 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
255 * since we want to cast this in an IEnumSTATSTG pointer */
257 LONG ref; /* Reference count */
258 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
259 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
261 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
265 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
266 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
268 /************************************************************************
272 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
274 if (index == 0xffffffff)
279 return index * BIG_BLOCK_SIZE;
282 /************************************************************************
283 ** Storage32BaseImpl implementation
285 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
286 ULARGE_INTEGER offset,
291 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
294 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
295 ULARGE_INTEGER offset,
300 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
303 /************************************************************************
304 * Storage32BaseImpl_QueryInterface (IUnknown)
306 * This method implements the common QueryInterface for all IStorage32
307 * implementations contained in this file.
309 * See Windows documentation for more details on IUnknown methods.
311 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
316 StorageBaseImpl *This = (StorageBaseImpl *)iface;
318 if ( (This==0) || (ppvObject==0) )
323 if (IsEqualGUID(&IID_IUnknown, riid) ||
324 IsEqualGUID(&IID_IStorage, riid))
328 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
330 *ppvObject = &This->pssVtbl;
334 return E_NOINTERFACE;
336 IStorage_AddRef(iface);
341 /************************************************************************
342 * Storage32BaseImpl_AddRef (IUnknown)
344 * This method implements the common AddRef for all IStorage32
345 * implementations contained in this file.
347 * See Windows documentation for more details on IUnknown methods.
349 static ULONG WINAPI StorageBaseImpl_AddRef(
352 StorageBaseImpl *This = (StorageBaseImpl *)iface;
353 ULONG ref = InterlockedIncrement(&This->ref);
355 TRACE("(%p) AddRef to %d\n", This, ref);
360 /************************************************************************
361 * Storage32BaseImpl_Release (IUnknown)
363 * This method implements the common Release for all IStorage32
364 * implementations contained in this file.
366 * See Windows documentation for more details on IUnknown methods.
368 static ULONG WINAPI StorageBaseImpl_Release(
371 StorageBaseImpl *This = (StorageBaseImpl *)iface;
373 ULONG ref = InterlockedDecrement(&This->ref);
375 TRACE("(%p) ReleaseRef to %d\n", This, ref);
380 * Since we are using a system of base-classes, we want to call the
381 * destructor of the appropriate derived class. To do this, we are
382 * using virtual functions to implement the destructor.
384 StorageBaseImpl_Destroy(This);
390 /************************************************************************
391 * Storage32BaseImpl_OpenStream (IStorage)
393 * This method will open the specified stream object from the current storage.
395 * See Windows documentation for more details on IStorage methods.
397 static HRESULT WINAPI StorageBaseImpl_OpenStream(
399 const OLECHAR* pwcsName, /* [string][in] */
400 void* reserved1, /* [unique][in] */
401 DWORD grfMode, /* [in] */
402 DWORD reserved2, /* [in] */
403 IStream** ppstm) /* [out] */
405 StorageBaseImpl *This = (StorageBaseImpl *)iface;
406 StgStreamImpl* newStream;
407 DirEntry currentEntry;
408 DirRef streamEntryRef;
409 HRESULT res = STG_E_UNKNOWN;
411 TRACE("(%p, %s, %p, %x, %d, %p)\n",
412 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
414 if ( (pwcsName==NULL) || (ppstm==0) )
422 if ( FAILED( validateSTGM(grfMode) ) ||
423 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
425 res = STG_E_INVALIDFLAG;
432 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
434 res = STG_E_INVALIDFUNCTION;
440 res = STG_E_REVERTED;
445 * Check that we're compatible with the parent's storage mode, but
446 * only if we are not in transacted mode
448 if(!(This->openFlags & STGM_TRANSACTED)) {
449 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
451 res = STG_E_INVALIDFLAG;
457 * Search for the element with the given name
459 streamEntryRef = findElement(
461 This->storageDirEntry,
466 * If it was found, construct the stream object and return a pointer to it.
468 if ( (streamEntryRef!=DIRENTRY_NULL) &&
469 (currentEntry.stgType==STGTY_STREAM) )
471 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
473 /* A single stream cannot be opened a second time. */
474 res = STG_E_ACCESSDENIED;
478 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
482 newStream->grfMode = grfMode;
483 *ppstm = (IStream*)newStream;
485 IStream_AddRef(*ppstm);
495 res = STG_E_FILENOTFOUND;
499 TRACE("<-- IStream %p\n", *ppstm);
500 TRACE("<-- %08x\n", res);
504 /************************************************************************
505 * Storage32BaseImpl_OpenStorage (IStorage)
507 * This method will open a new storage object from the current storage.
509 * See Windows documentation for more details on IStorage methods.
511 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
513 const OLECHAR* pwcsName, /* [string][unique][in] */
514 IStorage* pstgPriority, /* [unique][in] */
515 DWORD grfMode, /* [in] */
516 SNB snbExclude, /* [unique][in] */
517 DWORD reserved, /* [in] */
518 IStorage** ppstg) /* [out] */
520 StorageBaseImpl *This = (StorageBaseImpl *)iface;
521 StorageInternalImpl* newStorage;
522 StorageBaseImpl* newTransactedStorage;
523 DirEntry currentEntry;
524 DirRef storageEntryRef;
525 HRESULT res = STG_E_UNKNOWN;
527 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
528 iface, debugstr_w(pwcsName), pstgPriority,
529 grfMode, snbExclude, reserved, ppstg);
531 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
537 if (This->openFlags & STGM_SIMPLE)
539 res = STG_E_INVALIDFUNCTION;
544 if (snbExclude != NULL)
546 res = STG_E_INVALIDPARAMETER;
550 if ( FAILED( validateSTGM(grfMode) ))
552 res = STG_E_INVALIDFLAG;
559 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
560 (grfMode & STGM_DELETEONRELEASE) ||
561 (grfMode & STGM_PRIORITY) )
563 res = STG_E_INVALIDFUNCTION;
568 return STG_E_REVERTED;
571 * Check that we're compatible with the parent's storage mode,
572 * but only if we are not transacted
574 if(!(This->openFlags & STGM_TRANSACTED)) {
575 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
577 res = STG_E_ACCESSDENIED;
584 storageEntryRef = findElement(
586 This->storageDirEntry,
590 if ( (storageEntryRef!=DIRENTRY_NULL) &&
591 (currentEntry.stgType==STGTY_STORAGE) )
593 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
595 /* A single storage cannot be opened a second time. */
596 res = STG_E_ACCESSDENIED;
600 newStorage = StorageInternalImpl_Construct(
607 if (grfMode & STGM_TRANSACTED)
609 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
613 HeapFree(GetProcessHeap(), 0, newStorage);
617 *ppstg = (IStorage*)newTransactedStorage;
621 *ppstg = (IStorage*)newStorage;
624 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
630 res = STG_E_INSUFFICIENTMEMORY;
634 res = STG_E_FILENOTFOUND;
637 TRACE("<-- %08x\n", res);
641 /************************************************************************
642 * Storage32BaseImpl_EnumElements (IStorage)
644 * This method will create an enumerator object that can be used to
645 * retrieve information about all the elements in the storage object.
647 * See Windows documentation for more details on IStorage methods.
649 static HRESULT WINAPI StorageBaseImpl_EnumElements(
651 DWORD reserved1, /* [in] */
652 void* reserved2, /* [size_is][unique][in] */
653 DWORD reserved3, /* [in] */
654 IEnumSTATSTG** ppenum) /* [out] */
656 StorageBaseImpl *This = (StorageBaseImpl *)iface;
657 IEnumSTATSTGImpl* newEnum;
659 TRACE("(%p, %d, %p, %d, %p)\n",
660 iface, reserved1, reserved2, reserved3, ppenum);
662 if ( (This==0) || (ppenum==0))
666 return STG_E_REVERTED;
668 newEnum = IEnumSTATSTGImpl_Construct(
670 This->storageDirEntry);
674 *ppenum = (IEnumSTATSTG*)newEnum;
676 IEnumSTATSTG_AddRef(*ppenum);
681 return E_OUTOFMEMORY;
684 /************************************************************************
685 * Storage32BaseImpl_Stat (IStorage)
687 * This method will retrieve information about this storage object.
689 * See Windows documentation for more details on IStorage methods.
691 static HRESULT WINAPI StorageBaseImpl_Stat(
693 STATSTG* pstatstg, /* [out] */
694 DWORD grfStatFlag) /* [in] */
696 StorageBaseImpl *This = (StorageBaseImpl *)iface;
697 DirEntry currentEntry;
698 HRESULT res = STG_E_UNKNOWN;
700 TRACE("(%p, %p, %x)\n",
701 iface, pstatstg, grfStatFlag);
703 if ( (This==0) || (pstatstg==0))
711 res = STG_E_REVERTED;
715 res = StorageBaseImpl_ReadDirEntry(
717 This->storageDirEntry,
722 StorageUtl_CopyDirEntryToSTATSTG(
728 pstatstg->grfMode = This->openFlags;
729 pstatstg->grfStateBits = This->stateBits;
735 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);
737 TRACE("<-- %08x\n", res);
741 /************************************************************************
742 * Storage32BaseImpl_RenameElement (IStorage)
744 * This method will rename the specified element.
746 * See Windows documentation for more details on IStorage methods.
748 static HRESULT WINAPI StorageBaseImpl_RenameElement(
750 const OLECHAR* pwcsOldName, /* [in] */
751 const OLECHAR* pwcsNewName) /* [in] */
753 StorageBaseImpl *This = (StorageBaseImpl *)iface;
754 DirEntry currentEntry;
755 DirRef currentEntryRef;
757 TRACE("(%p, %s, %s)\n",
758 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
761 return STG_E_REVERTED;
763 currentEntryRef = findElement(This,
764 This->storageDirEntry,
768 if (currentEntryRef != DIRENTRY_NULL)
771 * There is already an element with the new name
773 return STG_E_FILEALREADYEXISTS;
777 * Search for the old element name
779 currentEntryRef = findElement(This,
780 This->storageDirEntry,
784 if (currentEntryRef != DIRENTRY_NULL)
786 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
787 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
789 WARN("Element is already open; cannot rename.\n");
790 return STG_E_ACCESSDENIED;
793 /* Remove the element from its current position in the tree */
794 removeFromTree(This, This->storageDirEntry,
797 /* Change the name of the element */
798 strcpyW(currentEntry.name, pwcsNewName);
800 /* Delete any sibling links */
801 currentEntry.leftChild = DIRENTRY_NULL;
802 currentEntry.rightChild = DIRENTRY_NULL;
804 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
807 /* Insert the element in a new position in the tree */
808 insertIntoTree(This, This->storageDirEntry,
814 * There is no element with the old name
816 return STG_E_FILENOTFOUND;
822 /************************************************************************
823 * Storage32BaseImpl_CreateStream (IStorage)
825 * This method will create a stream object within this storage
827 * See Windows documentation for more details on IStorage methods.
829 static HRESULT WINAPI StorageBaseImpl_CreateStream(
831 const OLECHAR* pwcsName, /* [string][in] */
832 DWORD grfMode, /* [in] */
833 DWORD reserved1, /* [in] */
834 DWORD reserved2, /* [in] */
835 IStream** ppstm) /* [out] */
837 StorageBaseImpl *This = (StorageBaseImpl *)iface;
838 StgStreamImpl* newStream;
839 DirEntry currentEntry, newStreamEntry;
840 DirRef currentEntryRef, newStreamEntryRef;
843 TRACE("(%p, %s, %x, %d, %d, %p)\n",
844 iface, debugstr_w(pwcsName), grfMode,
845 reserved1, reserved2, ppstm);
848 return STG_E_INVALIDPOINTER;
851 return STG_E_INVALIDNAME;
853 if (reserved1 || reserved2)
854 return STG_E_INVALIDPARAMETER;
856 if ( FAILED( validateSTGM(grfMode) ))
857 return STG_E_INVALIDFLAG;
859 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
860 return STG_E_INVALIDFLAG;
863 return STG_E_REVERTED;
868 if ((grfMode & STGM_DELETEONRELEASE) ||
869 (grfMode & STGM_TRANSACTED))
870 return STG_E_INVALIDFUNCTION;
873 * Don't worry about permissions in transacted mode, as we can always write
874 * changes; we just can't always commit them.
876 if(!(This->openFlags & STGM_TRANSACTED)) {
877 /* Can't create a stream on read-only storage */
878 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
879 return STG_E_ACCESSDENIED;
881 /* Can't create a stream with greater access than the parent. */
882 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
883 return STG_E_ACCESSDENIED;
886 if(This->openFlags & STGM_SIMPLE)
887 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
891 currentEntryRef = findElement(This,
892 This->storageDirEntry,
896 if (currentEntryRef != DIRENTRY_NULL)
899 * An element with this name already exists
901 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
903 IStorage_DestroyElement(iface, pwcsName);
906 return STG_E_FILEALREADYEXISTS;
910 * memset the empty entry
912 memset(&newStreamEntry, 0, sizeof(DirEntry));
914 newStreamEntry.sizeOfNameString =
915 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
917 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
918 return STG_E_INVALIDNAME;
920 strcpyW(newStreamEntry.name, pwcsName);
922 newStreamEntry.stgType = STGTY_STREAM;
923 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
924 newStreamEntry.size.u.LowPart = 0;
925 newStreamEntry.size.u.HighPart = 0;
927 newStreamEntry.leftChild = DIRENTRY_NULL;
928 newStreamEntry.rightChild = DIRENTRY_NULL;
929 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
931 /* call CoFileTime to get the current time
936 /* newStreamEntry.clsid */
939 * Create an entry with the new data
941 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
946 * Insert the new entry in the parent storage's tree.
950 This->storageDirEntry,
954 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
959 * Open the stream to return it.
961 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
965 *ppstm = (IStream*)newStream;
967 IStream_AddRef(*ppstm);
971 return STG_E_INSUFFICIENTMEMORY;
977 /************************************************************************
978 * Storage32BaseImpl_SetClass (IStorage)
980 * This method will write the specified CLSID in the directory entry of this
983 * See Windows documentation for more details on IStorage methods.
985 static HRESULT WINAPI StorageBaseImpl_SetClass(
987 REFCLSID clsid) /* [in] */
989 StorageBaseImpl *This = (StorageBaseImpl *)iface;
991 DirEntry currentEntry;
993 TRACE("(%p, %p)\n", iface, clsid);
996 return STG_E_REVERTED;
998 hRes = StorageBaseImpl_ReadDirEntry(This,
999 This->storageDirEntry,
1001 if (SUCCEEDED(hRes))
1003 currentEntry.clsid = *clsid;
1005 hRes = StorageBaseImpl_WriteDirEntry(This,
1006 This->storageDirEntry,
1013 /************************************************************************
1014 ** Storage32Impl implementation
1017 /************************************************************************
1018 * Storage32BaseImpl_CreateStorage (IStorage)
1020 * This method will create the storage object within the provided storage.
1022 * See Windows documentation for more details on IStorage methods.
1024 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1026 const OLECHAR *pwcsName, /* [string][in] */
1027 DWORD grfMode, /* [in] */
1028 DWORD reserved1, /* [in] */
1029 DWORD reserved2, /* [in] */
1030 IStorage **ppstg) /* [out] */
1032 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1034 DirEntry currentEntry;
1036 DirRef currentEntryRef;
1040 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1041 iface, debugstr_w(pwcsName), grfMode,
1042 reserved1, reserved2, ppstg);
1045 return STG_E_INVALIDPOINTER;
1047 if (This->openFlags & STGM_SIMPLE)
1049 return STG_E_INVALIDFUNCTION;
1053 return STG_E_INVALIDNAME;
1057 if ( FAILED( validateSTGM(grfMode) ) ||
1058 (grfMode & STGM_DELETEONRELEASE) )
1060 WARN("bad grfMode: 0x%x\n", grfMode);
1061 return STG_E_INVALIDFLAG;
1065 return STG_E_REVERTED;
1068 * Check that we're compatible with the parent's storage mode
1070 if ( !(This->openFlags & STGM_TRANSACTED) &&
1071 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1073 WARN("access denied\n");
1074 return STG_E_ACCESSDENIED;
1077 currentEntryRef = findElement(This,
1078 This->storageDirEntry,
1082 if (currentEntryRef != DIRENTRY_NULL)
1085 * An element with this name already exists
1087 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1088 ((This->openFlags & STGM_TRANSACTED) ||
1089 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1091 hr = IStorage_DestroyElement(iface, pwcsName);
1097 WARN("file already exists\n");
1098 return STG_E_FILEALREADYEXISTS;
1101 else if (!(This->openFlags & STGM_TRANSACTED) &&
1102 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1104 WARN("read-only storage\n");
1105 return STG_E_ACCESSDENIED;
1108 memset(&newEntry, 0, sizeof(DirEntry));
1110 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1112 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1114 FIXME("name too long\n");
1115 return STG_E_INVALIDNAME;
1118 strcpyW(newEntry.name, pwcsName);
1120 newEntry.stgType = STGTY_STORAGE;
1121 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1122 newEntry.size.u.LowPart = 0;
1123 newEntry.size.u.HighPart = 0;
1125 newEntry.leftChild = DIRENTRY_NULL;
1126 newEntry.rightChild = DIRENTRY_NULL;
1127 newEntry.dirRootEntry = DIRENTRY_NULL;
1129 /* call CoFileTime to get the current time
1134 /* newEntry.clsid */
1137 * Create a new directory entry for the storage
1139 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1144 * Insert the new directory entry into the parent storage's tree
1146 hr = insertIntoTree(
1148 This->storageDirEntry,
1152 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1157 * Open it to get a pointer to return.
1159 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1161 if( (hr != S_OK) || (*ppstg == NULL))
1171 /***************************************************************************
1175 * Reserve a directory entry in the file and initialize it.
1177 static HRESULT StorageImpl_CreateDirEntry(
1178 StorageBaseImpl *base,
1179 const DirEntry *newData,
1182 StorageImpl *storage = (StorageImpl*)base;
1183 ULONG currentEntryIndex = 0;
1184 ULONG newEntryIndex = DIRENTRY_NULL;
1186 BYTE currentData[RAW_DIRENTRY_SIZE];
1187 WORD sizeOfNameString;
1191 hr = StorageImpl_ReadRawDirEntry(storage,
1197 StorageUtl_ReadWord(
1199 OFFSET_PS_NAMELENGTH,
1202 if (sizeOfNameString == 0)
1205 * The entry exists and is available, we found it.
1207 newEntryIndex = currentEntryIndex;
1213 * We exhausted the directory entries, we will create more space below
1215 newEntryIndex = currentEntryIndex;
1217 currentEntryIndex++;
1219 } while (newEntryIndex == DIRENTRY_NULL);
1222 * grow the directory stream
1226 BYTE emptyData[RAW_DIRENTRY_SIZE];
1227 ULARGE_INTEGER newSize;
1229 ULONG lastEntry = 0;
1230 ULONG blockCount = 0;
1233 * obtain the new count of blocks in the directory stream
1235 blockCount = BlockChainStream_GetCount(
1236 storage->rootBlockChain)+1;
1239 * initialize the size used by the directory stream
1241 newSize.u.HighPart = 0;
1242 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1245 * add a block to the directory stream
1247 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1250 * memset the empty entry in order to initialize the unused newly
1253 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1258 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1261 entryIndex = newEntryIndex + 1;
1262 entryIndex < lastEntry;
1265 StorageImpl_WriteRawDirEntry(
1272 UpdateRawDirEntry(currentData, newData);
1274 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1277 *index = newEntryIndex;
1282 /***************************************************************************
1286 * Mark a directory entry in the file as free.
1288 static HRESULT StorageImpl_DestroyDirEntry(
1289 StorageBaseImpl *base,
1293 BYTE emptyData[RAW_DIRENTRY_SIZE];
1294 StorageImpl *storage = (StorageImpl*)base;
1296 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1298 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1304 /***************************************************************************
1308 * Destroy an entry, its attached data, and all entries reachable from it.
1310 static HRESULT DestroyReachableEntries(
1311 StorageBaseImpl *base,
1316 ULARGE_INTEGER zero;
1320 if (index != DIRENTRY_NULL)
1322 hr = StorageBaseImpl_ReadDirEntry(base, index, &data);
1325 hr = DestroyReachableEntries(base, data.dirRootEntry);
1328 hr = DestroyReachableEntries(base, data.leftChild);
1331 hr = DestroyReachableEntries(base, data.rightChild);
1334 hr = StorageBaseImpl_StreamSetSize(base, index, zero);
1337 hr = StorageBaseImpl_DestroyDirEntry(base, index);
1344 /****************************************************************************
1348 * Case insensitive comparison of DirEntry.name by first considering
1351 * Returns <0 when name1 < name2
1352 * >0 when name1 > name2
1353 * 0 when name1 == name2
1355 static LONG entryNameCmp(
1356 const OLECHAR *name1,
1357 const OLECHAR *name2)
1359 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1361 while (diff == 0 && *name1 != 0)
1364 * We compare the string themselves only when they are of the same length
1366 diff = toupperW(*name1++) - toupperW(*name2++);
1372 /****************************************************************************
1376 * Add a directory entry to a storage
1378 static HRESULT insertIntoTree(
1379 StorageBaseImpl *This,
1380 DirRef parentStorageIndex,
1381 DirRef newEntryIndex)
1383 DirEntry currentEntry;
1387 * Read the inserted entry
1389 StorageBaseImpl_ReadDirEntry(This,
1394 * Read the storage entry
1396 StorageBaseImpl_ReadDirEntry(This,
1400 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1403 * The root storage contains some element, therefore, start the research
1404 * for the appropriate location.
1407 DirRef current, next, previous, currentEntryId;
1410 * Keep a reference to the root of the storage's element tree
1412 currentEntryId = currentEntry.dirRootEntry;
1417 StorageBaseImpl_ReadDirEntry(This,
1418 currentEntry.dirRootEntry,
1421 previous = currentEntry.leftChild;
1422 next = currentEntry.rightChild;
1423 current = currentEntryId;
1427 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1431 if (previous != DIRENTRY_NULL)
1433 StorageBaseImpl_ReadDirEntry(This,
1440 currentEntry.leftChild = newEntryIndex;
1441 StorageBaseImpl_WriteDirEntry(This,
1449 if (next != DIRENTRY_NULL)
1451 StorageBaseImpl_ReadDirEntry(This,
1458 currentEntry.rightChild = newEntryIndex;
1459 StorageBaseImpl_WriteDirEntry(This,
1468 * Trying to insert an item with the same name in the
1469 * subtree structure.
1471 return STG_E_FILEALREADYEXISTS;
1474 previous = currentEntry.leftChild;
1475 next = currentEntry.rightChild;
1481 * The storage is empty, make the new entry the root of its element tree
1483 currentEntry.dirRootEntry = newEntryIndex;
1484 StorageBaseImpl_WriteDirEntry(This,
1492 /****************************************************************************
1496 * Find and read the element of a storage with the given name.
1498 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1499 const OLECHAR *name, DirEntry *data)
1501 DirRef currentEntry;
1503 /* Read the storage entry to find the root of the tree. */
1504 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1506 currentEntry = data->dirRootEntry;
1508 while (currentEntry != DIRENTRY_NULL)
1512 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1514 cmp = entryNameCmp(name, data->name);
1521 currentEntry = data->leftChild;
1524 currentEntry = data->rightChild;
1527 return currentEntry;
1530 /****************************************************************************
1534 * Find and read the binary tree parent of the element with the given name.
1536 * If there is no such element, find a place where it could be inserted and
1537 * return STG_E_FILENOTFOUND.
1539 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1540 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1546 /* Read the storage entry to find the root of the tree. */
1547 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1549 *parentEntry = storageEntry;
1550 *relation = DIRENTRY_RELATION_DIR;
1552 childEntry = parentData->dirRootEntry;
1554 while (childEntry != DIRENTRY_NULL)
1558 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1560 cmp = entryNameCmp(childName, childData.name);
1568 *parentData = childData;
1569 *parentEntry = childEntry;
1570 *relation = DIRENTRY_RELATION_PREVIOUS;
1572 childEntry = parentData->leftChild;
1577 *parentData = childData;
1578 *parentEntry = childEntry;
1579 *relation = DIRENTRY_RELATION_NEXT;
1581 childEntry = parentData->rightChild;
1585 if (childEntry == DIRENTRY_NULL)
1586 return STG_E_FILENOTFOUND;
1592 /*************************************************************************
1595 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1597 DWORD ciidExclude, /* [in] */
1598 const IID* rgiidExclude, /* [size_is][unique][in] */
1599 SNB snbExclude, /* [unique][in] */
1600 IStorage* pstgDest) /* [unique][in] */
1602 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1604 IEnumSTATSTG *elements = 0;
1605 STATSTG curElement, strStat;
1607 IStorage *pstgTmp, *pstgChild;
1608 IStream *pstrTmp, *pstrChild;
1611 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1614 TRACE("(%p, %d, %p, %p, %p)\n",
1615 iface, ciidExclude, rgiidExclude,
1616 snbExclude, pstgDest);
1618 if ( pstgDest == 0 )
1619 return STG_E_INVALIDPOINTER;
1622 * Enumerate the elements
1624 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1632 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1633 IStorage_SetClass( pstgDest, &curElement.clsid );
1635 for(i = 0; i < ciidExclude; ++i)
1637 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1638 skip_storage = TRUE;
1639 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1642 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1648 * Obtain the next element
1650 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1652 if ( hr == S_FALSE )
1654 hr = S_OK; /* done, every element has been copied */
1660 WCHAR **snb = snbExclude;
1662 while ( *snb != NULL && !skip )
1664 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1673 if (curElement.type == STGTY_STORAGE)
1679 * open child source storage
1681 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1682 STGM_READ|STGM_SHARE_EXCLUSIVE,
1683 NULL, 0, &pstgChild );
1689 * create a new storage in destination storage
1691 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1692 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1696 * if it already exist, don't create a new one use this one
1698 if (hr == STG_E_FILEALREADYEXISTS)
1700 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1701 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1702 NULL, 0, &pstgTmp );
1708 * do the copy recursively
1710 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1713 IStorage_Release( pstgTmp );
1716 IStorage_Release( pstgChild );
1718 else if (curElement.type == STGTY_STREAM)
1724 * create a new stream in destination storage. If the stream already
1725 * exist, it will be deleted and a new one will be created.
1727 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1728 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1735 * open child stream storage. This operation must succeed even if the
1736 * stream is already open, so we use internal functions to do it.
1738 srcEntryRef = findElement( This, This->storageDirEntry, curElement.pwcsName,
1742 ERR("source stream not found\n");
1743 hr = STG_E_DOCFILECORRUPT;
1748 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntryRef);
1750 IStream_AddRef(pstrChild);
1758 * Get the size of the source stream
1760 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1763 * Set the size of the destination stream.
1765 IStream_SetSize(pstrTmp, strStat.cbSize);
1770 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1773 IStream_Release( pstrChild );
1776 IStream_Release( pstrTmp );
1780 WARN("unknown element type: %d\n", curElement.type);
1784 CoTaskMemFree(curElement.pwcsName);
1785 } while (hr == S_OK);
1790 IEnumSTATSTG_Release(elements);
1795 /*************************************************************************
1796 * MoveElementTo (IStorage)
1798 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1800 const OLECHAR *pwcsName, /* [string][in] */
1801 IStorage *pstgDest, /* [unique][in] */
1802 const OLECHAR *pwcsNewName,/* [string][in] */
1803 DWORD grfFlags) /* [in] */
1805 FIXME("(%p %s %p %s %u): stub\n", iface,
1806 debugstr_w(pwcsName), pstgDest,
1807 debugstr_w(pwcsNewName), grfFlags);
1811 /*************************************************************************
1814 * Ensures that any changes made to a storage object open in transacted mode
1815 * are reflected in the parent storage
1818 * Wine doesn't implement transacted mode, which seems to be a basic
1819 * optimization, so we can ignore this stub for now.
1821 static HRESULT WINAPI StorageImpl_Commit(
1823 DWORD grfCommitFlags)/* [in] */
1825 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1829 /*************************************************************************
1832 * Discard all changes that have been made since the last commit operation
1834 static HRESULT WINAPI StorageImpl_Revert(
1837 TRACE("(%p)\n", iface);
1841 /*************************************************************************
1842 * DestroyElement (IStorage)
1844 * Strategy: This implementation is built this way for simplicity not for speed.
1845 * I always delete the topmost element of the enumeration and adjust
1846 * the deleted element pointer all the time. This takes longer to
1847 * do but allow to reinvoke DestroyElement whenever we encounter a
1848 * storage object. The optimisation resides in the usage of another
1849 * enumeration strategy that would give all the leaves of a storage
1850 * first. (postfix order)
1852 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1854 const OLECHAR *pwcsName)/* [string][in] */
1856 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1859 DirEntry entryToDelete;
1860 DirRef entryToDeleteRef;
1863 iface, debugstr_w(pwcsName));
1866 return STG_E_INVALIDPOINTER;
1869 return STG_E_REVERTED;
1871 if ( !(This->openFlags & STGM_TRANSACTED) &&
1872 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1873 return STG_E_ACCESSDENIED;
1875 entryToDeleteRef = findElement(
1877 This->storageDirEntry,
1881 if ( entryToDeleteRef == DIRENTRY_NULL )
1883 return STG_E_FILENOTFOUND;
1886 if ( entryToDelete.stgType == STGTY_STORAGE )
1888 hr = deleteStorageContents(
1893 else if ( entryToDelete.stgType == STGTY_STREAM )
1895 hr = deleteStreamContents(
1905 * Remove the entry from its parent storage
1907 hr = removeFromTree(
1909 This->storageDirEntry,
1913 * Invalidate the entry
1916 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1922 /******************************************************************************
1923 * Internal stream list handlers
1926 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1928 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1929 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1932 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1934 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1935 list_remove(&(strm->StrmListEntry));
1938 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1940 StgStreamImpl *strm;
1942 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1944 if (strm->dirEntry == streamEntry)
1953 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1955 StorageInternalImpl *childstg;
1957 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1959 if (childstg->base.storageDirEntry == storageEntry)
1968 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1970 struct list *cur, *cur2;
1971 StgStreamImpl *strm=NULL;
1972 StorageInternalImpl *childstg=NULL;
1974 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1975 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1976 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1977 strm->parentStorage = NULL;
1981 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1982 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1983 StorageBaseImpl_Invalidate( &childstg->base );
1986 if (stg->transactedChild)
1988 StorageBaseImpl_Invalidate(stg->transactedChild);
1990 stg->transactedChild = NULL;
1995 /*********************************************************************
1999 * Delete the contents of a storage entry.
2002 static HRESULT deleteStorageContents(
2003 StorageBaseImpl *parentStorage,
2004 DirRef indexToDelete,
2005 DirEntry entryDataToDelete)
2007 IEnumSTATSTG *elements = 0;
2008 IStorage *childStorage = 0;
2009 STATSTG currentElement;
2011 HRESULT destroyHr = S_OK;
2012 StorageInternalImpl *stg, *stg2;
2014 /* Invalidate any open storage objects. */
2015 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2017 if (stg->base.storageDirEntry == indexToDelete)
2019 StorageBaseImpl_Invalidate(&stg->base);
2024 * Open the storage and enumerate it
2026 hr = StorageBaseImpl_OpenStorage(
2027 (IStorage*)parentStorage,
2028 entryDataToDelete.name,
2030 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2041 * Enumerate the elements
2043 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2048 * Obtain the next element
2050 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
2053 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2055 CoTaskMemFree(currentElement.pwcsName);
2059 * We need to Reset the enumeration every time because we delete elements
2060 * and the enumeration could be invalid
2062 IEnumSTATSTG_Reset(elements);
2064 } while ((hr == S_OK) && (destroyHr == S_OK));
2066 IStorage_Release(childStorage);
2067 IEnumSTATSTG_Release(elements);
2072 /*********************************************************************
2076 * Perform the deletion of a stream's data
2079 static HRESULT deleteStreamContents(
2080 StorageBaseImpl *parentStorage,
2081 DirRef indexToDelete,
2082 DirEntry entryDataToDelete)
2086 ULARGE_INTEGER size;
2087 StgStreamImpl *strm, *strm2;
2089 /* Invalidate any open stream objects. */
2090 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2092 if (strm->dirEntry == indexToDelete)
2094 TRACE("Stream deleted %p\n", strm);
2095 strm->parentStorage = NULL;
2096 list_remove(&strm->StrmListEntry);
2100 size.u.HighPart = 0;
2103 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2104 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2114 hr = IStream_SetSize(pis, size);
2122 * Release the stream object.
2124 IStream_Release(pis);
2129 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2133 case DIRENTRY_RELATION_PREVIOUS:
2134 entry->leftChild = new_target;
2136 case DIRENTRY_RELATION_NEXT:
2137 entry->rightChild = new_target;
2139 case DIRENTRY_RELATION_DIR:
2140 entry->dirRootEntry = new_target;
2147 /*************************************************************************
2151 * This method removes a directory entry from its parent storage tree without
2152 * freeing any resources attached to it.
2154 static HRESULT removeFromTree(
2155 StorageBaseImpl *This,
2156 DirRef parentStorageIndex,
2157 DirRef deletedIndex)
2160 DirEntry entryToDelete;
2161 DirEntry parentEntry;
2162 DirRef parentEntryRef;
2163 ULONG typeOfRelation;
2165 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2171 * Find the element that links to the one we want to delete.
2173 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2174 &parentEntry, &parentEntryRef, &typeOfRelation);
2179 if (entryToDelete.leftChild != DIRENTRY_NULL)
2182 * Replace the deleted entry with its left child
2184 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2186 hr = StorageBaseImpl_WriteDirEntry(
2195 if (entryToDelete.rightChild != DIRENTRY_NULL)
2198 * We need to reinsert the right child somewhere. We already know it and
2199 * its children are greater than everything in the left tree, so we
2200 * insert it at the rightmost point in the left tree.
2202 DirRef newRightChildParent = entryToDelete.leftChild;
2203 DirEntry newRightChildParentEntry;
2207 hr = StorageBaseImpl_ReadDirEntry(
2209 newRightChildParent,
2210 &newRightChildParentEntry);
2216 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2217 newRightChildParent = newRightChildParentEntry.rightChild;
2218 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2220 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2222 hr = StorageBaseImpl_WriteDirEntry(
2224 newRightChildParent,
2225 &newRightChildParentEntry);
2235 * Replace the deleted entry with its right child
2237 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2239 hr = StorageBaseImpl_WriteDirEntry(
2253 /******************************************************************************
2254 * SetElementTimes (IStorage)
2256 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2258 const OLECHAR *pwcsName,/* [string][in] */
2259 const FILETIME *pctime, /* [in] */
2260 const FILETIME *patime, /* [in] */
2261 const FILETIME *pmtime) /* [in] */
2263 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2267 /******************************************************************************
2268 * SetStateBits (IStorage)
2270 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2272 DWORD grfStateBits,/* [in] */
2273 DWORD grfMask) /* [in] */
2275 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2278 return STG_E_REVERTED;
2280 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2284 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2285 DirRef index, const DirEntry *data)
2287 StorageImpl *This = (StorageImpl*)base;
2288 return StorageImpl_WriteDirEntry(This, index, data);
2291 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2292 DirRef index, DirEntry *data)
2294 StorageImpl *This = (StorageImpl*)base;
2295 return StorageImpl_ReadDirEntry(This, index, data);
2298 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2302 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2304 if (!This->blockChainCache[i])
2306 return &This->blockChainCache[i];
2310 i = This->blockChainToEvict;
2312 BlockChainStream_Destroy(This->blockChainCache[i]);
2313 This->blockChainCache[i] = NULL;
2315 This->blockChainToEvict++;
2316 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2317 This->blockChainToEvict = 0;
2319 return &This->blockChainCache[i];
2322 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2325 int i, free_index=-1;
2327 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2329 if (!This->blockChainCache[i])
2331 if (free_index == -1) free_index = i;
2333 else if (This->blockChainCache[i]->ownerDirEntry == index)
2335 return &This->blockChainCache[i];
2339 if (free_index == -1)
2341 free_index = This->blockChainToEvict;
2343 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2344 This->blockChainCache[free_index] = NULL;
2346 This->blockChainToEvict++;
2347 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2348 This->blockChainToEvict = 0;
2351 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2352 return &This->blockChainCache[free_index];
2355 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2356 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2358 StorageImpl *This = (StorageImpl*)base;
2363 hr = StorageImpl_ReadDirEntry(This, index, &data);
2364 if (FAILED(hr)) return hr;
2366 if (data.size.QuadPart == 0)
2372 if (offset.QuadPart + size > data.size.QuadPart)
2374 bytesToRead = data.size.QuadPart - offset.QuadPart;
2381 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2383 SmallBlockChainStream *stream;
2385 stream = SmallBlockChainStream_Construct(This, NULL, index);
2386 if (!stream) return E_OUTOFMEMORY;
2388 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2390 SmallBlockChainStream_Destroy(stream);
2396 BlockChainStream *stream = NULL;
2398 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2399 if (!stream) return E_OUTOFMEMORY;
2401 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2407 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2408 ULARGE_INTEGER newsize)
2410 StorageImpl *This = (StorageImpl*)base;
2413 SmallBlockChainStream *smallblock=NULL;
2414 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2416 hr = StorageImpl_ReadDirEntry(This, index, &data);
2417 if (FAILED(hr)) return hr;
2419 /* In simple mode keep the stream size above the small block limit */
2420 if (This->base.openFlags & STGM_SIMPLE)
2421 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2423 if (data.size.QuadPart == newsize.QuadPart)
2426 /* Create a block chain object of the appropriate type */
2427 if (data.size.QuadPart == 0)
2429 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2431 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2432 if (!smallblock) return E_OUTOFMEMORY;
2436 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2437 bigblock = *pbigblock;
2438 if (!bigblock) return E_OUTOFMEMORY;
2441 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2443 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2444 if (!smallblock) return E_OUTOFMEMORY;
2448 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2449 bigblock = *pbigblock;
2450 if (!bigblock) return E_OUTOFMEMORY;
2453 /* Change the block chain type if necessary. */
2454 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2456 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2459 SmallBlockChainStream_Destroy(smallblock);
2463 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2464 *pbigblock = bigblock;
2466 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2468 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2473 /* Set the size of the block chain. */
2476 SmallBlockChainStream_SetSize(smallblock, newsize);
2477 SmallBlockChainStream_Destroy(smallblock);
2481 BlockChainStream_SetSize(bigblock, newsize);
2484 /* Set the size in the directory entry. */
2485 hr = StorageImpl_ReadDirEntry(This, index, &data);
2488 data.size = newsize;
2490 hr = StorageImpl_WriteDirEntry(This, index, &data);
2495 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2496 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2498 StorageImpl *This = (StorageImpl*)base;
2501 ULARGE_INTEGER newSize;
2503 hr = StorageImpl_ReadDirEntry(This, index, &data);
2504 if (FAILED(hr)) return hr;
2506 /* Grow the stream if necessary */
2507 newSize.QuadPart = 0;
2508 newSize.QuadPart = offset.QuadPart + size;
2510 if (newSize.QuadPart > data.size.QuadPart)
2512 hr = StorageImpl_StreamSetSize(base, index, newSize);
2516 hr = StorageImpl_ReadDirEntry(This, index, &data);
2517 if (FAILED(hr)) return hr;
2520 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2522 SmallBlockChainStream *stream;
2524 stream = SmallBlockChainStream_Construct(This, NULL, index);
2525 if (!stream) return E_OUTOFMEMORY;
2527 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2529 SmallBlockChainStream_Destroy(stream);
2535 BlockChainStream *stream;
2537 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2538 if (!stream) return E_OUTOFMEMORY;
2540 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2547 * Virtual function table for the IStorage32Impl class.
2549 static const IStorageVtbl Storage32Impl_Vtbl =
2551 StorageBaseImpl_QueryInterface,
2552 StorageBaseImpl_AddRef,
2553 StorageBaseImpl_Release,
2554 StorageBaseImpl_CreateStream,
2555 StorageBaseImpl_OpenStream,
2556 StorageBaseImpl_CreateStorage,
2557 StorageBaseImpl_OpenStorage,
2558 StorageBaseImpl_CopyTo,
2559 StorageBaseImpl_MoveElementTo,
2562 StorageBaseImpl_EnumElements,
2563 StorageBaseImpl_DestroyElement,
2564 StorageBaseImpl_RenameElement,
2565 StorageBaseImpl_SetElementTimes,
2566 StorageBaseImpl_SetClass,
2567 StorageBaseImpl_SetStateBits,
2568 StorageBaseImpl_Stat
2571 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2573 StorageImpl_Destroy,
2574 StorageImpl_Invalidate,
2575 StorageImpl_CreateDirEntry,
2576 StorageImpl_BaseWriteDirEntry,
2577 StorageImpl_BaseReadDirEntry,
2578 StorageImpl_DestroyDirEntry,
2579 StorageImpl_StreamReadAt,
2580 StorageImpl_StreamWriteAt,
2581 StorageImpl_StreamSetSize
2584 static HRESULT StorageImpl_Construct(
2591 StorageImpl** result)
2595 DirEntry currentEntry;
2596 DirRef currentEntryRef;
2597 WCHAR fullpath[MAX_PATH];
2599 if ( FAILED( validateSTGM(openFlags) ))
2600 return STG_E_INVALIDFLAG;
2602 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2604 return E_OUTOFMEMORY;
2606 memset(This, 0, sizeof(StorageImpl));
2608 list_init(&This->base.strmHead);
2610 list_init(&This->base.storageHead);
2612 This->base.lpVtbl = &Storage32Impl_Vtbl;
2613 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2614 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2615 This->base.openFlags = (openFlags & ~STGM_CREATE);
2617 This->base.create = create;
2619 This->base.reverted = 0;
2621 This->hFile = hFile;
2624 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2626 lstrcpynW(fullpath, pwcsName, MAX_PATH);
2628 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2629 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2630 if (!This->pwcsName)
2632 hr = STG_E_INSUFFICIENTMEMORY;
2635 strcpyW(This->pwcsName, fullpath);
2636 This->base.filename = This->pwcsName;
2640 * Initialize the big block cache.
2642 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2643 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2644 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2650 if (This->bigBlockFile == 0)
2658 ULARGE_INTEGER size;
2659 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2662 * Initialize all header variables:
2663 * - The big block depot consists of one block and it is at block 0
2664 * - The directory table starts at block 1
2665 * - There is no small block depot
2667 memset( This->bigBlockDepotStart,
2669 sizeof(This->bigBlockDepotStart));
2671 This->bigBlockDepotCount = 1;
2672 This->bigBlockDepotStart[0] = 0;
2673 This->rootStartBlock = 1;
2674 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2675 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2676 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2677 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2678 This->extBigBlockDepotCount = 0;
2680 StorageImpl_SaveFileHeader(This);
2683 * Add one block for the big block depot and one block for the directory table
2685 size.u.HighPart = 0;
2686 size.u.LowPart = This->bigBlockSize * 3;
2687 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2690 * Initialize the big block depot
2692 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2693 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2694 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2695 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2700 * Load the header for the file.
2702 hr = StorageImpl_LoadFileHeader(This);
2711 * There is no block depot cached yet.
2713 This->indexBlockDepotCached = 0xFFFFFFFF;
2716 * Start searching for free blocks with block 0.
2718 This->prevFreeBlock = 0;
2721 * Create the block chain abstractions.
2723 if(!(This->rootBlockChain =
2724 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2726 hr = STG_E_READFAULT;
2730 if(!(This->smallBlockDepotChain =
2731 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2734 hr = STG_E_READFAULT;
2739 * Write the root storage entry (memory only)
2745 * Initialize the directory table
2747 memset(&rootEntry, 0, sizeof(rootEntry));
2748 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2749 sizeof(rootEntry.name)/sizeof(WCHAR) );
2750 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2751 rootEntry.stgType = STGTY_ROOT;
2752 rootEntry.leftChild = DIRENTRY_NULL;
2753 rootEntry.rightChild = DIRENTRY_NULL;
2754 rootEntry.dirRootEntry = DIRENTRY_NULL;
2755 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2756 rootEntry.size.u.HighPart = 0;
2757 rootEntry.size.u.LowPart = 0;
2759 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2763 * Find the ID of the root storage.
2765 currentEntryRef = 0;
2769 hr = StorageImpl_ReadDirEntry(
2776 if ( (currentEntry.sizeOfNameString != 0 ) &&
2777 (currentEntry.stgType == STGTY_ROOT) )
2779 This->base.storageDirEntry = currentEntryRef;
2785 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2789 hr = STG_E_READFAULT;
2794 * Create the block chain abstraction for the small block root chain.
2796 if(!(This->smallBlockRootChain =
2797 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2799 hr = STG_E_READFAULT;
2805 IStorage_Release((IStorage*)This);
2814 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2816 StorageImpl *This = (StorageImpl*) iface;
2818 StorageBaseImpl_DeleteAll(&This->base);
2820 This->base.reverted = 1;
2823 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2825 StorageImpl *This = (StorageImpl*) iface;
2827 TRACE("(%p)\n", This);
2829 StorageImpl_Invalidate(iface);
2831 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2833 BlockChainStream_Destroy(This->smallBlockRootChain);
2834 BlockChainStream_Destroy(This->rootBlockChain);
2835 BlockChainStream_Destroy(This->smallBlockDepotChain);
2837 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2838 BlockChainStream_Destroy(This->blockChainCache[i]);
2840 if (This->bigBlockFile)
2841 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2842 HeapFree(GetProcessHeap(), 0, This);
2845 /******************************************************************************
2846 * Storage32Impl_GetNextFreeBigBlock
2848 * Returns the index of the next free big block.
2849 * If the big block depot is filled, this method will enlarge it.
2852 static ULONG StorageImpl_GetNextFreeBigBlock(
2855 ULONG depotBlockIndexPos;
2856 BYTE depotBuffer[BIG_BLOCK_SIZE];
2858 ULONG depotBlockOffset;
2859 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2860 ULONG nextBlockIndex = BLOCK_SPECIAL;
2862 ULONG freeBlock = BLOCK_UNUSED;
2864 depotIndex = This->prevFreeBlock / blocksPerDepot;
2865 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2868 * Scan the entire big block depot until we find a block marked free
2870 while (nextBlockIndex != BLOCK_UNUSED)
2872 if (depotIndex < COUNT_BBDEPOTINHEADER)
2874 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2877 * Grow the primary depot.
2879 if (depotBlockIndexPos == BLOCK_UNUSED)
2881 depotBlockIndexPos = depotIndex*blocksPerDepot;
2884 * Add a block depot.
2886 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2887 This->bigBlockDepotCount++;
2888 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2891 * Flag it as a block depot.
2893 StorageImpl_SetNextBlockInChain(This,
2897 /* Save new header information.
2899 StorageImpl_SaveFileHeader(This);
2904 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2906 if (depotBlockIndexPos == BLOCK_UNUSED)
2909 * Grow the extended depot.
2911 ULONG extIndex = BLOCK_UNUSED;
2912 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2913 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2915 if (extBlockOffset == 0)
2917 /* We need an extended block.
2919 extIndex = Storage32Impl_AddExtBlockDepot(This);
2920 This->extBigBlockDepotCount++;
2921 depotBlockIndexPos = extIndex + 1;
2924 depotBlockIndexPos = depotIndex * blocksPerDepot;
2927 * Add a block depot and mark it in the extended block.
2929 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2930 This->bigBlockDepotCount++;
2931 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2933 /* Flag the block depot.
2935 StorageImpl_SetNextBlockInChain(This,
2939 /* If necessary, flag the extended depot block.
2941 if (extIndex != BLOCK_UNUSED)
2942 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2944 /* Save header information.
2946 StorageImpl_SaveFileHeader(This);
2950 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2954 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2955 ( nextBlockIndex != BLOCK_UNUSED))
2957 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2959 if (nextBlockIndex == BLOCK_UNUSED)
2961 freeBlock = (depotIndex * blocksPerDepot) +
2962 (depotBlockOffset/sizeof(ULONG));
2965 depotBlockOffset += sizeof(ULONG);
2970 depotBlockOffset = 0;
2974 * make sure that the block physically exists before using it
2976 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2978 This->prevFreeBlock = freeBlock;
2983 /******************************************************************************
2984 * Storage32Impl_AddBlockDepot
2986 * This will create a depot block, essentially it is a block initialized
2989 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2991 BYTE blockBuffer[BIG_BLOCK_SIZE];
2994 * Initialize blocks as free
2996 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2997 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3000 /******************************************************************************
3001 * Storage32Impl_GetExtDepotBlock
3003 * Returns the index of the block that corresponds to the specified depot
3004 * index. This method is only for depot indexes equal or greater than
3005 * COUNT_BBDEPOTINHEADER.
3007 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3009 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3010 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3011 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3012 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3013 ULONG blockIndex = BLOCK_UNUSED;
3014 ULONG extBlockIndex = This->extBigBlockDepotStart;
3016 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3018 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
3019 return BLOCK_UNUSED;
3021 while (extBlockCount > 0)
3023 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3027 if (extBlockIndex != BLOCK_UNUSED)
3028 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3029 extBlockOffset * sizeof(ULONG), &blockIndex);
3034 /******************************************************************************
3035 * Storage32Impl_SetExtDepotBlock
3037 * Associates the specified block index to the specified depot index.
3038 * This method is only for depot indexes equal or greater than
3039 * COUNT_BBDEPOTINHEADER.
3041 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3043 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3044 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3045 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3046 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3047 ULONG extBlockIndex = This->extBigBlockDepotStart;
3049 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3051 while (extBlockCount > 0)
3053 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3057 if (extBlockIndex != BLOCK_UNUSED)
3059 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3060 extBlockOffset * sizeof(ULONG),
3065 /******************************************************************************
3066 * Storage32Impl_AddExtBlockDepot
3068 * Creates an extended depot block.
3070 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3072 ULONG numExtBlocks = This->extBigBlockDepotCount;
3073 ULONG nextExtBlock = This->extBigBlockDepotStart;
3074 BYTE depotBuffer[BIG_BLOCK_SIZE];
3075 ULONG index = BLOCK_UNUSED;
3076 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3077 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3078 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3080 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3081 blocksPerDepotBlock;
3083 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3086 * The first extended block.
3088 This->extBigBlockDepotStart = index;
3094 * Follow the chain to the last one.
3096 for (i = 0; i < (numExtBlocks - 1); i++)
3098 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3102 * Add the new extended block to the chain.
3104 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3109 * Initialize this block.
3111 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3112 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3117 /******************************************************************************
3118 * Storage32Impl_FreeBigBlock
3120 * This method will flag the specified block as free in the big block depot.
3122 static void StorageImpl_FreeBigBlock(
3126 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3128 if (blockIndex < This->prevFreeBlock)
3129 This->prevFreeBlock = blockIndex;
3132 /************************************************************************
3133 * Storage32Impl_GetNextBlockInChain
3135 * This method will retrieve the block index of the next big block in
3138 * Params: This - Pointer to the Storage object.
3139 * blockIndex - Index of the block to retrieve the chain
3141 * nextBlockIndex - receives the return value.
3143 * Returns: This method returns the index of the next block in the chain.
3144 * It will return the constants:
3145 * BLOCK_SPECIAL - If the block given was not part of a
3147 * BLOCK_END_OF_CHAIN - If the block given was the last in
3149 * BLOCK_UNUSED - If the block given was not past of a chain
3151 * BLOCK_EXTBBDEPOT - This block is part of the extended
3154 * See Windows documentation for more details on IStorage methods.
3156 static HRESULT StorageImpl_GetNextBlockInChain(
3159 ULONG* nextBlockIndex)
3161 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3162 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3163 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3164 BYTE depotBuffer[BIG_BLOCK_SIZE];
3166 ULONG depotBlockIndexPos;
3169 *nextBlockIndex = BLOCK_SPECIAL;
3171 if(depotBlockCount >= This->bigBlockDepotCount)
3173 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3174 This->bigBlockDepotCount);
3175 return STG_E_READFAULT;
3179 * Cache the currently accessed depot block.
3181 if (depotBlockCount != This->indexBlockDepotCached)
3183 This->indexBlockDepotCached = depotBlockCount;
3185 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3187 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3192 * We have to look in the extended depot.
3194 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3197 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3200 return STG_E_READFAULT;
3202 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
3204 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3205 This->blockDepotCached[index] = *nextBlockIndex;
3209 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3214 /******************************************************************************
3215 * Storage32Impl_GetNextExtendedBlock
3217 * Given an extended block this method will return the next extended block.
3220 * The last ULONG of an extended block is the block index of the next
3221 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3225 * - The index of the next extended block
3226 * - BLOCK_UNUSED: there is no next extended block.
3227 * - Any other return values denotes failure.
3229 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3231 ULONG nextBlockIndex = BLOCK_SPECIAL;
3232 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3234 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3237 return nextBlockIndex;
3240 /******************************************************************************
3241 * Storage32Impl_SetNextBlockInChain
3243 * This method will write the index of the specified block's next block
3244 * in the big block depot.
3246 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3249 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3250 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3251 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3254 static void StorageImpl_SetNextBlockInChain(
3259 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3260 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3261 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3262 ULONG depotBlockIndexPos;
3264 assert(depotBlockCount < This->bigBlockDepotCount);
3265 assert(blockIndex != nextBlock);
3267 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3269 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3274 * We have to look in the extended depot.
3276 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3279 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3282 * Update the cached block depot, if necessary.
3284 if (depotBlockCount == This->indexBlockDepotCached)
3286 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3290 /******************************************************************************
3291 * Storage32Impl_LoadFileHeader
3293 * This method will read in the file header
3295 static HRESULT StorageImpl_LoadFileHeader(
3299 BYTE headerBigBlock[HEADER_SIZE];
3301 ULARGE_INTEGER offset;
3306 * Get a pointer to the big block of data containing the header.
3308 offset.u.HighPart = 0;
3309 offset.u.LowPart = 0;
3310 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3311 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3312 hr = STG_E_FILENOTFOUND;
3315 * Extract the information from the header.
3320 * Check for the "magic number" signature and return an error if it is not
3323 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3325 return STG_E_OLDFORMAT;
3328 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3330 return STG_E_INVALIDHEADER;
3333 StorageUtl_ReadWord(
3335 OFFSET_BIGBLOCKSIZEBITS,
3336 &This->bigBlockSizeBits);
3338 StorageUtl_ReadWord(
3340 OFFSET_SMALLBLOCKSIZEBITS,
3341 &This->smallBlockSizeBits);
3343 StorageUtl_ReadDWord(
3345 OFFSET_BBDEPOTCOUNT,
3346 &This->bigBlockDepotCount);
3348 StorageUtl_ReadDWord(
3350 OFFSET_ROOTSTARTBLOCK,
3351 &This->rootStartBlock);
3353 StorageUtl_ReadDWord(
3355 OFFSET_SBDEPOTSTART,
3356 &This->smallBlockDepotStart);
3358 StorageUtl_ReadDWord(
3360 OFFSET_EXTBBDEPOTSTART,
3361 &This->extBigBlockDepotStart);
3363 StorageUtl_ReadDWord(
3365 OFFSET_EXTBBDEPOTCOUNT,
3366 &This->extBigBlockDepotCount);
3368 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3370 StorageUtl_ReadDWord(
3372 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3373 &(This->bigBlockDepotStart[index]));
3377 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3379 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3380 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3383 * Right now, the code is making some assumptions about the size of the
3384 * blocks, just make sure they are what we're expecting.
3386 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3387 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3389 WARN("Broken OLE storage file\n");
3390 hr = STG_E_INVALIDHEADER;
3399 /******************************************************************************
3400 * Storage32Impl_SaveFileHeader
3402 * This method will save to the file the header
3404 static void StorageImpl_SaveFileHeader(
3407 BYTE headerBigBlock[HEADER_SIZE];
3410 ULARGE_INTEGER offset;
3411 DWORD bytes_read, bytes_written;
3414 * Get a pointer to the big block of data containing the header.
3416 offset.u.HighPart = 0;
3417 offset.u.LowPart = 0;
3418 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3419 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3420 hr = STG_E_FILENOTFOUND;
3423 * If the block read failed, the file is probably new.
3428 * Initialize for all unknown fields.
3430 memset(headerBigBlock, 0, HEADER_SIZE);
3433 * Initialize the magic number.
3435 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3438 * And a bunch of things we don't know what they mean
3440 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3441 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3442 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3443 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3447 * Write the information to the header.
3449 StorageUtl_WriteWord(
3451 OFFSET_BIGBLOCKSIZEBITS,
3452 This->bigBlockSizeBits);
3454 StorageUtl_WriteWord(
3456 OFFSET_SMALLBLOCKSIZEBITS,
3457 This->smallBlockSizeBits);
3459 StorageUtl_WriteDWord(
3461 OFFSET_BBDEPOTCOUNT,
3462 This->bigBlockDepotCount);
3464 StorageUtl_WriteDWord(
3466 OFFSET_ROOTSTARTBLOCK,
3467 This->rootStartBlock);
3469 StorageUtl_WriteDWord(
3471 OFFSET_SBDEPOTSTART,
3472 This->smallBlockDepotStart);
3474 StorageUtl_WriteDWord(
3476 OFFSET_SBDEPOTCOUNT,
3477 This->smallBlockDepotChain ?
3478 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3480 StorageUtl_WriteDWord(
3482 OFFSET_EXTBBDEPOTSTART,
3483 This->extBigBlockDepotStart);
3485 StorageUtl_WriteDWord(
3487 OFFSET_EXTBBDEPOTCOUNT,
3488 This->extBigBlockDepotCount);
3490 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3492 StorageUtl_WriteDWord(
3494 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3495 (This->bigBlockDepotStart[index]));
3499 * Write the big block back to the file.
3501 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3504 /******************************************************************************
3505 * StorageImpl_ReadRawDirEntry
3507 * This method will read the raw data from a directory entry in the file.
3509 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3511 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3513 ULARGE_INTEGER offset;
3517 offset.u.HighPart = 0;
3518 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3520 hr = BlockChainStream_ReadAt(
3521 This->rootBlockChain,
3530 /******************************************************************************
3531 * StorageImpl_WriteRawDirEntry
3533 * This method will write the raw data from a directory entry in the file.
3535 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3537 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3539 ULARGE_INTEGER offset;
3543 offset.u.HighPart = 0;
3544 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3546 hr = BlockChainStream_WriteAt(
3547 This->rootBlockChain,
3556 /******************************************************************************
3559 * Update raw directory entry data from the fields in newData.
3561 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3563 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3565 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3568 buffer + OFFSET_PS_NAME,
3570 DIRENTRY_NAME_BUFFER_LEN );
3572 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3574 StorageUtl_WriteWord(
3576 OFFSET_PS_NAMELENGTH,
3577 newData->sizeOfNameString);
3579 StorageUtl_WriteDWord(
3581 OFFSET_PS_LEFTCHILD,
3582 newData->leftChild);
3584 StorageUtl_WriteDWord(
3586 OFFSET_PS_RIGHTCHILD,
3587 newData->rightChild);
3589 StorageUtl_WriteDWord(
3592 newData->dirRootEntry);
3594 StorageUtl_WriteGUID(
3599 StorageUtl_WriteDWord(
3602 newData->ctime.dwLowDateTime);
3604 StorageUtl_WriteDWord(
3606 OFFSET_PS_CTIMEHIGH,
3607 newData->ctime.dwHighDateTime);
3609 StorageUtl_WriteDWord(
3612 newData->mtime.dwLowDateTime);
3614 StorageUtl_WriteDWord(
3616 OFFSET_PS_MTIMEHIGH,
3617 newData->ctime.dwHighDateTime);
3619 StorageUtl_WriteDWord(
3621 OFFSET_PS_STARTBLOCK,
3622 newData->startingBlock);
3624 StorageUtl_WriteDWord(
3627 newData->size.u.LowPart);
3630 /******************************************************************************
3631 * Storage32Impl_ReadDirEntry
3633 * This method will read the specified directory entry.
3635 HRESULT StorageImpl_ReadDirEntry(
3640 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3643 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3645 if (SUCCEEDED(readRes))
3647 memset(buffer->name, 0, sizeof(buffer->name));
3650 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3651 DIRENTRY_NAME_BUFFER_LEN );
3652 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3654 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3656 StorageUtl_ReadWord(
3658 OFFSET_PS_NAMELENGTH,
3659 &buffer->sizeOfNameString);
3661 StorageUtl_ReadDWord(
3663 OFFSET_PS_LEFTCHILD,
3664 &buffer->leftChild);
3666 StorageUtl_ReadDWord(
3668 OFFSET_PS_RIGHTCHILD,
3669 &buffer->rightChild);
3671 StorageUtl_ReadDWord(
3674 &buffer->dirRootEntry);
3676 StorageUtl_ReadGUID(
3681 StorageUtl_ReadDWord(
3684 &buffer->ctime.dwLowDateTime);
3686 StorageUtl_ReadDWord(
3688 OFFSET_PS_CTIMEHIGH,
3689 &buffer->ctime.dwHighDateTime);
3691 StorageUtl_ReadDWord(
3694 &buffer->mtime.dwLowDateTime);
3696 StorageUtl_ReadDWord(
3698 OFFSET_PS_MTIMEHIGH,
3699 &buffer->mtime.dwHighDateTime);
3701 StorageUtl_ReadDWord(
3703 OFFSET_PS_STARTBLOCK,
3704 &buffer->startingBlock);
3706 StorageUtl_ReadDWord(
3709 &buffer->size.u.LowPart);
3711 buffer->size.u.HighPart = 0;
3717 /*********************************************************************
3718 * Write the specified directory entry to the file
3720 HRESULT StorageImpl_WriteDirEntry(
3723 const DirEntry* buffer)
3725 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3728 UpdateRawDirEntry(currentEntry, buffer);
3730 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3734 static BOOL StorageImpl_ReadBigBlock(
3739 ULARGE_INTEGER ulOffset;
3742 ulOffset.u.HighPart = 0;
3743 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3745 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3746 return (read == This->bigBlockSize);
3749 static BOOL StorageImpl_ReadDWordFromBigBlock(
3755 ULARGE_INTEGER ulOffset;
3759 ulOffset.u.HighPart = 0;
3760 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3761 ulOffset.u.LowPart += offset;
3763 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3764 *value = lendian32toh(tmp);
3765 return (read == sizeof(DWORD));
3768 static BOOL StorageImpl_WriteBigBlock(
3773 ULARGE_INTEGER ulOffset;
3776 ulOffset.u.HighPart = 0;
3777 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3779 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3780 return (wrote == This->bigBlockSize);
3783 static BOOL StorageImpl_WriteDWordToBigBlock(
3789 ULARGE_INTEGER ulOffset;
3792 ulOffset.u.HighPart = 0;
3793 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3794 ulOffset.u.LowPart += offset;
3796 value = htole32(value);
3797 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3798 return (wrote == sizeof(DWORD));
3801 /******************************************************************************
3802 * Storage32Impl_SmallBlocksToBigBlocks
3804 * This method will convert a small block chain to a big block chain.
3805 * The small block chain will be destroyed.
3807 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3809 SmallBlockChainStream** ppsbChain)
3811 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3812 ULARGE_INTEGER size, offset;
3813 ULONG cbRead, cbWritten;
3814 ULARGE_INTEGER cbTotalRead;
3815 DirRef streamEntryRef;
3816 HRESULT resWrite = S_OK;
3818 DirEntry streamEntry;
3820 BlockChainStream *bbTempChain = NULL;
3821 BlockChainStream *bigBlockChain = NULL;
3824 * Create a temporary big block chain that doesn't have
3825 * an associated directory entry. This temporary chain will be
3826 * used to copy data from small blocks to big blocks.
3828 bbTempChain = BlockChainStream_Construct(This,
3831 if(!bbTempChain) return NULL;
3833 * Grow the big block chain.
3835 size = SmallBlockChainStream_GetSize(*ppsbChain);
3836 BlockChainStream_SetSize(bbTempChain, size);
3839 * Copy the contents of the small block chain to the big block chain
3840 * by small block size increments.
3842 offset.u.LowPart = 0;
3843 offset.u.HighPart = 0;
3844 cbTotalRead.QuadPart = 0;
3846 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3849 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3851 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3854 if (FAILED(resRead))
3859 cbTotalRead.QuadPart += cbRead;
3861 resWrite = BlockChainStream_WriteAt(bbTempChain,
3867 if (FAILED(resWrite))
3870 offset.u.LowPart += cbRead;
3872 } while (cbTotalRead.QuadPart < size.QuadPart);
3873 HeapFree(GetProcessHeap(),0,buffer);
3875 size.u.HighPart = 0;
3878 if (FAILED(resRead) || FAILED(resWrite))
3880 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3881 BlockChainStream_SetSize(bbTempChain, size);
3882 BlockChainStream_Destroy(bbTempChain);
3887 * Destroy the small block chain.
3889 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3890 SmallBlockChainStream_SetSize(*ppsbChain, size);
3891 SmallBlockChainStream_Destroy(*ppsbChain);
3895 * Change the directory entry. This chain is now a big block chain
3896 * and it doesn't reside in the small blocks chain anymore.
3898 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3900 streamEntry.startingBlock = bbHeadOfChain;
3902 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3905 * Destroy the temporary entryless big block chain.
3906 * Create a new big block chain associated with this entry.
3908 BlockChainStream_Destroy(bbTempChain);
3909 bigBlockChain = BlockChainStream_Construct(This,
3913 return bigBlockChain;
3916 /******************************************************************************
3917 * Storage32Impl_BigBlocksToSmallBlocks
3919 * This method will convert a big block chain to a small block chain.
3920 * The big block chain will be destroyed on success.
3922 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3924 BlockChainStream** ppbbChain)
3926 ULARGE_INTEGER size, offset, cbTotalRead;
3927 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3928 DirRef streamEntryRef;
3929 HRESULT resWrite = S_OK, resRead;
3930 DirEntry streamEntry;
3932 SmallBlockChainStream* sbTempChain;
3934 TRACE("%p %p\n", This, ppbbChain);
3936 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3942 size = BlockChainStream_GetSize(*ppbbChain);
3943 SmallBlockChainStream_SetSize(sbTempChain, size);
3945 offset.u.HighPart = 0;
3946 offset.u.LowPart = 0;
3947 cbTotalRead.QuadPart = 0;
3948 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3951 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3952 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3960 cbTotalRead.QuadPart += cbRead;
3962 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3963 cbRead, buffer, &cbWritten);
3965 if(FAILED(resWrite))
3968 offset.u.LowPart += cbRead;
3970 }while(cbTotalRead.QuadPart < size.QuadPart);
3971 HeapFree(GetProcessHeap(), 0, buffer);
3973 size.u.HighPart = 0;
3976 if(FAILED(resRead) || FAILED(resWrite))
3978 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3979 SmallBlockChainStream_SetSize(sbTempChain, size);
3980 SmallBlockChainStream_Destroy(sbTempChain);
3984 /* destroy the original big block chain */
3985 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3986 BlockChainStream_SetSize(*ppbbChain, size);
3987 BlockChainStream_Destroy(*ppbbChain);
3990 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3991 streamEntry.startingBlock = sbHeadOfChain;
3992 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3994 SmallBlockChainStream_Destroy(sbTempChain);
3995 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3998 static HRESULT CreateSnapshotFile(StorageBaseImpl* original, StorageBaseImpl **snapshot)
4001 DirEntry parentData, snapshotData;
4003 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DELETEONRELEASE,
4004 0, (IStorage**)snapshot);
4008 hr = StorageBaseImpl_ReadDirEntry(original,
4009 original->storageDirEntry, &parentData);
4012 hr = StorageBaseImpl_ReadDirEntry((*snapshot),
4013 (*snapshot)->storageDirEntry, &snapshotData);
4017 memcpy(snapshotData.name, parentData.name, sizeof(snapshotData.name));
4018 snapshotData.sizeOfNameString = parentData.sizeOfNameString;
4019 snapshotData.stgType = parentData.stgType;
4020 snapshotData.clsid = parentData.clsid;
4021 snapshotData.ctime = parentData.ctime;
4022 snapshotData.mtime = parentData.mtime;
4023 hr = StorageBaseImpl_WriteDirEntry((*snapshot),
4024 (*snapshot)->storageDirEntry, &snapshotData);
4028 hr = IStorage_CopyTo((IStorage*)original, 0, NULL, NULL,
4029 (IStorage*)(*snapshot));
4031 if (FAILED(hr)) IStorage_Release((IStorage*)(*snapshot));
4037 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4039 DWORD grfCommitFlags) /* [in] */
4041 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4043 DirEntry data, tempStorageData, snapshotRootData;
4044 DirRef tempStorageEntry, oldDirRoot;
4045 StorageInternalImpl *tempStorage;
4047 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4049 /* Cannot commit a read-only transacted storage */
4050 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4051 return STG_E_ACCESSDENIED;
4053 /* To prevent data loss, we create the new structure in the file before we
4054 * delete the old one, so that in case of errors the old data is intact. We
4055 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4056 * needed in the rare situation where we have just enough free disk space to
4057 * overwrite the existing data. */
4059 /* Create an orphaned storage in the parent for the new directory structure. */
4060 memset(&data, 0, sizeof(data));
4062 data.sizeOfNameString = 1;
4063 data.stgType = STGTY_STORAGE;
4064 data.leftChild = DIRENTRY_NULL;
4065 data.rightChild = DIRENTRY_NULL;
4066 data.dirRootEntry = DIRENTRY_NULL;
4067 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &data, &tempStorageEntry);
4069 if (FAILED(hr)) return hr;
4071 tempStorage = StorageInternalImpl_Construct(This->transactedParent,
4072 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, tempStorageEntry);
4075 hr = IStorage_CopyTo((IStorage*)This->snapshot, 0, NULL, NULL,
4076 (IStorage*)tempStorage);
4078 list_init(&tempStorage->ParentListEntry);
4080 IStorage_Release((IStorage*) tempStorage);
4087 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4091 /* Update the storage to use the new data in one step. */
4092 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4093 This->transactedParent->storageDirEntry, &data);
4097 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4098 tempStorageEntry, &tempStorageData);
4103 hr = StorageBaseImpl_ReadDirEntry(This->snapshot,
4104 This->snapshot->storageDirEntry, &snapshotRootData);
4109 oldDirRoot = data.dirRootEntry;
4110 data.dirRootEntry = tempStorageData.dirRootEntry;
4111 data.clsid = snapshotRootData.clsid;
4112 data.ctime = snapshotRootData.ctime;
4113 data.mtime = snapshotRootData.mtime;
4115 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4116 This->transactedParent->storageDirEntry, &data);
4121 /* Destroy the old now-orphaned data. */
4122 DestroyReachableEntries(This->transactedParent, oldDirRoot);
4123 StorageBaseImpl_DestroyDirEntry(This->transactedParent, tempStorageEntry);
4127 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4133 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4136 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4137 StorageBaseImpl *newSnapshot;
4140 TRACE("(%p)\n", iface);
4142 /* Create a new copy of the parent data. */
4143 hr = CreateSnapshotFile(This->transactedParent, &newSnapshot);
4144 if (FAILED(hr)) return hr;
4146 /* Destroy the open objects. */
4147 StorageBaseImpl_DeleteAll(&This->base);
4149 /* Replace our current snapshot. */
4150 IStorage_Release((IStorage*)This->snapshot);
4151 This->snapshot = newSnapshot;
4156 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4158 if (!This->reverted)
4160 TRACE("Storage invalidated (stg=%p)\n", This);
4164 StorageBaseImpl_DeleteAll(This);
4168 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4170 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4172 TransactedSnapshotImpl_Invalidate(iface);
4174 IStorage_Release((IStorage*)This->transactedParent);
4176 IStorage_Release((IStorage*)This->snapshot);
4178 HeapFree(GetProcessHeap(), 0, This);
4181 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4182 const DirEntry *newData, DirRef *index)
4184 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4186 return StorageBaseImpl_CreateDirEntry(This->snapshot,
4190 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4191 DirRef index, const DirEntry *data)
4193 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4195 return StorageBaseImpl_WriteDirEntry(This->snapshot,
4199 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4200 DirRef index, DirEntry *data)
4202 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4204 return StorageBaseImpl_ReadDirEntry(This->snapshot,
4208 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4211 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4213 return StorageBaseImpl_DestroyDirEntry(This->snapshot,
4217 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4218 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4220 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4222 return StorageBaseImpl_StreamReadAt(This->snapshot,
4223 index, offset, size, buffer, bytesRead);
4226 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4227 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4229 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4231 return StorageBaseImpl_StreamWriteAt(This->snapshot,
4232 index, offset, size, buffer, bytesWritten);
4235 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4236 DirRef index, ULARGE_INTEGER newsize)
4238 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4240 return StorageBaseImpl_StreamSetSize(This->snapshot,
4244 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4246 StorageBaseImpl_QueryInterface,
4247 StorageBaseImpl_AddRef,
4248 StorageBaseImpl_Release,
4249 StorageBaseImpl_CreateStream,
4250 StorageBaseImpl_OpenStream,
4251 StorageBaseImpl_CreateStorage,
4252 StorageBaseImpl_OpenStorage,
4253 StorageBaseImpl_CopyTo,
4254 StorageBaseImpl_MoveElementTo,
4255 TransactedSnapshotImpl_Commit,
4256 TransactedSnapshotImpl_Revert,
4257 StorageBaseImpl_EnumElements,
4258 StorageBaseImpl_DestroyElement,
4259 StorageBaseImpl_RenameElement,
4260 StorageBaseImpl_SetElementTimes,
4261 StorageBaseImpl_SetClass,
4262 StorageBaseImpl_SetStateBits,
4263 StorageBaseImpl_Stat
4266 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4268 TransactedSnapshotImpl_Destroy,
4269 TransactedSnapshotImpl_Invalidate,
4270 TransactedSnapshotImpl_CreateDirEntry,
4271 TransactedSnapshotImpl_WriteDirEntry,
4272 TransactedSnapshotImpl_ReadDirEntry,
4273 TransactedSnapshotImpl_DestroyDirEntry,
4274 TransactedSnapshotImpl_StreamReadAt,
4275 TransactedSnapshotImpl_StreamWriteAt,
4276 TransactedSnapshotImpl_StreamSetSize
4279 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4280 TransactedSnapshotImpl** result)
4284 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4287 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4289 /* This is OK because the property set storage functions use the IStorage functions. */
4290 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4292 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4294 list_init(&(*result)->base.strmHead);
4296 list_init(&(*result)->base.storageHead);
4298 (*result)->base.ref = 1;
4300 (*result)->base.openFlags = parentStorage->openFlags;
4302 (*result)->base.filename = parentStorage->filename;
4304 /* Create a new temporary storage to act as the snapshot */
4305 hr = CreateSnapshotFile(parentStorage, &(*result)->snapshot);
4309 (*result)->base.storageDirEntry = (*result)->snapshot->storageDirEntry;
4311 /* parentStorage already has 1 reference, which we take over here. */
4312 (*result)->transactedParent = parentStorage;
4314 parentStorage->transactedChild = (StorageBaseImpl*)*result;
4317 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4322 return E_OUTOFMEMORY;
4325 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
4326 StorageBaseImpl** result)
4330 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
4332 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
4335 return TransactedSnapshotImpl_Construct(parentStorage,
4336 (TransactedSnapshotImpl**)result);
4339 static HRESULT Storage_Construct(
4346 StorageBaseImpl** result)
4348 StorageImpl *newStorage;
4349 StorageBaseImpl *newTransactedStorage;
4352 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, &newStorage);
4353 if (FAILED(hr)) goto end;
4355 if (openFlags & STGM_TRANSACTED)
4357 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
4359 IStorage_Release((IStorage*)newStorage);
4361 *result = newTransactedStorage;
4364 *result = &newStorage->base;
4370 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
4372 StorageInternalImpl* This = (StorageInternalImpl*) base;
4374 if (!This->base.reverted)
4376 TRACE("Storage invalidated (stg=%p)\n", This);
4378 This->base.reverted = 1;
4380 This->parentStorage = NULL;
4382 StorageBaseImpl_DeleteAll(&This->base);
4384 list_remove(&This->ParentListEntry);
4388 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
4390 StorageInternalImpl* This = (StorageInternalImpl*) iface;
4392 StorageInternalImpl_Invalidate(&This->base);
4394 HeapFree(GetProcessHeap(), 0, This);
4397 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
4398 const DirEntry *newData, DirRef *index)
4400 StorageInternalImpl* This = (StorageInternalImpl*) base;
4402 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
4406 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
4407 DirRef index, const DirEntry *data)
4409 StorageInternalImpl* This = (StorageInternalImpl*) base;
4411 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
4415 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
4416 DirRef index, DirEntry *data)
4418 StorageInternalImpl* This = (StorageInternalImpl*) base;
4420 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
4424 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
4427 StorageInternalImpl* This = (StorageInternalImpl*) base;
4429 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
4433 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
4434 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4436 StorageInternalImpl* This = (StorageInternalImpl*) base;
4438 return StorageBaseImpl_StreamReadAt(This->parentStorage,
4439 index, offset, size, buffer, bytesRead);
4442 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
4443 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4445 StorageInternalImpl* This = (StorageInternalImpl*) base;
4447 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
4448 index, offset, size, buffer, bytesWritten);
4451 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
4452 DirRef index, ULARGE_INTEGER newsize)
4454 StorageInternalImpl* This = (StorageInternalImpl*) base;
4456 return StorageBaseImpl_StreamSetSize(This->parentStorage,
4460 /******************************************************************************
4462 ** Storage32InternalImpl_Commit
4465 static HRESULT WINAPI StorageInternalImpl_Commit(
4467 DWORD grfCommitFlags) /* [in] */
4469 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
4473 /******************************************************************************
4475 ** Storage32InternalImpl_Revert
4478 static HRESULT WINAPI StorageInternalImpl_Revert(
4481 FIXME("(%p): stub\n", iface);
4485 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
4487 IStorage_Release((IStorage*)This->parentStorage);
4488 HeapFree(GetProcessHeap(), 0, This);
4491 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
4492 IEnumSTATSTG* iface,
4496 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4499 return E_INVALIDARG;
4503 if (IsEqualGUID(&IID_IUnknown, riid) ||
4504 IsEqualGUID(&IID_IEnumSTATSTG, riid))
4507 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
4511 return E_NOINTERFACE;
4514 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
4515 IEnumSTATSTG* iface)
4517 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4518 return InterlockedIncrement(&This->ref);
4521 static ULONG WINAPI IEnumSTATSTGImpl_Release(
4522 IEnumSTATSTG* iface)
4524 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4528 newRef = InterlockedDecrement(&This->ref);
4532 IEnumSTATSTGImpl_Destroy(This);
4538 static HRESULT IEnumSTATSTGImpl_GetNextRef(
4539 IEnumSTATSTGImpl* This,
4542 DirRef result = DIRENTRY_NULL;
4546 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
4548 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
4549 This->parentStorage->storageDirEntry, &entry);
4550 searchNode = entry.dirRootEntry;
4552 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
4554 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
4558 LONG diff = entryNameCmp( entry.name, This->name);
4562 searchNode = entry.rightChild;
4566 result = searchNode;
4567 memcpy(result_name, entry.name, sizeof(result_name));
4568 searchNode = entry.leftChild;
4576 if (result != DIRENTRY_NULL)
4577 memcpy(This->name, result_name, sizeof(result_name));
4583 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
4584 IEnumSTATSTG* iface,
4587 ULONG* pceltFetched)
4589 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4591 DirEntry currentEntry;
4592 STATSTG* currentReturnStruct = rgelt;
4593 ULONG objectFetched = 0;
4594 DirRef currentSearchNode;
4597 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
4598 return E_INVALIDARG;
4600 if (This->parentStorage->reverted)
4601 return STG_E_REVERTED;
4604 * To avoid the special case, get another pointer to a ULONG value if
4605 * the caller didn't supply one.
4607 if (pceltFetched==0)
4608 pceltFetched = &objectFetched;
4611 * Start the iteration, we will iterate until we hit the end of the
4612 * linked list or until we hit the number of items to iterate through
4616 while ( *pceltFetched < celt )
4618 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
4620 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
4624 * Read the entry from the storage.
4626 StorageBaseImpl_ReadDirEntry(This->parentStorage,
4631 * Copy the information to the return buffer.
4633 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
4634 currentReturnStruct,
4639 * Step to the next item in the iteration
4642 currentReturnStruct++;
4645 if (SUCCEEDED(hr) && *pceltFetched != celt)
4652 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
4653 IEnumSTATSTG* iface,
4656 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4658 ULONG objectFetched = 0;
4659 DirRef currentSearchNode;
4662 if (This->parentStorage->reverted)
4663 return STG_E_REVERTED;
4665 while ( (objectFetched < celt) )
4667 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
4669 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
4675 if (SUCCEEDED(hr) && objectFetched != celt)
4681 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
4682 IEnumSTATSTG* iface)
4684 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4686 if (This->parentStorage->reverted)
4687 return STG_E_REVERTED;
4694 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
4695 IEnumSTATSTG* iface,
4696 IEnumSTATSTG** ppenum)
4698 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4700 IEnumSTATSTGImpl* newClone;
4702 if (This->parentStorage->reverted)
4703 return STG_E_REVERTED;
4706 * Perform a sanity check on the parameters.
4709 return E_INVALIDARG;
4711 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
4712 This->storageDirEntry);
4716 * The new clone enumeration must point to the same current node as
4719 memcpy(newClone->name, This->name, sizeof(newClone->name));
4721 *ppenum = (IEnumSTATSTG*)newClone;
4724 * Don't forget to nail down a reference to the clone before
4727 IEnumSTATSTGImpl_AddRef(*ppenum);
4733 * Virtual function table for the IEnumSTATSTGImpl class.
4735 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4737 IEnumSTATSTGImpl_QueryInterface,
4738 IEnumSTATSTGImpl_AddRef,
4739 IEnumSTATSTGImpl_Release,
4740 IEnumSTATSTGImpl_Next,
4741 IEnumSTATSTGImpl_Skip,
4742 IEnumSTATSTGImpl_Reset,
4743 IEnumSTATSTGImpl_Clone
4746 /******************************************************************************
4747 ** IEnumSTATSTGImpl implementation
4750 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4751 StorageBaseImpl* parentStorage,
4752 DirRef storageDirEntry)
4754 IEnumSTATSTGImpl* newEnumeration;
4756 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4758 if (newEnumeration!=0)
4761 * Set-up the virtual function table and reference count.
4763 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4764 newEnumeration->ref = 0;
4767 * We want to nail-down the reference to the storage in case the
4768 * enumeration out-lives the storage in the client application.
4770 newEnumeration->parentStorage = parentStorage;
4771 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4773 newEnumeration->storageDirEntry = storageDirEntry;
4776 * Make sure the current node of the iterator is the first one.
4778 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4781 return newEnumeration;
4785 * Virtual function table for the Storage32InternalImpl class.
4787 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4789 StorageBaseImpl_QueryInterface,
4790 StorageBaseImpl_AddRef,
4791 StorageBaseImpl_Release,
4792 StorageBaseImpl_CreateStream,
4793 StorageBaseImpl_OpenStream,
4794 StorageBaseImpl_CreateStorage,
4795 StorageBaseImpl_OpenStorage,
4796 StorageBaseImpl_CopyTo,
4797 StorageBaseImpl_MoveElementTo,
4798 StorageInternalImpl_Commit,
4799 StorageInternalImpl_Revert,
4800 StorageBaseImpl_EnumElements,
4801 StorageBaseImpl_DestroyElement,
4802 StorageBaseImpl_RenameElement,
4803 StorageBaseImpl_SetElementTimes,
4804 StorageBaseImpl_SetClass,
4805 StorageBaseImpl_SetStateBits,
4806 StorageBaseImpl_Stat
4809 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
4811 StorageInternalImpl_Destroy,
4812 StorageInternalImpl_Invalidate,
4813 StorageInternalImpl_CreateDirEntry,
4814 StorageInternalImpl_WriteDirEntry,
4815 StorageInternalImpl_ReadDirEntry,
4816 StorageInternalImpl_DestroyDirEntry,
4817 StorageInternalImpl_StreamReadAt,
4818 StorageInternalImpl_StreamWriteAt,
4819 StorageInternalImpl_StreamSetSize
4822 /******************************************************************************
4823 ** Storage32InternalImpl implementation
4826 static StorageInternalImpl* StorageInternalImpl_Construct(
4827 StorageBaseImpl* parentStorage,
4829 DirRef storageDirEntry)
4831 StorageInternalImpl* newStorage;
4833 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4837 list_init(&newStorage->base.strmHead);
4839 list_init(&newStorage->base.storageHead);
4842 * Initialize the virtual function table.
4844 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4845 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
4846 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4848 newStorage->base.reverted = 0;
4850 newStorage->base.ref = 1;
4852 newStorage->parentStorage = parentStorage;
4855 * Keep a reference to the directory entry of this storage
4857 newStorage->base.storageDirEntry = storageDirEntry;
4859 newStorage->base.create = 0;
4867 /******************************************************************************
4868 ** StorageUtl implementation
4871 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4875 memcpy(&tmp, buffer+offset, sizeof(WORD));
4876 *value = lendian16toh(tmp);
4879 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4881 value = htole16(value);
4882 memcpy(buffer+offset, &value, sizeof(WORD));
4885 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4889 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4890 *value = lendian32toh(tmp);
4893 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4895 value = htole32(value);
4896 memcpy(buffer+offset, &value, sizeof(DWORD));
4899 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4900 ULARGE_INTEGER* value)
4902 #ifdef WORDS_BIGENDIAN
4905 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4906 value->u.LowPart = htole32(tmp.u.HighPart);
4907 value->u.HighPart = htole32(tmp.u.LowPart);
4909 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4913 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4914 const ULARGE_INTEGER *value)
4916 #ifdef WORDS_BIGENDIAN
4919 tmp.u.LowPart = htole32(value->u.HighPart);
4920 tmp.u.HighPart = htole32(value->u.LowPart);
4921 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4923 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4927 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4929 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4930 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4931 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4933 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4936 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4938 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4939 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4940 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4942 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4945 void StorageUtl_CopyDirEntryToSTATSTG(
4946 StorageBaseImpl* storage,
4947 STATSTG* destination,
4948 const DirEntry* source,
4953 if (source->stgType == STGTY_ROOT)
4955 /* replace the name of root entry (often "Root Entry") by the file name */
4956 entryName = storage->filename;
4960 entryName = source->name;
4964 * The copy of the string occurs only when the flag is not set
4966 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4967 (entryName == NULL) ||
4968 (entryName[0] == 0) )
4970 destination->pwcsName = 0;
4974 destination->pwcsName =
4975 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
4977 strcpyW(destination->pwcsName, entryName);
4980 switch (source->stgType)
4984 destination->type = STGTY_STORAGE;
4987 destination->type = STGTY_STREAM;
4990 destination->type = STGTY_STREAM;
4994 destination->cbSize = source->size;
4996 currentReturnStruct->mtime = {0}; TODO
4997 currentReturnStruct->ctime = {0};
4998 currentReturnStruct->atime = {0};
5000 destination->grfMode = 0;
5001 destination->grfLocksSupported = 0;
5002 destination->clsid = source->clsid;
5003 destination->grfStateBits = 0;
5004 destination->reserved = 0;
5007 /******************************************************************************
5008 ** BlockChainStream implementation
5011 BlockChainStream* BlockChainStream_Construct(
5012 StorageImpl* parentStorage,
5013 ULONG* headOfStreamPlaceHolder,
5016 BlockChainStream* newStream;
5019 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5021 newStream->parentStorage = parentStorage;
5022 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5023 newStream->ownerDirEntry = dirEntry;
5024 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
5025 newStream->tailIndex = BLOCK_END_OF_CHAIN;
5026 newStream->numBlocks = 0;
5028 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
5030 while (blockIndex != BLOCK_END_OF_CHAIN)
5032 newStream->numBlocks++;
5033 newStream->tailIndex = blockIndex;
5035 if(FAILED(StorageImpl_GetNextBlockInChain(
5040 HeapFree(GetProcessHeap(), 0, newStream);
5048 void BlockChainStream_Destroy(BlockChainStream* This)
5050 HeapFree(GetProcessHeap(), 0, This);
5053 /******************************************************************************
5054 * BlockChainStream_GetHeadOfChain
5056 * Returns the head of this stream chain.
5057 * Some special chains don't have directory entries, their heads are kept in
5058 * This->headOfStreamPlaceHolder.
5061 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5063 DirEntry chainEntry;
5066 if (This->headOfStreamPlaceHolder != 0)
5067 return *(This->headOfStreamPlaceHolder);
5069 if (This->ownerDirEntry != DIRENTRY_NULL)
5071 hr = StorageImpl_ReadDirEntry(
5072 This->parentStorage,
5073 This->ownerDirEntry,
5078 return chainEntry.startingBlock;
5082 return BLOCK_END_OF_CHAIN;
5085 /******************************************************************************
5086 * BlockChainStream_GetCount
5088 * Returns the number of blocks that comprises this chain.
5089 * This is not the size of the stream as the last block may not be full!
5092 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5097 blockIndex = BlockChainStream_GetHeadOfChain(This);
5099 while (blockIndex != BLOCK_END_OF_CHAIN)
5103 if(FAILED(StorageImpl_GetNextBlockInChain(
5104 This->parentStorage,
5113 /******************************************************************************
5114 * BlockChainStream_ReadAt
5116 * Reads a specified number of bytes from this chain at the specified offset.
5117 * bytesRead may be NULL.
5118 * Failure will be returned if the specified number of bytes has not been read.
5120 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5121 ULARGE_INTEGER offset,
5126 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5127 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5128 ULONG bytesToReadInBuffer;
5132 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5135 * Find the first block in the stream that contains part of the buffer.
5137 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5138 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5139 (blockNoInSequence < This->lastBlockNoInSequence) )
5141 blockIndex = BlockChainStream_GetHeadOfChain(This);
5142 This->lastBlockNoInSequence = blockNoInSequence;
5146 ULONG temp = blockNoInSequence;
5148 blockIndex = This->lastBlockNoInSequenceIndex;
5149 blockNoInSequence -= This->lastBlockNoInSequence;
5150 This->lastBlockNoInSequence = temp;
5153 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5155 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5156 return STG_E_DOCFILECORRUPT;
5157 blockNoInSequence--;
5160 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
5161 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
5163 This->lastBlockNoInSequenceIndex = blockIndex;
5166 * Start reading the buffer.
5169 bufferWalker = buffer;
5171 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5173 ULARGE_INTEGER ulOffset;
5176 * Calculate how many bytes we can copy from this big block.
5178 bytesToReadInBuffer =
5179 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5181 TRACE("block %i\n",blockIndex);
5182 ulOffset.u.HighPart = 0;
5183 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5186 StorageImpl_ReadAt(This->parentStorage,
5189 bytesToReadInBuffer,
5192 * Step to the next big block.
5194 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5195 return STG_E_DOCFILECORRUPT;
5197 bufferWalker += bytesReadAt;
5198 size -= bytesReadAt;
5199 *bytesRead += bytesReadAt;
5200 offsetInBlock = 0; /* There is no offset on the next block */
5202 if (bytesToReadInBuffer != bytesReadAt)
5206 return (size == 0) ? S_OK : STG_E_READFAULT;
5209 /******************************************************************************
5210 * BlockChainStream_WriteAt
5212 * Writes the specified number of bytes to this chain at the specified offset.
5213 * Will fail if not all specified number of bytes have been written.
5215 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5216 ULARGE_INTEGER offset,
5219 ULONG* bytesWritten)
5221 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5222 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5225 const BYTE* bufferWalker;
5228 * Find the first block in the stream that contains part of the buffer.
5230 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5231 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5232 (blockNoInSequence < This->lastBlockNoInSequence) )
5234 blockIndex = BlockChainStream_GetHeadOfChain(This);
5235 This->lastBlockNoInSequence = blockNoInSequence;
5239 ULONG temp = blockNoInSequence;
5241 blockIndex = This->lastBlockNoInSequenceIndex;
5242 blockNoInSequence -= This->lastBlockNoInSequence;
5243 This->lastBlockNoInSequence = temp;
5246 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5248 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5250 return STG_E_DOCFILECORRUPT;
5251 blockNoInSequence--;
5254 This->lastBlockNoInSequenceIndex = blockIndex;
5256 /* BlockChainStream_SetSize should have already been called to ensure we have
5257 * enough blocks in the chain to write into */
5258 if (blockIndex == BLOCK_END_OF_CHAIN)
5260 ERR("not enough blocks in chain to write data\n");
5261 return STG_E_DOCFILECORRUPT;
5265 bufferWalker = buffer;
5267 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5269 ULARGE_INTEGER ulOffset;
5270 DWORD bytesWrittenAt;
5272 * Calculate how many bytes we can copy from this big block.
5275 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5277 TRACE("block %i\n",blockIndex);
5278 ulOffset.u.HighPart = 0;
5279 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5282 StorageImpl_WriteAt(This->parentStorage,
5289 * Step to the next big block.
5291 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5293 return STG_E_DOCFILECORRUPT;
5295 bufferWalker += bytesWrittenAt;
5296 size -= bytesWrittenAt;
5297 *bytesWritten += bytesWrittenAt;
5298 offsetInBlock = 0; /* There is no offset on the next block */
5300 if (bytesWrittenAt != bytesToWrite)
5304 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5307 /******************************************************************************
5308 * BlockChainStream_Shrink
5310 * Shrinks this chain in the big block depot.
5312 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
5313 ULARGE_INTEGER newSize)
5315 ULONG blockIndex, extraBlock;
5320 * Reset the last accessed block cache.
5322 This->lastBlockNoInSequence = 0xFFFFFFFF;
5323 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
5326 * Figure out how many blocks are needed to contain the new size
5328 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5330 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5333 blockIndex = BlockChainStream_GetHeadOfChain(This);
5336 * Go to the new end of chain
5338 while (count < numBlocks)
5340 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5346 /* Get the next block before marking the new end */
5347 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5351 /* Mark the new end of chain */
5352 StorageImpl_SetNextBlockInChain(
5353 This->parentStorage,
5355 BLOCK_END_OF_CHAIN);
5357 This->tailIndex = blockIndex;
5358 This->numBlocks = numBlocks;
5361 * Mark the extra blocks as free
5363 while (extraBlock != BLOCK_END_OF_CHAIN)
5365 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
5368 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
5369 extraBlock = blockIndex;
5375 /******************************************************************************
5376 * BlockChainStream_Enlarge
5378 * Grows this chain in the big block depot.
5380 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
5381 ULARGE_INTEGER newSize)
5383 ULONG blockIndex, currentBlock;
5385 ULONG oldNumBlocks = 0;
5387 blockIndex = BlockChainStream_GetHeadOfChain(This);
5390 * Empty chain. Create the head.
5392 if (blockIndex == BLOCK_END_OF_CHAIN)
5394 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5395 StorageImpl_SetNextBlockInChain(This->parentStorage,
5397 BLOCK_END_OF_CHAIN);
5399 if (This->headOfStreamPlaceHolder != 0)
5401 *(This->headOfStreamPlaceHolder) = blockIndex;
5405 DirEntry chainEntry;
5406 assert(This->ownerDirEntry != DIRENTRY_NULL);
5408 StorageImpl_ReadDirEntry(
5409 This->parentStorage,
5410 This->ownerDirEntry,
5413 chainEntry.startingBlock = blockIndex;
5415 StorageImpl_WriteDirEntry(
5416 This->parentStorage,
5417 This->ownerDirEntry,
5421 This->tailIndex = blockIndex;
5422 This->numBlocks = 1;
5426 * Figure out how many blocks are needed to contain this stream
5428 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5430 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5434 * Go to the current end of chain
5436 if (This->tailIndex == BLOCK_END_OF_CHAIN)
5438 currentBlock = blockIndex;
5440 while (blockIndex != BLOCK_END_OF_CHAIN)
5443 currentBlock = blockIndex;
5445 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
5450 This->tailIndex = currentBlock;
5453 currentBlock = This->tailIndex;
5454 oldNumBlocks = This->numBlocks;
5457 * Add new blocks to the chain
5459 if (oldNumBlocks < newNumBlocks)
5461 while (oldNumBlocks < newNumBlocks)
5463 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5465 StorageImpl_SetNextBlockInChain(
5466 This->parentStorage,
5470 StorageImpl_SetNextBlockInChain(
5471 This->parentStorage,
5473 BLOCK_END_OF_CHAIN);
5475 currentBlock = blockIndex;
5479 This->tailIndex = blockIndex;
5480 This->numBlocks = newNumBlocks;
5486 /******************************************************************************
5487 * BlockChainStream_SetSize
5489 * Sets the size of this stream. The big block depot will be updated.
5490 * The file will grow if we grow the chain.
5492 * TODO: Free the actual blocks in the file when we shrink the chain.
5493 * Currently, the blocks are still in the file. So the file size
5494 * doesn't shrink even if we shrink streams.
5496 BOOL BlockChainStream_SetSize(
5497 BlockChainStream* This,
5498 ULARGE_INTEGER newSize)
5500 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
5502 if (newSize.u.LowPart == size.u.LowPart)
5505 if (newSize.u.LowPart < size.u.LowPart)
5507 BlockChainStream_Shrink(This, newSize);
5511 BlockChainStream_Enlarge(This, newSize);
5517 /******************************************************************************
5518 * BlockChainStream_GetSize
5520 * Returns the size of this chain.
5521 * Will return the block count if this chain doesn't have a directory entry.
5523 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
5525 DirEntry chainEntry;
5527 if(This->headOfStreamPlaceHolder == NULL)
5530 * This chain has a directory entry so use the size value from there.
5532 StorageImpl_ReadDirEntry(
5533 This->parentStorage,
5534 This->ownerDirEntry,
5537 return chainEntry.size;
5542 * this chain is a chain that does not have a directory entry, figure out the
5543 * size by making the product number of used blocks times the
5546 ULARGE_INTEGER result;
5547 result.u.HighPart = 0;
5550 BlockChainStream_GetCount(This) *
5551 This->parentStorage->bigBlockSize;
5557 /******************************************************************************
5558 ** SmallBlockChainStream implementation
5561 SmallBlockChainStream* SmallBlockChainStream_Construct(
5562 StorageImpl* parentStorage,
5563 ULONG* headOfStreamPlaceHolder,
5566 SmallBlockChainStream* newStream;
5568 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
5570 newStream->parentStorage = parentStorage;
5571 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5572 newStream->ownerDirEntry = dirEntry;
5577 void SmallBlockChainStream_Destroy(
5578 SmallBlockChainStream* This)
5580 HeapFree(GetProcessHeap(), 0, This);
5583 /******************************************************************************
5584 * SmallBlockChainStream_GetHeadOfChain
5586 * Returns the head of this chain of small blocks.
5588 static ULONG SmallBlockChainStream_GetHeadOfChain(
5589 SmallBlockChainStream* This)
5591 DirEntry chainEntry;
5594 if (This->headOfStreamPlaceHolder != NULL)
5595 return *(This->headOfStreamPlaceHolder);
5597 if (This->ownerDirEntry)
5599 hr = StorageImpl_ReadDirEntry(
5600 This->parentStorage,
5601 This->ownerDirEntry,
5606 return chainEntry.startingBlock;
5611 return BLOCK_END_OF_CHAIN;
5614 /******************************************************************************
5615 * SmallBlockChainStream_GetNextBlockInChain
5617 * Returns the index of the next small block in this chain.
5620 * - BLOCK_END_OF_CHAIN: end of this chain
5621 * - BLOCK_UNUSED: small block 'blockIndex' is free
5623 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5624 SmallBlockChainStream* This,
5626 ULONG* nextBlockInChain)
5628 ULARGE_INTEGER offsetOfBlockInDepot;
5633 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5635 offsetOfBlockInDepot.u.HighPart = 0;
5636 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5639 * Read those bytes in the buffer from the small block file.
5641 res = BlockChainStream_ReadAt(
5642 This->parentStorage->smallBlockDepotChain,
5643 offsetOfBlockInDepot,
5650 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5657 /******************************************************************************
5658 * SmallBlockChainStream_SetNextBlockInChain
5660 * Writes the index of the next block of the specified block in the small
5662 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5663 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5665 static void SmallBlockChainStream_SetNextBlockInChain(
5666 SmallBlockChainStream* This,
5670 ULARGE_INTEGER offsetOfBlockInDepot;
5674 offsetOfBlockInDepot.u.HighPart = 0;
5675 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5677 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5680 * Read those bytes in the buffer from the small block file.
5682 BlockChainStream_WriteAt(
5683 This->parentStorage->smallBlockDepotChain,
5684 offsetOfBlockInDepot,
5690 /******************************************************************************
5691 * SmallBlockChainStream_FreeBlock
5693 * Flag small block 'blockIndex' as free in the small block depot.
5695 static void SmallBlockChainStream_FreeBlock(
5696 SmallBlockChainStream* This,
5699 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5702 /******************************************************************************
5703 * SmallBlockChainStream_GetNextFreeBlock
5705 * Returns the index of a free small block. The small block depot will be
5706 * enlarged if necessary. The small block chain will also be enlarged if
5709 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5710 SmallBlockChainStream* This)
5712 ULARGE_INTEGER offsetOfBlockInDepot;
5715 ULONG blockIndex = 0;
5716 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5718 ULONG smallBlocksPerBigBlock;
5720 offsetOfBlockInDepot.u.HighPart = 0;
5723 * Scan the small block depot for a free block
5725 while (nextBlockIndex != BLOCK_UNUSED)
5727 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5729 res = BlockChainStream_ReadAt(
5730 This->parentStorage->smallBlockDepotChain,
5731 offsetOfBlockInDepot,
5737 * If we run out of space for the small block depot, enlarge it
5741 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5743 if (nextBlockIndex != BLOCK_UNUSED)
5749 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5751 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5752 ULONG nextBlock, newsbdIndex;
5753 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5755 nextBlock = sbdIndex;
5756 while (nextBlock != BLOCK_END_OF_CHAIN)
5758 sbdIndex = nextBlock;
5759 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5762 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5763 if (sbdIndex != BLOCK_END_OF_CHAIN)
5764 StorageImpl_SetNextBlockInChain(
5765 This->parentStorage,
5769 StorageImpl_SetNextBlockInChain(
5770 This->parentStorage,
5772 BLOCK_END_OF_CHAIN);
5775 * Initialize all the small blocks to free
5777 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5778 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5783 * We have just created the small block depot.
5789 * Save it in the header
5791 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5792 StorageImpl_SaveFileHeader(This->parentStorage);
5795 * And allocate the first big block that will contain small blocks
5798 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5800 StorageImpl_SetNextBlockInChain(
5801 This->parentStorage,
5803 BLOCK_END_OF_CHAIN);
5805 StorageImpl_ReadDirEntry(
5806 This->parentStorage,
5807 This->parentStorage->base.storageDirEntry,
5810 rootEntry.startingBlock = sbStartIndex;
5811 rootEntry.size.u.HighPart = 0;
5812 rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
5814 StorageImpl_WriteDirEntry(
5815 This->parentStorage,
5816 This->parentStorage->base.storageDirEntry,
5820 StorageImpl_SaveFileHeader(This->parentStorage);
5824 smallBlocksPerBigBlock =
5825 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5828 * Verify if we have to allocate big blocks to contain small blocks
5830 if (blockIndex % smallBlocksPerBigBlock == 0)
5833 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5835 StorageImpl_ReadDirEntry(
5836 This->parentStorage,
5837 This->parentStorage->base.storageDirEntry,
5840 if (rootEntry.size.u.LowPart <
5841 (blocksRequired * This->parentStorage->bigBlockSize))
5843 rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5845 BlockChainStream_SetSize(
5846 This->parentStorage->smallBlockRootChain,
5849 StorageImpl_WriteDirEntry(
5850 This->parentStorage,
5851 This->parentStorage->base.storageDirEntry,
5859 /******************************************************************************
5860 * SmallBlockChainStream_ReadAt
5862 * Reads a specified number of bytes from this chain at the specified offset.
5863 * bytesRead may be NULL.
5864 * Failure will be returned if the specified number of bytes has not been read.
5866 HRESULT SmallBlockChainStream_ReadAt(
5867 SmallBlockChainStream* This,
5868 ULARGE_INTEGER offset,
5874 ULARGE_INTEGER offsetInBigBlockFile;
5875 ULONG blockNoInSequence =
5876 offset.u.LowPart / This->parentStorage->smallBlockSize;
5878 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5879 ULONG bytesToReadInBuffer;
5881 ULONG bytesReadFromBigBlockFile;
5885 * This should never happen on a small block file.
5887 assert(offset.u.HighPart==0);
5890 * Find the first block in the stream that contains part of the buffer.
5892 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5894 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5896 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5899 blockNoInSequence--;
5903 * Start reading the buffer.
5906 bufferWalker = buffer;
5908 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5911 * Calculate how many bytes we can copy from this small block.
5913 bytesToReadInBuffer =
5914 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5917 * Calculate the offset of the small block in the small block file.
5919 offsetInBigBlockFile.u.HighPart = 0;
5920 offsetInBigBlockFile.u.LowPart =
5921 blockIndex * This->parentStorage->smallBlockSize;
5923 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5926 * Read those bytes in the buffer from the small block file.
5927 * The small block has already been identified so it shouldn't fail
5928 * unless the file is corrupt.
5930 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5931 offsetInBigBlockFile,
5932 bytesToReadInBuffer,
5934 &bytesReadFromBigBlockFile);
5940 * Step to the next big block.
5942 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5944 return STG_E_DOCFILECORRUPT;
5946 bufferWalker += bytesReadFromBigBlockFile;
5947 size -= bytesReadFromBigBlockFile;
5948 *bytesRead += bytesReadFromBigBlockFile;
5949 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5952 return (size == 0) ? S_OK : STG_E_READFAULT;
5955 /******************************************************************************
5956 * SmallBlockChainStream_WriteAt
5958 * Writes the specified number of bytes to this chain at the specified offset.
5959 * Will fail if not all specified number of bytes have been written.
5961 HRESULT SmallBlockChainStream_WriteAt(
5962 SmallBlockChainStream* This,
5963 ULARGE_INTEGER offset,
5966 ULONG* bytesWritten)
5968 ULARGE_INTEGER offsetInBigBlockFile;
5969 ULONG blockNoInSequence =
5970 offset.u.LowPart / This->parentStorage->smallBlockSize;
5972 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5973 ULONG bytesToWriteInBuffer;
5975 ULONG bytesWrittenToBigBlockFile;
5976 const BYTE* bufferWalker;
5980 * This should never happen on a small block file.
5982 assert(offset.u.HighPart==0);
5985 * Find the first block in the stream that contains part of the buffer.
5987 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5989 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5991 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5992 return STG_E_DOCFILECORRUPT;
5993 blockNoInSequence--;
5997 * Start writing the buffer.
6000 bufferWalker = buffer;
6001 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6004 * Calculate how many bytes we can copy to this small block.
6006 bytesToWriteInBuffer =
6007 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6010 * Calculate the offset of the small block in the small block file.
6012 offsetInBigBlockFile.u.HighPart = 0;
6013 offsetInBigBlockFile.u.LowPart =
6014 blockIndex * This->parentStorage->smallBlockSize;
6016 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6019 * Write those bytes in the buffer to the small block file.
6021 res = BlockChainStream_WriteAt(
6022 This->parentStorage->smallBlockRootChain,
6023 offsetInBigBlockFile,
6024 bytesToWriteInBuffer,
6026 &bytesWrittenToBigBlockFile);
6031 * Step to the next big block.
6033 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6036 bufferWalker += bytesWrittenToBigBlockFile;
6037 size -= bytesWrittenToBigBlockFile;
6038 *bytesWritten += bytesWrittenToBigBlockFile;
6039 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6042 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6045 /******************************************************************************
6046 * SmallBlockChainStream_Shrink
6048 * Shrinks this chain in the small block depot.
6050 static BOOL SmallBlockChainStream_Shrink(
6051 SmallBlockChainStream* This,
6052 ULARGE_INTEGER newSize)
6054 ULONG blockIndex, extraBlock;
6058 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6060 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6063 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6066 * Go to the new end of chain
6068 while (count < numBlocks)
6070 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6077 * If the count is 0, we have a special case, the head of the chain was
6082 DirEntry chainEntry;
6084 StorageImpl_ReadDirEntry(This->parentStorage,
6085 This->ownerDirEntry,
6088 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6090 StorageImpl_WriteDirEntry(This->parentStorage,
6091 This->ownerDirEntry,
6095 * We start freeing the chain at the head block.
6097 extraBlock = blockIndex;
6101 /* Get the next block before marking the new end */
6102 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6106 /* Mark the new end of chain */
6107 SmallBlockChainStream_SetNextBlockInChain(
6110 BLOCK_END_OF_CHAIN);
6114 * Mark the extra blocks as free
6116 while (extraBlock != BLOCK_END_OF_CHAIN)
6118 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6121 SmallBlockChainStream_FreeBlock(This, extraBlock);
6122 extraBlock = blockIndex;
6128 /******************************************************************************
6129 * SmallBlockChainStream_Enlarge
6131 * Grows this chain in the small block depot.
6133 static BOOL SmallBlockChainStream_Enlarge(
6134 SmallBlockChainStream* This,
6135 ULARGE_INTEGER newSize)
6137 ULONG blockIndex, currentBlock;
6139 ULONG oldNumBlocks = 0;
6141 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6144 * Empty chain. Create the head.
6146 if (blockIndex == BLOCK_END_OF_CHAIN)
6148 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6149 SmallBlockChainStream_SetNextBlockInChain(
6152 BLOCK_END_OF_CHAIN);
6154 if (This->headOfStreamPlaceHolder != NULL)
6156 *(This->headOfStreamPlaceHolder) = blockIndex;
6160 DirEntry chainEntry;
6162 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6165 chainEntry.startingBlock = blockIndex;
6167 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6172 currentBlock = blockIndex;
6175 * Figure out how many blocks are needed to contain this stream
6177 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6179 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6183 * Go to the current end of chain
6185 while (blockIndex != BLOCK_END_OF_CHAIN)
6188 currentBlock = blockIndex;
6189 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6194 * Add new blocks to the chain
6196 while (oldNumBlocks < newNumBlocks)
6198 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6199 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6201 SmallBlockChainStream_SetNextBlockInChain(
6204 BLOCK_END_OF_CHAIN);
6206 currentBlock = blockIndex;
6213 /******************************************************************************
6214 * SmallBlockChainStream_SetSize
6216 * Sets the size of this stream.
6217 * The file will grow if we grow the chain.
6219 * TODO: Free the actual blocks in the file when we shrink the chain.
6220 * Currently, the blocks are still in the file. So the file size
6221 * doesn't shrink even if we shrink streams.
6223 BOOL SmallBlockChainStream_SetSize(
6224 SmallBlockChainStream* This,
6225 ULARGE_INTEGER newSize)
6227 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6229 if (newSize.u.LowPart == size.u.LowPart)
6232 if (newSize.u.LowPart < size.u.LowPart)
6234 SmallBlockChainStream_Shrink(This, newSize);
6238 SmallBlockChainStream_Enlarge(This, newSize);
6244 /******************************************************************************
6245 * SmallBlockChainStream_GetCount
6247 * Returns the number of small blocks that comprises this chain.
6248 * This is not the size of the stream as the last block may not be full!
6251 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6256 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6258 while(blockIndex != BLOCK_END_OF_CHAIN)
6262 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6263 blockIndex, &blockIndex)))
6270 /******************************************************************************
6271 * SmallBlockChainStream_GetSize
6273 * Returns the size of this chain.
6275 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6277 DirEntry chainEntry;
6279 if(This->headOfStreamPlaceHolder != NULL)
6281 ULARGE_INTEGER result;
6282 result.u.HighPart = 0;
6284 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
6285 This->parentStorage->smallBlockSize;
6290 StorageImpl_ReadDirEntry(
6291 This->parentStorage,
6292 This->ownerDirEntry,
6295 return chainEntry.size;
6298 /******************************************************************************
6299 * StgCreateDocfile [OLE32.@]
6300 * Creates a new compound file storage object
6303 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6304 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6305 * reserved [ ?] unused?, usually 0
6306 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6309 * S_OK if the file was successfully created
6310 * some STG_E_ value if error
6312 * if pwcsName is NULL, create file with new unique name
6313 * the function can returns
6314 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6317 HRESULT WINAPI StgCreateDocfile(
6321 IStorage **ppstgOpen)
6323 StorageBaseImpl* newStorage = 0;
6324 HANDLE hFile = INVALID_HANDLE_VALUE;
6325 HRESULT hr = STG_E_INVALIDFLAG;
6329 DWORD fileAttributes;
6330 WCHAR tempFileName[MAX_PATH];
6332 TRACE("(%s, %x, %d, %p)\n",
6333 debugstr_w(pwcsName), grfMode,
6334 reserved, ppstgOpen);
6337 return STG_E_INVALIDPOINTER;
6339 return STG_E_INVALIDPARAMETER;
6341 /* if no share mode given then DENY_NONE is the default */
6342 if (STGM_SHARE_MODE(grfMode) == 0)
6343 grfMode |= STGM_SHARE_DENY_NONE;
6345 if ( FAILED( validateSTGM(grfMode) ))
6348 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6349 switch(STGM_ACCESS_MODE(grfMode))
6352 case STGM_READWRITE:
6358 /* in direct mode, can only use SHARE_EXCLUSIVE */
6359 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
6362 /* but in transacted mode, any share mode is valid */
6365 * Generate a unique name.
6369 WCHAR tempPath[MAX_PATH];
6370 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
6372 memset(tempPath, 0, sizeof(tempPath));
6373 memset(tempFileName, 0, sizeof(tempFileName));
6375 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
6378 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
6379 pwcsName = tempFileName;
6382 hr = STG_E_INSUFFICIENTMEMORY;
6386 creationMode = TRUNCATE_EXISTING;
6390 creationMode = GetCreationModeFromSTGM(grfMode);
6394 * Interpret the STGM value grfMode
6396 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6397 accessMode = GetAccessModeFromSTGM(grfMode);
6399 if (grfMode & STGM_DELETEONRELEASE)
6400 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
6402 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
6404 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
6408 FIXME("Storage share mode not implemented.\n");
6413 hFile = CreateFileW(pwcsName,
6421 if (hFile == INVALID_HANDLE_VALUE)
6423 if(GetLastError() == ERROR_FILE_EXISTS)
6424 hr = STG_E_FILEALREADYEXISTS;
6431 * Allocate and initialize the new IStorage32object.
6433 hr = Storage_Construct(
6448 * Get an "out" pointer for the caller.
6450 *ppstgOpen = (IStorage*)newStorage;
6453 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
6458 /******************************************************************************
6459 * StgCreateStorageEx [OLE32.@]
6461 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6463 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6464 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6466 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
6468 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6469 return STG_E_INVALIDPARAMETER;
6472 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
6474 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6475 return STG_E_INVALIDPARAMETER;
6478 if (stgfmt == STGFMT_FILE)
6480 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6481 return STG_E_INVALIDPARAMETER;
6484 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
6486 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6487 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
6490 ERR("Invalid stgfmt argument\n");
6491 return STG_E_INVALIDPARAMETER;
6494 /******************************************************************************
6495 * StgCreatePropSetStg [OLE32.@]
6497 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
6498 IPropertySetStorage **ppPropSetStg)
6502 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
6504 hr = STG_E_INVALIDPARAMETER;
6506 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
6507 (void**)ppPropSetStg);
6511 /******************************************************************************
6512 * StgOpenStorageEx [OLE32.@]
6514 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6516 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6517 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6519 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
6521 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6522 return STG_E_INVALIDPARAMETER;
6528 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6529 return STG_E_INVALIDPARAMETER;
6531 case STGFMT_STORAGE:
6534 case STGFMT_DOCFILE:
6535 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
6537 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6538 return STG_E_INVALIDPARAMETER;
6540 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6544 WARN("STGFMT_ANY assuming storage\n");
6548 return STG_E_INVALIDPARAMETER;
6551 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
6555 /******************************************************************************
6556 * StgOpenStorage [OLE32.@]
6558 HRESULT WINAPI StgOpenStorage(
6559 const OLECHAR *pwcsName,
6560 IStorage *pstgPriority,
6564 IStorage **ppstgOpen)
6566 StorageBaseImpl* newStorage = 0;
6572 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6573 debugstr_w(pwcsName), pstgPriority, grfMode,
6574 snbExclude, reserved, ppstgOpen);
6578 hr = STG_E_INVALIDNAME;
6584 hr = STG_E_INVALIDPOINTER;
6590 hr = STG_E_INVALIDPARAMETER;
6594 if (grfMode & STGM_PRIORITY)
6596 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
6597 return STG_E_INVALIDFLAG;
6598 if (grfMode & STGM_DELETEONRELEASE)
6599 return STG_E_INVALIDFUNCTION;
6600 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
6601 return STG_E_INVALIDFLAG;
6602 grfMode &= ~0xf0; /* remove the existing sharing mode */
6603 grfMode |= STGM_SHARE_DENY_NONE;
6605 /* STGM_PRIORITY stops other IStorage objects on the same file from
6606 * committing until the STGM_PRIORITY IStorage is closed. it also
6607 * stops non-transacted mode StgOpenStorage calls with write access from
6608 * succeeding. obviously, both of these cannot be achieved through just
6609 * file share flags */
6610 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6614 * Validate the sharing mode
6616 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
6617 switch(STGM_SHARE_MODE(grfMode))
6619 case STGM_SHARE_EXCLUSIVE:
6620 case STGM_SHARE_DENY_WRITE:
6623 hr = STG_E_INVALIDFLAG;
6627 if ( FAILED( validateSTGM(grfMode) ) ||
6628 (grfMode&STGM_CREATE))
6630 hr = STG_E_INVALIDFLAG;
6634 /* shared reading requires transacted mode */
6635 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6636 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6637 !(grfMode&STGM_TRANSACTED) )
6639 hr = STG_E_INVALIDFLAG;
6644 * Interpret the STGM value grfMode
6646 shareMode = GetShareModeFromSTGM(grfMode);
6647 accessMode = GetAccessModeFromSTGM(grfMode);
6651 hFile = CreateFileW( pwcsName,
6656 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6659 if (hFile==INVALID_HANDLE_VALUE)
6661 DWORD last_error = GetLastError();
6667 case ERROR_FILE_NOT_FOUND:
6668 hr = STG_E_FILENOTFOUND;
6671 case ERROR_PATH_NOT_FOUND:
6672 hr = STG_E_PATHNOTFOUND;
6675 case ERROR_ACCESS_DENIED:
6676 case ERROR_WRITE_PROTECT:
6677 hr = STG_E_ACCESSDENIED;
6680 case ERROR_SHARING_VIOLATION:
6681 hr = STG_E_SHAREVIOLATION;
6692 * Refuse to open the file if it's too small to be a structured storage file
6693 * FIXME: verify the file when reading instead of here
6695 if (GetFileSize(hFile, NULL) < 0x100)
6698 hr = STG_E_FILEALREADYEXISTS;
6703 * Allocate and initialize the new IStorage32object.
6705 hr = Storage_Construct(
6717 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6719 if(hr == STG_E_INVALIDHEADER)
6720 hr = STG_E_FILEALREADYEXISTS;
6725 * Get an "out" pointer for the caller.
6727 *ppstgOpen = (IStorage*)newStorage;
6730 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6734 /******************************************************************************
6735 * StgCreateDocfileOnILockBytes [OLE32.@]
6737 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6741 IStorage** ppstgOpen)
6743 StorageBaseImpl* newStorage = 0;
6746 if ((ppstgOpen == 0) || (plkbyt == 0))
6747 return STG_E_INVALIDPOINTER;
6750 * Allocate and initialize the new IStorage object.
6752 hr = Storage_Construct(
6767 * Get an "out" pointer for the caller.
6769 *ppstgOpen = (IStorage*)newStorage;
6774 /******************************************************************************
6775 * StgOpenStorageOnILockBytes [OLE32.@]
6777 HRESULT WINAPI StgOpenStorageOnILockBytes(
6779 IStorage *pstgPriority,
6783 IStorage **ppstgOpen)
6785 StorageBaseImpl* newStorage = 0;
6788 if ((plkbyt == 0) || (ppstgOpen == 0))
6789 return STG_E_INVALIDPOINTER;
6791 if ( FAILED( validateSTGM(grfMode) ))
6792 return STG_E_INVALIDFLAG;
6797 * Allocate and initialize the new IStorage object.
6799 hr = Storage_Construct(
6814 * Get an "out" pointer for the caller.
6816 *ppstgOpen = (IStorage*)newStorage;
6821 /******************************************************************************
6822 * StgSetTimes [ole32.@]
6823 * StgSetTimes [OLE32.@]
6827 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6828 FILETIME const *patime, FILETIME const *pmtime)
6830 IStorage *stg = NULL;
6833 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6835 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6839 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6840 IStorage_Release(stg);
6846 /******************************************************************************
6847 * StgIsStorageILockBytes [OLE32.@]
6849 * Determines if the ILockBytes contains a storage object.
6851 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6854 ULARGE_INTEGER offset;
6856 offset.u.HighPart = 0;
6857 offset.u.LowPart = 0;
6859 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6861 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6867 /******************************************************************************
6868 * WriteClassStg [OLE32.@]
6870 * This method will store the specified CLSID in the specified storage object
6872 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6877 return E_INVALIDARG;
6880 return STG_E_INVALIDPOINTER;
6882 hRes = IStorage_SetClass(pStg, rclsid);
6887 /***********************************************************************
6888 * ReadClassStg (OLE32.@)
6890 * This method reads the CLSID previously written to a storage object with
6891 * the WriteClassStg.
6894 * pstg [I] IStorage pointer
6895 * pclsid [O] Pointer to where the CLSID is written
6899 * Failure: HRESULT code.
6901 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6906 TRACE("(%p, %p)\n", pstg, pclsid);
6908 if(!pstg || !pclsid)
6909 return E_INVALIDARG;
6912 * read a STATSTG structure (contains the clsid) from the storage
6914 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6917 *pclsid=pstatstg.clsid;
6922 /***********************************************************************
6923 * OleLoadFromStream (OLE32.@)
6925 * This function loads an object from stream
6927 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6931 LPPERSISTSTREAM xstm;
6933 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6935 res=ReadClassStm(pStm,&clsid);
6938 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6941 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6943 IUnknown_Release((IUnknown*)*ppvObj);
6946 res=IPersistStream_Load(xstm,pStm);
6947 IPersistStream_Release(xstm);
6948 /* FIXME: all refcounts ok at this point? I think they should be:
6951 * xstm : 0 (released)
6956 /***********************************************************************
6957 * OleSaveToStream (OLE32.@)
6959 * This function saves an object with the IPersistStream interface on it
6960 * to the specified stream.
6962 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6968 TRACE("(%p,%p)\n",pPStm,pStm);
6970 res=IPersistStream_GetClassID(pPStm,&clsid);
6972 if (SUCCEEDED(res)){
6974 res=WriteClassStm(pStm,&clsid);
6978 res=IPersistStream_Save(pPStm,pStm,TRUE);
6981 TRACE("Finished Save\n");
6985 /****************************************************************************
6986 * This method validate a STGM parameter that can contain the values below
6988 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6989 * The stgm values contained in 0xffff0000 are bitmasks.
6991 * STGM_DIRECT 0x00000000
6992 * STGM_TRANSACTED 0x00010000
6993 * STGM_SIMPLE 0x08000000
6995 * STGM_READ 0x00000000
6996 * STGM_WRITE 0x00000001
6997 * STGM_READWRITE 0x00000002
6999 * STGM_SHARE_DENY_NONE 0x00000040
7000 * STGM_SHARE_DENY_READ 0x00000030
7001 * STGM_SHARE_DENY_WRITE 0x00000020
7002 * STGM_SHARE_EXCLUSIVE 0x00000010
7004 * STGM_PRIORITY 0x00040000
7005 * STGM_DELETEONRELEASE 0x04000000
7007 * STGM_CREATE 0x00001000
7008 * STGM_CONVERT 0x00020000
7009 * STGM_FAILIFTHERE 0x00000000
7011 * STGM_NOSCRATCH 0x00100000
7012 * STGM_NOSNAPSHOT 0x00200000
7014 static HRESULT validateSTGM(DWORD stgm)
7016 DWORD access = STGM_ACCESS_MODE(stgm);
7017 DWORD share = STGM_SHARE_MODE(stgm);
7018 DWORD create = STGM_CREATE_MODE(stgm);
7020 if (stgm&~STGM_KNOWN_FLAGS)
7022 ERR("unknown flags %08x\n", stgm);
7030 case STGM_READWRITE:
7038 case STGM_SHARE_DENY_NONE:
7039 case STGM_SHARE_DENY_READ:
7040 case STGM_SHARE_DENY_WRITE:
7041 case STGM_SHARE_EXCLUSIVE:
7050 case STGM_FAILIFTHERE:
7057 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7059 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7063 * STGM_CREATE | STGM_CONVERT
7064 * if both are false, STGM_FAILIFTHERE is set to TRUE
7066 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7070 * STGM_NOSCRATCH requires STGM_TRANSACTED
7072 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7076 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7077 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7079 if ( (stgm & STGM_NOSNAPSHOT) &&
7080 (!(stgm & STGM_TRANSACTED) ||
7081 share == STGM_SHARE_EXCLUSIVE ||
7082 share == STGM_SHARE_DENY_WRITE) )
7088 /****************************************************************************
7089 * GetShareModeFromSTGM
7091 * This method will return a share mode flag from a STGM value.
7092 * The STGM value is assumed valid.
7094 static DWORD GetShareModeFromSTGM(DWORD stgm)
7096 switch (STGM_SHARE_MODE(stgm))
7098 case STGM_SHARE_DENY_NONE:
7099 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7100 case STGM_SHARE_DENY_READ:
7101 return FILE_SHARE_WRITE;
7102 case STGM_SHARE_DENY_WRITE:
7103 return FILE_SHARE_READ;
7104 case STGM_SHARE_EXCLUSIVE:
7107 ERR("Invalid share mode!\n");
7112 /****************************************************************************
7113 * GetAccessModeFromSTGM
7115 * This method will return an access mode flag from a STGM value.
7116 * The STGM value is assumed valid.
7118 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7120 switch (STGM_ACCESS_MODE(stgm))
7123 return GENERIC_READ;
7125 case STGM_READWRITE:
7126 return GENERIC_READ | GENERIC_WRITE;
7128 ERR("Invalid access mode!\n");
7133 /****************************************************************************
7134 * GetCreationModeFromSTGM
7136 * This method will return a creation mode flag from a STGM value.
7137 * The STGM value is assumed valid.
7139 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7141 switch(STGM_CREATE_MODE(stgm))
7144 return CREATE_ALWAYS;
7146 FIXME("STGM_CONVERT not implemented!\n");
7148 case STGM_FAILIFTHERE:
7151 ERR("Invalid create mode!\n");
7157 /*************************************************************************
7158 * OLECONVERT_LoadOLE10 [Internal]
7160 * Loads the OLE10 STREAM to memory
7163 * pOleStream [I] The OLESTREAM
7164 * pData [I] Data Structure for the OLESTREAM Data
7168 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7169 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7172 * This function is used by OleConvertOLESTREAMToIStorage only.
7174 * Memory allocated for pData must be freed by the caller
7176 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7179 HRESULT hRes = S_OK;
7183 pData->pData = NULL;
7184 pData->pstrOleObjFileName = NULL;
7186 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7189 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7190 if(dwSize != sizeof(pData->dwOleID))
7192 hRes = CONVERT10_E_OLESTREAM_GET;
7194 else if(pData->dwOleID != OLESTREAM_ID)
7196 hRes = CONVERT10_E_OLESTREAM_FMT;
7207 /* Get the TypeID... more info needed for this field */
7208 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7209 if(dwSize != sizeof(pData->dwTypeID))
7211 hRes = CONVERT10_E_OLESTREAM_GET;
7216 if(pData->dwTypeID != 0)
7218 /* Get the length of the OleTypeName */
7219 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7220 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7222 hRes = CONVERT10_E_OLESTREAM_GET;
7227 if(pData->dwOleTypeNameLength > 0)
7229 /* Get the OleTypeName */
7230 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7231 if(dwSize != pData->dwOleTypeNameLength)
7233 hRes = CONVERT10_E_OLESTREAM_GET;
7239 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7240 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7242 hRes = CONVERT10_E_OLESTREAM_GET;
7246 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7247 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7248 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7249 if(pData->pstrOleObjFileName)
7251 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7252 if(dwSize != pData->dwOleObjFileNameLength)
7254 hRes = CONVERT10_E_OLESTREAM_GET;
7258 hRes = CONVERT10_E_OLESTREAM_GET;
7263 /* Get the Width of the Metafile */
7264 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7265 if(dwSize != sizeof(pData->dwMetaFileWidth))
7267 hRes = CONVERT10_E_OLESTREAM_GET;
7271 /* Get the Height of the Metafile */
7272 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7273 if(dwSize != sizeof(pData->dwMetaFileHeight))
7275 hRes = CONVERT10_E_OLESTREAM_GET;
7281 /* Get the Length of the Data */
7282 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7283 if(dwSize != sizeof(pData->dwDataLength))
7285 hRes = CONVERT10_E_OLESTREAM_GET;
7289 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7291 if(!bStrem1) /* if it is a second OLE stream data */
7293 pData->dwDataLength -= 8;
7294 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7295 if(dwSize != sizeof(pData->strUnknown))
7297 hRes = CONVERT10_E_OLESTREAM_GET;
7303 if(pData->dwDataLength > 0)
7305 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
7307 /* Get Data (ex. IStorage, Metafile, or BMP) */
7310 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
7311 if(dwSize != pData->dwDataLength)
7313 hRes = CONVERT10_E_OLESTREAM_GET;
7318 hRes = CONVERT10_E_OLESTREAM_GET;
7327 /*************************************************************************
7328 * OLECONVERT_SaveOLE10 [Internal]
7330 * Saves the OLE10 STREAM From memory
7333 * pData [I] Data Structure for the OLESTREAM Data
7334 * pOleStream [I] The OLESTREAM to save
7338 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7341 * This function is used by OleConvertIStorageToOLESTREAM only.
7344 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
7347 HRESULT hRes = S_OK;
7351 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7352 if(dwSize != sizeof(pData->dwOleID))
7354 hRes = CONVERT10_E_OLESTREAM_PUT;
7359 /* Set the TypeID */
7360 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7361 if(dwSize != sizeof(pData->dwTypeID))
7363 hRes = CONVERT10_E_OLESTREAM_PUT;
7367 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
7369 /* Set the Length of the OleTypeName */
7370 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7371 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7373 hRes = CONVERT10_E_OLESTREAM_PUT;
7378 if(pData->dwOleTypeNameLength > 0)
7380 /* Set the OleTypeName */
7381 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7382 if(dwSize != pData->dwOleTypeNameLength)
7384 hRes = CONVERT10_E_OLESTREAM_PUT;
7391 /* Set the width of the Metafile */
7392 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7393 if(dwSize != sizeof(pData->dwMetaFileWidth))
7395 hRes = CONVERT10_E_OLESTREAM_PUT;
7401 /* Set the height of the Metafile */
7402 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7403 if(dwSize != sizeof(pData->dwMetaFileHeight))
7405 hRes = CONVERT10_E_OLESTREAM_PUT;
7411 /* Set the length of the Data */
7412 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7413 if(dwSize != sizeof(pData->dwDataLength))
7415 hRes = CONVERT10_E_OLESTREAM_PUT;
7421 if(pData->dwDataLength > 0)
7423 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7424 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
7425 if(dwSize != pData->dwDataLength)
7427 hRes = CONVERT10_E_OLESTREAM_PUT;
7435 /*************************************************************************
7436 * OLECONVERT_GetOLE20FromOLE10[Internal]
7438 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7439 * opens it, and copies the content to the dest IStorage for
7440 * OleConvertOLESTREAMToIStorage
7444 * pDestStorage [I] The IStorage to copy the data to
7445 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7446 * nBufferLength [I] The size of the buffer
7455 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
7459 IStorage *pTempStorage;
7460 DWORD dwNumOfBytesWritten;
7461 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7462 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7464 /* Create a temp File */
7465 GetTempPathW(MAX_PATH, wstrTempDir);
7466 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7467 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
7469 if(hFile != INVALID_HANDLE_VALUE)
7471 /* Write IStorage Data to File */
7472 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
7475 /* Open and copy temp storage to the Dest Storage */
7476 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
7479 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
7480 IStorage_Release(pTempStorage);
7482 DeleteFileW(wstrTempFile);
7487 /*************************************************************************
7488 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7490 * Saves the OLE10 STREAM From memory
7493 * pStorage [I] The Src IStorage to copy
7494 * pData [I] The Dest Memory to write to.
7497 * The size in bytes allocated for pData
7500 * Memory allocated for pData must be freed by the caller
7502 * Used by OleConvertIStorageToOLESTREAM only.
7505 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
7509 DWORD nDataLength = 0;
7510 IStorage *pTempStorage;
7511 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7512 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7516 /* Create temp Storage */
7517 GetTempPathW(MAX_PATH, wstrTempDir);
7518 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7519 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
7523 /* Copy Src Storage to the Temp Storage */
7524 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
7525 IStorage_Release(pTempStorage);
7527 /* Open Temp Storage as a file and copy to memory */
7528 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7529 if(hFile != INVALID_HANDLE_VALUE)
7531 nDataLength = GetFileSize(hFile, NULL);
7532 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
7533 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
7536 DeleteFileW(wstrTempFile);
7541 /*************************************************************************
7542 * OLECONVERT_CreateOleStream [Internal]
7544 * Creates the "\001OLE" stream in the IStorage if necessary.
7547 * pStorage [I] Dest storage to create the stream in
7553 * This function is used by OleConvertOLESTREAMToIStorage only.
7555 * This stream is still unknown, MS Word seems to have extra data
7556 * but since the data is stored in the OLESTREAM there should be
7557 * no need to recreate the stream. If the stream is manually
7558 * deleted it will create it with this default data.
7561 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7565 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7566 BYTE pOleStreamHeader [] =
7568 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7569 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7570 0x00, 0x00, 0x00, 0x00
7573 /* Create stream if not present */
7574 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7575 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7579 /* Write default Data */
7580 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7581 IStream_Release(pStream);
7585 /* write a string to a stream, preceded by its length */
7586 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7593 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7594 r = IStream_Write( stm, &len, sizeof(len), NULL);
7599 str = CoTaskMemAlloc( len );
7600 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7601 r = IStream_Write( stm, str, len, NULL);
7602 CoTaskMemFree( str );
7606 /* read a string preceded by its length from a stream */
7607 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7610 DWORD len, count = 0;
7614 r = IStream_Read( stm, &len, sizeof(len), &count );
7617 if( count != sizeof(len) )
7618 return E_OUTOFMEMORY;
7620 TRACE("%d bytes\n",len);
7622 str = CoTaskMemAlloc( len );
7624 return E_OUTOFMEMORY;
7626 r = IStream_Read( stm, str, len, &count );
7631 CoTaskMemFree( str );
7632 return E_OUTOFMEMORY;
7635 TRACE("Read string %s\n",debugstr_an(str,len));
7637 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7638 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7640 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7641 CoTaskMemFree( str );
7649 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7650 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7654 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7656 static const BYTE unknown1[12] =
7657 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7658 0xFF, 0xFF, 0xFF, 0xFF};
7659 static const BYTE unknown2[16] =
7660 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7661 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7663 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7664 debugstr_w(lpszUserType), debugstr_w(szClipName),
7665 debugstr_w(szProgIDName));
7667 /* Create a CompObj stream */
7668 r = IStorage_CreateStream(pstg, szwStreamName,
7669 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7673 /* Write CompObj Structure to stream */
7674 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7676 if( SUCCEEDED( r ) )
7677 r = WriteClassStm( pstm, clsid );
7679 if( SUCCEEDED( r ) )
7680 r = STREAM_WriteString( pstm, lpszUserType );
7681 if( SUCCEEDED( r ) )
7682 r = STREAM_WriteString( pstm, szClipName );
7683 if( SUCCEEDED( r ) )
7684 r = STREAM_WriteString( pstm, szProgIDName );
7685 if( SUCCEEDED( r ) )
7686 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7688 IStream_Release( pstm );
7693 /***********************************************************************
7694 * WriteFmtUserTypeStg (OLE32.@)
7696 HRESULT WINAPI WriteFmtUserTypeStg(
7697 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7700 WCHAR szwClipName[0x40];
7701 CLSID clsid = CLSID_NULL;
7702 LPWSTR wstrProgID = NULL;
7705 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7707 /* get the clipboard format name */
7708 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7711 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7713 /* FIXME: There's room to save a CLSID and its ProgID, but
7714 the CLSID is not looked up in the registry and in all the
7715 tests I wrote it was CLSID_NULL. Where does it come from?
7718 /* get the real program ID. This may fail, but that's fine */
7719 ProgIDFromCLSID(&clsid, &wstrProgID);
7721 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7723 r = STORAGE_WriteCompObj( pstg, &clsid,
7724 lpszUserType, szwClipName, wstrProgID );
7726 CoTaskMemFree(wstrProgID);
7732 /******************************************************************************
7733 * ReadFmtUserTypeStg [OLE32.@]
7735 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7739 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7740 unsigned char unknown1[12];
7741 unsigned char unknown2[16];
7743 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7746 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7748 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7749 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7752 WARN("Failed to open stream r = %08x\n", r);
7756 /* read the various parts of the structure */
7757 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7758 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7760 r = ReadClassStm( stm, &clsid );
7764 r = STREAM_ReadString( stm, &szCLSIDName );
7768 r = STREAM_ReadString( stm, &szOleTypeName );
7772 r = STREAM_ReadString( stm, &szProgIDName );
7776 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7777 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7780 /* ok, success... now we just need to store what we found */
7782 *pcf = RegisterClipboardFormatW( szOleTypeName );
7783 CoTaskMemFree( szOleTypeName );
7785 if( lplpszUserType )
7786 *lplpszUserType = szCLSIDName;
7787 CoTaskMemFree( szProgIDName );
7790 IStream_Release( stm );
7796 /*************************************************************************
7797 * OLECONVERT_CreateCompObjStream [Internal]
7799 * Creates a "\001CompObj" is the destination IStorage if necessary.
7802 * pStorage [I] The dest IStorage to create the CompObj Stream
7804 * strOleTypeName [I] The ProgID
7808 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7811 * This function is used by OleConvertOLESTREAMToIStorage only.
7813 * The stream data is stored in the OLESTREAM and there should be
7814 * no need to recreate the stream. If the stream is manually
7815 * deleted it will attempt to create it by querying the registry.
7819 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7822 HRESULT hStorageRes, hRes = S_OK;
7823 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7824 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7825 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7827 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7828 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7830 /* Initialize the CompObj structure */
7831 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7832 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7833 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7836 /* Create a CompObj stream if it doesn't exist */
7837 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7838 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7839 if(hStorageRes == S_OK)
7841 /* copy the OleTypeName to the compobj struct */
7842 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7843 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7845 /* copy the OleTypeName to the compobj struct */
7846 /* Note: in the test made, these were Identical */
7847 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7848 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7851 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7852 bufferW, OLESTREAM_MAX_STR_LEN );
7853 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7859 /* Get the CLSID Default Name from the Registry */
7860 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7861 if(hErr == ERROR_SUCCESS)
7863 char strTemp[OLESTREAM_MAX_STR_LEN];
7864 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7865 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7866 if(hErr == ERROR_SUCCESS)
7868 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7874 /* Write CompObj Structure to stream */
7875 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7877 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7879 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7880 if(IStorageCompObj.dwCLSIDNameLength > 0)
7882 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7884 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7885 if(IStorageCompObj.dwOleTypeNameLength > 0)
7887 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7889 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7890 if(IStorageCompObj.dwProgIDNameLength > 0)
7892 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7894 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7895 IStream_Release(pStream);
7901 /*************************************************************************
7902 * OLECONVERT_CreateOlePresStream[Internal]
7904 * Creates the "\002OlePres000" Stream with the Metafile data
7907 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7908 * dwExtentX [I] Width of the Metafile
7909 * dwExtentY [I] Height of the Metafile
7910 * pData [I] Metafile data
7911 * dwDataLength [I] Size of the Metafile data
7915 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7918 * This function is used by OleConvertOLESTREAMToIStorage only.
7921 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7925 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7926 BYTE pOlePresStreamHeader [] =
7928 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7929 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7930 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7931 0x00, 0x00, 0x00, 0x00
7934 BYTE pOlePresStreamHeaderEmpty [] =
7936 0x00, 0x00, 0x00, 0x00,
7937 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7938 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7939 0x00, 0x00, 0x00, 0x00
7942 /* Create the OlePres000 Stream */
7943 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7944 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7949 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7951 memset(&OlePres, 0, sizeof(OlePres));
7952 /* Do we have any metafile data to save */
7953 if(dwDataLength > 0)
7955 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7956 nHeaderSize = sizeof(pOlePresStreamHeader);
7960 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7961 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7963 /* Set width and height of the metafile */
7964 OlePres.dwExtentX = dwExtentX;
7965 OlePres.dwExtentY = -dwExtentY;
7967 /* Set Data and Length */
7968 if(dwDataLength > sizeof(METAFILEPICT16))
7970 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7971 OlePres.pData = &(pData[8]);
7973 /* Save OlePres000 Data to Stream */
7974 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7975 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7976 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7977 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7978 if(OlePres.dwSize > 0)
7980 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7982 IStream_Release(pStream);
7986 /*************************************************************************
7987 * OLECONVERT_CreateOle10NativeStream [Internal]
7989 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7992 * pStorage [I] Dest storage to create the stream in
7993 * pData [I] Ole10 Native Data (ex. bmp)
7994 * dwDataLength [I] Size of the Ole10 Native Data
8000 * This function is used by OleConvertOLESTREAMToIStorage only.
8002 * Might need to verify the data and return appropriate error message
8005 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8009 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8011 /* Create the Ole10Native Stream */
8012 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8013 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8017 /* Write info to stream */
8018 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8019 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8020 IStream_Release(pStream);
8025 /*************************************************************************
8026 * OLECONVERT_GetOLE10ProgID [Internal]
8028 * Finds the ProgID (or OleTypeID) from the IStorage
8031 * pStorage [I] The Src IStorage to get the ProgID
8032 * strProgID [I] the ProgID string to get
8033 * dwSize [I] the size of the string
8037 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8040 * This function is used by OleConvertIStorageToOLESTREAM only.
8044 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8048 LARGE_INTEGER iSeekPos;
8049 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8050 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8052 /* Open the CompObj Stream */
8053 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8054 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8058 /*Get the OleType from the CompObj Stream */
8059 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8060 iSeekPos.u.HighPart = 0;
8062 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8063 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8064 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8065 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8066 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8067 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8068 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8070 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8073 IStream_Read(pStream, strProgID, *dwSize, NULL);
8075 IStream_Release(pStream);
8080 LPOLESTR wstrProgID;
8082 /* Get the OleType from the registry */
8083 REFCLSID clsid = &(stat.clsid);
8084 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8085 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8088 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8095 /*************************************************************************
8096 * OLECONVERT_GetOle10PresData [Internal]
8098 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8101 * pStorage [I] Src IStroage
8102 * pOleStream [I] Dest OleStream Mem Struct
8108 * This function is used by OleConvertIStorageToOLESTREAM only.
8110 * Memory allocated for pData must be freed by the caller
8114 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8119 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8121 /* Initialize Default data for OLESTREAM */
8122 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8123 pOleStreamData[0].dwTypeID = 2;
8124 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8125 pOleStreamData[1].dwTypeID = 0;
8126 pOleStreamData[0].dwMetaFileWidth = 0;
8127 pOleStreamData[0].dwMetaFileHeight = 0;
8128 pOleStreamData[0].pData = NULL;
8129 pOleStreamData[1].pData = NULL;
8131 /* Open Ole10Native Stream */
8132 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8133 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8137 /* Read Size and Data */
8138 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8139 if(pOleStreamData->dwDataLength > 0)
8141 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8142 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8144 IStream_Release(pStream);
8150 /*************************************************************************
8151 * OLECONVERT_GetOle20PresData[Internal]
8153 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8156 * pStorage [I] Src IStroage
8157 * pOleStreamData [I] Dest OleStream Mem Struct
8163 * This function is used by OleConvertIStorageToOLESTREAM only.
8165 * Memory allocated for pData must be freed by the caller
8167 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8171 OLECONVERT_ISTORAGE_OLEPRES olePress;
8172 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8174 /* Initialize Default data for OLESTREAM */
8175 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8176 pOleStreamData[0].dwTypeID = 2;
8177 pOleStreamData[0].dwMetaFileWidth = 0;
8178 pOleStreamData[0].dwMetaFileHeight = 0;
8179 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8180 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8181 pOleStreamData[1].dwTypeID = 0;
8182 pOleStreamData[1].dwOleTypeNameLength = 0;
8183 pOleStreamData[1].strOleTypeName[0] = 0;
8184 pOleStreamData[1].dwMetaFileWidth = 0;
8185 pOleStreamData[1].dwMetaFileHeight = 0;
8186 pOleStreamData[1].pData = NULL;
8187 pOleStreamData[1].dwDataLength = 0;
8190 /* Open OlePress000 stream */
8191 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8192 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8195 LARGE_INTEGER iSeekPos;
8196 METAFILEPICT16 MetaFilePict;
8197 static const char strMetafilePictName[] = "METAFILEPICT";
8199 /* Set the TypeID for a Metafile */
8200 pOleStreamData[1].dwTypeID = 5;
8202 /* Set the OleTypeName to Metafile */
8203 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8204 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8206 iSeekPos.u.HighPart = 0;
8207 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8209 /* Get Presentation Data */
8210 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8211 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8212 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8213 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8215 /*Set width and Height */
8216 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8217 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8218 if(olePress.dwSize > 0)
8221 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8223 /* Set MetaFilePict struct */
8224 MetaFilePict.mm = 8;
8225 MetaFilePict.xExt = olePress.dwExtentX;
8226 MetaFilePict.yExt = olePress.dwExtentY;
8227 MetaFilePict.hMF = 0;
8229 /* Get Metafile Data */
8230 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8231 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8232 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8234 IStream_Release(pStream);
8238 /*************************************************************************
8239 * OleConvertOLESTREAMToIStorage [OLE32.@]
8244 * DVTARGETDEVICE parameter is not handled
8245 * Still unsure of some mem fields for OLE 10 Stream
8246 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8247 * and "\001OLE" streams
8250 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8251 LPOLESTREAM pOleStream,
8253 const DVTARGETDEVICE* ptd)
8257 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8259 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8261 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8265 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8268 if(pstg == NULL || pOleStream == NULL)
8270 hRes = E_INVALIDARG;
8275 /* Load the OLESTREAM to Memory */
8276 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8281 /* Load the OLESTREAM to Memory (part 2)*/
8282 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8288 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8290 /* Do we have the IStorage Data in the OLESTREAM */
8291 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8293 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8294 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8298 /* It must be an original OLE 1.0 source */
8299 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8304 /* It must be an original OLE 1.0 source */
8305 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8308 /* Create CompObj Stream if necessary */
8309 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
8312 /*Create the Ole Stream if necessary */
8313 OLECONVERT_CreateOleStream(pstg);
8318 /* Free allocated memory */
8319 for(i=0; i < 2; i++)
8321 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8322 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
8323 pOleStreamData[i].pstrOleObjFileName = NULL;
8328 /*************************************************************************
8329 * OleConvertIStorageToOLESTREAM [OLE32.@]
8336 * Still unsure of some mem fields for OLE 10 Stream
8337 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8338 * and "\001OLE" streams.
8341 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
8343 LPOLESTREAM pOleStream)
8346 HRESULT hRes = S_OK;
8348 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8349 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8351 TRACE("%p %p\n", pstg, pOleStream);
8353 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8355 if(pstg == NULL || pOleStream == NULL)
8357 hRes = E_INVALIDARG;
8361 /* Get the ProgID */
8362 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
8363 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
8367 /* Was it originally Ole10 */
8368 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
8371 IStream_Release(pStream);
8372 /* Get Presentation Data for Ole10Native */
8373 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
8377 /* Get Presentation Data (OLE20) */
8378 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
8381 /* Save OLESTREAM */
8382 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
8385 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
8390 /* Free allocated memory */
8391 for(i=0; i < 2; i++)
8393 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8399 /***********************************************************************
8400 * GetConvertStg (OLE32.@)
8402 HRESULT WINAPI GetConvertStg(IStorage *stg) {
8403 FIXME("unimplemented stub!\n");
8407 /******************************************************************************
8408 * StgIsStorageFile [OLE32.@]
8409 * Verify if the file contains a storage object
8415 * S_OK if file has magic bytes as a storage object
8416 * S_FALSE if file is not storage
8419 StgIsStorageFile(LPCOLESTR fn)
8425 TRACE("%s\n", debugstr_w(fn));
8426 hf = CreateFileW(fn, GENERIC_READ,
8427 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
8428 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8430 if (hf == INVALID_HANDLE_VALUE)
8431 return STG_E_FILENOTFOUND;
8433 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
8435 WARN(" unable to read file\n");
8442 if (bytes_read != 8) {
8443 TRACE(" too short\n");
8447 if (!memcmp(magic,STORAGE_magic,8)) {
8452 TRACE(" -> Invalid header.\n");
8456 /***********************************************************************
8457 * WriteClassStm (OLE32.@)
8459 * Writes a CLSID to a stream.
8462 * pStm [I] Stream to write to.
8463 * rclsid [I] CLSID to write.
8467 * Failure: HRESULT code.
8469 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
8471 TRACE("(%p,%p)\n",pStm,rclsid);
8473 if (!pStm || !rclsid)
8474 return E_INVALIDARG;
8476 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
8479 /***********************************************************************
8480 * ReadClassStm (OLE32.@)
8482 * Reads a CLSID from a stream.
8485 * pStm [I] Stream to read from.
8486 * rclsid [O] CLSID to read.
8490 * Failure: HRESULT code.
8492 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
8497 TRACE("(%p,%p)\n",pStm,pclsid);
8499 if (!pStm || !pclsid)
8500 return E_INVALIDARG;
8502 /* clear the output args */
8503 *pclsid = CLSID_NULL;
8505 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
8510 if (nbByte != sizeof(CLSID))
8511 return STG_E_READFAULT;