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"
55 #include "compobj_private.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(storage);
59 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
60 #define OLESTREAM_ID 0x501
61 #define OLESTREAM_MAX_STR_LEN 255
64 * These are signatures to detect the type of Document file.
66 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
67 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
69 static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
71 return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
74 static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface )
76 return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
79 /****************************************************************************
80 * Storage32InternalImpl definitions.
82 * Definition of the implementation structure for the IStorage32 interface.
83 * This one implements the IStorage32 interface for storage that are
84 * inside another storage.
86 struct StorageInternalImpl
88 struct StorageBaseImpl base;
91 * Entry in the parent's stream tracking list
93 struct list ParentListEntry;
95 StorageBaseImpl *parentStorage;
97 typedef struct StorageInternalImpl StorageInternalImpl;
99 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
100 static const IStorageVtbl Storage32InternalImpl_Vtbl;
102 /* Method definitions for the Storage32InternalImpl class. */
103 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
104 DWORD openFlags, DirRef storageDirEntry);
105 static void StorageImpl_Destroy(StorageBaseImpl* iface);
106 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
107 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
108 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
109 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
110 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
111 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
112 static void StorageImpl_SaveFileHeader(StorageImpl* This);
114 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
115 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
116 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
117 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
118 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
120 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
121 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
122 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
124 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
125 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
126 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
127 ULONG blockIndex, ULONG offset, DWORD value);
128 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
129 ULONG blockIndex, ULONG offset, DWORD* value);
131 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
132 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
134 typedef struct TransactedDirEntry
136 /* If applicable, a reference to the original DirEntry in the transacted
137 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
138 DirRef transactedParentEntry;
140 /* True if this entry is being used. */
143 /* True if data is up to date. */
146 /* True if this entry has been modified. */
149 /* True if this entry's stream has been modified. */
152 /* True if this entry has been deleted in the transacted storage, but the
153 * delete has not yet been committed. */
156 /* If this entry's stream has been modified, a reference to where the stream
157 * is stored in the snapshot file. */
160 /* This directory entry's data, including any changes that have been made. */
163 /* A reference to the parent of this node. This is only valid while we are
164 * committing changes. */
167 /* A reference to a newly-created entry in the transacted parent. This is
168 * always equal to transactedParentEntry except when committing changes. */
169 DirRef newTransactedParentEntry;
170 } TransactedDirEntry;
172 /****************************************************************************
173 * Transacted storage object.
175 typedef struct TransactedSnapshotImpl
177 struct StorageBaseImpl base;
180 * Modified streams are temporarily saved to the scratch file.
182 StorageBaseImpl *scratch;
184 /* The directory structure is kept here, so that we can track how these
185 * entries relate to those in the parent storage. */
186 TransactedDirEntry *entries;
188 ULONG firstFreeEntry;
191 * Changes are committed to the transacted parent.
193 StorageBaseImpl *transactedParent;
194 } TransactedSnapshotImpl;
196 /* Generic function to create a transacted wrapper for a direct storage object. */
197 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
199 /* OLESTREAM memory structure to use for Get and Put Routines */
200 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
205 DWORD dwOleTypeNameLength;
206 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
207 CHAR *pstrOleObjFileName;
208 DWORD dwOleObjFileNameLength;
209 DWORD dwMetaFileWidth;
210 DWORD dwMetaFileHeight;
211 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
214 }OLECONVERT_OLESTREAM_DATA;
216 /* CompObj Stream structure */
217 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
222 DWORD dwCLSIDNameLength;
223 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
224 DWORD dwOleTypeNameLength;
225 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
226 DWORD dwProgIDNameLength;
227 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
229 }OLECONVERT_ISTORAGE_COMPOBJ;
232 /* Ole Presentation Stream structure */
233 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
241 }OLECONVERT_ISTORAGE_OLEPRES;
245 /***********************************************************************
246 * Forward declaration of internal functions used by the method DestroyElement
248 static HRESULT deleteStorageContents(
249 StorageBaseImpl *parentStorage,
250 DirRef indexToDelete,
251 DirEntry entryDataToDelete);
253 static HRESULT deleteStreamContents(
254 StorageBaseImpl *parentStorage,
255 DirRef indexToDelete,
256 DirEntry entryDataToDelete);
258 static HRESULT removeFromTree(
259 StorageBaseImpl *This,
260 DirRef parentStorageIndex,
261 DirRef deletedIndex);
263 /***********************************************************************
264 * Declaration of the functions used to manipulate DirEntry
267 static HRESULT insertIntoTree(
268 StorageBaseImpl *This,
269 DirRef parentStorageIndex,
270 DirRef newEntryIndex);
272 static LONG entryNameCmp(
273 const OLECHAR *name1,
274 const OLECHAR *name2);
276 static DirRef findElement(
277 StorageBaseImpl *storage,
282 static HRESULT findTreeParent(
283 StorageBaseImpl *storage,
285 const OLECHAR *childName,
286 DirEntry *parentData,
290 /***********************************************************************
291 * Declaration of miscellaneous functions...
293 static HRESULT validateSTGM(DWORD stgmValue);
295 static DWORD GetShareModeFromSTGM(DWORD stgm);
296 static DWORD GetAccessModeFromSTGM(DWORD stgm);
297 static DWORD GetCreationModeFromSTGM(DWORD stgm);
299 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
302 /****************************************************************************
303 * IEnumSTATSTGImpl definitions.
305 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
306 * This class allows iterating through the content of a storage and to find
307 * specific items inside it.
309 struct IEnumSTATSTGImpl
311 IEnumSTATSTG IEnumSTATSTG_iface;
313 LONG ref; /* Reference count */
314 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
315 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
317 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
320 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
322 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
326 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
327 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
329 /************************************************************************
333 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
335 return (index+1) * This->bigBlockSize;
338 /************************************************************************
339 ** Storage32BaseImpl implementation
341 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
342 ULARGE_INTEGER offset,
347 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
350 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
351 ULARGE_INTEGER offset,
356 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
359 /************************************************************************
360 * Storage32BaseImpl_QueryInterface (IUnknown)
362 * This method implements the common QueryInterface for all IStorage32
363 * implementations contained in this file.
365 * See Windows documentation for more details on IUnknown methods.
367 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
372 StorageBaseImpl *This = impl_from_IStorage(iface);
379 if (IsEqualGUID(&IID_IUnknown, riid) ||
380 IsEqualGUID(&IID_IStorage, riid))
382 *ppvObject = &This->IStorage_iface;
384 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
386 *ppvObject = &This->IPropertySetStorage_iface;
388 /* locking interface is reported for writer only */
389 else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
391 *ppvObject = &This->IDirectWriterLock_iface;
394 return E_NOINTERFACE;
396 IStorage_AddRef(iface);
401 /************************************************************************
402 * Storage32BaseImpl_AddRef (IUnknown)
404 * This method implements the common AddRef for all IStorage32
405 * implementations contained in this file.
407 * See Windows documentation for more details on IUnknown methods.
409 static ULONG WINAPI StorageBaseImpl_AddRef(
412 StorageBaseImpl *This = impl_from_IStorage(iface);
413 ULONG ref = InterlockedIncrement(&This->ref);
415 TRACE("(%p) AddRef to %d\n", This, ref);
420 /************************************************************************
421 * Storage32BaseImpl_Release (IUnknown)
423 * This method implements the common Release for all IStorage32
424 * implementations contained in this file.
426 * See Windows documentation for more details on IUnknown methods.
428 static ULONG WINAPI StorageBaseImpl_Release(
431 StorageBaseImpl *This = impl_from_IStorage(iface);
433 ULONG ref = InterlockedDecrement(&This->ref);
435 TRACE("(%p) ReleaseRef to %d\n", This, ref);
440 * Since we are using a system of base-classes, we want to call the
441 * destructor of the appropriate derived class. To do this, we are
442 * using virtual functions to implement the destructor.
444 StorageBaseImpl_Destroy(This);
450 /************************************************************************
451 * Storage32BaseImpl_OpenStream (IStorage)
453 * This method will open the specified stream object from the current storage.
455 * See Windows documentation for more details on IStorage methods.
457 static HRESULT WINAPI StorageBaseImpl_OpenStream(
459 const OLECHAR* pwcsName, /* [string][in] */
460 void* reserved1, /* [unique][in] */
461 DWORD grfMode, /* [in] */
462 DWORD reserved2, /* [in] */
463 IStream** ppstm) /* [out] */
465 StorageBaseImpl *This = impl_from_IStorage(iface);
466 StgStreamImpl* newStream;
467 DirEntry currentEntry;
468 DirRef streamEntryRef;
469 HRESULT res = STG_E_UNKNOWN;
471 TRACE("(%p, %s, %p, %x, %d, %p)\n",
472 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
474 if ( (pwcsName==NULL) || (ppstm==0) )
482 if ( FAILED( validateSTGM(grfMode) ) ||
483 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
485 res = STG_E_INVALIDFLAG;
492 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
494 res = STG_E_INVALIDFUNCTION;
500 res = STG_E_REVERTED;
505 * Check that we're compatible with the parent's storage mode, but
506 * only if we are not in transacted mode
508 if(!(This->openFlags & STGM_TRANSACTED)) {
509 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
511 res = STG_E_INVALIDFLAG;
517 * Search for the element with the given name
519 streamEntryRef = findElement(
521 This->storageDirEntry,
526 * If it was found, construct the stream object and return a pointer to it.
528 if ( (streamEntryRef!=DIRENTRY_NULL) &&
529 (currentEntry.stgType==STGTY_STREAM) )
531 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
533 /* A single stream cannot be opened a second time. */
534 res = STG_E_ACCESSDENIED;
538 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
542 newStream->grfMode = grfMode;
543 *ppstm = &newStream->IStream_iface;
545 IStream_AddRef(*ppstm);
555 res = STG_E_FILENOTFOUND;
559 TRACE("<-- IStream %p\n", *ppstm);
560 TRACE("<-- %08x\n", res);
564 /************************************************************************
565 * Storage32BaseImpl_OpenStorage (IStorage)
567 * This method will open a new storage object from the current storage.
569 * See Windows documentation for more details on IStorage methods.
571 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
573 const OLECHAR* pwcsName, /* [string][unique][in] */
574 IStorage* pstgPriority, /* [unique][in] */
575 DWORD grfMode, /* [in] */
576 SNB snbExclude, /* [unique][in] */
577 DWORD reserved, /* [in] */
578 IStorage** ppstg) /* [out] */
580 StorageBaseImpl *This = impl_from_IStorage(iface);
581 StorageInternalImpl* newStorage;
582 StorageBaseImpl* newTransactedStorage;
583 DirEntry currentEntry;
584 DirRef storageEntryRef;
585 HRESULT res = STG_E_UNKNOWN;
587 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
588 iface, debugstr_w(pwcsName), pstgPriority,
589 grfMode, snbExclude, reserved, ppstg);
591 if ((pwcsName==NULL) || (ppstg==0) )
597 if (This->openFlags & STGM_SIMPLE)
599 res = STG_E_INVALIDFUNCTION;
604 if (snbExclude != NULL)
606 res = STG_E_INVALIDPARAMETER;
610 if ( FAILED( validateSTGM(grfMode) ))
612 res = STG_E_INVALIDFLAG;
619 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
620 (grfMode & STGM_DELETEONRELEASE) ||
621 (grfMode & STGM_PRIORITY) )
623 res = STG_E_INVALIDFUNCTION;
628 return STG_E_REVERTED;
631 * Check that we're compatible with the parent's storage mode,
632 * but only if we are not transacted
634 if(!(This->openFlags & STGM_TRANSACTED)) {
635 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
637 res = STG_E_ACCESSDENIED;
644 storageEntryRef = findElement(
646 This->storageDirEntry,
650 if ( (storageEntryRef!=DIRENTRY_NULL) &&
651 (currentEntry.stgType==STGTY_STORAGE) )
653 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
655 /* A single storage cannot be opened a second time. */
656 res = STG_E_ACCESSDENIED;
660 newStorage = StorageInternalImpl_Construct(
667 if (grfMode & STGM_TRANSACTED)
669 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
673 HeapFree(GetProcessHeap(), 0, newStorage);
677 *ppstg = &newTransactedStorage->IStorage_iface;
681 *ppstg = &newStorage->base.IStorage_iface;
684 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
690 res = STG_E_INSUFFICIENTMEMORY;
694 res = STG_E_FILENOTFOUND;
697 TRACE("<-- %08x\n", res);
701 /************************************************************************
702 * Storage32BaseImpl_EnumElements (IStorage)
704 * This method will create an enumerator object that can be used to
705 * retrieve information about all the elements in the storage object.
707 * See Windows documentation for more details on IStorage methods.
709 static HRESULT WINAPI StorageBaseImpl_EnumElements(
711 DWORD reserved1, /* [in] */
712 void* reserved2, /* [size_is][unique][in] */
713 DWORD reserved3, /* [in] */
714 IEnumSTATSTG** ppenum) /* [out] */
716 StorageBaseImpl *This = impl_from_IStorage(iface);
717 IEnumSTATSTGImpl* newEnum;
719 TRACE("(%p, %d, %p, %d, %p)\n",
720 iface, reserved1, reserved2, reserved3, ppenum);
726 return STG_E_REVERTED;
728 newEnum = IEnumSTATSTGImpl_Construct(
730 This->storageDirEntry);
734 *ppenum = &newEnum->IEnumSTATSTG_iface;
738 return E_OUTOFMEMORY;
741 /************************************************************************
742 * Storage32BaseImpl_Stat (IStorage)
744 * This method will retrieve information about this storage object.
746 * See Windows documentation for more details on IStorage methods.
748 static HRESULT WINAPI StorageBaseImpl_Stat(
750 STATSTG* pstatstg, /* [out] */
751 DWORD grfStatFlag) /* [in] */
753 StorageBaseImpl *This = impl_from_IStorage(iface);
754 DirEntry currentEntry;
755 HRESULT res = STG_E_UNKNOWN;
757 TRACE("(%p, %p, %x)\n",
758 iface, pstatstg, grfStatFlag);
768 res = STG_E_REVERTED;
772 res = StorageBaseImpl_ReadDirEntry(
774 This->storageDirEntry,
779 StorageUtl_CopyDirEntryToSTATSTG(
785 pstatstg->grfMode = This->openFlags;
786 pstatstg->grfStateBits = This->stateBits;
792 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);
794 TRACE("<-- %08x\n", res);
798 /************************************************************************
799 * Storage32BaseImpl_RenameElement (IStorage)
801 * This method will rename the specified element.
803 * See Windows documentation for more details on IStorage methods.
805 static HRESULT WINAPI StorageBaseImpl_RenameElement(
807 const OLECHAR* pwcsOldName, /* [in] */
808 const OLECHAR* pwcsNewName) /* [in] */
810 StorageBaseImpl *This = impl_from_IStorage(iface);
811 DirEntry currentEntry;
812 DirRef currentEntryRef;
814 TRACE("(%p, %s, %s)\n",
815 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
818 return STG_E_REVERTED;
820 currentEntryRef = findElement(This,
821 This->storageDirEntry,
825 if (currentEntryRef != DIRENTRY_NULL)
828 * There is already an element with the new name
830 return STG_E_FILEALREADYEXISTS;
834 * Search for the old element name
836 currentEntryRef = findElement(This,
837 This->storageDirEntry,
841 if (currentEntryRef != DIRENTRY_NULL)
843 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
844 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
846 WARN("Element is already open; cannot rename.\n");
847 return STG_E_ACCESSDENIED;
850 /* Remove the element from its current position in the tree */
851 removeFromTree(This, This->storageDirEntry,
854 /* Change the name of the element */
855 strcpyW(currentEntry.name, pwcsNewName);
857 /* Delete any sibling links */
858 currentEntry.leftChild = DIRENTRY_NULL;
859 currentEntry.rightChild = DIRENTRY_NULL;
861 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
864 /* Insert the element in a new position in the tree */
865 insertIntoTree(This, This->storageDirEntry,
871 * There is no element with the old name
873 return STG_E_FILENOTFOUND;
876 return StorageBaseImpl_Flush(This);
879 /************************************************************************
880 * Storage32BaseImpl_CreateStream (IStorage)
882 * This method will create a stream object within this storage
884 * See Windows documentation for more details on IStorage methods.
886 static HRESULT WINAPI StorageBaseImpl_CreateStream(
888 const OLECHAR* pwcsName, /* [string][in] */
889 DWORD grfMode, /* [in] */
890 DWORD reserved1, /* [in] */
891 DWORD reserved2, /* [in] */
892 IStream** ppstm) /* [out] */
894 StorageBaseImpl *This = impl_from_IStorage(iface);
895 StgStreamImpl* newStream;
896 DirEntry currentEntry, newStreamEntry;
897 DirRef currentEntryRef, newStreamEntryRef;
900 TRACE("(%p, %s, %x, %d, %d, %p)\n",
901 iface, debugstr_w(pwcsName), grfMode,
902 reserved1, reserved2, ppstm);
905 return STG_E_INVALIDPOINTER;
908 return STG_E_INVALIDNAME;
910 if (reserved1 || reserved2)
911 return STG_E_INVALIDPARAMETER;
913 if ( FAILED( validateSTGM(grfMode) ))
914 return STG_E_INVALIDFLAG;
916 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
917 return STG_E_INVALIDFLAG;
920 return STG_E_REVERTED;
925 if ((grfMode & STGM_DELETEONRELEASE) ||
926 (grfMode & STGM_TRANSACTED))
927 return STG_E_INVALIDFUNCTION;
930 * Don't worry about permissions in transacted mode, as we can always write
931 * changes; we just can't always commit them.
933 if(!(This->openFlags & STGM_TRANSACTED)) {
934 /* Can't create a stream on read-only storage */
935 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
936 return STG_E_ACCESSDENIED;
938 /* Can't create a stream with greater access than the parent. */
939 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
940 return STG_E_ACCESSDENIED;
943 if(This->openFlags & STGM_SIMPLE)
944 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
948 currentEntryRef = findElement(This,
949 This->storageDirEntry,
953 if (currentEntryRef != DIRENTRY_NULL)
956 * An element with this name already exists
958 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
960 IStorage_DestroyElement(iface, pwcsName);
963 return STG_E_FILEALREADYEXISTS;
967 * memset the empty entry
969 memset(&newStreamEntry, 0, sizeof(DirEntry));
971 newStreamEntry.sizeOfNameString =
972 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
974 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
975 return STG_E_INVALIDNAME;
977 strcpyW(newStreamEntry.name, pwcsName);
979 newStreamEntry.stgType = STGTY_STREAM;
980 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
981 newStreamEntry.size.u.LowPart = 0;
982 newStreamEntry.size.u.HighPart = 0;
984 newStreamEntry.leftChild = DIRENTRY_NULL;
985 newStreamEntry.rightChild = DIRENTRY_NULL;
986 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
988 /* call CoFileTime to get the current time
993 /* newStreamEntry.clsid */
996 * Create an entry with the new data
998 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
1003 * Insert the new entry in the parent storage's tree.
1005 hr = insertIntoTree(
1007 This->storageDirEntry,
1011 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
1016 * Open the stream to return it.
1018 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1022 *ppstm = &newStream->IStream_iface;
1023 IStream_AddRef(*ppstm);
1027 return STG_E_INSUFFICIENTMEMORY;
1030 return StorageBaseImpl_Flush(This);
1033 /************************************************************************
1034 * Storage32BaseImpl_SetClass (IStorage)
1036 * This method will write the specified CLSID in the directory entry of this
1039 * See Windows documentation for more details on IStorage methods.
1041 static HRESULT WINAPI StorageBaseImpl_SetClass(
1043 REFCLSID clsid) /* [in] */
1045 StorageBaseImpl *This = impl_from_IStorage(iface);
1047 DirEntry currentEntry;
1049 TRACE("(%p, %p)\n", iface, clsid);
1052 return STG_E_REVERTED;
1054 hRes = StorageBaseImpl_ReadDirEntry(This,
1055 This->storageDirEntry,
1057 if (SUCCEEDED(hRes))
1059 currentEntry.clsid = *clsid;
1061 hRes = StorageBaseImpl_WriteDirEntry(This,
1062 This->storageDirEntry,
1066 if (SUCCEEDED(hRes))
1067 hRes = StorageBaseImpl_Flush(This);
1072 /************************************************************************
1073 ** Storage32Impl implementation
1076 /************************************************************************
1077 * Storage32BaseImpl_CreateStorage (IStorage)
1079 * This method will create the storage object within the provided storage.
1081 * See Windows documentation for more details on IStorage methods.
1083 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1085 const OLECHAR *pwcsName, /* [string][in] */
1086 DWORD grfMode, /* [in] */
1087 DWORD reserved1, /* [in] */
1088 DWORD reserved2, /* [in] */
1089 IStorage **ppstg) /* [out] */
1091 StorageBaseImpl* This = impl_from_IStorage(iface);
1093 DirEntry currentEntry;
1095 DirRef currentEntryRef;
1099 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1100 iface, debugstr_w(pwcsName), grfMode,
1101 reserved1, reserved2, ppstg);
1104 return STG_E_INVALIDPOINTER;
1106 if (This->openFlags & STGM_SIMPLE)
1108 return STG_E_INVALIDFUNCTION;
1112 return STG_E_INVALIDNAME;
1116 if ( FAILED( validateSTGM(grfMode) ) ||
1117 (grfMode & STGM_DELETEONRELEASE) )
1119 WARN("bad grfMode: 0x%x\n", grfMode);
1120 return STG_E_INVALIDFLAG;
1124 return STG_E_REVERTED;
1127 * Check that we're compatible with the parent's storage mode
1129 if ( !(This->openFlags & STGM_TRANSACTED) &&
1130 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1132 WARN("access denied\n");
1133 return STG_E_ACCESSDENIED;
1136 currentEntryRef = findElement(This,
1137 This->storageDirEntry,
1141 if (currentEntryRef != DIRENTRY_NULL)
1144 * An element with this name already exists
1146 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1147 ((This->openFlags & STGM_TRANSACTED) ||
1148 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1150 hr = IStorage_DestroyElement(iface, pwcsName);
1156 WARN("file already exists\n");
1157 return STG_E_FILEALREADYEXISTS;
1160 else if (!(This->openFlags & STGM_TRANSACTED) &&
1161 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1163 WARN("read-only storage\n");
1164 return STG_E_ACCESSDENIED;
1167 memset(&newEntry, 0, sizeof(DirEntry));
1169 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1171 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1173 FIXME("name too long\n");
1174 return STG_E_INVALIDNAME;
1177 strcpyW(newEntry.name, pwcsName);
1179 newEntry.stgType = STGTY_STORAGE;
1180 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1181 newEntry.size.u.LowPart = 0;
1182 newEntry.size.u.HighPart = 0;
1184 newEntry.leftChild = DIRENTRY_NULL;
1185 newEntry.rightChild = DIRENTRY_NULL;
1186 newEntry.dirRootEntry = DIRENTRY_NULL;
1188 /* call CoFileTime to get the current time
1193 /* newEntry.clsid */
1196 * Create a new directory entry for the storage
1198 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1203 * Insert the new directory entry into the parent storage's tree
1205 hr = insertIntoTree(
1207 This->storageDirEntry,
1211 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1216 * Open it to get a pointer to return.
1218 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1220 if( (hr != S_OK) || (*ppstg == NULL))
1226 hr = StorageBaseImpl_Flush(This);
1232 /***************************************************************************
1236 * Reserve a directory entry in the file and initialize it.
1238 static HRESULT StorageImpl_CreateDirEntry(
1239 StorageBaseImpl *base,
1240 const DirEntry *newData,
1243 StorageImpl *storage = (StorageImpl*)base;
1244 ULONG currentEntryIndex = 0;
1245 ULONG newEntryIndex = DIRENTRY_NULL;
1247 BYTE currentData[RAW_DIRENTRY_SIZE];
1248 WORD sizeOfNameString;
1252 hr = StorageImpl_ReadRawDirEntry(storage,
1258 StorageUtl_ReadWord(
1260 OFFSET_PS_NAMELENGTH,
1263 if (sizeOfNameString == 0)
1266 * The entry exists and is available, we found it.
1268 newEntryIndex = currentEntryIndex;
1274 * We exhausted the directory entries, we will create more space below
1276 newEntryIndex = currentEntryIndex;
1278 currentEntryIndex++;
1280 } while (newEntryIndex == DIRENTRY_NULL);
1283 * grow the directory stream
1287 BYTE emptyData[RAW_DIRENTRY_SIZE];
1288 ULARGE_INTEGER newSize;
1290 ULONG lastEntry = 0;
1291 ULONG blockCount = 0;
1294 * obtain the new count of blocks in the directory stream
1296 blockCount = BlockChainStream_GetCount(
1297 storage->rootBlockChain)+1;
1300 * initialize the size used by the directory stream
1302 newSize.u.HighPart = 0;
1303 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1306 * add a block to the directory stream
1308 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1311 * memset the empty entry in order to initialize the unused newly
1314 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1319 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1322 entryIndex = newEntryIndex + 1;
1323 entryIndex < lastEntry;
1326 StorageImpl_WriteRawDirEntry(
1332 StorageImpl_SaveFileHeader(storage);
1335 UpdateRawDirEntry(currentData, newData);
1337 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1340 *index = newEntryIndex;
1345 /***************************************************************************
1349 * Mark a directory entry in the file as free.
1351 static HRESULT StorageImpl_DestroyDirEntry(
1352 StorageBaseImpl *base,
1355 BYTE emptyData[RAW_DIRENTRY_SIZE];
1356 StorageImpl *storage = (StorageImpl*)base;
1358 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1360 return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1364 /****************************************************************************
1368 * Case insensitive comparison of DirEntry.name by first considering
1371 * Returns <0 when name1 < name2
1372 * >0 when name1 > name2
1373 * 0 when name1 == name2
1375 static LONG entryNameCmp(
1376 const OLECHAR *name1,
1377 const OLECHAR *name2)
1379 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1381 while (diff == 0 && *name1 != 0)
1384 * We compare the string themselves only when they are of the same length
1386 diff = toupperW(*name1++) - toupperW(*name2++);
1392 /****************************************************************************
1396 * Add a directory entry to a storage
1398 static HRESULT insertIntoTree(
1399 StorageBaseImpl *This,
1400 DirRef parentStorageIndex,
1401 DirRef newEntryIndex)
1403 DirEntry currentEntry;
1407 * Read the inserted entry
1409 StorageBaseImpl_ReadDirEntry(This,
1414 * Read the storage entry
1416 StorageBaseImpl_ReadDirEntry(This,
1420 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1423 * The root storage contains some element, therefore, start the research
1424 * for the appropriate location.
1427 DirRef current, next, previous, currentEntryId;
1430 * Keep a reference to the root of the storage's element tree
1432 currentEntryId = currentEntry.dirRootEntry;
1437 StorageBaseImpl_ReadDirEntry(This,
1438 currentEntry.dirRootEntry,
1441 previous = currentEntry.leftChild;
1442 next = currentEntry.rightChild;
1443 current = currentEntryId;
1447 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1451 if (previous != DIRENTRY_NULL)
1453 StorageBaseImpl_ReadDirEntry(This,
1460 currentEntry.leftChild = newEntryIndex;
1461 StorageBaseImpl_WriteDirEntry(This,
1469 if (next != DIRENTRY_NULL)
1471 StorageBaseImpl_ReadDirEntry(This,
1478 currentEntry.rightChild = newEntryIndex;
1479 StorageBaseImpl_WriteDirEntry(This,
1488 * Trying to insert an item with the same name in the
1489 * subtree structure.
1491 return STG_E_FILEALREADYEXISTS;
1494 previous = currentEntry.leftChild;
1495 next = currentEntry.rightChild;
1501 * The storage is empty, make the new entry the root of its element tree
1503 currentEntry.dirRootEntry = newEntryIndex;
1504 StorageBaseImpl_WriteDirEntry(This,
1512 /****************************************************************************
1516 * Find and read the element of a storage with the given name.
1518 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1519 const OLECHAR *name, DirEntry *data)
1521 DirRef currentEntry;
1523 /* Read the storage entry to find the root of the tree. */
1524 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1526 currentEntry = data->dirRootEntry;
1528 while (currentEntry != DIRENTRY_NULL)
1532 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1534 cmp = entryNameCmp(name, data->name);
1541 currentEntry = data->leftChild;
1544 currentEntry = data->rightChild;
1547 return currentEntry;
1550 /****************************************************************************
1554 * Find and read the binary tree parent of the element with the given name.
1556 * If there is no such element, find a place where it could be inserted and
1557 * return STG_E_FILENOTFOUND.
1559 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1560 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1566 /* Read the storage entry to find the root of the tree. */
1567 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1569 *parentEntry = storageEntry;
1570 *relation = DIRENTRY_RELATION_DIR;
1572 childEntry = parentData->dirRootEntry;
1574 while (childEntry != DIRENTRY_NULL)
1578 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1580 cmp = entryNameCmp(childName, childData.name);
1588 *parentData = childData;
1589 *parentEntry = childEntry;
1590 *relation = DIRENTRY_RELATION_PREVIOUS;
1592 childEntry = parentData->leftChild;
1597 *parentData = childData;
1598 *parentEntry = childEntry;
1599 *relation = DIRENTRY_RELATION_NEXT;
1601 childEntry = parentData->rightChild;
1605 if (childEntry == DIRENTRY_NULL)
1606 return STG_E_FILENOTFOUND;
1612 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1613 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1614 SNB snbExclude, IStorage *pstgDest);
1616 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
1617 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1618 SNB snbExclude, IStorage *pstgDest)
1624 IStream *pstrChild, *pstrTmp;
1627 if (srcEntry == DIRENTRY_NULL)
1630 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1637 WCHAR **snb = snbExclude;
1639 while ( *snb != NULL && !skip )
1641 if ( lstrcmpW(data.name, *snb) == 0 )
1649 if (data.stgType == STGTY_STORAGE && !skip_storage)
1652 * create a new storage in destination storage
1654 hr = IStorage_CreateStorage( pstgDest, data.name,
1655 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1660 * if it already exist, don't create a new one use this one
1662 if (hr == STG_E_FILEALREADYEXISTS)
1664 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1665 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1666 NULL, 0, &pstgTmp );
1671 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1672 skip_stream, NULL, pstgTmp );
1674 IStorage_Release(pstgTmp);
1677 else if (data.stgType == STGTY_STREAM && !skip_stream)
1680 * create a new stream in destination storage. If the stream already
1681 * exist, it will be deleted and a new one will be created.
1683 hr = IStorage_CreateStream( pstgDest, data.name,
1684 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1688 * open child stream storage. This operation must succeed even if the
1689 * stream is already open, so we use internal functions to do it.
1693 StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1697 pstrChild = &streamimpl->IStream_iface;
1699 IStream_AddRef(pstrChild);
1711 * Get the size of the source stream
1713 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1716 * Set the size of the destination stream.
1718 IStream_SetSize(pstrTmp, strStat.cbSize);
1723 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1726 IStream_Release( pstrChild );
1729 IStream_Release( pstrTmp );
1735 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1736 skip_stream, snbExclude, pstgDest );
1739 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1740 skip_stream, snbExclude, pstgDest );
1745 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1746 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1747 SNB snbExclude, IStorage *pstgDest)
1752 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1755 hr = IStorage_SetClass( pstgDest, &data.clsid );
1758 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
1759 skip_stream, snbExclude, pstgDest );
1764 /*************************************************************************
1767 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1769 DWORD ciidExclude, /* [in] */
1770 const IID* rgiidExclude, /* [size_is][unique][in] */
1771 SNB snbExclude, /* [unique][in] */
1772 IStorage* pstgDest) /* [unique][in] */
1774 StorageBaseImpl *This = impl_from_IStorage(iface);
1776 BOOL skip_storage = FALSE, skip_stream = FALSE;
1779 TRACE("(%p, %d, %p, %p, %p)\n",
1780 iface, ciidExclude, rgiidExclude,
1781 snbExclude, pstgDest);
1783 if ( pstgDest == 0 )
1784 return STG_E_INVALIDPOINTER;
1786 for(i = 0; i < ciidExclude; ++i)
1788 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1789 skip_storage = TRUE;
1790 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1793 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1798 /* Give up early if it looks like this would be infinitely recursive.
1799 * Oddly enough, this includes some cases that aren't really recursive, like
1800 * copying to a transacted child. */
1801 IStorage *pstgDestAncestor = pstgDest;
1802 IStorage *pstgDestAncestorChild = NULL;
1804 /* Go up the chain from the destination until we find the source storage. */
1805 while (pstgDestAncestor != iface) {
1806 pstgDestAncestorChild = pstgDest;
1808 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
1810 TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
1812 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
1814 else if (pstgDestAncestor->lpVtbl == &Storage32InternalImpl_Vtbl)
1816 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
1818 pstgDestAncestor = &internal->parentStorage->IStorage_iface;
1824 if (pstgDestAncestor == iface)
1828 if (pstgDestAncestorChild && snbExclude)
1830 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
1832 WCHAR **snb = snbExclude;
1834 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
1836 while ( *snb != NULL && fail )
1838 if ( lstrcmpW(data.name, *snb) == 0 )
1845 return STG_E_ACCESSDENIED;
1849 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
1850 skip_storage, skip_stream, snbExclude, pstgDest );
1853 /*************************************************************************
1854 * MoveElementTo (IStorage)
1856 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1858 const OLECHAR *pwcsName, /* [string][in] */
1859 IStorage *pstgDest, /* [unique][in] */
1860 const OLECHAR *pwcsNewName,/* [string][in] */
1861 DWORD grfFlags) /* [in] */
1863 FIXME("(%p %s %p %s %u): stub\n", iface,
1864 debugstr_w(pwcsName), pstgDest,
1865 debugstr_w(pwcsNewName), grfFlags);
1869 /*************************************************************************
1872 * Ensures that any changes made to a storage object open in transacted mode
1873 * are reflected in the parent storage
1875 * In a non-transacted mode, this ensures all cached writes are completed.
1877 static HRESULT WINAPI StorageImpl_Commit(
1879 DWORD grfCommitFlags)/* [in] */
1881 StorageBaseImpl* This = impl_from_IStorage(iface);
1882 TRACE("(%p %d)\n", iface, grfCommitFlags);
1883 return StorageBaseImpl_Flush(This);
1886 /*************************************************************************
1889 * Discard all changes that have been made since the last commit operation
1891 static HRESULT WINAPI StorageImpl_Revert(
1894 TRACE("(%p)\n", iface);
1898 /*************************************************************************
1899 * DestroyElement (IStorage)
1901 * Strategy: This implementation is built this way for simplicity not for speed.
1902 * I always delete the topmost element of the enumeration and adjust
1903 * the deleted element pointer all the time. This takes longer to
1904 * do but allow to reinvoke DestroyElement whenever we encounter a
1905 * storage object. The optimisation resides in the usage of another
1906 * enumeration strategy that would give all the leaves of a storage
1907 * first. (postfix order)
1909 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1911 const OLECHAR *pwcsName)/* [string][in] */
1913 StorageBaseImpl *This = impl_from_IStorage(iface);
1916 DirEntry entryToDelete;
1917 DirRef entryToDeleteRef;
1920 iface, debugstr_w(pwcsName));
1923 return STG_E_INVALIDPOINTER;
1926 return STG_E_REVERTED;
1928 if ( !(This->openFlags & STGM_TRANSACTED) &&
1929 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1930 return STG_E_ACCESSDENIED;
1932 entryToDeleteRef = findElement(
1934 This->storageDirEntry,
1938 if ( entryToDeleteRef == DIRENTRY_NULL )
1940 return STG_E_FILENOTFOUND;
1943 if ( entryToDelete.stgType == STGTY_STORAGE )
1945 hr = deleteStorageContents(
1950 else if ( entryToDelete.stgType == STGTY_STREAM )
1952 hr = deleteStreamContents(
1962 * Remove the entry from its parent storage
1964 hr = removeFromTree(
1966 This->storageDirEntry,
1970 * Invalidate the entry
1973 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1976 hr = StorageBaseImpl_Flush(This);
1982 /******************************************************************************
1983 * Internal stream list handlers
1986 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1988 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1989 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1992 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1994 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1995 list_remove(&(strm->StrmListEntry));
1998 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
2000 StgStreamImpl *strm;
2002 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
2004 if (strm->dirEntry == streamEntry)
2013 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
2015 StorageInternalImpl *childstg;
2017 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
2019 if (childstg->base.storageDirEntry == storageEntry)
2028 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2030 struct list *cur, *cur2;
2031 StgStreamImpl *strm=NULL;
2032 StorageInternalImpl *childstg=NULL;
2034 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2035 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2036 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2037 strm->parentStorage = NULL;
2041 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2042 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2043 StorageBaseImpl_Invalidate( &childstg->base );
2046 if (stg->transactedChild)
2048 StorageBaseImpl_Invalidate(stg->transactedChild);
2050 stg->transactedChild = NULL;
2055 /*********************************************************************
2059 * Delete the contents of a storage entry.
2062 static HRESULT deleteStorageContents(
2063 StorageBaseImpl *parentStorage,
2064 DirRef indexToDelete,
2065 DirEntry entryDataToDelete)
2067 IEnumSTATSTG *elements = 0;
2068 IStorage *childStorage = 0;
2069 STATSTG currentElement;
2071 HRESULT destroyHr = S_OK;
2072 StorageInternalImpl *stg, *stg2;
2074 /* Invalidate any open storage objects. */
2075 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2077 if (stg->base.storageDirEntry == indexToDelete)
2079 StorageBaseImpl_Invalidate(&stg->base);
2084 * Open the storage and enumerate it
2086 hr = IStorage_OpenStorage(
2087 &parentStorage->IStorage_iface,
2088 entryDataToDelete.name,
2090 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2101 * Enumerate the elements
2103 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2108 * Obtain the next element
2110 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
2113 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2115 CoTaskMemFree(currentElement.pwcsName);
2119 * We need to Reset the enumeration every time because we delete elements
2120 * and the enumeration could be invalid
2122 IEnumSTATSTG_Reset(elements);
2124 } while ((hr == S_OK) && (destroyHr == S_OK));
2126 IStorage_Release(childStorage);
2127 IEnumSTATSTG_Release(elements);
2132 /*********************************************************************
2136 * Perform the deletion of a stream's data
2139 static HRESULT deleteStreamContents(
2140 StorageBaseImpl *parentStorage,
2141 DirRef indexToDelete,
2142 DirEntry entryDataToDelete)
2146 ULARGE_INTEGER size;
2147 StgStreamImpl *strm, *strm2;
2149 /* Invalidate any open stream objects. */
2150 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2152 if (strm->dirEntry == indexToDelete)
2154 TRACE("Stream deleted %p\n", strm);
2155 strm->parentStorage = NULL;
2156 list_remove(&strm->StrmListEntry);
2160 size.u.HighPart = 0;
2163 hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2164 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2174 hr = IStream_SetSize(pis, size);
2182 * Release the stream object.
2184 IStream_Release(pis);
2189 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2193 case DIRENTRY_RELATION_PREVIOUS:
2194 entry->leftChild = new_target;
2196 case DIRENTRY_RELATION_NEXT:
2197 entry->rightChild = new_target;
2199 case DIRENTRY_RELATION_DIR:
2200 entry->dirRootEntry = new_target;
2207 /*************************************************************************
2211 * This method removes a directory entry from its parent storage tree without
2212 * freeing any resources attached to it.
2214 static HRESULT removeFromTree(
2215 StorageBaseImpl *This,
2216 DirRef parentStorageIndex,
2217 DirRef deletedIndex)
2219 DirEntry entryToDelete;
2220 DirEntry parentEntry;
2221 DirRef parentEntryRef;
2222 ULONG typeOfRelation;
2225 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2231 * Find the element that links to the one we want to delete.
2233 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2234 &parentEntry, &parentEntryRef, &typeOfRelation);
2239 if (entryToDelete.leftChild != DIRENTRY_NULL)
2242 * Replace the deleted entry with its left child
2244 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2246 hr = StorageBaseImpl_WriteDirEntry(
2255 if (entryToDelete.rightChild != DIRENTRY_NULL)
2258 * We need to reinsert the right child somewhere. We already know it and
2259 * its children are greater than everything in the left tree, so we
2260 * insert it at the rightmost point in the left tree.
2262 DirRef newRightChildParent = entryToDelete.leftChild;
2263 DirEntry newRightChildParentEntry;
2267 hr = StorageBaseImpl_ReadDirEntry(
2269 newRightChildParent,
2270 &newRightChildParentEntry);
2276 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2277 newRightChildParent = newRightChildParentEntry.rightChild;
2278 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2280 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2282 hr = StorageBaseImpl_WriteDirEntry(
2284 newRightChildParent,
2285 &newRightChildParentEntry);
2295 * Replace the deleted entry with its right child
2297 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2299 hr = StorageBaseImpl_WriteDirEntry(
2313 /******************************************************************************
2314 * SetElementTimes (IStorage)
2316 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2318 const OLECHAR *pwcsName,/* [string][in] */
2319 const FILETIME *pctime, /* [in] */
2320 const FILETIME *patime, /* [in] */
2321 const FILETIME *pmtime) /* [in] */
2323 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2327 /******************************************************************************
2328 * SetStateBits (IStorage)
2330 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2332 DWORD grfStateBits,/* [in] */
2333 DWORD grfMask) /* [in] */
2335 StorageBaseImpl *This = impl_from_IStorage(iface);
2338 return STG_E_REVERTED;
2340 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2344 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2345 DirRef index, const DirEntry *data)
2347 StorageImpl *This = (StorageImpl*)base;
2348 return StorageImpl_WriteDirEntry(This, index, data);
2351 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2352 DirRef index, DirEntry *data)
2354 StorageImpl *This = (StorageImpl*)base;
2355 return StorageImpl_ReadDirEntry(This, index, data);
2358 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2362 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2364 if (!This->blockChainCache[i])
2366 return &This->blockChainCache[i];
2370 i = This->blockChainToEvict;
2372 BlockChainStream_Destroy(This->blockChainCache[i]);
2373 This->blockChainCache[i] = NULL;
2375 This->blockChainToEvict++;
2376 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2377 This->blockChainToEvict = 0;
2379 return &This->blockChainCache[i];
2382 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2385 int i, free_index=-1;
2387 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2389 if (!This->blockChainCache[i])
2391 if (free_index == -1) free_index = i;
2393 else if (This->blockChainCache[i]->ownerDirEntry == index)
2395 return &This->blockChainCache[i];
2399 if (free_index == -1)
2401 free_index = This->blockChainToEvict;
2403 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2404 This->blockChainCache[free_index] = NULL;
2406 This->blockChainToEvict++;
2407 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2408 This->blockChainToEvict = 0;
2411 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2412 return &This->blockChainCache[free_index];
2415 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2419 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2421 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2423 BlockChainStream_Destroy(This->blockChainCache[i]);
2424 This->blockChainCache[i] = NULL;
2430 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2431 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2433 StorageImpl *This = (StorageImpl*)base;
2438 hr = StorageImpl_ReadDirEntry(This, index, &data);
2439 if (FAILED(hr)) return hr;
2441 if (data.size.QuadPart == 0)
2447 if (offset.QuadPart + size > data.size.QuadPart)
2449 bytesToRead = data.size.QuadPart - offset.QuadPart;
2456 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2458 SmallBlockChainStream *stream;
2460 stream = SmallBlockChainStream_Construct(This, NULL, index);
2461 if (!stream) return E_OUTOFMEMORY;
2463 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2465 SmallBlockChainStream_Destroy(stream);
2471 BlockChainStream *stream = NULL;
2473 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2474 if (!stream) return E_OUTOFMEMORY;
2476 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2482 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2483 ULARGE_INTEGER newsize)
2485 StorageImpl *This = (StorageImpl*)base;
2488 SmallBlockChainStream *smallblock=NULL;
2489 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2491 hr = StorageImpl_ReadDirEntry(This, index, &data);
2492 if (FAILED(hr)) return hr;
2494 /* In simple mode keep the stream size above the small block limit */
2495 if (This->base.openFlags & STGM_SIMPLE)
2496 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2498 if (data.size.QuadPart == newsize.QuadPart)
2501 /* Create a block chain object of the appropriate type */
2502 if (data.size.QuadPart == 0)
2504 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2506 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2507 if (!smallblock) return E_OUTOFMEMORY;
2511 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2512 bigblock = *pbigblock;
2513 if (!bigblock) return E_OUTOFMEMORY;
2516 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2518 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2519 if (!smallblock) return E_OUTOFMEMORY;
2523 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2524 bigblock = *pbigblock;
2525 if (!bigblock) return E_OUTOFMEMORY;
2528 /* Change the block chain type if necessary. */
2529 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2531 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2534 SmallBlockChainStream_Destroy(smallblock);
2538 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2539 *pbigblock = bigblock;
2541 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2543 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
2548 /* Set the size of the block chain. */
2551 SmallBlockChainStream_SetSize(smallblock, newsize);
2552 SmallBlockChainStream_Destroy(smallblock);
2556 BlockChainStream_SetSize(bigblock, newsize);
2559 /* Set the size in the directory entry. */
2560 hr = StorageImpl_ReadDirEntry(This, index, &data);
2563 data.size = newsize;
2565 hr = StorageImpl_WriteDirEntry(This, index, &data);
2570 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2571 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2573 StorageImpl *This = (StorageImpl*)base;
2576 ULARGE_INTEGER newSize;
2578 hr = StorageImpl_ReadDirEntry(This, index, &data);
2579 if (FAILED(hr)) return hr;
2581 /* Grow the stream if necessary */
2582 newSize.QuadPart = 0;
2583 newSize.QuadPart = offset.QuadPart + size;
2585 if (newSize.QuadPart > data.size.QuadPart)
2587 hr = StorageImpl_StreamSetSize(base, index, newSize);
2591 hr = StorageImpl_ReadDirEntry(This, index, &data);
2592 if (FAILED(hr)) return hr;
2595 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2597 SmallBlockChainStream *stream;
2599 stream = SmallBlockChainStream_Construct(This, NULL, index);
2600 if (!stream) return E_OUTOFMEMORY;
2602 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2604 SmallBlockChainStream_Destroy(stream);
2610 BlockChainStream *stream;
2612 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2613 if (!stream) return E_OUTOFMEMORY;
2615 return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2619 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2622 StorageImpl *This = (StorageImpl*)base;
2623 DirEntry dst_data, src_data;
2626 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2629 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2633 StorageImpl_DeleteCachedBlockChainStream(This, src);
2634 dst_data.startingBlock = src_data.startingBlock;
2635 dst_data.size = src_data.size;
2637 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2643 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2645 StorageImpl *This = (StorageImpl*) iface;
2649 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2651 *result = statstg.pwcsName;
2656 static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
2658 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2659 return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
2662 static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
2664 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2665 return IStorage_AddRef(&This->IStorage_iface);
2668 static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
2670 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2671 return IStorage_Release(&This->IStorage_iface);
2674 static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
2676 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2677 FIXME("(%p)->(%d): stub\n", This, timeout);
2681 static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
2683 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2684 FIXME("(%p): stub\n", This);
2688 static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
2690 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2691 FIXME("(%p): stub\n", This);
2695 static const IDirectWriterLockVtbl DirectWriterLockVtbl =
2697 directwriterlock_QueryInterface,
2698 directwriterlock_AddRef,
2699 directwriterlock_Release,
2700 directwriterlock_WaitForWriteAccess,
2701 directwriterlock_ReleaseWriteAccess,
2702 directwriterlock_HaveWriteAccess
2706 * Virtual function table for the IStorage32Impl class.
2708 static const IStorageVtbl Storage32Impl_Vtbl =
2710 StorageBaseImpl_QueryInterface,
2711 StorageBaseImpl_AddRef,
2712 StorageBaseImpl_Release,
2713 StorageBaseImpl_CreateStream,
2714 StorageBaseImpl_OpenStream,
2715 StorageBaseImpl_CreateStorage,
2716 StorageBaseImpl_OpenStorage,
2717 StorageBaseImpl_CopyTo,
2718 StorageBaseImpl_MoveElementTo,
2721 StorageBaseImpl_EnumElements,
2722 StorageBaseImpl_DestroyElement,
2723 StorageBaseImpl_RenameElement,
2724 StorageBaseImpl_SetElementTimes,
2725 StorageBaseImpl_SetClass,
2726 StorageBaseImpl_SetStateBits,
2727 StorageBaseImpl_Stat
2730 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2732 StorageImpl_Destroy,
2733 StorageImpl_Invalidate,
2735 StorageImpl_GetFilename,
2736 StorageImpl_CreateDirEntry,
2737 StorageImpl_BaseWriteDirEntry,
2738 StorageImpl_BaseReadDirEntry,
2739 StorageImpl_DestroyDirEntry,
2740 StorageImpl_StreamReadAt,
2741 StorageImpl_StreamWriteAt,
2742 StorageImpl_StreamSetSize,
2743 StorageImpl_StreamLink
2746 static HRESULT StorageImpl_Construct(
2754 StorageImpl** result)
2758 DirEntry currentEntry;
2759 DirRef currentEntryRef;
2761 if ( FAILED( validateSTGM(openFlags) ))
2762 return STG_E_INVALIDFLAG;
2764 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2766 return E_OUTOFMEMORY;
2768 memset(This, 0, sizeof(StorageImpl));
2770 list_init(&This->base.strmHead);
2772 list_init(&This->base.storageHead);
2774 This->base.IStorage_iface.lpVtbl = &Storage32Impl_Vtbl;
2775 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
2776 This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
2777 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2778 This->base.openFlags = (openFlags & ~STGM_CREATE);
2780 This->base.create = create;
2782 if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
2783 This->base.lockingrole = SWMR_Writer;
2784 else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
2785 This->base.lockingrole = SWMR_Reader;
2787 This->base.lockingrole = SWMR_None;
2789 This->base.reverted = 0;
2792 * Initialize the big block cache.
2794 This->bigBlockSize = sector_size;
2795 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2797 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2800 This->lockBytes = pLkbyt;
2801 ILockBytes_AddRef(pLkbyt);
2809 ULARGE_INTEGER size;
2810 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2812 /* Discard any existing data. */
2814 ILockBytes_SetSize(This->lockBytes, size);
2817 * Initialize all header variables:
2818 * - The big block depot consists of one block and it is at block 0
2819 * - The directory table starts at block 1
2820 * - There is no small block depot
2822 memset( This->bigBlockDepotStart,
2824 sizeof(This->bigBlockDepotStart));
2826 This->bigBlockDepotCount = 1;
2827 This->bigBlockDepotStart[0] = 0;
2828 This->rootStartBlock = 1;
2829 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2830 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2831 if (sector_size == 4096)
2832 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2834 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2835 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2836 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2837 This->extBigBlockDepotCount = 0;
2839 StorageImpl_SaveFileHeader(This);
2842 * Add one block for the big block depot and one block for the directory table
2844 size.u.HighPart = 0;
2845 size.u.LowPart = This->bigBlockSize * 3;
2846 ILockBytes_SetSize(This->lockBytes, size);
2849 * Initialize the big block depot
2851 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2852 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2853 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2854 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2859 * Load the header for the file.
2861 hr = StorageImpl_LoadFileHeader(This);
2870 * There is no block depot cached yet.
2872 This->indexBlockDepotCached = 0xFFFFFFFF;
2873 This->indexExtBlockDepotCached = 0xFFFFFFFF;
2876 * Start searching for free blocks with block 0.
2878 This->prevFreeBlock = 0;
2880 This->firstFreeSmallBlock = 0;
2882 /* Read the extended big block depot locations. */
2883 if (This->extBigBlockDepotCount != 0)
2885 ULONG current_block = This->extBigBlockDepotStart;
2886 ULONG cache_size = This->extBigBlockDepotCount * 2;
2889 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
2890 if (!This->extBigBlockDepotLocations)
2896 This->extBigBlockDepotLocationsSize = cache_size;
2898 for (i=0; i<This->extBigBlockDepotCount; i++)
2900 if (current_block == BLOCK_END_OF_CHAIN)
2902 WARN("File has too few extended big block depot blocks.\n");
2903 hr = STG_E_DOCFILECORRUPT;
2906 This->extBigBlockDepotLocations[i] = current_block;
2907 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
2912 This->extBigBlockDepotLocations = NULL;
2913 This->extBigBlockDepotLocationsSize = 0;
2917 * Create the block chain abstractions.
2919 if(!(This->rootBlockChain =
2920 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2922 hr = STG_E_READFAULT;
2926 if(!(This->smallBlockDepotChain =
2927 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2930 hr = STG_E_READFAULT;
2935 * Write the root storage entry (memory only)
2939 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
2942 * Initialize the directory table
2944 memset(&rootEntry, 0, sizeof(rootEntry));
2945 strcpyW(rootEntry.name, rootentryW);
2946 rootEntry.sizeOfNameString = sizeof(rootentryW);
2947 rootEntry.stgType = STGTY_ROOT;
2948 rootEntry.leftChild = DIRENTRY_NULL;
2949 rootEntry.rightChild = DIRENTRY_NULL;
2950 rootEntry.dirRootEntry = DIRENTRY_NULL;
2951 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2952 rootEntry.size.u.HighPart = 0;
2953 rootEntry.size.u.LowPart = 0;
2955 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2959 * Find the ID of the root storage.
2961 currentEntryRef = 0;
2965 hr = StorageImpl_ReadDirEntry(
2972 if ( (currentEntry.sizeOfNameString != 0 ) &&
2973 (currentEntry.stgType == STGTY_ROOT) )
2975 This->base.storageDirEntry = currentEntryRef;
2981 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2985 hr = STG_E_READFAULT;
2990 * Create the block chain abstraction for the small block root chain.
2992 if(!(This->smallBlockRootChain =
2993 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2995 hr = STG_E_READFAULT;
3001 IStorage_Release(&This->base.IStorage_iface);
3006 StorageImpl_Flush(&This->base);
3013 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
3015 StorageImpl *This = (StorageImpl*) iface;
3017 StorageBaseImpl_DeleteAll(&This->base);
3019 This->base.reverted = 1;
3022 static void StorageImpl_Destroy(StorageBaseImpl* iface)
3024 StorageImpl *This = (StorageImpl*) iface;
3026 TRACE("(%p)\n", This);
3028 StorageImpl_Flush(iface);
3030 StorageImpl_Invalidate(iface);
3032 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3034 BlockChainStream_Destroy(This->smallBlockRootChain);
3035 BlockChainStream_Destroy(This->rootBlockChain);
3036 BlockChainStream_Destroy(This->smallBlockDepotChain);
3038 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
3039 BlockChainStream_Destroy(This->blockChainCache[i]);
3041 if (This->lockBytes)
3042 ILockBytes_Release(This->lockBytes);
3043 HeapFree(GetProcessHeap(), 0, This);
3046 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
3048 StorageImpl *This = (StorageImpl*)storage;
3051 TRACE("(%p)\n", This);
3053 hr = BlockChainStream_Flush(This->smallBlockRootChain);
3056 hr = BlockChainStream_Flush(This->rootBlockChain);
3059 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
3061 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
3062 if (This->blockChainCache[i])
3063 hr = BlockChainStream_Flush(This->blockChainCache[i]);
3066 hr = ILockBytes_Flush(This->lockBytes);
3071 /******************************************************************************
3072 * Storage32Impl_GetNextFreeBigBlock
3074 * Returns the index of the next free big block.
3075 * If the big block depot is filled, this method will enlarge it.
3078 static ULONG StorageImpl_GetNextFreeBigBlock(
3081 ULONG depotBlockIndexPos;
3082 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3084 ULONG depotBlockOffset;
3085 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3086 ULONG nextBlockIndex = BLOCK_SPECIAL;
3088 ULONG freeBlock = BLOCK_UNUSED;
3089 ULARGE_INTEGER neededSize;
3092 depotIndex = This->prevFreeBlock / blocksPerDepot;
3093 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
3096 * Scan the entire big block depot until we find a block marked free
3098 while (nextBlockIndex != BLOCK_UNUSED)
3100 if (depotIndex < COUNT_BBDEPOTINHEADER)
3102 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
3105 * Grow the primary depot.
3107 if (depotBlockIndexPos == BLOCK_UNUSED)
3109 depotBlockIndexPos = depotIndex*blocksPerDepot;
3112 * Add a block depot.
3114 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3115 This->bigBlockDepotCount++;
3116 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3119 * Flag it as a block depot.
3121 StorageImpl_SetNextBlockInChain(This,
3125 /* Save new header information.
3127 StorageImpl_SaveFileHeader(This);
3132 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3134 if (depotBlockIndexPos == BLOCK_UNUSED)
3137 * Grow the extended depot.
3139 ULONG extIndex = BLOCK_UNUSED;
3140 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3141 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3143 if (extBlockOffset == 0)
3145 /* We need an extended block.
3147 extIndex = Storage32Impl_AddExtBlockDepot(This);
3148 This->extBigBlockDepotCount++;
3149 depotBlockIndexPos = extIndex + 1;
3152 depotBlockIndexPos = depotIndex * blocksPerDepot;
3155 * Add a block depot and mark it in the extended block.
3157 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3158 This->bigBlockDepotCount++;
3159 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3161 /* Flag the block depot.
3163 StorageImpl_SetNextBlockInChain(This,
3167 /* If necessary, flag the extended depot block.
3169 if (extIndex != BLOCK_UNUSED)
3170 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3172 /* Save header information.
3174 StorageImpl_SaveFileHeader(This);
3178 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3182 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3183 ( nextBlockIndex != BLOCK_UNUSED))
3185 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3187 if (nextBlockIndex == BLOCK_UNUSED)
3189 freeBlock = (depotIndex * blocksPerDepot) +
3190 (depotBlockOffset/sizeof(ULONG));
3193 depotBlockOffset += sizeof(ULONG);
3198 depotBlockOffset = 0;
3202 * make sure that the block physically exists before using it
3204 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3206 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3208 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3209 ILockBytes_SetSize(This->lockBytes, neededSize);
3211 This->prevFreeBlock = freeBlock;
3216 /******************************************************************************
3217 * Storage32Impl_AddBlockDepot
3219 * This will create a depot block, essentially it is a block initialized
3222 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3224 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3227 * Initialize blocks as free
3229 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3230 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3233 /******************************************************************************
3234 * Storage32Impl_GetExtDepotBlock
3236 * Returns the index of the block that corresponds to the specified depot
3237 * index. This method is only for depot indexes equal or greater than
3238 * COUNT_BBDEPOTINHEADER.
3240 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3242 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3243 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3244 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3245 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3246 ULONG blockIndex = BLOCK_UNUSED;
3247 ULONG extBlockIndex;
3248 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3249 int index, num_blocks;
3251 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3253 if (extBlockCount >= This->extBigBlockDepotCount)
3254 return BLOCK_UNUSED;
3256 if (This->indexExtBlockDepotCached != extBlockCount)
3258 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3260 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer);
3262 num_blocks = This->bigBlockSize / 4;
3264 for (index = 0; index < num_blocks; index++)
3266 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3267 This->extBlockDepotCached[index] = blockIndex;
3270 This->indexExtBlockDepotCached = extBlockCount;
3273 blockIndex = This->extBlockDepotCached[extBlockOffset];
3278 /******************************************************************************
3279 * Storage32Impl_SetExtDepotBlock
3281 * Associates the specified block index to the specified depot index.
3282 * This method is only for depot indexes equal or greater than
3283 * COUNT_BBDEPOTINHEADER.
3285 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3287 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3288 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3289 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3290 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3291 ULONG extBlockIndex;
3293 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3295 assert(extBlockCount < This->extBigBlockDepotCount);
3297 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3299 if (extBlockIndex != BLOCK_UNUSED)
3301 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3302 extBlockOffset * sizeof(ULONG),
3306 if (This->indexExtBlockDepotCached == extBlockCount)
3308 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3312 /******************************************************************************
3313 * Storage32Impl_AddExtBlockDepot
3315 * Creates an extended depot block.
3317 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3319 ULONG numExtBlocks = This->extBigBlockDepotCount;
3320 ULONG nextExtBlock = This->extBigBlockDepotStart;
3321 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3322 ULONG index = BLOCK_UNUSED;
3323 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3324 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3325 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3327 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3328 blocksPerDepotBlock;
3330 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3333 * The first extended block.
3335 This->extBigBlockDepotStart = index;
3340 * Find the last existing extended block.
3342 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3345 * Add the new extended block to the chain.
3347 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3352 * Initialize this block.
3354 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3355 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3357 /* Add the block to our cache. */
3358 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3360 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3361 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3363 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3364 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3366 This->extBigBlockDepotLocations = new_cache;
3367 This->extBigBlockDepotLocationsSize = new_cache_size;
3369 This->extBigBlockDepotLocations[numExtBlocks] = index;
3374 /******************************************************************************
3375 * Storage32Impl_FreeBigBlock
3377 * This method will flag the specified block as free in the big block depot.
3379 static void StorageImpl_FreeBigBlock(
3383 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3385 if (blockIndex < This->prevFreeBlock)
3386 This->prevFreeBlock = blockIndex;
3389 /************************************************************************
3390 * Storage32Impl_GetNextBlockInChain
3392 * This method will retrieve the block index of the next big block in
3395 * Params: This - Pointer to the Storage object.
3396 * blockIndex - Index of the block to retrieve the chain
3398 * nextBlockIndex - receives the return value.
3400 * Returns: This method returns the index of the next block in the chain.
3401 * It will return the constants:
3402 * BLOCK_SPECIAL - If the block given was not part of a
3404 * BLOCK_END_OF_CHAIN - If the block given was the last in
3406 * BLOCK_UNUSED - If the block given was not past of a chain
3408 * BLOCK_EXTBBDEPOT - This block is part of the extended
3411 * See Windows documentation for more details on IStorage methods.
3413 static HRESULT StorageImpl_GetNextBlockInChain(
3416 ULONG* nextBlockIndex)
3418 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3419 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3420 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3421 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3423 ULONG depotBlockIndexPos;
3424 int index, num_blocks;
3426 *nextBlockIndex = BLOCK_SPECIAL;
3428 if(depotBlockCount >= This->bigBlockDepotCount)
3430 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3431 This->bigBlockDepotCount);
3432 return STG_E_READFAULT;
3436 * Cache the currently accessed depot block.
3438 if (depotBlockCount != This->indexBlockDepotCached)
3440 This->indexBlockDepotCached = depotBlockCount;
3442 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3444 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3449 * We have to look in the extended depot.
3451 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3454 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3457 return STG_E_READFAULT;
3459 num_blocks = This->bigBlockSize / 4;
3461 for (index = 0; index < num_blocks; index++)
3463 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3464 This->blockDepotCached[index] = *nextBlockIndex;
3468 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3473 /******************************************************************************
3474 * Storage32Impl_GetNextExtendedBlock
3476 * Given an extended block this method will return the next extended block.
3479 * The last ULONG of an extended block is the block index of the next
3480 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3484 * - The index of the next extended block
3485 * - BLOCK_UNUSED: there is no next extended block.
3486 * - Any other return values denotes failure.
3488 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3490 ULONG nextBlockIndex = BLOCK_SPECIAL;
3491 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3493 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3496 return nextBlockIndex;
3499 /******************************************************************************
3500 * Storage32Impl_SetNextBlockInChain
3502 * This method will write the index of the specified block's next block
3503 * in the big block depot.
3505 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3508 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3509 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3510 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3513 static void StorageImpl_SetNextBlockInChain(
3518 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3519 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3520 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3521 ULONG depotBlockIndexPos;
3523 assert(depotBlockCount < This->bigBlockDepotCount);
3524 assert(blockIndex != nextBlock);
3526 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3528 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3533 * We have to look in the extended depot.
3535 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3538 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3541 * Update the cached block depot, if necessary.
3543 if (depotBlockCount == This->indexBlockDepotCached)
3545 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3549 /******************************************************************************
3550 * Storage32Impl_LoadFileHeader
3552 * This method will read in the file header
3554 static HRESULT StorageImpl_LoadFileHeader(
3558 BYTE headerBigBlock[HEADER_SIZE];
3560 ULARGE_INTEGER offset;
3565 * Get a pointer to the big block of data containing the header.
3567 offset.u.HighPart = 0;
3568 offset.u.LowPart = 0;
3569 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3570 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3571 hr = STG_E_FILENOTFOUND;
3574 * Extract the information from the header.
3579 * Check for the "magic number" signature and return an error if it is not
3582 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3584 return STG_E_OLDFORMAT;
3587 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3589 return STG_E_INVALIDHEADER;
3592 StorageUtl_ReadWord(
3594 OFFSET_BIGBLOCKSIZEBITS,
3595 &This->bigBlockSizeBits);
3597 StorageUtl_ReadWord(
3599 OFFSET_SMALLBLOCKSIZEBITS,
3600 &This->smallBlockSizeBits);
3602 StorageUtl_ReadDWord(
3604 OFFSET_BBDEPOTCOUNT,
3605 &This->bigBlockDepotCount);
3607 StorageUtl_ReadDWord(
3609 OFFSET_ROOTSTARTBLOCK,
3610 &This->rootStartBlock);
3612 StorageUtl_ReadDWord(
3614 OFFSET_SMALLBLOCKLIMIT,
3615 &This->smallBlockLimit);
3617 StorageUtl_ReadDWord(
3619 OFFSET_SBDEPOTSTART,
3620 &This->smallBlockDepotStart);
3622 StorageUtl_ReadDWord(
3624 OFFSET_EXTBBDEPOTSTART,
3625 &This->extBigBlockDepotStart);
3627 StorageUtl_ReadDWord(
3629 OFFSET_EXTBBDEPOTCOUNT,
3630 &This->extBigBlockDepotCount);
3632 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3634 StorageUtl_ReadDWord(
3636 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3637 &(This->bigBlockDepotStart[index]));
3641 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3643 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3644 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3647 * Right now, the code is making some assumptions about the size of the
3648 * blocks, just make sure they are what we're expecting.
3650 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3651 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3652 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3654 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3655 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3656 hr = STG_E_INVALIDHEADER;
3665 /******************************************************************************
3666 * Storage32Impl_SaveFileHeader
3668 * This method will save to the file the header
3670 static void StorageImpl_SaveFileHeader(
3673 BYTE headerBigBlock[HEADER_SIZE];
3676 ULARGE_INTEGER offset;
3677 DWORD bytes_read, bytes_written;
3678 DWORD major_version, dirsectorcount;
3681 * Get a pointer to the big block of data containing the header.
3683 offset.u.HighPart = 0;
3684 offset.u.LowPart = 0;
3685 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3686 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3687 hr = STG_E_FILENOTFOUND;
3689 if (This->bigBlockSizeBits == 0x9)
3691 else if (This->bigBlockSizeBits == 0xc)
3695 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3700 * If the block read failed, the file is probably new.
3705 * Initialize for all unknown fields.
3707 memset(headerBigBlock, 0, HEADER_SIZE);
3710 * Initialize the magic number.
3712 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3716 * Write the information to the header.
3718 StorageUtl_WriteWord(
3720 OFFSET_MINORVERSION,
3723 StorageUtl_WriteWord(
3725 OFFSET_MAJORVERSION,
3728 StorageUtl_WriteWord(
3730 OFFSET_BYTEORDERMARKER,
3733 StorageUtl_WriteWord(
3735 OFFSET_BIGBLOCKSIZEBITS,
3736 This->bigBlockSizeBits);
3738 StorageUtl_WriteWord(
3740 OFFSET_SMALLBLOCKSIZEBITS,
3741 This->smallBlockSizeBits);
3743 if (major_version >= 4)
3745 if (This->rootBlockChain)
3746 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3748 /* This file is being created, and it will start out with one block. */
3752 /* This field must be 0 in versions older than 4 */
3755 StorageUtl_WriteDWord(
3757 OFFSET_DIRSECTORCOUNT,
3760 StorageUtl_WriteDWord(
3762 OFFSET_BBDEPOTCOUNT,
3763 This->bigBlockDepotCount);
3765 StorageUtl_WriteDWord(
3767 OFFSET_ROOTSTARTBLOCK,
3768 This->rootStartBlock);
3770 StorageUtl_WriteDWord(
3772 OFFSET_SMALLBLOCKLIMIT,
3773 This->smallBlockLimit);
3775 StorageUtl_WriteDWord(
3777 OFFSET_SBDEPOTSTART,
3778 This->smallBlockDepotStart);
3780 StorageUtl_WriteDWord(
3782 OFFSET_SBDEPOTCOUNT,
3783 This->smallBlockDepotChain ?
3784 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3786 StorageUtl_WriteDWord(
3788 OFFSET_EXTBBDEPOTSTART,
3789 This->extBigBlockDepotStart);
3791 StorageUtl_WriteDWord(
3793 OFFSET_EXTBBDEPOTCOUNT,
3794 This->extBigBlockDepotCount);
3796 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3798 StorageUtl_WriteDWord(
3800 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3801 (This->bigBlockDepotStart[index]));
3805 * Write the big block back to the file.
3807 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3810 /******************************************************************************
3811 * StorageImpl_ReadRawDirEntry
3813 * This method will read the raw data from a directory entry in the file.
3815 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3817 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3819 ULARGE_INTEGER offset;
3823 offset.u.HighPart = 0;
3824 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3826 hr = BlockChainStream_ReadAt(
3827 This->rootBlockChain,
3833 if (bytesRead != RAW_DIRENTRY_SIZE)
3834 return STG_E_READFAULT;
3839 /******************************************************************************
3840 * StorageImpl_WriteRawDirEntry
3842 * This method will write the raw data from a directory entry in the file.
3844 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3846 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3848 ULARGE_INTEGER offset;
3851 offset.u.HighPart = 0;
3852 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3854 return BlockChainStream_WriteAt(
3855 This->rootBlockChain,
3862 /******************************************************************************
3865 * Update raw directory entry data from the fields in newData.
3867 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3869 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3871 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3874 buffer + OFFSET_PS_NAME,
3876 DIRENTRY_NAME_BUFFER_LEN );
3878 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3880 StorageUtl_WriteWord(
3882 OFFSET_PS_NAMELENGTH,
3883 newData->sizeOfNameString);
3885 StorageUtl_WriteDWord(
3887 OFFSET_PS_LEFTCHILD,
3888 newData->leftChild);
3890 StorageUtl_WriteDWord(
3892 OFFSET_PS_RIGHTCHILD,
3893 newData->rightChild);
3895 StorageUtl_WriteDWord(
3898 newData->dirRootEntry);
3900 StorageUtl_WriteGUID(
3905 StorageUtl_WriteDWord(
3908 newData->ctime.dwLowDateTime);
3910 StorageUtl_WriteDWord(
3912 OFFSET_PS_CTIMEHIGH,
3913 newData->ctime.dwHighDateTime);
3915 StorageUtl_WriteDWord(
3918 newData->mtime.dwLowDateTime);
3920 StorageUtl_WriteDWord(
3922 OFFSET_PS_MTIMEHIGH,
3923 newData->ctime.dwHighDateTime);
3925 StorageUtl_WriteDWord(
3927 OFFSET_PS_STARTBLOCK,
3928 newData->startingBlock);
3930 StorageUtl_WriteDWord(
3933 newData->size.u.LowPart);
3936 /******************************************************************************
3937 * Storage32Impl_ReadDirEntry
3939 * This method will read the specified directory entry.
3941 HRESULT StorageImpl_ReadDirEntry(
3946 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3949 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3951 if (SUCCEEDED(readRes))
3953 memset(buffer->name, 0, sizeof(buffer->name));
3956 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3957 DIRENTRY_NAME_BUFFER_LEN );
3958 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3960 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3962 StorageUtl_ReadWord(
3964 OFFSET_PS_NAMELENGTH,
3965 &buffer->sizeOfNameString);
3967 StorageUtl_ReadDWord(
3969 OFFSET_PS_LEFTCHILD,
3970 &buffer->leftChild);
3972 StorageUtl_ReadDWord(
3974 OFFSET_PS_RIGHTCHILD,
3975 &buffer->rightChild);
3977 StorageUtl_ReadDWord(
3980 &buffer->dirRootEntry);
3982 StorageUtl_ReadGUID(
3987 StorageUtl_ReadDWord(
3990 &buffer->ctime.dwLowDateTime);
3992 StorageUtl_ReadDWord(
3994 OFFSET_PS_CTIMEHIGH,
3995 &buffer->ctime.dwHighDateTime);
3997 StorageUtl_ReadDWord(
4000 &buffer->mtime.dwLowDateTime);
4002 StorageUtl_ReadDWord(
4004 OFFSET_PS_MTIMEHIGH,
4005 &buffer->mtime.dwHighDateTime);
4007 StorageUtl_ReadDWord(
4009 OFFSET_PS_STARTBLOCK,
4010 &buffer->startingBlock);
4012 StorageUtl_ReadDWord(
4015 &buffer->size.u.LowPart);
4017 buffer->size.u.HighPart = 0;
4023 /*********************************************************************
4024 * Write the specified directory entry to the file
4026 HRESULT StorageImpl_WriteDirEntry(
4029 const DirEntry* buffer)
4031 BYTE currentEntry[RAW_DIRENTRY_SIZE];
4033 UpdateRawDirEntry(currentEntry, buffer);
4035 return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
4038 static BOOL StorageImpl_ReadBigBlock(
4043 ULARGE_INTEGER ulOffset;
4046 ulOffset.u.HighPart = 0;
4047 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4049 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
4051 if (read && read < This->bigBlockSize)
4053 /* File ends during this block; fill the rest with 0's. */
4054 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
4060 static BOOL StorageImpl_ReadDWordFromBigBlock(
4066 ULARGE_INTEGER ulOffset;
4070 ulOffset.u.HighPart = 0;
4071 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4072 ulOffset.u.LowPart += offset;
4074 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
4075 *value = lendian32toh(tmp);
4076 return (read == sizeof(DWORD));
4079 static BOOL StorageImpl_WriteBigBlock(
4084 ULARGE_INTEGER ulOffset;
4087 ulOffset.u.HighPart = 0;
4088 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4090 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
4091 return (wrote == This->bigBlockSize);
4094 static BOOL StorageImpl_WriteDWordToBigBlock(
4100 ULARGE_INTEGER ulOffset;
4103 ulOffset.u.HighPart = 0;
4104 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4105 ulOffset.u.LowPart += offset;
4107 value = htole32(value);
4108 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4109 return (wrote == sizeof(DWORD));
4112 /******************************************************************************
4113 * Storage32Impl_SmallBlocksToBigBlocks
4115 * This method will convert a small block chain to a big block chain.
4116 * The small block chain will be destroyed.
4118 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4120 SmallBlockChainStream** ppsbChain)
4122 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4123 ULARGE_INTEGER size, offset;
4124 ULONG cbRead, cbWritten;
4125 ULARGE_INTEGER cbTotalRead;
4126 DirRef streamEntryRef;
4127 HRESULT resWrite = S_OK;
4129 DirEntry streamEntry;
4131 BlockChainStream *bbTempChain = NULL;
4132 BlockChainStream *bigBlockChain = NULL;
4135 * Create a temporary big block chain that doesn't have
4136 * an associated directory entry. This temporary chain will be
4137 * used to copy data from small blocks to big blocks.
4139 bbTempChain = BlockChainStream_Construct(This,
4142 if(!bbTempChain) return NULL;
4144 * Grow the big block chain.
4146 size = SmallBlockChainStream_GetSize(*ppsbChain);
4147 BlockChainStream_SetSize(bbTempChain, size);
4150 * Copy the contents of the small block chain to the big block chain
4151 * by small block size increments.
4153 offset.u.LowPart = 0;
4154 offset.u.HighPart = 0;
4155 cbTotalRead.QuadPart = 0;
4157 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4160 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4162 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4165 if (FAILED(resRead))
4170 cbTotalRead.QuadPart += cbRead;
4172 resWrite = BlockChainStream_WriteAt(bbTempChain,
4178 if (FAILED(resWrite))
4181 offset.u.LowPart += cbRead;
4185 resRead = STG_E_READFAULT;
4188 } while (cbTotalRead.QuadPart < size.QuadPart);
4189 HeapFree(GetProcessHeap(),0,buffer);
4191 size.u.HighPart = 0;
4194 if (FAILED(resRead) || FAILED(resWrite))
4196 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4197 BlockChainStream_SetSize(bbTempChain, size);
4198 BlockChainStream_Destroy(bbTempChain);
4203 * Destroy the small block chain.
4205 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4206 SmallBlockChainStream_SetSize(*ppsbChain, size);
4207 SmallBlockChainStream_Destroy(*ppsbChain);
4211 * Change the directory entry. This chain is now a big block chain
4212 * and it doesn't reside in the small blocks chain anymore.
4214 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4216 streamEntry.startingBlock = bbHeadOfChain;
4218 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4221 * Destroy the temporary entryless big block chain.
4222 * Create a new big block chain associated with this entry.
4224 BlockChainStream_Destroy(bbTempChain);
4225 bigBlockChain = BlockChainStream_Construct(This,
4229 return bigBlockChain;
4232 /******************************************************************************
4233 * Storage32Impl_BigBlocksToSmallBlocks
4235 * This method will convert a big block chain to a small block chain.
4236 * The big block chain will be destroyed on success.
4238 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4240 BlockChainStream** ppbbChain,
4241 ULARGE_INTEGER newSize)
4243 ULARGE_INTEGER size, offset, cbTotalRead;
4244 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4245 DirRef streamEntryRef;
4246 HRESULT resWrite = S_OK, resRead = S_OK;
4247 DirEntry streamEntry;
4249 SmallBlockChainStream* sbTempChain;
4251 TRACE("%p %p\n", This, ppbbChain);
4253 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4259 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4260 size = BlockChainStream_GetSize(*ppbbChain);
4261 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4263 offset.u.HighPart = 0;
4264 offset.u.LowPart = 0;
4265 cbTotalRead.QuadPart = 0;
4266 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4267 while(cbTotalRead.QuadPart < size.QuadPart)
4269 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4270 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4278 cbTotalRead.QuadPart += cbRead;
4280 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4281 cbRead, buffer, &cbWritten);
4283 if(FAILED(resWrite))
4286 offset.u.LowPart += cbRead;
4290 resRead = STG_E_READFAULT;
4294 HeapFree(GetProcessHeap(), 0, buffer);
4296 size.u.HighPart = 0;
4299 if(FAILED(resRead) || FAILED(resWrite))
4301 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4302 SmallBlockChainStream_SetSize(sbTempChain, size);
4303 SmallBlockChainStream_Destroy(sbTempChain);
4307 /* destroy the original big block chain */
4308 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4309 BlockChainStream_SetSize(*ppbbChain, size);
4310 BlockChainStream_Destroy(*ppbbChain);
4313 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4314 streamEntry.startingBlock = sbHeadOfChain;
4315 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4317 SmallBlockChainStream_Destroy(sbTempChain);
4318 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4321 static HRESULT StorageBaseImpl_CopyStream(
4322 StorageBaseImpl *dst, DirRef dst_entry,
4323 StorageBaseImpl *src, DirRef src_entry)
4328 ULARGE_INTEGER bytes_copied;
4329 ULONG bytestocopy, bytesread, byteswritten;
4331 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4335 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4337 bytes_copied.QuadPart = 0;
4338 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4340 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4342 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4344 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4347 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4348 data, &byteswritten);
4351 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4352 bytes_copied.QuadPart += byteswritten;
4360 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4362 DirRef result=This->firstFreeEntry;
4364 while (result < This->entries_size && This->entries[result].inuse)
4367 if (result == This->entries_size)
4369 ULONG new_size = This->entries_size * 2;
4370 TransactedDirEntry *new_entries;
4372 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4373 if (!new_entries) return DIRENTRY_NULL;
4375 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4376 HeapFree(GetProcessHeap(), 0, This->entries);
4378 This->entries = new_entries;
4379 This->entries_size = new_size;
4382 This->entries[result].inuse = 1;
4384 This->firstFreeEntry = result+1;
4389 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4390 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4392 DirRef stubEntryRef;
4393 TransactedDirEntry *entry;
4395 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4397 if (stubEntryRef != DIRENTRY_NULL)
4399 entry = &This->entries[stubEntryRef];
4401 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4406 return stubEntryRef;
4409 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4410 TransactedSnapshotImpl *This, DirRef entry)
4415 if (!This->entries[entry].read)
4417 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4418 This->entries[entry].transactedParentEntry,
4421 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4423 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4425 if (data.leftChild == DIRENTRY_NULL)
4429 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4431 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4433 if (data.rightChild == DIRENTRY_NULL)
4437 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4439 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4441 if (data.dirRootEntry == DIRENTRY_NULL)
4447 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4448 This->entries[entry].read = 1;
4455 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4456 TransactedSnapshotImpl *This, DirRef entry)
4460 if (!This->entries[entry].stream_dirty)
4462 DirEntry new_entrydata;
4464 memset(&new_entrydata, 0, sizeof(DirEntry));
4465 new_entrydata.name[0] = 'S';
4466 new_entrydata.sizeOfNameString = 1;
4467 new_entrydata.stgType = STGTY_STREAM;
4468 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4469 new_entrydata.leftChild = DIRENTRY_NULL;
4470 new_entrydata.rightChild = DIRENTRY_NULL;
4471 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4473 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4474 &This->entries[entry].stream_entry);
4476 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4478 hr = StorageBaseImpl_CopyStream(
4479 This->scratch, This->entries[entry].stream_entry,
4480 This->transactedParent, This->entries[entry].transactedParentEntry);
4483 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4487 This->entries[entry].stream_dirty = 1;
4489 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4491 /* Since this entry is modified, and we aren't using its stream data, we
4492 * no longer care about the original entry. */
4494 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4496 if (delete_ref != DIRENTRY_NULL)
4497 This->entries[delete_ref].deleted = 1;
4499 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4506 /* Find the first entry in a depth-first traversal. */
4507 static DirRef TransactedSnapshotImpl_FindFirstChild(
4508 TransactedSnapshotImpl* This, DirRef parent)
4510 DirRef cursor, prev;
4511 TransactedDirEntry *entry;
4514 entry = &This->entries[cursor];
4517 if (entry->data.leftChild != DIRENTRY_NULL)
4520 cursor = entry->data.leftChild;
4521 entry = &This->entries[cursor];
4522 entry->parent = prev;
4524 else if (entry->data.rightChild != DIRENTRY_NULL)
4527 cursor = entry->data.rightChild;
4528 entry = &This->entries[cursor];
4529 entry->parent = prev;
4531 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4534 cursor = entry->data.dirRootEntry;
4535 entry = &This->entries[cursor];
4536 entry->parent = prev;
4545 /* Find the next entry in a depth-first traversal. */
4546 static DirRef TransactedSnapshotImpl_FindNextChild(
4547 TransactedSnapshotImpl* This, DirRef current)
4550 TransactedDirEntry *parent_entry;
4552 parent = This->entries[current].parent;
4553 parent_entry = &This->entries[parent];
4555 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4557 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4559 This->entries[parent_entry->data.rightChild].parent = parent;
4560 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4563 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4565 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4566 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4573 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4574 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4575 TransactedSnapshotImpl* This, DirRef entry)
4577 return entry != DIRENTRY_NULL &&
4578 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4581 /* Destroy the entries created by CopyTree. */
4582 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4583 TransactedSnapshotImpl* This, DirRef stop)
4586 TransactedDirEntry *entry;
4587 ULARGE_INTEGER zero;
4591 if (!This->entries[This->base.storageDirEntry].read)
4594 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4596 if (cursor == DIRENTRY_NULL)
4599 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4601 while (cursor != DIRENTRY_NULL && cursor != stop)
4603 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4605 entry = &This->entries[cursor];
4607 if (entry->stream_dirty)
4608 StorageBaseImpl_StreamSetSize(This->transactedParent,
4609 entry->newTransactedParentEntry, zero);
4611 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4612 entry->newTransactedParentEntry);
4614 entry->newTransactedParentEntry = entry->transactedParentEntry;
4617 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4621 /* Make a copy of our edited tree that we can use in the parent. */
4622 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4625 TransactedDirEntry *entry;
4628 cursor = This->base.storageDirEntry;
4629 entry = &This->entries[cursor];
4630 entry->parent = DIRENTRY_NULL;
4631 entry->newTransactedParentEntry = entry->transactedParentEntry;
4633 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4636 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4638 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4639 entry = &This->entries[cursor];
4641 while (cursor != DIRENTRY_NULL)
4643 /* Make a copy of this entry in the transacted parent. */
4645 (!entry->dirty && !entry->stream_dirty &&
4646 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4647 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4648 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4649 entry->newTransactedParentEntry = entry->transactedParentEntry;
4654 memcpy(&newData, &entry->data, sizeof(DirEntry));
4656 newData.size.QuadPart = 0;
4657 newData.startingBlock = BLOCK_END_OF_CHAIN;
4659 if (newData.leftChild != DIRENTRY_NULL)
4660 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4662 if (newData.rightChild != DIRENTRY_NULL)
4663 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4665 if (newData.dirRootEntry != DIRENTRY_NULL)
4666 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4668 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4669 &entry->newTransactedParentEntry);
4672 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4676 if (entry->stream_dirty)
4678 hr = StorageBaseImpl_CopyStream(
4679 This->transactedParent, entry->newTransactedParentEntry,
4680 This->scratch, entry->stream_entry);
4682 else if (entry->data.size.QuadPart)
4684 hr = StorageBaseImpl_StreamLink(
4685 This->transactedParent, entry->newTransactedParentEntry,
4686 entry->transactedParentEntry);
4691 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4692 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4697 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4698 entry = &This->entries[cursor];
4704 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4706 DWORD grfCommitFlags) /* [in] */
4708 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
4709 TransactedDirEntry *root_entry;
4710 DirRef i, dir_root_ref;
4712 ULARGE_INTEGER zero;
4717 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4719 /* Cannot commit a read-only transacted storage */
4720 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4721 return STG_E_ACCESSDENIED;
4723 /* To prevent data loss, we create the new structure in the file before we
4724 * delete the old one, so that in case of errors the old data is intact. We
4725 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4726 * needed in the rare situation where we have just enough free disk space to
4727 * overwrite the existing data. */
4729 root_entry = &This->entries[This->base.storageDirEntry];
4731 if (!root_entry->read)
4734 hr = TransactedSnapshotImpl_CopyTree(This);
4735 if (FAILED(hr)) return hr;
4737 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4738 dir_root_ref = DIRENTRY_NULL;
4740 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4742 hr = StorageBaseImpl_Flush(This->transactedParent);
4744 /* Update the storage to use the new data in one step. */
4746 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4747 root_entry->transactedParentEntry, &data);
4751 data.dirRootEntry = dir_root_ref;
4752 data.clsid = root_entry->data.clsid;
4753 data.ctime = root_entry->data.ctime;
4754 data.mtime = root_entry->data.mtime;
4756 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4757 root_entry->transactedParentEntry, &data);
4760 /* Try to flush after updating the root storage, but if the flush fails, keep
4761 * going, on the theory that it'll either succeed later or the subsequent
4762 * writes will fail. */
4763 StorageBaseImpl_Flush(This->transactedParent);
4767 /* Destroy the old now-orphaned data. */
4768 for (i=0; i<This->entries_size; i++)
4770 TransactedDirEntry *entry = &This->entries[i];
4775 StorageBaseImpl_StreamSetSize(This->transactedParent,
4776 entry->transactedParentEntry, zero);
4777 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4778 entry->transactedParentEntry);
4779 memset(entry, 0, sizeof(TransactedDirEntry));
4780 This->firstFreeEntry = min(i, This->firstFreeEntry);
4782 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4784 if (entry->transactedParentEntry != DIRENTRY_NULL)
4785 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4786 entry->transactedParentEntry);
4787 if (entry->stream_dirty)
4789 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4790 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4791 entry->stream_dirty = 0;
4794 entry->transactedParentEntry = entry->newTransactedParentEntry;
4801 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4805 hr = StorageBaseImpl_Flush(This->transactedParent);
4810 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4813 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
4814 ULARGE_INTEGER zero;
4817 TRACE("(%p)\n", iface);
4819 /* Destroy the open objects. */
4820 StorageBaseImpl_DeleteAll(&This->base);
4822 /* Clear out the scratch file. */
4824 for (i=0; i<This->entries_size; i++)
4826 if (This->entries[i].stream_dirty)
4828 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4831 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4835 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4837 This->firstFreeEntry = 0;
4838 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4843 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4845 if (!This->reverted)
4847 TRACE("Storage invalidated (stg=%p)\n", This);
4851 StorageBaseImpl_DeleteAll(This);
4855 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4857 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4859 IStorage_Revert(&This->base.IStorage_iface);
4860 IStorage_Release(&This->transactedParent->IStorage_iface);
4861 IStorage_Release(&This->scratch->IStorage_iface);
4862 HeapFree(GetProcessHeap(), 0, This->entries);
4863 HeapFree(GetProcessHeap(), 0, This);
4866 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4868 /* We only need to flush when committing. */
4872 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4874 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4876 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4879 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4880 const DirEntry *newData, DirRef *index)
4882 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4884 TransactedDirEntry *new_entry;
4886 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4887 if (new_ref == DIRENTRY_NULL)
4888 return E_OUTOFMEMORY;
4890 new_entry = &This->entries[new_ref];
4892 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4893 new_entry->read = 1;
4894 new_entry->dirty = 1;
4895 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4899 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4904 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4905 DirRef index, const DirEntry *data)
4907 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4910 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4912 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4913 if (FAILED(hr)) return hr;
4915 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4917 if (index != This->base.storageDirEntry)
4919 This->entries[index].dirty = 1;
4921 if (data->size.QuadPart == 0 &&
4922 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4924 /* Since this entry is modified, and we aren't using its stream data, we
4925 * no longer care about the original entry. */
4927 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4929 if (delete_ref != DIRENTRY_NULL)
4930 This->entries[delete_ref].deleted = 1;
4932 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4939 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4940 DirRef index, DirEntry *data)
4942 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4945 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4946 if (FAILED(hr)) return hr;
4948 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4950 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4955 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4958 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4960 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4961 This->entries[index].data.size.QuadPart != 0)
4963 /* If we deleted this entry while it has stream data. We must have left the
4964 * data because some other entry is using it, and we need to leave the
4965 * original entry alone. */
4966 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4967 This->firstFreeEntry = min(index, This->firstFreeEntry);
4971 This->entries[index].deleted = 1;
4977 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4978 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4980 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4982 if (This->entries[index].stream_dirty)
4984 return StorageBaseImpl_StreamReadAt(This->scratch,
4985 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4987 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4989 /* This stream doesn't live in the parent, and we haven't allocated storage
4996 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4997 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
5001 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
5002 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5004 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5007 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5008 if (FAILED(hr)) return hr;
5010 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5011 if (FAILED(hr)) return hr;
5013 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
5014 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
5016 if (SUCCEEDED(hr) && size != 0)
5017 This->entries[index].data.size.QuadPart = max(
5018 This->entries[index].data.size.QuadPart,
5019 offset.QuadPart + size);
5024 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
5025 DirRef index, ULARGE_INTEGER newsize)
5027 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5030 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5031 if (FAILED(hr)) return hr;
5033 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
5036 if (newsize.QuadPart == 0)
5038 /* Destroy any parent references or entries in the scratch file. */
5039 if (This->entries[index].stream_dirty)
5041 ULARGE_INTEGER zero;
5043 StorageBaseImpl_StreamSetSize(This->scratch,
5044 This->entries[index].stream_entry, zero);
5045 StorageBaseImpl_DestroyDirEntry(This->scratch,
5046 This->entries[index].stream_entry);
5047 This->entries[index].stream_dirty = 0;
5049 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
5052 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
5054 if (delete_ref != DIRENTRY_NULL)
5055 This->entries[delete_ref].deleted = 1;
5057 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5062 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5063 if (FAILED(hr)) return hr;
5065 hr = StorageBaseImpl_StreamSetSize(This->scratch,
5066 This->entries[index].stream_entry, newsize);
5070 This->entries[index].data.size = newsize;
5075 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
5076 DirRef dst, DirRef src)
5078 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5080 TransactedDirEntry *dst_entry, *src_entry;
5082 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
5083 if (FAILED(hr)) return hr;
5085 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
5086 if (FAILED(hr)) return hr;
5088 dst_entry = &This->entries[dst];
5089 src_entry = &This->entries[src];
5091 dst_entry->stream_dirty = src_entry->stream_dirty;
5092 dst_entry->stream_entry = src_entry->stream_entry;
5093 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
5094 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
5095 dst_entry->data.size = src_entry->data.size;
5100 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5102 StorageBaseImpl_QueryInterface,
5103 StorageBaseImpl_AddRef,
5104 StorageBaseImpl_Release,
5105 StorageBaseImpl_CreateStream,
5106 StorageBaseImpl_OpenStream,
5107 StorageBaseImpl_CreateStorage,
5108 StorageBaseImpl_OpenStorage,
5109 StorageBaseImpl_CopyTo,
5110 StorageBaseImpl_MoveElementTo,
5111 TransactedSnapshotImpl_Commit,
5112 TransactedSnapshotImpl_Revert,
5113 StorageBaseImpl_EnumElements,
5114 StorageBaseImpl_DestroyElement,
5115 StorageBaseImpl_RenameElement,
5116 StorageBaseImpl_SetElementTimes,
5117 StorageBaseImpl_SetClass,
5118 StorageBaseImpl_SetStateBits,
5119 StorageBaseImpl_Stat
5122 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5124 TransactedSnapshotImpl_Destroy,
5125 TransactedSnapshotImpl_Invalidate,
5126 TransactedSnapshotImpl_Flush,
5127 TransactedSnapshotImpl_GetFilename,
5128 TransactedSnapshotImpl_CreateDirEntry,
5129 TransactedSnapshotImpl_WriteDirEntry,
5130 TransactedSnapshotImpl_ReadDirEntry,
5131 TransactedSnapshotImpl_DestroyDirEntry,
5132 TransactedSnapshotImpl_StreamReadAt,
5133 TransactedSnapshotImpl_StreamWriteAt,
5134 TransactedSnapshotImpl_StreamSetSize,
5135 TransactedSnapshotImpl_StreamLink
5138 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5139 TransactedSnapshotImpl** result)
5143 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5148 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5150 /* This is OK because the property set storage functions use the IStorage functions. */
5151 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
5152 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5154 list_init(&(*result)->base.strmHead);
5156 list_init(&(*result)->base.storageHead);
5158 (*result)->base.ref = 1;
5160 (*result)->base.openFlags = parentStorage->openFlags;
5162 /* Create a new temporary storage to act as the scratch file. */
5163 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
5165 (*result)->scratch = impl_from_IStorage(scratch);
5169 ULONG num_entries = 20;
5171 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5172 (*result)->entries_size = num_entries;
5173 (*result)->firstFreeEntry = 0;
5175 if ((*result)->entries)
5177 /* parentStorage already has 1 reference, which we take over here. */
5178 (*result)->transactedParent = parentStorage;
5180 parentStorage->transactedChild = &(*result)->base;
5182 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5186 IStorage_Release(scratch);
5192 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
5197 return E_OUTOFMEMORY;
5200 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5201 StorageBaseImpl** result)
5205 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5207 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5210 return TransactedSnapshotImpl_Construct(parentStorage,
5211 (TransactedSnapshotImpl**)result);
5214 static HRESULT Storage_Construct(
5222 StorageBaseImpl** result)
5224 StorageImpl *newStorage;
5225 StorageBaseImpl *newTransactedStorage;
5228 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5229 if (FAILED(hr)) goto end;
5231 if (openFlags & STGM_TRANSACTED)
5233 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5235 IStorage_Release(&newStorage->base.IStorage_iface);
5237 *result = newTransactedStorage;
5240 *result = &newStorage->base;
5246 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5248 StorageInternalImpl* This = (StorageInternalImpl*) base;
5250 if (!This->base.reverted)
5252 TRACE("Storage invalidated (stg=%p)\n", This);
5254 This->base.reverted = 1;
5256 This->parentStorage = NULL;
5258 StorageBaseImpl_DeleteAll(&This->base);
5260 list_remove(&This->ParentListEntry);
5264 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5266 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5268 StorageInternalImpl_Invalidate(&This->base);
5270 HeapFree(GetProcessHeap(), 0, This);
5273 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5275 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5277 return StorageBaseImpl_Flush(This->parentStorage);
5280 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5282 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5284 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5287 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5288 const DirEntry *newData, DirRef *index)
5290 StorageInternalImpl* This = (StorageInternalImpl*) base;
5292 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5296 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5297 DirRef index, const DirEntry *data)
5299 StorageInternalImpl* This = (StorageInternalImpl*) base;
5301 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5305 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5306 DirRef index, DirEntry *data)
5308 StorageInternalImpl* This = (StorageInternalImpl*) base;
5310 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5314 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5317 StorageInternalImpl* This = (StorageInternalImpl*) base;
5319 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5323 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5324 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5326 StorageInternalImpl* This = (StorageInternalImpl*) base;
5328 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5329 index, offset, size, buffer, bytesRead);
5332 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5333 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5335 StorageInternalImpl* This = (StorageInternalImpl*) base;
5337 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5338 index, offset, size, buffer, bytesWritten);
5341 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5342 DirRef index, ULARGE_INTEGER newsize)
5344 StorageInternalImpl* This = (StorageInternalImpl*) base;
5346 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5350 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5351 DirRef dst, DirRef src)
5353 StorageInternalImpl* This = (StorageInternalImpl*) base;
5355 return StorageBaseImpl_StreamLink(This->parentStorage,
5359 /******************************************************************************
5361 ** Storage32InternalImpl_Commit
5364 static HRESULT WINAPI StorageInternalImpl_Commit(
5366 DWORD grfCommitFlags) /* [in] */
5368 StorageBaseImpl* This = impl_from_IStorage(iface);
5369 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5370 return StorageBaseImpl_Flush(This);
5373 /******************************************************************************
5375 ** Storage32InternalImpl_Revert
5378 static HRESULT WINAPI StorageInternalImpl_Revert(
5381 FIXME("(%p): stub\n", iface);
5385 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5387 IStorage_Release(&This->parentStorage->IStorage_iface);
5388 HeapFree(GetProcessHeap(), 0, This);
5391 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5392 IEnumSTATSTG* iface,
5396 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5399 return E_INVALIDARG;
5403 if (IsEqualGUID(&IID_IUnknown, riid) ||
5404 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5407 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
5411 return E_NOINTERFACE;
5414 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5415 IEnumSTATSTG* iface)
5417 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5418 return InterlockedIncrement(&This->ref);
5421 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5422 IEnumSTATSTG* iface)
5424 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5428 newRef = InterlockedDecrement(&This->ref);
5432 IEnumSTATSTGImpl_Destroy(This);
5438 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5439 IEnumSTATSTGImpl* This,
5442 DirRef result = DIRENTRY_NULL;
5446 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5448 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5449 This->parentStorage->storageDirEntry, &entry);
5450 searchNode = entry.dirRootEntry;
5452 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5454 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5458 LONG diff = entryNameCmp( entry.name, This->name);
5462 searchNode = entry.rightChild;
5466 result = searchNode;
5467 memcpy(result_name, entry.name, sizeof(result_name));
5468 searchNode = entry.leftChild;
5476 if (result != DIRENTRY_NULL)
5477 memcpy(This->name, result_name, sizeof(result_name));
5483 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5484 IEnumSTATSTG* iface,
5487 ULONG* pceltFetched)
5489 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5491 DirEntry currentEntry;
5492 STATSTG* currentReturnStruct = rgelt;
5493 ULONG objectFetched = 0;
5494 DirRef currentSearchNode;
5497 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5498 return E_INVALIDARG;
5500 if (This->parentStorage->reverted)
5501 return STG_E_REVERTED;
5504 * To avoid the special case, get another pointer to a ULONG value if
5505 * the caller didn't supply one.
5507 if (pceltFetched==0)
5508 pceltFetched = &objectFetched;
5511 * Start the iteration, we will iterate until we hit the end of the
5512 * linked list or until we hit the number of items to iterate through
5516 while ( *pceltFetched < celt )
5518 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
5520 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5524 * Read the entry from the storage.
5526 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5531 * Copy the information to the return buffer.
5533 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5534 currentReturnStruct,
5539 * Step to the next item in the iteration
5542 currentReturnStruct++;
5545 if (SUCCEEDED(hr) && *pceltFetched != celt)
5552 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5553 IEnumSTATSTG* iface,
5556 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5558 ULONG objectFetched = 0;
5559 DirRef currentSearchNode;
5562 if (This->parentStorage->reverted)
5563 return STG_E_REVERTED;
5565 while ( (objectFetched < celt) )
5567 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
5569 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5575 if (SUCCEEDED(hr) && objectFetched != celt)
5581 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5582 IEnumSTATSTG* iface)
5584 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5586 if (This->parentStorage->reverted)
5587 return STG_E_REVERTED;
5594 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5595 IEnumSTATSTG* iface,
5596 IEnumSTATSTG** ppenum)
5598 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5599 IEnumSTATSTGImpl* newClone;
5601 if (This->parentStorage->reverted)
5602 return STG_E_REVERTED;
5605 return E_INVALIDARG;
5607 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5608 This->storageDirEntry);
5612 return E_OUTOFMEMORY;
5616 * The new clone enumeration must point to the same current node as
5619 memcpy(newClone->name, This->name, sizeof(newClone->name));
5621 *ppenum = &newClone->IEnumSTATSTG_iface;
5627 * Virtual function table for the IEnumSTATSTGImpl class.
5629 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5631 IEnumSTATSTGImpl_QueryInterface,
5632 IEnumSTATSTGImpl_AddRef,
5633 IEnumSTATSTGImpl_Release,
5634 IEnumSTATSTGImpl_Next,
5635 IEnumSTATSTGImpl_Skip,
5636 IEnumSTATSTGImpl_Reset,
5637 IEnumSTATSTGImpl_Clone
5640 /******************************************************************************
5641 ** IEnumSTATSTGImpl implementation
5644 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5645 StorageBaseImpl* parentStorage,
5646 DirRef storageDirEntry)
5648 IEnumSTATSTGImpl* newEnumeration;
5650 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5654 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5655 newEnumeration->ref = 1;
5656 newEnumeration->name[0] = 0;
5659 * We want to nail-down the reference to the storage in case the
5660 * enumeration out-lives the storage in the client application.
5662 newEnumeration->parentStorage = parentStorage;
5663 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
5665 newEnumeration->storageDirEntry = storageDirEntry;
5668 return newEnumeration;
5672 * Virtual function table for the Storage32InternalImpl class.
5674 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5676 StorageBaseImpl_QueryInterface,
5677 StorageBaseImpl_AddRef,
5678 StorageBaseImpl_Release,
5679 StorageBaseImpl_CreateStream,
5680 StorageBaseImpl_OpenStream,
5681 StorageBaseImpl_CreateStorage,
5682 StorageBaseImpl_OpenStorage,
5683 StorageBaseImpl_CopyTo,
5684 StorageBaseImpl_MoveElementTo,
5685 StorageInternalImpl_Commit,
5686 StorageInternalImpl_Revert,
5687 StorageBaseImpl_EnumElements,
5688 StorageBaseImpl_DestroyElement,
5689 StorageBaseImpl_RenameElement,
5690 StorageBaseImpl_SetElementTimes,
5691 StorageBaseImpl_SetClass,
5692 StorageBaseImpl_SetStateBits,
5693 StorageBaseImpl_Stat
5696 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5698 StorageInternalImpl_Destroy,
5699 StorageInternalImpl_Invalidate,
5700 StorageInternalImpl_Flush,
5701 StorageInternalImpl_GetFilename,
5702 StorageInternalImpl_CreateDirEntry,
5703 StorageInternalImpl_WriteDirEntry,
5704 StorageInternalImpl_ReadDirEntry,
5705 StorageInternalImpl_DestroyDirEntry,
5706 StorageInternalImpl_StreamReadAt,
5707 StorageInternalImpl_StreamWriteAt,
5708 StorageInternalImpl_StreamSetSize,
5709 StorageInternalImpl_StreamLink
5712 /******************************************************************************
5713 ** Storage32InternalImpl implementation
5716 static StorageInternalImpl* StorageInternalImpl_Construct(
5717 StorageBaseImpl* parentStorage,
5719 DirRef storageDirEntry)
5721 StorageInternalImpl* newStorage;
5723 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5727 list_init(&newStorage->base.strmHead);
5729 list_init(&newStorage->base.storageHead);
5732 * Initialize the virtual function table.
5734 newStorage->base.IStorage_iface.lpVtbl = &Storage32InternalImpl_Vtbl;
5735 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5736 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5737 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5739 newStorage->base.reverted = 0;
5741 newStorage->base.ref = 1;
5743 newStorage->parentStorage = parentStorage;
5746 * Keep a reference to the directory entry of this storage
5748 newStorage->base.storageDirEntry = storageDirEntry;
5750 newStorage->base.create = 0;
5758 /******************************************************************************
5759 ** StorageUtl implementation
5762 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5766 memcpy(&tmp, buffer+offset, sizeof(WORD));
5767 *value = lendian16toh(tmp);
5770 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5772 value = htole16(value);
5773 memcpy(buffer+offset, &value, sizeof(WORD));
5776 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5780 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5781 *value = lendian32toh(tmp);
5784 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5786 value = htole32(value);
5787 memcpy(buffer+offset, &value, sizeof(DWORD));
5790 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5791 ULARGE_INTEGER* value)
5793 #ifdef WORDS_BIGENDIAN
5796 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5797 value->u.LowPart = htole32(tmp.u.HighPart);
5798 value->u.HighPart = htole32(tmp.u.LowPart);
5800 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5804 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5805 const ULARGE_INTEGER *value)
5807 #ifdef WORDS_BIGENDIAN
5810 tmp.u.LowPart = htole32(value->u.HighPart);
5811 tmp.u.HighPart = htole32(value->u.LowPart);
5812 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5814 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5818 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5820 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5821 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5822 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5824 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5827 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5829 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5830 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5831 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5833 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5836 void StorageUtl_CopyDirEntryToSTATSTG(
5837 StorageBaseImpl* storage,
5838 STATSTG* destination,
5839 const DirEntry* source,
5843 * The copy of the string occurs only when the flag is not set
5845 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5847 /* Use the filename for the root storage. */
5848 destination->pwcsName = 0;
5849 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5851 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5852 (source->name[0] == 0) )
5854 destination->pwcsName = 0;
5858 destination->pwcsName =
5859 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5861 strcpyW(destination->pwcsName, source->name);
5864 switch (source->stgType)
5868 destination->type = STGTY_STORAGE;
5871 destination->type = STGTY_STREAM;
5874 destination->type = STGTY_STREAM;
5878 destination->cbSize = source->size;
5880 currentReturnStruct->mtime = {0}; TODO
5881 currentReturnStruct->ctime = {0};
5882 currentReturnStruct->atime = {0};
5884 destination->grfMode = 0;
5885 destination->grfLocksSupported = 0;
5886 destination->clsid = source->clsid;
5887 destination->grfStateBits = 0;
5888 destination->reserved = 0;
5891 /******************************************************************************
5892 ** BlockChainStream implementation
5895 /* Read and save the index of all blocks in this stream. */
5896 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5898 ULONG next_sector, next_offset;
5900 struct BlockChainRun *last_run;
5902 if (This->indexCacheLen == 0)
5906 next_sector = BlockChainStream_GetHeadOfChain(This);
5910 last_run = &This->indexCache[This->indexCacheLen-1];
5911 next_offset = last_run->lastOffset+1;
5912 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5913 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5915 if (FAILED(hr)) return hr;
5918 while (next_sector != BLOCK_END_OF_CHAIN)
5920 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5922 /* Add the current block to the cache. */
5923 if (This->indexCacheSize == 0)
5925 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5926 if (!This->indexCache) return E_OUTOFMEMORY;
5927 This->indexCacheSize = 16;
5929 else if (This->indexCacheSize == This->indexCacheLen)
5931 struct BlockChainRun *new_cache;
5934 new_size = This->indexCacheSize * 2;
5935 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5936 if (!new_cache) return E_OUTOFMEMORY;
5937 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5939 HeapFree(GetProcessHeap(), 0, This->indexCache);
5940 This->indexCache = new_cache;
5941 This->indexCacheSize = new_size;
5944 This->indexCacheLen++;
5945 last_run = &This->indexCache[This->indexCacheLen-1];
5946 last_run->firstSector = next_sector;
5947 last_run->firstOffset = next_offset;
5950 last_run->lastOffset = next_offset;
5952 /* Find the next block. */
5954 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5955 if (FAILED(hr)) return hr;
5958 if (This->indexCacheLen)
5960 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5961 This->numBlocks = last_run->lastOffset+1;
5965 This->tailIndex = BLOCK_END_OF_CHAIN;
5966 This->numBlocks = 0;
5972 /* Locate the nth block in this stream. */
5973 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5975 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5976 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5978 if (offset >= This->numBlocks)
5979 return BLOCK_END_OF_CHAIN;
5981 while (min_run < max_run)
5983 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5984 if (offset < This->indexCache[run_to_check].firstOffset)
5986 max_offset = This->indexCache[run_to_check].firstOffset-1;
5987 max_run = run_to_check-1;
5989 else if (offset > This->indexCache[run_to_check].lastOffset)
5991 min_offset = This->indexCache[run_to_check].lastOffset+1;
5992 min_run = run_to_check+1;
5995 /* Block is in this run. */
5996 min_run = max_run = run_to_check;
5999 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
6002 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
6003 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
6005 BlockChainBlock *result=NULL;
6009 if (This->cachedBlocks[i].index == index)
6011 *sector = This->cachedBlocks[i].sector;
6012 *block = &This->cachedBlocks[i];
6016 *sector = BlockChainStream_GetSectorOfOffset(This, index);
6017 if (*sector == BLOCK_END_OF_CHAIN)
6018 return STG_E_DOCFILECORRUPT;
6022 if (This->cachedBlocks[0].index == 0xffffffff)
6023 result = &This->cachedBlocks[0];
6024 else if (This->cachedBlocks[1].index == 0xffffffff)
6025 result = &This->cachedBlocks[1];
6028 result = &This->cachedBlocks[This->blockToEvict++];
6029 if (This->blockToEvict == 2)
6030 This->blockToEvict = 0;
6035 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
6036 return STG_E_WRITEFAULT;
6041 result->index = index;
6042 result->sector = *sector;
6049 BlockChainStream* BlockChainStream_Construct(
6050 StorageImpl* parentStorage,
6051 ULONG* headOfStreamPlaceHolder,
6054 BlockChainStream* newStream;
6056 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
6058 newStream->parentStorage = parentStorage;
6059 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6060 newStream->ownerDirEntry = dirEntry;
6061 newStream->indexCache = NULL;
6062 newStream->indexCacheLen = 0;
6063 newStream->indexCacheSize = 0;
6064 newStream->cachedBlocks[0].index = 0xffffffff;
6065 newStream->cachedBlocks[0].dirty = 0;
6066 newStream->cachedBlocks[1].index = 0xffffffff;
6067 newStream->cachedBlocks[1].dirty = 0;
6068 newStream->blockToEvict = 0;
6070 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
6072 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
6073 HeapFree(GetProcessHeap(), 0, newStream);
6080 HRESULT BlockChainStream_Flush(BlockChainStream* This)
6083 if (!This) return S_OK;
6086 if (This->cachedBlocks[i].dirty)
6088 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6089 This->cachedBlocks[i].dirty = 0;
6091 return STG_E_WRITEFAULT;
6097 void BlockChainStream_Destroy(BlockChainStream* This)
6101 BlockChainStream_Flush(This);
6102 HeapFree(GetProcessHeap(), 0, This->indexCache);
6104 HeapFree(GetProcessHeap(), 0, This);
6107 /******************************************************************************
6108 * BlockChainStream_GetHeadOfChain
6110 * Returns the head of this stream chain.
6111 * Some special chains don't have directory entries, their heads are kept in
6112 * This->headOfStreamPlaceHolder.
6115 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6117 DirEntry chainEntry;
6120 if (This->headOfStreamPlaceHolder != 0)
6121 return *(This->headOfStreamPlaceHolder);
6123 if (This->ownerDirEntry != DIRENTRY_NULL)
6125 hr = StorageImpl_ReadDirEntry(
6126 This->parentStorage,
6127 This->ownerDirEntry,
6132 return chainEntry.startingBlock;
6136 return BLOCK_END_OF_CHAIN;
6139 /******************************************************************************
6140 * BlockChainStream_GetCount
6142 * Returns the number of blocks that comprises this chain.
6143 * This is not the size of the stream as the last block may not be full!
6145 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6147 return This->numBlocks;
6150 /******************************************************************************
6151 * BlockChainStream_ReadAt
6153 * Reads a specified number of bytes from this chain at the specified offset.
6154 * bytesRead may be NULL.
6155 * Failure will be returned if the specified number of bytes has not been read.
6157 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6158 ULARGE_INTEGER offset,
6163 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6164 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6165 ULONG bytesToReadInBuffer;
6168 ULARGE_INTEGER stream_size;
6170 BlockChainBlock *cachedBlock;
6172 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6175 * Find the first block in the stream that contains part of the buffer.
6177 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6181 stream_size = BlockChainStream_GetSize(This);
6182 if (stream_size.QuadPart > offset.QuadPart)
6183 size = min(stream_size.QuadPart - offset.QuadPart, size);
6188 * Start reading the buffer.
6190 bufferWalker = buffer;
6194 ULARGE_INTEGER ulOffset;
6198 * Calculate how many bytes we can copy from this big block.
6200 bytesToReadInBuffer =
6201 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6203 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6210 /* Not in cache, and we're going to read past the end of the block. */
6211 ulOffset.u.HighPart = 0;
6212 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6215 StorageImpl_ReadAt(This->parentStorage,
6218 bytesToReadInBuffer,
6223 if (!cachedBlock->read)
6225 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6226 return STG_E_READFAULT;
6228 cachedBlock->read = 1;
6231 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6232 bytesReadAt = bytesToReadInBuffer;
6235 blockNoInSequence++;
6236 bufferWalker += bytesReadAt;
6237 size -= bytesReadAt;
6238 *bytesRead += bytesReadAt;
6239 offsetInBlock = 0; /* There is no offset on the next block */
6241 if (bytesToReadInBuffer != bytesReadAt)
6248 /******************************************************************************
6249 * BlockChainStream_WriteAt
6251 * Writes the specified number of bytes to this chain at the specified offset.
6252 * Will fail if not all specified number of bytes have been written.
6254 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6255 ULARGE_INTEGER offset,
6258 ULONG* bytesWritten)
6260 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6261 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6264 const BYTE* bufferWalker;
6266 BlockChainBlock *cachedBlock;
6269 bufferWalker = buffer;
6273 ULARGE_INTEGER ulOffset;
6274 DWORD bytesWrittenAt;
6277 * Calculate how many bytes we can copy to this big block.
6280 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6282 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6284 /* BlockChainStream_SetSize should have already been called to ensure we have
6285 * enough blocks in the chain to write into */
6288 ERR("not enough blocks in chain to write data\n");
6294 /* Not in cache, and we're going to write past the end of the block. */
6295 ulOffset.u.HighPart = 0;
6296 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6299 StorageImpl_WriteAt(This->parentStorage,
6307 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6309 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6310 return STG_E_READFAULT;
6313 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6314 bytesWrittenAt = bytesToWrite;
6315 cachedBlock->read = 1;
6316 cachedBlock->dirty = 1;
6319 blockNoInSequence++;
6320 bufferWalker += bytesWrittenAt;
6321 size -= bytesWrittenAt;
6322 *bytesWritten += bytesWrittenAt;
6323 offsetInBlock = 0; /* There is no offset on the next block */
6325 if (bytesWrittenAt != bytesToWrite)
6329 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6332 /******************************************************************************
6333 * BlockChainStream_Shrink
6335 * Shrinks this chain in the big block depot.
6337 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6338 ULARGE_INTEGER newSize)
6345 * Figure out how many blocks are needed to contain the new size
6347 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6349 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6355 * Go to the new end of chain
6357 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6359 /* Mark the new end of chain */
6360 StorageImpl_SetNextBlockInChain(
6361 This->parentStorage,
6363 BLOCK_END_OF_CHAIN);
6365 This->tailIndex = blockIndex;
6369 if (This->headOfStreamPlaceHolder != 0)
6371 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6375 DirEntry chainEntry;
6376 assert(This->ownerDirEntry != DIRENTRY_NULL);
6378 StorageImpl_ReadDirEntry(
6379 This->parentStorage,
6380 This->ownerDirEntry,
6383 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6385 StorageImpl_WriteDirEntry(
6386 This->parentStorage,
6387 This->ownerDirEntry,
6391 This->tailIndex = BLOCK_END_OF_CHAIN;
6394 This->numBlocks = numBlocks;
6397 * Mark the extra blocks as free
6399 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6401 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6402 StorageImpl_FreeBigBlock(This->parentStorage,
6403 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6404 if (last_run->lastOffset == last_run->firstOffset)
6405 This->indexCacheLen--;
6407 last_run->lastOffset--;
6411 * Reset the last accessed block cache.
6415 if (This->cachedBlocks[i].index >= numBlocks)
6417 This->cachedBlocks[i].index = 0xffffffff;
6418 This->cachedBlocks[i].dirty = 0;
6425 /******************************************************************************
6426 * BlockChainStream_Enlarge
6428 * Grows this chain in the big block depot.
6430 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6431 ULARGE_INTEGER newSize)
6433 ULONG blockIndex, currentBlock;
6435 ULONG oldNumBlocks = 0;
6437 blockIndex = BlockChainStream_GetHeadOfChain(This);
6440 * Empty chain. Create the head.
6442 if (blockIndex == BLOCK_END_OF_CHAIN)
6444 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6445 StorageImpl_SetNextBlockInChain(This->parentStorage,
6447 BLOCK_END_OF_CHAIN);
6449 if (This->headOfStreamPlaceHolder != 0)
6451 *(This->headOfStreamPlaceHolder) = blockIndex;
6455 DirEntry chainEntry;
6456 assert(This->ownerDirEntry != DIRENTRY_NULL);
6458 StorageImpl_ReadDirEntry(
6459 This->parentStorage,
6460 This->ownerDirEntry,
6463 chainEntry.startingBlock = blockIndex;
6465 StorageImpl_WriteDirEntry(
6466 This->parentStorage,
6467 This->ownerDirEntry,
6471 This->tailIndex = blockIndex;
6472 This->numBlocks = 1;
6476 * Figure out how many blocks are needed to contain this stream
6478 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6480 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6484 * Go to the current end of chain
6486 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6488 currentBlock = blockIndex;
6490 while (blockIndex != BLOCK_END_OF_CHAIN)
6493 currentBlock = blockIndex;
6495 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6500 This->tailIndex = currentBlock;
6503 currentBlock = This->tailIndex;
6504 oldNumBlocks = This->numBlocks;
6507 * Add new blocks to the chain
6509 if (oldNumBlocks < newNumBlocks)
6511 while (oldNumBlocks < newNumBlocks)
6513 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6515 StorageImpl_SetNextBlockInChain(
6516 This->parentStorage,
6520 StorageImpl_SetNextBlockInChain(
6521 This->parentStorage,
6523 BLOCK_END_OF_CHAIN);
6525 currentBlock = blockIndex;
6529 This->tailIndex = blockIndex;
6530 This->numBlocks = newNumBlocks;
6533 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6539 /******************************************************************************
6540 * BlockChainStream_SetSize
6542 * Sets the size of this stream. The big block depot will be updated.
6543 * The file will grow if we grow the chain.
6545 * TODO: Free the actual blocks in the file when we shrink the chain.
6546 * Currently, the blocks are still in the file. So the file size
6547 * doesn't shrink even if we shrink streams.
6549 BOOL BlockChainStream_SetSize(
6550 BlockChainStream* This,
6551 ULARGE_INTEGER newSize)
6553 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6555 if (newSize.u.LowPart == size.u.LowPart)
6558 if (newSize.u.LowPart < size.u.LowPart)
6560 BlockChainStream_Shrink(This, newSize);
6564 BlockChainStream_Enlarge(This, newSize);
6570 /******************************************************************************
6571 * BlockChainStream_GetSize
6573 * Returns the size of this chain.
6574 * Will return the block count if this chain doesn't have a directory entry.
6576 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6578 DirEntry chainEntry;
6580 if(This->headOfStreamPlaceHolder == NULL)
6583 * This chain has a directory entry so use the size value from there.
6585 StorageImpl_ReadDirEntry(
6586 This->parentStorage,
6587 This->ownerDirEntry,
6590 return chainEntry.size;
6595 * this chain is a chain that does not have a directory entry, figure out the
6596 * size by making the product number of used blocks times the
6599 ULARGE_INTEGER result;
6600 result.u.HighPart = 0;
6603 BlockChainStream_GetCount(This) *
6604 This->parentStorage->bigBlockSize;
6610 /******************************************************************************
6611 ** SmallBlockChainStream implementation
6614 SmallBlockChainStream* SmallBlockChainStream_Construct(
6615 StorageImpl* parentStorage,
6616 ULONG* headOfStreamPlaceHolder,
6619 SmallBlockChainStream* newStream;
6621 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6623 newStream->parentStorage = parentStorage;
6624 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6625 newStream->ownerDirEntry = dirEntry;
6630 void SmallBlockChainStream_Destroy(
6631 SmallBlockChainStream* This)
6633 HeapFree(GetProcessHeap(), 0, This);
6636 /******************************************************************************
6637 * SmallBlockChainStream_GetHeadOfChain
6639 * Returns the head of this chain of small blocks.
6641 static ULONG SmallBlockChainStream_GetHeadOfChain(
6642 SmallBlockChainStream* This)
6644 DirEntry chainEntry;
6647 if (This->headOfStreamPlaceHolder != NULL)
6648 return *(This->headOfStreamPlaceHolder);
6650 if (This->ownerDirEntry)
6652 hr = StorageImpl_ReadDirEntry(
6653 This->parentStorage,
6654 This->ownerDirEntry,
6659 return chainEntry.startingBlock;
6664 return BLOCK_END_OF_CHAIN;
6667 /******************************************************************************
6668 * SmallBlockChainStream_GetNextBlockInChain
6670 * Returns the index of the next small block in this chain.
6673 * - BLOCK_END_OF_CHAIN: end of this chain
6674 * - BLOCK_UNUSED: small block 'blockIndex' is free
6676 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6677 SmallBlockChainStream* This,
6679 ULONG* nextBlockInChain)
6681 ULARGE_INTEGER offsetOfBlockInDepot;
6686 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6688 offsetOfBlockInDepot.u.HighPart = 0;
6689 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6692 * Read those bytes in the buffer from the small block file.
6694 res = BlockChainStream_ReadAt(
6695 This->parentStorage->smallBlockDepotChain,
6696 offsetOfBlockInDepot,
6701 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6702 res = STG_E_READFAULT;
6706 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6713 /******************************************************************************
6714 * SmallBlockChainStream_SetNextBlockInChain
6716 * Writes the index of the next block of the specified block in the small
6718 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6719 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6721 static void SmallBlockChainStream_SetNextBlockInChain(
6722 SmallBlockChainStream* This,
6726 ULARGE_INTEGER offsetOfBlockInDepot;
6730 offsetOfBlockInDepot.u.HighPart = 0;
6731 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6733 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6736 * Read those bytes in the buffer from the small block file.
6738 BlockChainStream_WriteAt(
6739 This->parentStorage->smallBlockDepotChain,
6740 offsetOfBlockInDepot,
6746 /******************************************************************************
6747 * SmallBlockChainStream_FreeBlock
6749 * Flag small block 'blockIndex' as free in the small block depot.
6751 static void SmallBlockChainStream_FreeBlock(
6752 SmallBlockChainStream* This,
6755 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6758 /******************************************************************************
6759 * SmallBlockChainStream_GetNextFreeBlock
6761 * Returns the index of a free small block. The small block depot will be
6762 * enlarged if necessary. The small block chain will also be enlarged if
6765 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6766 SmallBlockChainStream* This)
6768 ULARGE_INTEGER offsetOfBlockInDepot;
6771 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6772 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6774 ULONG smallBlocksPerBigBlock;
6776 ULONG blocksRequired;
6777 ULARGE_INTEGER old_size, size_required;
6779 offsetOfBlockInDepot.u.HighPart = 0;
6782 * Scan the small block depot for a free block
6784 while (nextBlockIndex != BLOCK_UNUSED)
6786 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6788 res = BlockChainStream_ReadAt(
6789 This->parentStorage->smallBlockDepotChain,
6790 offsetOfBlockInDepot,
6796 * If we run out of space for the small block depot, enlarge it
6798 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6800 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6802 if (nextBlockIndex != BLOCK_UNUSED)
6808 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6810 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6811 ULARGE_INTEGER newSize, offset;
6814 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6815 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6818 * Initialize all the small blocks to free
6820 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6821 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6822 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6823 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6825 StorageImpl_SaveFileHeader(This->parentStorage);
6829 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6831 smallBlocksPerBigBlock =
6832 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6835 * Verify if we have to allocate big blocks to contain small blocks
6837 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6839 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6841 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6843 if (size_required.QuadPart > old_size.QuadPart)
6845 BlockChainStream_SetSize(
6846 This->parentStorage->smallBlockRootChain,
6849 StorageImpl_ReadDirEntry(
6850 This->parentStorage,
6851 This->parentStorage->base.storageDirEntry,
6854 rootEntry.size = size_required;
6856 StorageImpl_WriteDirEntry(
6857 This->parentStorage,
6858 This->parentStorage->base.storageDirEntry,
6865 /******************************************************************************
6866 * SmallBlockChainStream_ReadAt
6868 * Reads a specified number of bytes from this chain at the specified offset.
6869 * bytesRead may be NULL.
6870 * Failure will be returned if the specified number of bytes has not been read.
6872 HRESULT SmallBlockChainStream_ReadAt(
6873 SmallBlockChainStream* This,
6874 ULARGE_INTEGER offset,
6880 ULARGE_INTEGER offsetInBigBlockFile;
6881 ULONG blockNoInSequence =
6882 offset.u.LowPart / This->parentStorage->smallBlockSize;
6884 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6885 ULONG bytesToReadInBuffer;
6887 ULONG bytesReadFromBigBlockFile;
6889 ULARGE_INTEGER stream_size;
6892 * This should never happen on a small block file.
6894 assert(offset.u.HighPart==0);
6898 stream_size = SmallBlockChainStream_GetSize(This);
6899 if (stream_size.QuadPart > offset.QuadPart)
6900 size = min(stream_size.QuadPart - offset.QuadPart, size);
6905 * Find the first block in the stream that contains part of the buffer.
6907 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6909 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6911 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6914 blockNoInSequence--;
6918 * Start reading the buffer.
6920 bufferWalker = buffer;
6922 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6925 * Calculate how many bytes we can copy from this small block.
6927 bytesToReadInBuffer =
6928 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6931 * Calculate the offset of the small block in the small block file.
6933 offsetInBigBlockFile.u.HighPart = 0;
6934 offsetInBigBlockFile.u.LowPart =
6935 blockIndex * This->parentStorage->smallBlockSize;
6937 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6940 * Read those bytes in the buffer from the small block file.
6941 * The small block has already been identified so it shouldn't fail
6942 * unless the file is corrupt.
6944 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6945 offsetInBigBlockFile,
6946 bytesToReadInBuffer,
6948 &bytesReadFromBigBlockFile);
6953 if (!bytesReadFromBigBlockFile)
6954 return STG_E_DOCFILECORRUPT;
6957 * Step to the next big block.
6959 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6961 return STG_E_DOCFILECORRUPT;
6963 bufferWalker += bytesReadFromBigBlockFile;
6964 size -= bytesReadFromBigBlockFile;
6965 *bytesRead += bytesReadFromBigBlockFile;
6966 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6972 /******************************************************************************
6973 * SmallBlockChainStream_WriteAt
6975 * Writes the specified number of bytes to this chain at the specified offset.
6976 * Will fail if not all specified number of bytes have been written.
6978 HRESULT SmallBlockChainStream_WriteAt(
6979 SmallBlockChainStream* This,
6980 ULARGE_INTEGER offset,
6983 ULONG* bytesWritten)
6985 ULARGE_INTEGER offsetInBigBlockFile;
6986 ULONG blockNoInSequence =
6987 offset.u.LowPart / This->parentStorage->smallBlockSize;
6989 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6990 ULONG bytesToWriteInBuffer;
6992 ULONG bytesWrittenToBigBlockFile;
6993 const BYTE* bufferWalker;
6997 * This should never happen on a small block file.
6999 assert(offset.u.HighPart==0);
7002 * Find the first block in the stream that contains part of the buffer.
7004 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7006 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7008 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
7009 return STG_E_DOCFILECORRUPT;
7010 blockNoInSequence--;
7014 * Start writing the buffer.
7017 bufferWalker = buffer;
7018 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7021 * Calculate how many bytes we can copy to this small block.
7023 bytesToWriteInBuffer =
7024 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7027 * Calculate the offset of the small block in the small block file.
7029 offsetInBigBlockFile.u.HighPart = 0;
7030 offsetInBigBlockFile.u.LowPart =
7031 blockIndex * This->parentStorage->smallBlockSize;
7033 offsetInBigBlockFile.u.LowPart += offsetInBlock;
7036 * Write those bytes in the buffer to the small block file.
7038 res = BlockChainStream_WriteAt(
7039 This->parentStorage->smallBlockRootChain,
7040 offsetInBigBlockFile,
7041 bytesToWriteInBuffer,
7043 &bytesWrittenToBigBlockFile);
7048 * Step to the next big block.
7050 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7053 bufferWalker += bytesWrittenToBigBlockFile;
7054 size -= bytesWrittenToBigBlockFile;
7055 *bytesWritten += bytesWrittenToBigBlockFile;
7056 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7059 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7062 /******************************************************************************
7063 * SmallBlockChainStream_Shrink
7065 * Shrinks this chain in the small block depot.
7067 static BOOL SmallBlockChainStream_Shrink(
7068 SmallBlockChainStream* This,
7069 ULARGE_INTEGER newSize)
7071 ULONG blockIndex, extraBlock;
7075 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7077 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7080 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7083 * Go to the new end of chain
7085 while (count < numBlocks)
7087 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7094 * If the count is 0, we have a special case, the head of the chain was
7099 DirEntry chainEntry;
7101 StorageImpl_ReadDirEntry(This->parentStorage,
7102 This->ownerDirEntry,
7105 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7107 StorageImpl_WriteDirEntry(This->parentStorage,
7108 This->ownerDirEntry,
7112 * We start freeing the chain at the head block.
7114 extraBlock = blockIndex;
7118 /* Get the next block before marking the new end */
7119 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7123 /* Mark the new end of chain */
7124 SmallBlockChainStream_SetNextBlockInChain(
7127 BLOCK_END_OF_CHAIN);
7131 * Mark the extra blocks as free
7133 while (extraBlock != BLOCK_END_OF_CHAIN)
7135 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7138 SmallBlockChainStream_FreeBlock(This, extraBlock);
7139 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7140 extraBlock = blockIndex;
7146 /******************************************************************************
7147 * SmallBlockChainStream_Enlarge
7149 * Grows this chain in the small block depot.
7151 static BOOL SmallBlockChainStream_Enlarge(
7152 SmallBlockChainStream* This,
7153 ULARGE_INTEGER newSize)
7155 ULONG blockIndex, currentBlock;
7157 ULONG oldNumBlocks = 0;
7159 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7162 * Empty chain. Create the head.
7164 if (blockIndex == BLOCK_END_OF_CHAIN)
7166 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7167 SmallBlockChainStream_SetNextBlockInChain(
7170 BLOCK_END_OF_CHAIN);
7172 if (This->headOfStreamPlaceHolder != NULL)
7174 *(This->headOfStreamPlaceHolder) = blockIndex;
7178 DirEntry chainEntry;
7180 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7183 chainEntry.startingBlock = blockIndex;
7185 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7190 currentBlock = blockIndex;
7193 * Figure out how many blocks are needed to contain this stream
7195 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7197 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7201 * Go to the current end of chain
7203 while (blockIndex != BLOCK_END_OF_CHAIN)
7206 currentBlock = blockIndex;
7207 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7212 * Add new blocks to the chain
7214 while (oldNumBlocks < newNumBlocks)
7216 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7217 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7219 SmallBlockChainStream_SetNextBlockInChain(
7222 BLOCK_END_OF_CHAIN);
7224 currentBlock = blockIndex;
7231 /******************************************************************************
7232 * SmallBlockChainStream_SetSize
7234 * Sets the size of this stream.
7235 * The file will grow if we grow the chain.
7237 * TODO: Free the actual blocks in the file when we shrink the chain.
7238 * Currently, the blocks are still in the file. So the file size
7239 * doesn't shrink even if we shrink streams.
7241 BOOL SmallBlockChainStream_SetSize(
7242 SmallBlockChainStream* This,
7243 ULARGE_INTEGER newSize)
7245 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7247 if (newSize.u.LowPart == size.u.LowPart)
7250 if (newSize.u.LowPart < size.u.LowPart)
7252 SmallBlockChainStream_Shrink(This, newSize);
7256 SmallBlockChainStream_Enlarge(This, newSize);
7262 /******************************************************************************
7263 * SmallBlockChainStream_GetCount
7265 * Returns the number of small blocks that comprises this chain.
7266 * This is not the size of the stream as the last block may not be full!
7269 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7274 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7276 while(blockIndex != BLOCK_END_OF_CHAIN)
7280 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7281 blockIndex, &blockIndex)))
7288 /******************************************************************************
7289 * SmallBlockChainStream_GetSize
7291 * Returns the size of this chain.
7293 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7295 DirEntry chainEntry;
7297 if(This->headOfStreamPlaceHolder != NULL)
7299 ULARGE_INTEGER result;
7300 result.u.HighPart = 0;
7302 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7303 This->parentStorage->smallBlockSize;
7308 StorageImpl_ReadDirEntry(
7309 This->parentStorage,
7310 This->ownerDirEntry,
7313 return chainEntry.size;
7316 static HRESULT create_storagefile(
7320 STGOPTIONS* pStgOptions,
7324 StorageBaseImpl* newStorage = 0;
7325 HANDLE hFile = INVALID_HANDLE_VALUE;
7326 HRESULT hr = STG_E_INVALIDFLAG;
7330 DWORD fileAttributes;
7331 WCHAR tempFileName[MAX_PATH];
7334 return STG_E_INVALIDPOINTER;
7336 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7337 return STG_E_INVALIDPARAMETER;
7339 /* if no share mode given then DENY_NONE is the default */
7340 if (STGM_SHARE_MODE(grfMode) == 0)
7341 grfMode |= STGM_SHARE_DENY_NONE;
7343 if ( FAILED( validateSTGM(grfMode) ))
7346 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7347 switch(STGM_ACCESS_MODE(grfMode))
7350 case STGM_READWRITE:
7356 /* in direct mode, can only use SHARE_EXCLUSIVE */
7357 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7360 /* but in transacted mode, any share mode is valid */
7363 * Generate a unique name.
7367 WCHAR tempPath[MAX_PATH];
7368 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7370 memset(tempPath, 0, sizeof(tempPath));
7371 memset(tempFileName, 0, sizeof(tempFileName));
7373 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7376 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7377 pwcsName = tempFileName;
7380 hr = STG_E_INSUFFICIENTMEMORY;
7384 creationMode = TRUNCATE_EXISTING;
7388 creationMode = GetCreationModeFromSTGM(grfMode);
7392 * Interpret the STGM value grfMode
7394 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7395 accessMode = GetAccessModeFromSTGM(grfMode);
7397 if (grfMode & STGM_DELETEONRELEASE)
7398 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7400 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7402 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7406 FIXME("Storage share mode not implemented.\n");
7411 hFile = CreateFileW(pwcsName,
7419 if (hFile == INVALID_HANDLE_VALUE)
7421 if(GetLastError() == ERROR_FILE_EXISTS)
7422 hr = STG_E_FILEALREADYEXISTS;
7429 * Allocate and initialize the new IStorage32object.
7431 hr = Storage_Construct(
7438 pStgOptions->ulSectorSize,
7446 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
7447 IStorage_Release(&newStorage->IStorage_iface);
7450 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7455 /******************************************************************************
7456 * StgCreateDocfile [OLE32.@]
7457 * Creates a new compound file storage object
7460 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7461 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7462 * reserved [ ?] unused?, usually 0
7463 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7466 * S_OK if the file was successfully created
7467 * some STG_E_ value if error
7469 * if pwcsName is NULL, create file with new unique name
7470 * the function can returns
7471 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7474 HRESULT WINAPI StgCreateDocfile(
7478 IStorage **ppstgOpen)
7480 STGOPTIONS stgoptions = {1, 0, 512};
7482 TRACE("(%s, %x, %d, %p)\n",
7483 debugstr_w(pwcsName), grfMode,
7484 reserved, ppstgOpen);
7487 return STG_E_INVALIDPOINTER;
7489 return STG_E_INVALIDPARAMETER;
7491 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7494 /******************************************************************************
7495 * StgCreateStorageEx [OLE32.@]
7497 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7499 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7500 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7502 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7504 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7505 return STG_E_INVALIDPARAMETER;
7508 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7510 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7511 return STG_E_INVALIDPARAMETER;
7514 if (stgfmt == STGFMT_FILE)
7516 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7517 return STG_E_INVALIDPARAMETER;
7520 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7522 STGOPTIONS defaultOptions = {1, 0, 512};
7524 if (!pStgOptions) pStgOptions = &defaultOptions;
7525 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7529 ERR("Invalid stgfmt argument\n");
7530 return STG_E_INVALIDPARAMETER;
7533 /******************************************************************************
7534 * StgCreatePropSetStg [OLE32.@]
7536 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7537 IPropertySetStorage **propset)
7539 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
7541 return STG_E_INVALIDPARAMETER;
7543 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
7546 /******************************************************************************
7547 * StgOpenStorageEx [OLE32.@]
7549 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7551 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7552 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7554 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7556 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7557 return STG_E_INVALIDPARAMETER;
7563 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7564 return STG_E_INVALIDPARAMETER;
7566 case STGFMT_STORAGE:
7569 case STGFMT_DOCFILE:
7570 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7572 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7573 return STG_E_INVALIDPARAMETER;
7575 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7579 WARN("STGFMT_ANY assuming storage\n");
7583 return STG_E_INVALIDPARAMETER;
7586 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7590 /******************************************************************************
7591 * StgOpenStorage [OLE32.@]
7593 HRESULT WINAPI StgOpenStorage(
7594 const OLECHAR *pwcsName,
7595 IStorage *pstgPriority,
7599 IStorage **ppstgOpen)
7601 StorageBaseImpl* newStorage = 0;
7607 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7608 debugstr_w(pwcsName), pstgPriority, grfMode,
7609 snbExclude, reserved, ppstgOpen);
7613 hr = STG_E_INVALIDNAME;
7619 hr = STG_E_INVALIDPOINTER;
7625 hr = STG_E_INVALIDPARAMETER;
7629 if (grfMode & STGM_PRIORITY)
7631 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7632 return STG_E_INVALIDFLAG;
7633 if (grfMode & STGM_DELETEONRELEASE)
7634 return STG_E_INVALIDFUNCTION;
7635 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7636 return STG_E_INVALIDFLAG;
7637 grfMode &= ~0xf0; /* remove the existing sharing mode */
7638 grfMode |= STGM_SHARE_DENY_NONE;
7640 /* STGM_PRIORITY stops other IStorage objects on the same file from
7641 * committing until the STGM_PRIORITY IStorage is closed. it also
7642 * stops non-transacted mode StgOpenStorage calls with write access from
7643 * succeeding. obviously, both of these cannot be achieved through just
7644 * file share flags */
7645 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7649 * Validate the sharing mode
7651 if (grfMode & STGM_DIRECT_SWMR)
7653 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
7654 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
7656 hr = STG_E_INVALIDFLAG;
7660 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7661 switch(STGM_SHARE_MODE(grfMode))
7663 case STGM_SHARE_EXCLUSIVE:
7664 case STGM_SHARE_DENY_WRITE:
7667 hr = STG_E_INVALIDFLAG;
7671 if ( FAILED( validateSTGM(grfMode) ) ||
7672 (grfMode&STGM_CREATE))
7674 hr = STG_E_INVALIDFLAG;
7678 /* shared reading requires transacted or single writer mode */
7679 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7680 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7681 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
7683 hr = STG_E_INVALIDFLAG;
7688 * Interpret the STGM value grfMode
7690 shareMode = GetShareModeFromSTGM(grfMode);
7691 accessMode = GetAccessModeFromSTGM(grfMode);
7695 hFile = CreateFileW( pwcsName,
7700 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7703 if (hFile==INVALID_HANDLE_VALUE)
7705 DWORD last_error = GetLastError();
7711 case ERROR_FILE_NOT_FOUND:
7712 hr = STG_E_FILENOTFOUND;
7715 case ERROR_PATH_NOT_FOUND:
7716 hr = STG_E_PATHNOTFOUND;
7719 case ERROR_ACCESS_DENIED:
7720 case ERROR_WRITE_PROTECT:
7721 hr = STG_E_ACCESSDENIED;
7724 case ERROR_SHARING_VIOLATION:
7725 hr = STG_E_SHAREVIOLATION;
7736 * Refuse to open the file if it's too small to be a structured storage file
7737 * FIXME: verify the file when reading instead of here
7739 if (GetFileSize(hFile, NULL) < 0x100)
7742 hr = STG_E_FILEALREADYEXISTS;
7747 * Allocate and initialize the new IStorage32object.
7749 hr = Storage_Construct(
7762 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7764 if(hr == STG_E_INVALIDHEADER)
7765 hr = STG_E_FILEALREADYEXISTS;
7769 *ppstgOpen = &newStorage->IStorage_iface;
7772 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7776 /******************************************************************************
7777 * StgCreateDocfileOnILockBytes [OLE32.@]
7779 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7783 IStorage** ppstgOpen)
7785 StorageBaseImpl* newStorage = 0;
7788 if ((ppstgOpen == 0) || (plkbyt == 0))
7789 return STG_E_INVALIDPOINTER;
7792 * Allocate and initialize the new IStorage object.
7794 hr = Storage_Construct(
7809 *ppstgOpen = &newStorage->IStorage_iface;
7814 /******************************************************************************
7815 * StgOpenStorageOnILockBytes [OLE32.@]
7817 HRESULT WINAPI StgOpenStorageOnILockBytes(
7819 IStorage *pstgPriority,
7823 IStorage **ppstgOpen)
7825 StorageBaseImpl* newStorage = 0;
7828 if ((plkbyt == 0) || (ppstgOpen == 0))
7829 return STG_E_INVALIDPOINTER;
7831 if ( FAILED( validateSTGM(grfMode) ))
7832 return STG_E_INVALIDFLAG;
7837 * Allocate and initialize the new IStorage object.
7839 hr = Storage_Construct(
7854 *ppstgOpen = &newStorage->IStorage_iface;
7859 /******************************************************************************
7860 * StgSetTimes [ole32.@]
7861 * StgSetTimes [OLE32.@]
7865 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7866 FILETIME const *patime, FILETIME const *pmtime)
7868 IStorage *stg = NULL;
7871 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7873 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7877 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7878 IStorage_Release(stg);
7884 /******************************************************************************
7885 * StgIsStorageILockBytes [OLE32.@]
7887 * Determines if the ILockBytes contains a storage object.
7889 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7891 BYTE sig[sizeof(STORAGE_magic)];
7892 ULARGE_INTEGER offset;
7895 offset.u.HighPart = 0;
7896 offset.u.LowPart = 0;
7898 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
7900 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
7906 /******************************************************************************
7907 * WriteClassStg [OLE32.@]
7909 * This method will store the specified CLSID in the specified storage object
7911 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7914 return E_INVALIDARG;
7917 return STG_E_INVALIDPOINTER;
7919 return IStorage_SetClass(pStg, rclsid);
7922 /***********************************************************************
7923 * ReadClassStg (OLE32.@)
7925 * This method reads the CLSID previously written to a storage object with
7926 * the WriteClassStg.
7929 * pstg [I] IStorage pointer
7930 * pclsid [O] Pointer to where the CLSID is written
7934 * Failure: HRESULT code.
7936 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7941 TRACE("(%p, %p)\n", pstg, pclsid);
7943 if(!pstg || !pclsid)
7944 return E_INVALIDARG;
7947 * read a STATSTG structure (contains the clsid) from the storage
7949 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7952 *pclsid=pstatstg.clsid;
7957 /***********************************************************************
7958 * OleLoadFromStream (OLE32.@)
7960 * This function loads an object from stream
7962 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7966 LPPERSISTSTREAM xstm;
7968 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7970 res=ReadClassStm(pStm,&clsid);
7973 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7976 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7978 IUnknown_Release((IUnknown*)*ppvObj);
7981 res=IPersistStream_Load(xstm,pStm);
7982 IPersistStream_Release(xstm);
7983 /* FIXME: all refcounts ok at this point? I think they should be:
7986 * xstm : 0 (released)
7991 /***********************************************************************
7992 * OleSaveToStream (OLE32.@)
7994 * This function saves an object with the IPersistStream interface on it
7995 * to the specified stream.
7997 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
8003 TRACE("(%p,%p)\n",pPStm,pStm);
8005 res=IPersistStream_GetClassID(pPStm,&clsid);
8007 if (SUCCEEDED(res)){
8009 res=WriteClassStm(pStm,&clsid);
8013 res=IPersistStream_Save(pPStm,pStm,TRUE);
8016 TRACE("Finished Save\n");
8020 /****************************************************************************
8021 * This method validate a STGM parameter that can contain the values below
8023 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8024 * The stgm values contained in 0xffff0000 are bitmasks.
8026 * STGM_DIRECT 0x00000000
8027 * STGM_TRANSACTED 0x00010000
8028 * STGM_SIMPLE 0x08000000
8030 * STGM_READ 0x00000000
8031 * STGM_WRITE 0x00000001
8032 * STGM_READWRITE 0x00000002
8034 * STGM_SHARE_DENY_NONE 0x00000040
8035 * STGM_SHARE_DENY_READ 0x00000030
8036 * STGM_SHARE_DENY_WRITE 0x00000020
8037 * STGM_SHARE_EXCLUSIVE 0x00000010
8039 * STGM_PRIORITY 0x00040000
8040 * STGM_DELETEONRELEASE 0x04000000
8042 * STGM_CREATE 0x00001000
8043 * STGM_CONVERT 0x00020000
8044 * STGM_FAILIFTHERE 0x00000000
8046 * STGM_NOSCRATCH 0x00100000
8047 * STGM_NOSNAPSHOT 0x00200000
8049 static HRESULT validateSTGM(DWORD stgm)
8051 DWORD access = STGM_ACCESS_MODE(stgm);
8052 DWORD share = STGM_SHARE_MODE(stgm);
8053 DWORD create = STGM_CREATE_MODE(stgm);
8055 if (stgm&~STGM_KNOWN_FLAGS)
8057 ERR("unknown flags %08x\n", stgm);
8065 case STGM_READWRITE:
8073 case STGM_SHARE_DENY_NONE:
8074 case STGM_SHARE_DENY_READ:
8075 case STGM_SHARE_DENY_WRITE:
8076 case STGM_SHARE_EXCLUSIVE:
8085 case STGM_FAILIFTHERE:
8092 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8094 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8098 * STGM_CREATE | STGM_CONVERT
8099 * if both are false, STGM_FAILIFTHERE is set to TRUE
8101 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8105 * STGM_NOSCRATCH requires STGM_TRANSACTED
8107 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8111 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8112 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8114 if ( (stgm & STGM_NOSNAPSHOT) &&
8115 (!(stgm & STGM_TRANSACTED) ||
8116 share == STGM_SHARE_EXCLUSIVE ||
8117 share == STGM_SHARE_DENY_WRITE) )
8123 /****************************************************************************
8124 * GetShareModeFromSTGM
8126 * This method will return a share mode flag from a STGM value.
8127 * The STGM value is assumed valid.
8129 static DWORD GetShareModeFromSTGM(DWORD stgm)
8131 switch (STGM_SHARE_MODE(stgm))
8133 case STGM_SHARE_DENY_NONE:
8134 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8135 case STGM_SHARE_DENY_READ:
8136 return FILE_SHARE_WRITE;
8137 case STGM_SHARE_DENY_WRITE:
8138 return FILE_SHARE_READ;
8139 case STGM_SHARE_EXCLUSIVE:
8142 ERR("Invalid share mode!\n");
8147 /****************************************************************************
8148 * GetAccessModeFromSTGM
8150 * This method will return an access mode flag from a STGM value.
8151 * The STGM value is assumed valid.
8153 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8155 switch (STGM_ACCESS_MODE(stgm))
8158 return GENERIC_READ;
8160 case STGM_READWRITE:
8161 return GENERIC_READ | GENERIC_WRITE;
8163 ERR("Invalid access mode!\n");
8168 /****************************************************************************
8169 * GetCreationModeFromSTGM
8171 * This method will return a creation mode flag from a STGM value.
8172 * The STGM value is assumed valid.
8174 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8176 switch(STGM_CREATE_MODE(stgm))
8179 return CREATE_ALWAYS;
8181 FIXME("STGM_CONVERT not implemented!\n");
8183 case STGM_FAILIFTHERE:
8186 ERR("Invalid create mode!\n");
8192 /*************************************************************************
8193 * OLECONVERT_LoadOLE10 [Internal]
8195 * Loads the OLE10 STREAM to memory
8198 * pOleStream [I] The OLESTREAM
8199 * pData [I] Data Structure for the OLESTREAM Data
8203 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8204 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8207 * This function is used by OleConvertOLESTREAMToIStorage only.
8209 * Memory allocated for pData must be freed by the caller
8211 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8214 HRESULT hRes = S_OK;
8218 pData->pData = NULL;
8219 pData->pstrOleObjFileName = NULL;
8221 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8224 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8225 if(dwSize != sizeof(pData->dwOleID))
8227 hRes = CONVERT10_E_OLESTREAM_GET;
8229 else if(pData->dwOleID != OLESTREAM_ID)
8231 hRes = CONVERT10_E_OLESTREAM_FMT;
8242 /* Get the TypeID... more info needed for this field */
8243 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8244 if(dwSize != sizeof(pData->dwTypeID))
8246 hRes = CONVERT10_E_OLESTREAM_GET;
8251 if(pData->dwTypeID != 0)
8253 /* Get the length of the OleTypeName */
8254 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8255 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8257 hRes = CONVERT10_E_OLESTREAM_GET;
8262 if(pData->dwOleTypeNameLength > 0)
8264 /* Get the OleTypeName */
8265 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8266 if(dwSize != pData->dwOleTypeNameLength)
8268 hRes = CONVERT10_E_OLESTREAM_GET;
8274 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8275 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8277 hRes = CONVERT10_E_OLESTREAM_GET;
8281 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8282 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8283 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8284 if(pData->pstrOleObjFileName)
8286 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8287 if(dwSize != pData->dwOleObjFileNameLength)
8289 hRes = CONVERT10_E_OLESTREAM_GET;
8293 hRes = CONVERT10_E_OLESTREAM_GET;
8298 /* Get the Width of the Metafile */
8299 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8300 if(dwSize != sizeof(pData->dwMetaFileWidth))
8302 hRes = CONVERT10_E_OLESTREAM_GET;
8306 /* Get the Height of the Metafile */
8307 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8308 if(dwSize != sizeof(pData->dwMetaFileHeight))
8310 hRes = CONVERT10_E_OLESTREAM_GET;
8316 /* Get the Length of the Data */
8317 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8318 if(dwSize != sizeof(pData->dwDataLength))
8320 hRes = CONVERT10_E_OLESTREAM_GET;
8324 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8326 if(!bStrem1) /* if it is a second OLE stream data */
8328 pData->dwDataLength -= 8;
8329 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8330 if(dwSize != sizeof(pData->strUnknown))
8332 hRes = CONVERT10_E_OLESTREAM_GET;
8338 if(pData->dwDataLength > 0)
8340 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8342 /* Get Data (ex. IStorage, Metafile, or BMP) */
8345 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8346 if(dwSize != pData->dwDataLength)
8348 hRes = CONVERT10_E_OLESTREAM_GET;
8353 hRes = CONVERT10_E_OLESTREAM_GET;
8362 /*************************************************************************
8363 * OLECONVERT_SaveOLE10 [Internal]
8365 * Saves the OLE10 STREAM From memory
8368 * pData [I] Data Structure for the OLESTREAM Data
8369 * pOleStream [I] The OLESTREAM to save
8373 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8376 * This function is used by OleConvertIStorageToOLESTREAM only.
8379 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8382 HRESULT hRes = S_OK;
8386 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8387 if(dwSize != sizeof(pData->dwOleID))
8389 hRes = CONVERT10_E_OLESTREAM_PUT;
8394 /* Set the TypeID */
8395 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8396 if(dwSize != sizeof(pData->dwTypeID))
8398 hRes = CONVERT10_E_OLESTREAM_PUT;
8402 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8404 /* Set the Length of the OleTypeName */
8405 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8406 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8408 hRes = CONVERT10_E_OLESTREAM_PUT;
8413 if(pData->dwOleTypeNameLength > 0)
8415 /* Set the OleTypeName */
8416 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8417 if(dwSize != pData->dwOleTypeNameLength)
8419 hRes = CONVERT10_E_OLESTREAM_PUT;
8426 /* Set the width of the Metafile */
8427 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8428 if(dwSize != sizeof(pData->dwMetaFileWidth))
8430 hRes = CONVERT10_E_OLESTREAM_PUT;
8436 /* Set the height of the Metafile */
8437 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8438 if(dwSize != sizeof(pData->dwMetaFileHeight))
8440 hRes = CONVERT10_E_OLESTREAM_PUT;
8446 /* Set the length of the Data */
8447 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8448 if(dwSize != sizeof(pData->dwDataLength))
8450 hRes = CONVERT10_E_OLESTREAM_PUT;
8456 if(pData->dwDataLength > 0)
8458 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8459 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8460 if(dwSize != pData->dwDataLength)
8462 hRes = CONVERT10_E_OLESTREAM_PUT;
8470 /*************************************************************************
8471 * OLECONVERT_GetOLE20FromOLE10[Internal]
8473 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8474 * opens it, and copies the content to the dest IStorage for
8475 * OleConvertOLESTREAMToIStorage
8479 * pDestStorage [I] The IStorage to copy the data to
8480 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8481 * nBufferLength [I] The size of the buffer
8490 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8494 IStorage *pTempStorage;
8495 DWORD dwNumOfBytesWritten;
8496 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8497 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8499 /* Create a temp File */
8500 GetTempPathW(MAX_PATH, wstrTempDir);
8501 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8502 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8504 if(hFile != INVALID_HANDLE_VALUE)
8506 /* Write IStorage Data to File */
8507 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8510 /* Open and copy temp storage to the Dest Storage */
8511 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8514 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8515 IStorage_Release(pTempStorage);
8517 DeleteFileW(wstrTempFile);
8522 /*************************************************************************
8523 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8525 * Saves the OLE10 STREAM From memory
8528 * pStorage [I] The Src IStorage to copy
8529 * pData [I] The Dest Memory to write to.
8532 * The size in bytes allocated for pData
8535 * Memory allocated for pData must be freed by the caller
8537 * Used by OleConvertIStorageToOLESTREAM only.
8540 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8544 DWORD nDataLength = 0;
8545 IStorage *pTempStorage;
8546 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8547 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8551 /* Create temp Storage */
8552 GetTempPathW(MAX_PATH, wstrTempDir);
8553 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8554 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8558 /* Copy Src Storage to the Temp Storage */
8559 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8560 IStorage_Release(pTempStorage);
8562 /* Open Temp Storage as a file and copy to memory */
8563 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8564 if(hFile != INVALID_HANDLE_VALUE)
8566 nDataLength = GetFileSize(hFile, NULL);
8567 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8568 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8571 DeleteFileW(wstrTempFile);
8576 /*************************************************************************
8577 * STORAGE_CreateOleStream [Internal]
8579 * Creates the "\001OLE" stream in the IStorage if necessary.
8582 * storage [I] Dest storage to create the stream in
8583 * flags [I] flags to be set for newly created stream
8586 * HRESULT return value
8590 * This stream is still unknown, MS Word seems to have extra data
8591 * but since the data is stored in the OLESTREAM there should be
8592 * no need to recreate the stream. If the stream is manually
8593 * deleted it will create it with this default data.
8596 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
8598 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
8599 static const DWORD version_magic = 0x02000001;
8603 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
8606 struct empty_1ole_stream {
8607 DWORD version_magic;
8609 DWORD update_options;
8611 DWORD mon_stream_size;
8613 struct empty_1ole_stream stream_data;
8615 stream_data.version_magic = version_magic;
8616 stream_data.flags = flags;
8617 stream_data.update_options = 0;
8618 stream_data.reserved = 0;
8619 stream_data.mon_stream_size = 0;
8621 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
8622 IStream_Release(stream);
8628 /* write a string to a stream, preceded by its length */
8629 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8636 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8637 r = IStream_Write( stm, &len, sizeof(len), NULL);
8642 str = CoTaskMemAlloc( len );
8643 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8644 r = IStream_Write( stm, str, len, NULL);
8645 CoTaskMemFree( str );
8649 /* read a string preceded by its length from a stream */
8650 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8653 DWORD len, count = 0;
8657 r = IStream_Read( stm, &len, sizeof(len), &count );
8660 if( count != sizeof(len) )
8661 return E_OUTOFMEMORY;
8663 TRACE("%d bytes\n",len);
8665 str = CoTaskMemAlloc( len );
8667 return E_OUTOFMEMORY;
8669 r = IStream_Read( stm, str, len, &count );
8674 CoTaskMemFree( str );
8675 return E_OUTOFMEMORY;
8678 TRACE("Read string %s\n",debugstr_an(str,len));
8680 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8681 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8683 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8684 CoTaskMemFree( str );
8692 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8693 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8697 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8699 static const BYTE unknown1[12] =
8700 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8701 0xFF, 0xFF, 0xFF, 0xFF};
8702 static const BYTE unknown2[16] =
8703 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8704 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8706 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8707 debugstr_w(lpszUserType), debugstr_w(szClipName),
8708 debugstr_w(szProgIDName));
8710 /* Create a CompObj stream */
8711 r = IStorage_CreateStream(pstg, szwStreamName,
8712 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8716 /* Write CompObj Structure to stream */
8717 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8719 if( SUCCEEDED( r ) )
8720 r = WriteClassStm( pstm, clsid );
8722 if( SUCCEEDED( r ) )
8723 r = STREAM_WriteString( pstm, lpszUserType );
8724 if( SUCCEEDED( r ) )
8725 r = STREAM_WriteString( pstm, szClipName );
8726 if( SUCCEEDED( r ) )
8727 r = STREAM_WriteString( pstm, szProgIDName );
8728 if( SUCCEEDED( r ) )
8729 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8731 IStream_Release( pstm );
8736 /***********************************************************************
8737 * WriteFmtUserTypeStg (OLE32.@)
8739 HRESULT WINAPI WriteFmtUserTypeStg(
8740 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8743 WCHAR szwClipName[0x40];
8744 CLSID clsid = CLSID_NULL;
8745 LPWSTR wstrProgID = NULL;
8748 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8750 /* get the clipboard format name */
8751 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8754 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8756 /* FIXME: There's room to save a CLSID and its ProgID, but
8757 the CLSID is not looked up in the registry and in all the
8758 tests I wrote it was CLSID_NULL. Where does it come from?
8761 /* get the real program ID. This may fail, but that's fine */
8762 ProgIDFromCLSID(&clsid, &wstrProgID);
8764 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8766 r = STORAGE_WriteCompObj( pstg, &clsid,
8767 lpszUserType, szwClipName, wstrProgID );
8769 CoTaskMemFree(wstrProgID);
8775 /******************************************************************************
8776 * ReadFmtUserTypeStg [OLE32.@]
8778 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8782 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8783 unsigned char unknown1[12];
8784 unsigned char unknown2[16];
8786 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8789 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8791 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8792 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8795 WARN("Failed to open stream r = %08x\n", r);
8799 /* read the various parts of the structure */
8800 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8801 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8803 r = ReadClassStm( stm, &clsid );
8807 r = STREAM_ReadString( stm, &szCLSIDName );
8811 r = STREAM_ReadString( stm, &szOleTypeName );
8815 r = STREAM_ReadString( stm, &szProgIDName );
8819 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8820 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8823 /* ok, success... now we just need to store what we found */
8825 *pcf = RegisterClipboardFormatW( szOleTypeName );
8827 if( lplpszUserType )
8829 *lplpszUserType = szCLSIDName;
8834 CoTaskMemFree( szCLSIDName );
8835 CoTaskMemFree( szOleTypeName );
8836 CoTaskMemFree( szProgIDName );
8837 IStream_Release( stm );
8843 /*************************************************************************
8844 * OLECONVERT_CreateCompObjStream [Internal]
8846 * Creates a "\001CompObj" is the destination IStorage if necessary.
8849 * pStorage [I] The dest IStorage to create the CompObj Stream
8851 * strOleTypeName [I] The ProgID
8855 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8858 * This function is used by OleConvertOLESTREAMToIStorage only.
8860 * The stream data is stored in the OLESTREAM and there should be
8861 * no need to recreate the stream. If the stream is manually
8862 * deleted it will attempt to create it by querying the registry.
8866 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8869 HRESULT hStorageRes, hRes = S_OK;
8870 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8871 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8872 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8874 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8875 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8877 /* Initialize the CompObj structure */
8878 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8879 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8880 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8883 /* Create a CompObj stream if it doesn't exist */
8884 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8885 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8886 if(hStorageRes == S_OK)
8888 /* copy the OleTypeName to the compobj struct */
8889 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8890 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8892 /* copy the OleTypeName to the compobj struct */
8893 /* Note: in the test made, these were Identical */
8894 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8895 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8898 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8899 bufferW, OLESTREAM_MAX_STR_LEN );
8900 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8906 /* Get the CLSID Default Name from the Registry */
8907 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
8908 if(hErr == ERROR_SUCCESS)
8910 char strTemp[OLESTREAM_MAX_STR_LEN];
8911 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8912 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8913 if(hErr == ERROR_SUCCESS)
8915 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8921 /* Write CompObj Structure to stream */
8922 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8924 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8926 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8927 if(IStorageCompObj.dwCLSIDNameLength > 0)
8929 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8931 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8932 if(IStorageCompObj.dwOleTypeNameLength > 0)
8934 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8936 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8937 if(IStorageCompObj.dwProgIDNameLength > 0)
8939 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8941 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8942 IStream_Release(pStream);
8948 /*************************************************************************
8949 * OLECONVERT_CreateOlePresStream[Internal]
8951 * Creates the "\002OlePres000" Stream with the Metafile data
8954 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8955 * dwExtentX [I] Width of the Metafile
8956 * dwExtentY [I] Height of the Metafile
8957 * pData [I] Metafile data
8958 * dwDataLength [I] Size of the Metafile data
8962 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8965 * This function is used by OleConvertOLESTREAMToIStorage only.
8968 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8972 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8973 BYTE pOlePresStreamHeader [] =
8975 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8976 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8977 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8978 0x00, 0x00, 0x00, 0x00
8981 BYTE pOlePresStreamHeaderEmpty [] =
8983 0x00, 0x00, 0x00, 0x00,
8984 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8985 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8986 0x00, 0x00, 0x00, 0x00
8989 /* Create the OlePres000 Stream */
8990 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8991 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8996 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8998 memset(&OlePres, 0, sizeof(OlePres));
8999 /* Do we have any metafile data to save */
9000 if(dwDataLength > 0)
9002 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
9003 nHeaderSize = sizeof(pOlePresStreamHeader);
9007 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
9008 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
9010 /* Set width and height of the metafile */
9011 OlePres.dwExtentX = dwExtentX;
9012 OlePres.dwExtentY = -dwExtentY;
9014 /* Set Data and Length */
9015 if(dwDataLength > sizeof(METAFILEPICT16))
9017 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
9018 OlePres.pData = &(pData[8]);
9020 /* Save OlePres000 Data to Stream */
9021 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
9022 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
9023 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
9024 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
9025 if(OlePres.dwSize > 0)
9027 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
9029 IStream_Release(pStream);
9033 /*************************************************************************
9034 * OLECONVERT_CreateOle10NativeStream [Internal]
9036 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9039 * pStorage [I] Dest storage to create the stream in
9040 * pData [I] Ole10 Native Data (ex. bmp)
9041 * dwDataLength [I] Size of the Ole10 Native Data
9047 * This function is used by OleConvertOLESTREAMToIStorage only.
9049 * Might need to verify the data and return appropriate error message
9052 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
9056 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9058 /* Create the Ole10Native Stream */
9059 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9060 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9064 /* Write info to stream */
9065 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9066 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9067 IStream_Release(pStream);
9072 /*************************************************************************
9073 * OLECONVERT_GetOLE10ProgID [Internal]
9075 * Finds the ProgID (or OleTypeID) from the IStorage
9078 * pStorage [I] The Src IStorage to get the ProgID
9079 * strProgID [I] the ProgID string to get
9080 * dwSize [I] the size of the string
9084 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9087 * This function is used by OleConvertIStorageToOLESTREAM only.
9091 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9095 LARGE_INTEGER iSeekPos;
9096 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9097 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9099 /* Open the CompObj Stream */
9100 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9101 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9105 /*Get the OleType from the CompObj Stream */
9106 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9107 iSeekPos.u.HighPart = 0;
9109 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9110 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9111 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9112 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9113 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9114 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9115 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9117 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9120 IStream_Read(pStream, strProgID, *dwSize, NULL);
9122 IStream_Release(pStream);
9127 LPOLESTR wstrProgID;
9129 /* Get the OleType from the registry */
9130 REFCLSID clsid = &(stat.clsid);
9131 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9132 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9135 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9136 CoTaskMemFree(wstrProgID);
9143 /*************************************************************************
9144 * OLECONVERT_GetOle10PresData [Internal]
9146 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9149 * pStorage [I] Src IStroage
9150 * pOleStream [I] Dest OleStream Mem Struct
9156 * This function is used by OleConvertIStorageToOLESTREAM only.
9158 * Memory allocated for pData must be freed by the caller
9162 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9167 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9169 /* Initialize Default data for OLESTREAM */
9170 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9171 pOleStreamData[0].dwTypeID = 2;
9172 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9173 pOleStreamData[1].dwTypeID = 0;
9174 pOleStreamData[0].dwMetaFileWidth = 0;
9175 pOleStreamData[0].dwMetaFileHeight = 0;
9176 pOleStreamData[0].pData = NULL;
9177 pOleStreamData[1].pData = NULL;
9179 /* Open Ole10Native Stream */
9180 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9181 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9185 /* Read Size and Data */
9186 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9187 if(pOleStreamData->dwDataLength > 0)
9189 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9190 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9192 IStream_Release(pStream);
9198 /*************************************************************************
9199 * OLECONVERT_GetOle20PresData[Internal]
9201 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9204 * pStorage [I] Src IStroage
9205 * pOleStreamData [I] Dest OleStream Mem Struct
9211 * This function is used by OleConvertIStorageToOLESTREAM only.
9213 * Memory allocated for pData must be freed by the caller
9215 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9219 OLECONVERT_ISTORAGE_OLEPRES olePress;
9220 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9222 /* Initialize Default data for OLESTREAM */
9223 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9224 pOleStreamData[0].dwTypeID = 2;
9225 pOleStreamData[0].dwMetaFileWidth = 0;
9226 pOleStreamData[0].dwMetaFileHeight = 0;
9227 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9228 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9229 pOleStreamData[1].dwTypeID = 0;
9230 pOleStreamData[1].dwOleTypeNameLength = 0;
9231 pOleStreamData[1].strOleTypeName[0] = 0;
9232 pOleStreamData[1].dwMetaFileWidth = 0;
9233 pOleStreamData[1].dwMetaFileHeight = 0;
9234 pOleStreamData[1].pData = NULL;
9235 pOleStreamData[1].dwDataLength = 0;
9238 /* Open OlePress000 stream */
9239 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9240 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9243 LARGE_INTEGER iSeekPos;
9244 METAFILEPICT16 MetaFilePict;
9245 static const char strMetafilePictName[] = "METAFILEPICT";
9247 /* Set the TypeID for a Metafile */
9248 pOleStreamData[1].dwTypeID = 5;
9250 /* Set the OleTypeName to Metafile */
9251 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9252 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9254 iSeekPos.u.HighPart = 0;
9255 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9257 /* Get Presentation Data */
9258 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9259 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9260 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9261 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9263 /*Set width and Height */
9264 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9265 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9266 if(olePress.dwSize > 0)
9269 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9271 /* Set MetaFilePict struct */
9272 MetaFilePict.mm = 8;
9273 MetaFilePict.xExt = olePress.dwExtentX;
9274 MetaFilePict.yExt = olePress.dwExtentY;
9275 MetaFilePict.hMF = 0;
9277 /* Get Metafile Data */
9278 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9279 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9280 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9282 IStream_Release(pStream);
9286 /*************************************************************************
9287 * OleConvertOLESTREAMToIStorage [OLE32.@]
9292 * DVTARGETDEVICE parameter is not handled
9293 * Still unsure of some mem fields for OLE 10 Stream
9294 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9295 * and "\001OLE" streams
9298 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9299 LPOLESTREAM pOleStream,
9301 const DVTARGETDEVICE* ptd)
9305 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9307 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9309 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9313 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9316 if(pstg == NULL || pOleStream == NULL)
9318 hRes = E_INVALIDARG;
9323 /* Load the OLESTREAM to Memory */
9324 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9329 /* Load the OLESTREAM to Memory (part 2)*/
9330 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9336 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9338 /* Do we have the IStorage Data in the OLESTREAM */
9339 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9341 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9342 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9346 /* It must be an original OLE 1.0 source */
9347 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9352 /* It must be an original OLE 1.0 source */
9353 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9356 /* Create CompObj Stream if necessary */
9357 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9360 /*Create the Ole Stream if necessary */
9361 STORAGE_CreateOleStream(pstg, 0);
9366 /* Free allocated memory */
9367 for(i=0; i < 2; i++)
9369 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9370 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9371 pOleStreamData[i].pstrOleObjFileName = NULL;
9376 /*************************************************************************
9377 * OleConvertIStorageToOLESTREAM [OLE32.@]
9384 * Still unsure of some mem fields for OLE 10 Stream
9385 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9386 * and "\001OLE" streams.
9389 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9391 LPOLESTREAM pOleStream)
9394 HRESULT hRes = S_OK;
9396 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9397 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9399 TRACE("%p %p\n", pstg, pOleStream);
9401 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9403 if(pstg == NULL || pOleStream == NULL)
9405 hRes = E_INVALIDARG;
9409 /* Get the ProgID */
9410 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9411 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9415 /* Was it originally Ole10 */
9416 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9419 IStream_Release(pStream);
9420 /* Get Presentation Data for Ole10Native */
9421 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9425 /* Get Presentation Data (OLE20) */
9426 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9429 /* Save OLESTREAM */
9430 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9433 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9438 /* Free allocated memory */
9439 for(i=0; i < 2; i++)
9441 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9447 enum stream_1ole_flags {
9448 OleStream_LinkedObject = 0x00000001,
9449 OleStream_Convert = 0x00000004
9452 /***********************************************************************
9453 * GetConvertStg (OLE32.@)
9455 HRESULT WINAPI GetConvertStg(IStorage *stg)
9457 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9458 static const DWORD version_magic = 0x02000001;
9465 if (!stg) return E_INVALIDARG;
9467 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
9468 if (FAILED(hr)) return hr;
9470 hr = IStream_Read(stream, header, sizeof(header), NULL);
9471 IStream_Release(stream);
9472 if (FAILED(hr)) return hr;
9474 if (header[0] != version_magic)
9476 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
9480 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
9483 /***********************************************************************
9484 * SetConvertStg (OLE32.@)
9486 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
9488 DWORD flags = convert ? OleStream_Convert : 0;
9491 TRACE("(%p, %d)\n", storage, convert);
9493 hr = STORAGE_CreateOleStream(storage, flags);
9494 if (hr == STG_E_FILEALREADYEXISTS)
9496 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9500 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
9501 if (FAILED(hr)) return hr;
9503 hr = IStream_Read(stream, header, sizeof(header), NULL);
9506 IStream_Release(stream);
9510 /* update flag if differs */
9511 if ((header[1] ^ flags) & OleStream_Convert)
9515 if (header[1] & OleStream_Convert)
9516 flags = header[1] & ~OleStream_Convert;
9518 flags = header[1] | OleStream_Convert;
9520 pos.QuadPart = sizeof(DWORD);
9521 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
9524 IStream_Release(stream);
9528 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
9530 IStream_Release(stream);
9536 /******************************************************************************
9537 * StgIsStorageFile [OLE32.@]
9538 * Verify if the file contains a storage object
9544 * S_OK if file has magic bytes as a storage object
9545 * S_FALSE if file is not storage
9548 StgIsStorageFile(LPCOLESTR fn)
9554 TRACE("%s\n", debugstr_w(fn));
9555 hf = CreateFileW(fn, GENERIC_READ,
9556 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9557 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9559 if (hf == INVALID_HANDLE_VALUE)
9560 return STG_E_FILENOTFOUND;
9562 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9564 WARN(" unable to read file\n");
9571 if (bytes_read != 8) {
9572 TRACE(" too short\n");
9576 if (!memcmp(magic,STORAGE_magic,8)) {
9581 TRACE(" -> Invalid header.\n");
9585 /***********************************************************************
9586 * WriteClassStm (OLE32.@)
9588 * Writes a CLSID to a stream.
9591 * pStm [I] Stream to write to.
9592 * rclsid [I] CLSID to write.
9596 * Failure: HRESULT code.
9598 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9600 TRACE("(%p,%p)\n",pStm,rclsid);
9602 if (!pStm || !rclsid)
9603 return E_INVALIDARG;
9605 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9608 /***********************************************************************
9609 * ReadClassStm (OLE32.@)
9611 * Reads a CLSID from a stream.
9614 * pStm [I] Stream to read from.
9615 * rclsid [O] CLSID to read.
9619 * Failure: HRESULT code.
9621 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9626 TRACE("(%p,%p)\n",pStm,pclsid);
9628 if (!pStm || !pclsid)
9629 return E_INVALIDARG;
9631 /* clear the output args */
9632 *pclsid = CLSID_NULL;
9634 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9639 if (nbByte != sizeof(CLSID))
9640 return STG_E_READFAULT;