2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootEntryName[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base;
82 * Entry in the parent's stream tracking list
84 struct list ParentListEntry;
86 StorageBaseImpl *parentStorage;
88 typedef struct StorageInternalImpl StorageInternalImpl;
90 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
91 static const IStorageVtbl Storage32InternalImpl_Vtbl;
93 /* Method definitions for the Storage32InternalImpl class. */
94 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
95 DWORD openFlags, DirRef storageDirEntry);
96 static void StorageImpl_Destroy(StorageBaseImpl* iface);
97 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
98 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
99 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
100 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
101 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
102 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
103 static void StorageImpl_SaveFileHeader(StorageImpl* This);
105 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
106 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
107 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
108 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
109 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
111 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
112 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
113 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
115 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
116 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
117 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
118 ULONG blockIndex, ULONG offset, DWORD value);
119 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
120 ULONG blockIndex, ULONG offset, DWORD* value);
122 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
123 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
125 typedef struct TransactedDirEntry
127 /* If applicable, a reference to the original DirEntry in the transacted
128 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
129 DirRef transactedParentEntry;
131 /* True if this entry is being used. */
134 /* True if data is up to date. */
137 /* True if this entry has been modified. */
140 /* True if this entry's stream has been modified. */
143 /* True if this entry has been deleted in the transacted storage, but the
144 * delete has not yet been committed. */
147 /* If this entry's stream has been modified, a reference to where the stream
148 * is stored in the snapshot file. */
151 /* This directory entry's data, including any changes that have been made. */
154 /* A reference to the parent of this node. This is only valid while we are
155 * committing changes. */
158 /* A reference to a newly-created entry in the transacted parent. This is
159 * always equal to transactedParentEntry except when committing changes. */
160 DirRef newTransactedParentEntry;
161 } TransactedDirEntry;
163 /****************************************************************************
164 * Transacted storage object.
166 typedef struct TransactedSnapshotImpl
168 struct StorageBaseImpl base;
171 * Modified streams are temporarily saved to the scratch file.
173 StorageBaseImpl *scratch;
175 /* The directory structure is kept here, so that we can track how these
176 * entries relate to those in the parent storage. */
177 TransactedDirEntry *entries;
179 ULONG firstFreeEntry;
182 * Changes are committed to the transacted parent.
184 StorageBaseImpl *transactedParent;
185 } TransactedSnapshotImpl;
187 /* Generic function to create a transacted wrapper for a direct storage object. */
188 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
190 /* OLESTREAM memory structure to use for Get and Put Routines */
191 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
196 DWORD dwOleTypeNameLength;
197 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
198 CHAR *pstrOleObjFileName;
199 DWORD dwOleObjFileNameLength;
200 DWORD dwMetaFileWidth;
201 DWORD dwMetaFileHeight;
202 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
205 }OLECONVERT_OLESTREAM_DATA;
207 /* CompObj Stream structure */
208 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
213 DWORD dwCLSIDNameLength;
214 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
215 DWORD dwOleTypeNameLength;
216 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
217 DWORD dwProgIDNameLength;
218 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
220 }OLECONVERT_ISTORAGE_COMPOBJ;
223 /* Ole Presentation Stream structure */
224 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
232 }OLECONVERT_ISTORAGE_OLEPRES;
236 /***********************************************************************
237 * Forward declaration of internal functions used by the method DestroyElement
239 static HRESULT deleteStorageContents(
240 StorageBaseImpl *parentStorage,
241 DirRef indexToDelete,
242 DirEntry entryDataToDelete);
244 static HRESULT deleteStreamContents(
245 StorageBaseImpl *parentStorage,
246 DirRef indexToDelete,
247 DirEntry entryDataToDelete);
249 static HRESULT removeFromTree(
250 StorageBaseImpl *This,
251 DirRef parentStorageIndex,
252 DirRef deletedIndex);
254 /***********************************************************************
255 * Declaration of the functions used to manipulate DirEntry
258 static HRESULT insertIntoTree(
259 StorageBaseImpl *This,
260 DirRef parentStorageIndex,
261 DirRef newEntryIndex);
263 static LONG entryNameCmp(
264 const OLECHAR *name1,
265 const OLECHAR *name2);
267 static DirRef findElement(
268 StorageBaseImpl *storage,
273 static HRESULT findTreeParent(
274 StorageBaseImpl *storage,
276 const OLECHAR *childName,
277 DirEntry *parentData,
281 /***********************************************************************
282 * Declaration of miscellaneous functions...
284 static HRESULT validateSTGM(DWORD stgmValue);
286 static DWORD GetShareModeFromSTGM(DWORD stgm);
287 static DWORD GetAccessModeFromSTGM(DWORD stgm);
288 static DWORD GetCreationModeFromSTGM(DWORD stgm);
290 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
293 /****************************************************************************
294 * IEnumSTATSTGImpl definitions.
296 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
297 * This class allows iterating through the content of a storage and to find
298 * specific items inside it.
300 struct IEnumSTATSTGImpl
302 IEnumSTATSTG IEnumSTATSTG_iface;
304 LONG ref; /* Reference count */
305 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
306 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
308 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
311 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
313 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
317 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
318 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
320 /************************************************************************
324 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
326 return (index+1) * This->bigBlockSize;
329 /************************************************************************
330 ** Storage32BaseImpl implementation
332 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
333 ULARGE_INTEGER offset,
338 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
341 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
342 ULARGE_INTEGER offset,
347 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
350 /************************************************************************
351 * Storage32BaseImpl_QueryInterface (IUnknown)
353 * This method implements the common QueryInterface for all IStorage32
354 * implementations contained in this file.
356 * See Windows documentation for more details on IUnknown methods.
358 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
363 StorageBaseImpl *This = (StorageBaseImpl *)iface;
365 if ( (This==0) || (ppvObject==0) )
370 if (IsEqualGUID(&IID_IUnknown, riid) ||
371 IsEqualGUID(&IID_IStorage, riid))
375 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
377 *ppvObject = &This->pssVtbl;
381 return E_NOINTERFACE;
383 IStorage_AddRef(iface);
388 /************************************************************************
389 * Storage32BaseImpl_AddRef (IUnknown)
391 * This method implements the common AddRef for all IStorage32
392 * implementations contained in this file.
394 * See Windows documentation for more details on IUnknown methods.
396 static ULONG WINAPI StorageBaseImpl_AddRef(
399 StorageBaseImpl *This = (StorageBaseImpl *)iface;
400 ULONG ref = InterlockedIncrement(&This->ref);
402 TRACE("(%p) AddRef to %d\n", This, ref);
407 /************************************************************************
408 * Storage32BaseImpl_Release (IUnknown)
410 * This method implements the common Release for all IStorage32
411 * implementations contained in this file.
413 * See Windows documentation for more details on IUnknown methods.
415 static ULONG WINAPI StorageBaseImpl_Release(
418 StorageBaseImpl *This = (StorageBaseImpl *)iface;
420 ULONG ref = InterlockedDecrement(&This->ref);
422 TRACE("(%p) ReleaseRef to %d\n", This, ref);
427 * Since we are using a system of base-classes, we want to call the
428 * destructor of the appropriate derived class. To do this, we are
429 * using virtual functions to implement the destructor.
431 StorageBaseImpl_Destroy(This);
437 /************************************************************************
438 * Storage32BaseImpl_OpenStream (IStorage)
440 * This method will open the specified stream object from the current storage.
442 * See Windows documentation for more details on IStorage methods.
444 static HRESULT WINAPI StorageBaseImpl_OpenStream(
446 const OLECHAR* pwcsName, /* [string][in] */
447 void* reserved1, /* [unique][in] */
448 DWORD grfMode, /* [in] */
449 DWORD reserved2, /* [in] */
450 IStream** ppstm) /* [out] */
452 StorageBaseImpl *This = (StorageBaseImpl *)iface;
453 StgStreamImpl* newStream;
454 DirEntry currentEntry;
455 DirRef streamEntryRef;
456 HRESULT res = STG_E_UNKNOWN;
458 TRACE("(%p, %s, %p, %x, %d, %p)\n",
459 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
461 if ( (pwcsName==NULL) || (ppstm==0) )
469 if ( FAILED( validateSTGM(grfMode) ) ||
470 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
472 res = STG_E_INVALIDFLAG;
479 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
481 res = STG_E_INVALIDFUNCTION;
487 res = STG_E_REVERTED;
492 * Check that we're compatible with the parent's storage mode, but
493 * only if we are not in transacted mode
495 if(!(This->openFlags & STGM_TRANSACTED)) {
496 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
498 res = STG_E_INVALIDFLAG;
504 * Search for the element with the given name
506 streamEntryRef = findElement(
508 This->storageDirEntry,
513 * If it was found, construct the stream object and return a pointer to it.
515 if ( (streamEntryRef!=DIRENTRY_NULL) &&
516 (currentEntry.stgType==STGTY_STREAM) )
518 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
520 /* A single stream cannot be opened a second time. */
521 res = STG_E_ACCESSDENIED;
525 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
529 newStream->grfMode = grfMode;
530 *ppstm = (IStream*)newStream;
532 IStream_AddRef(*ppstm);
542 res = STG_E_FILENOTFOUND;
546 TRACE("<-- IStream %p\n", *ppstm);
547 TRACE("<-- %08x\n", res);
551 /************************************************************************
552 * Storage32BaseImpl_OpenStorage (IStorage)
554 * This method will open a new storage object from the current storage.
556 * See Windows documentation for more details on IStorage methods.
558 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
560 const OLECHAR* pwcsName, /* [string][unique][in] */
561 IStorage* pstgPriority, /* [unique][in] */
562 DWORD grfMode, /* [in] */
563 SNB snbExclude, /* [unique][in] */
564 DWORD reserved, /* [in] */
565 IStorage** ppstg) /* [out] */
567 StorageBaseImpl *This = (StorageBaseImpl *)iface;
568 StorageInternalImpl* newStorage;
569 StorageBaseImpl* newTransactedStorage;
570 DirEntry currentEntry;
571 DirRef storageEntryRef;
572 HRESULT res = STG_E_UNKNOWN;
574 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
575 iface, debugstr_w(pwcsName), pstgPriority,
576 grfMode, snbExclude, reserved, ppstg);
578 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
584 if (This->openFlags & STGM_SIMPLE)
586 res = STG_E_INVALIDFUNCTION;
591 if (snbExclude != NULL)
593 res = STG_E_INVALIDPARAMETER;
597 if ( FAILED( validateSTGM(grfMode) ))
599 res = STG_E_INVALIDFLAG;
606 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
607 (grfMode & STGM_DELETEONRELEASE) ||
608 (grfMode & STGM_PRIORITY) )
610 res = STG_E_INVALIDFUNCTION;
615 return STG_E_REVERTED;
618 * Check that we're compatible with the parent's storage mode,
619 * but only if we are not transacted
621 if(!(This->openFlags & STGM_TRANSACTED)) {
622 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
624 res = STG_E_ACCESSDENIED;
631 storageEntryRef = findElement(
633 This->storageDirEntry,
637 if ( (storageEntryRef!=DIRENTRY_NULL) &&
638 (currentEntry.stgType==STGTY_STORAGE) )
640 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
642 /* A single storage cannot be opened a second time. */
643 res = STG_E_ACCESSDENIED;
647 newStorage = StorageInternalImpl_Construct(
654 if (grfMode & STGM_TRANSACTED)
656 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
660 HeapFree(GetProcessHeap(), 0, newStorage);
664 *ppstg = (IStorage*)newTransactedStorage;
668 *ppstg = (IStorage*)newStorage;
671 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
677 res = STG_E_INSUFFICIENTMEMORY;
681 res = STG_E_FILENOTFOUND;
684 TRACE("<-- %08x\n", res);
688 /************************************************************************
689 * Storage32BaseImpl_EnumElements (IStorage)
691 * This method will create an enumerator object that can be used to
692 * retrieve information about all the elements in the storage object.
694 * See Windows documentation for more details on IStorage methods.
696 static HRESULT WINAPI StorageBaseImpl_EnumElements(
698 DWORD reserved1, /* [in] */
699 void* reserved2, /* [size_is][unique][in] */
700 DWORD reserved3, /* [in] */
701 IEnumSTATSTG** ppenum) /* [out] */
703 StorageBaseImpl *This = (StorageBaseImpl *)iface;
704 IEnumSTATSTGImpl* newEnum;
706 TRACE("(%p, %d, %p, %d, %p)\n",
707 iface, reserved1, reserved2, reserved3, ppenum);
709 if ( (This==0) || (ppenum==0))
713 return STG_E_REVERTED;
715 newEnum = IEnumSTATSTGImpl_Construct(
717 This->storageDirEntry);
721 *ppenum = &newEnum->IEnumSTATSTG_iface;
723 IEnumSTATSTG_AddRef(*ppenum);
728 return E_OUTOFMEMORY;
731 /************************************************************************
732 * Storage32BaseImpl_Stat (IStorage)
734 * This method will retrieve information about this storage object.
736 * See Windows documentation for more details on IStorage methods.
738 static HRESULT WINAPI StorageBaseImpl_Stat(
740 STATSTG* pstatstg, /* [out] */
741 DWORD grfStatFlag) /* [in] */
743 StorageBaseImpl *This = (StorageBaseImpl *)iface;
744 DirEntry currentEntry;
745 HRESULT res = STG_E_UNKNOWN;
747 TRACE("(%p, %p, %x)\n",
748 iface, pstatstg, grfStatFlag);
750 if ( (This==0) || (pstatstg==0))
758 res = STG_E_REVERTED;
762 res = StorageBaseImpl_ReadDirEntry(
764 This->storageDirEntry,
769 StorageUtl_CopyDirEntryToSTATSTG(
775 pstatstg->grfMode = This->openFlags;
776 pstatstg->grfStateBits = This->stateBits;
782 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);
784 TRACE("<-- %08x\n", res);
788 /************************************************************************
789 * Storage32BaseImpl_RenameElement (IStorage)
791 * This method will rename the specified element.
793 * See Windows documentation for more details on IStorage methods.
795 static HRESULT WINAPI StorageBaseImpl_RenameElement(
797 const OLECHAR* pwcsOldName, /* [in] */
798 const OLECHAR* pwcsNewName) /* [in] */
800 StorageBaseImpl *This = (StorageBaseImpl *)iface;
801 DirEntry currentEntry;
802 DirRef currentEntryRef;
804 TRACE("(%p, %s, %s)\n",
805 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
808 return STG_E_REVERTED;
810 currentEntryRef = findElement(This,
811 This->storageDirEntry,
815 if (currentEntryRef != DIRENTRY_NULL)
818 * There is already an element with the new name
820 return STG_E_FILEALREADYEXISTS;
824 * Search for the old element name
826 currentEntryRef = findElement(This,
827 This->storageDirEntry,
831 if (currentEntryRef != DIRENTRY_NULL)
833 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
834 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
836 WARN("Element is already open; cannot rename.\n");
837 return STG_E_ACCESSDENIED;
840 /* Remove the element from its current position in the tree */
841 removeFromTree(This, This->storageDirEntry,
844 /* Change the name of the element */
845 strcpyW(currentEntry.name, pwcsNewName);
847 /* Delete any sibling links */
848 currentEntry.leftChild = DIRENTRY_NULL;
849 currentEntry.rightChild = DIRENTRY_NULL;
851 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
854 /* Insert the element in a new position in the tree */
855 insertIntoTree(This, This->storageDirEntry,
861 * There is no element with the old name
863 return STG_E_FILENOTFOUND;
866 return StorageBaseImpl_Flush(This);
869 /************************************************************************
870 * Storage32BaseImpl_CreateStream (IStorage)
872 * This method will create a stream object within this storage
874 * See Windows documentation for more details on IStorage methods.
876 static HRESULT WINAPI StorageBaseImpl_CreateStream(
878 const OLECHAR* pwcsName, /* [string][in] */
879 DWORD grfMode, /* [in] */
880 DWORD reserved1, /* [in] */
881 DWORD reserved2, /* [in] */
882 IStream** ppstm) /* [out] */
884 StorageBaseImpl *This = (StorageBaseImpl *)iface;
885 StgStreamImpl* newStream;
886 DirEntry currentEntry, newStreamEntry;
887 DirRef currentEntryRef, newStreamEntryRef;
890 TRACE("(%p, %s, %x, %d, %d, %p)\n",
891 iface, debugstr_w(pwcsName), grfMode,
892 reserved1, reserved2, ppstm);
895 return STG_E_INVALIDPOINTER;
898 return STG_E_INVALIDNAME;
900 if (reserved1 || reserved2)
901 return STG_E_INVALIDPARAMETER;
903 if ( FAILED( validateSTGM(grfMode) ))
904 return STG_E_INVALIDFLAG;
906 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
907 return STG_E_INVALIDFLAG;
910 return STG_E_REVERTED;
915 if ((grfMode & STGM_DELETEONRELEASE) ||
916 (grfMode & STGM_TRANSACTED))
917 return STG_E_INVALIDFUNCTION;
920 * Don't worry about permissions in transacted mode, as we can always write
921 * changes; we just can't always commit them.
923 if(!(This->openFlags & STGM_TRANSACTED)) {
924 /* Can't create a stream on read-only storage */
925 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
926 return STG_E_ACCESSDENIED;
928 /* Can't create a stream with greater access than the parent. */
929 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
930 return STG_E_ACCESSDENIED;
933 if(This->openFlags & STGM_SIMPLE)
934 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
938 currentEntryRef = findElement(This,
939 This->storageDirEntry,
943 if (currentEntryRef != DIRENTRY_NULL)
946 * An element with this name already exists
948 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
950 IStorage_DestroyElement(iface, pwcsName);
953 return STG_E_FILEALREADYEXISTS;
957 * memset the empty entry
959 memset(&newStreamEntry, 0, sizeof(DirEntry));
961 newStreamEntry.sizeOfNameString =
962 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
964 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
965 return STG_E_INVALIDNAME;
967 strcpyW(newStreamEntry.name, pwcsName);
969 newStreamEntry.stgType = STGTY_STREAM;
970 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
971 newStreamEntry.size.u.LowPart = 0;
972 newStreamEntry.size.u.HighPart = 0;
974 newStreamEntry.leftChild = DIRENTRY_NULL;
975 newStreamEntry.rightChild = DIRENTRY_NULL;
976 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
978 /* call CoFileTime to get the current time
983 /* newStreamEntry.clsid */
986 * Create an entry with the new data
988 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
993 * Insert the new entry in the parent storage's tree.
997 This->storageDirEntry,
1001 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
1006 * Open the stream to return it.
1008 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1012 *ppstm = (IStream*)newStream;
1014 IStream_AddRef(*ppstm);
1018 return STG_E_INSUFFICIENTMEMORY;
1021 return StorageBaseImpl_Flush(This);
1024 /************************************************************************
1025 * Storage32BaseImpl_SetClass (IStorage)
1027 * This method will write the specified CLSID in the directory entry of this
1030 * See Windows documentation for more details on IStorage methods.
1032 static HRESULT WINAPI StorageBaseImpl_SetClass(
1034 REFCLSID clsid) /* [in] */
1036 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1038 DirEntry currentEntry;
1040 TRACE("(%p, %p)\n", iface, clsid);
1043 return STG_E_REVERTED;
1045 hRes = StorageBaseImpl_ReadDirEntry(This,
1046 This->storageDirEntry,
1048 if (SUCCEEDED(hRes))
1050 currentEntry.clsid = *clsid;
1052 hRes = StorageBaseImpl_WriteDirEntry(This,
1053 This->storageDirEntry,
1057 if (SUCCEEDED(hRes))
1058 hRes = StorageBaseImpl_Flush(This);
1063 /************************************************************************
1064 ** Storage32Impl implementation
1067 /************************************************************************
1068 * Storage32BaseImpl_CreateStorage (IStorage)
1070 * This method will create the storage object within the provided storage.
1072 * See Windows documentation for more details on IStorage methods.
1074 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1076 const OLECHAR *pwcsName, /* [string][in] */
1077 DWORD grfMode, /* [in] */
1078 DWORD reserved1, /* [in] */
1079 DWORD reserved2, /* [in] */
1080 IStorage **ppstg) /* [out] */
1082 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1084 DirEntry currentEntry;
1086 DirRef currentEntryRef;
1090 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1091 iface, debugstr_w(pwcsName), grfMode,
1092 reserved1, reserved2, ppstg);
1095 return STG_E_INVALIDPOINTER;
1097 if (This->openFlags & STGM_SIMPLE)
1099 return STG_E_INVALIDFUNCTION;
1103 return STG_E_INVALIDNAME;
1107 if ( FAILED( validateSTGM(grfMode) ) ||
1108 (grfMode & STGM_DELETEONRELEASE) )
1110 WARN("bad grfMode: 0x%x\n", grfMode);
1111 return STG_E_INVALIDFLAG;
1115 return STG_E_REVERTED;
1118 * Check that we're compatible with the parent's storage mode
1120 if ( !(This->openFlags & STGM_TRANSACTED) &&
1121 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1123 WARN("access denied\n");
1124 return STG_E_ACCESSDENIED;
1127 currentEntryRef = findElement(This,
1128 This->storageDirEntry,
1132 if (currentEntryRef != DIRENTRY_NULL)
1135 * An element with this name already exists
1137 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1138 ((This->openFlags & STGM_TRANSACTED) ||
1139 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1141 hr = IStorage_DestroyElement(iface, pwcsName);
1147 WARN("file already exists\n");
1148 return STG_E_FILEALREADYEXISTS;
1151 else if (!(This->openFlags & STGM_TRANSACTED) &&
1152 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1154 WARN("read-only storage\n");
1155 return STG_E_ACCESSDENIED;
1158 memset(&newEntry, 0, sizeof(DirEntry));
1160 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1162 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1164 FIXME("name too long\n");
1165 return STG_E_INVALIDNAME;
1168 strcpyW(newEntry.name, pwcsName);
1170 newEntry.stgType = STGTY_STORAGE;
1171 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1172 newEntry.size.u.LowPart = 0;
1173 newEntry.size.u.HighPart = 0;
1175 newEntry.leftChild = DIRENTRY_NULL;
1176 newEntry.rightChild = DIRENTRY_NULL;
1177 newEntry.dirRootEntry = DIRENTRY_NULL;
1179 /* call CoFileTime to get the current time
1184 /* newEntry.clsid */
1187 * Create a new directory entry for the storage
1189 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1194 * Insert the new directory entry into the parent storage's tree
1196 hr = insertIntoTree(
1198 This->storageDirEntry,
1202 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1207 * Open it to get a pointer to return.
1209 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1211 if( (hr != S_OK) || (*ppstg == NULL))
1217 hr = StorageBaseImpl_Flush(This);
1223 /***************************************************************************
1227 * Reserve a directory entry in the file and initialize it.
1229 static HRESULT StorageImpl_CreateDirEntry(
1230 StorageBaseImpl *base,
1231 const DirEntry *newData,
1234 StorageImpl *storage = (StorageImpl*)base;
1235 ULONG currentEntryIndex = 0;
1236 ULONG newEntryIndex = DIRENTRY_NULL;
1238 BYTE currentData[RAW_DIRENTRY_SIZE];
1239 WORD sizeOfNameString;
1243 hr = StorageImpl_ReadRawDirEntry(storage,
1249 StorageUtl_ReadWord(
1251 OFFSET_PS_NAMELENGTH,
1254 if (sizeOfNameString == 0)
1257 * The entry exists and is available, we found it.
1259 newEntryIndex = currentEntryIndex;
1265 * We exhausted the directory entries, we will create more space below
1267 newEntryIndex = currentEntryIndex;
1269 currentEntryIndex++;
1271 } while (newEntryIndex == DIRENTRY_NULL);
1274 * grow the directory stream
1278 BYTE emptyData[RAW_DIRENTRY_SIZE];
1279 ULARGE_INTEGER newSize;
1281 ULONG lastEntry = 0;
1282 ULONG blockCount = 0;
1285 * obtain the new count of blocks in the directory stream
1287 blockCount = BlockChainStream_GetCount(
1288 storage->rootBlockChain)+1;
1291 * initialize the size used by the directory stream
1293 newSize.u.HighPart = 0;
1294 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1297 * add a block to the directory stream
1299 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1302 * memset the empty entry in order to initialize the unused newly
1305 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1310 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1313 entryIndex = newEntryIndex + 1;
1314 entryIndex < lastEntry;
1317 StorageImpl_WriteRawDirEntry(
1323 StorageImpl_SaveFileHeader(storage);
1326 UpdateRawDirEntry(currentData, newData);
1328 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1331 *index = newEntryIndex;
1336 /***************************************************************************
1340 * Mark a directory entry in the file as free.
1342 static HRESULT StorageImpl_DestroyDirEntry(
1343 StorageBaseImpl *base,
1347 BYTE emptyData[RAW_DIRENTRY_SIZE];
1348 StorageImpl *storage = (StorageImpl*)base;
1350 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1352 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1358 /****************************************************************************
1362 * Case insensitive comparison of DirEntry.name by first considering
1365 * Returns <0 when name1 < name2
1366 * >0 when name1 > name2
1367 * 0 when name1 == name2
1369 static LONG entryNameCmp(
1370 const OLECHAR *name1,
1371 const OLECHAR *name2)
1373 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1375 while (diff == 0 && *name1 != 0)
1378 * We compare the string themselves only when they are of the same length
1380 diff = toupperW(*name1++) - toupperW(*name2++);
1386 /****************************************************************************
1390 * Add a directory entry to a storage
1392 static HRESULT insertIntoTree(
1393 StorageBaseImpl *This,
1394 DirRef parentStorageIndex,
1395 DirRef newEntryIndex)
1397 DirEntry currentEntry;
1401 * Read the inserted entry
1403 StorageBaseImpl_ReadDirEntry(This,
1408 * Read the storage entry
1410 StorageBaseImpl_ReadDirEntry(This,
1414 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1417 * The root storage contains some element, therefore, start the research
1418 * for the appropriate location.
1421 DirRef current, next, previous, currentEntryId;
1424 * Keep a reference to the root of the storage's element tree
1426 currentEntryId = currentEntry.dirRootEntry;
1431 StorageBaseImpl_ReadDirEntry(This,
1432 currentEntry.dirRootEntry,
1435 previous = currentEntry.leftChild;
1436 next = currentEntry.rightChild;
1437 current = currentEntryId;
1441 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1445 if (previous != DIRENTRY_NULL)
1447 StorageBaseImpl_ReadDirEntry(This,
1454 currentEntry.leftChild = newEntryIndex;
1455 StorageBaseImpl_WriteDirEntry(This,
1463 if (next != DIRENTRY_NULL)
1465 StorageBaseImpl_ReadDirEntry(This,
1472 currentEntry.rightChild = newEntryIndex;
1473 StorageBaseImpl_WriteDirEntry(This,
1482 * Trying to insert an item with the same name in the
1483 * subtree structure.
1485 return STG_E_FILEALREADYEXISTS;
1488 previous = currentEntry.leftChild;
1489 next = currentEntry.rightChild;
1495 * The storage is empty, make the new entry the root of its element tree
1497 currentEntry.dirRootEntry = newEntryIndex;
1498 StorageBaseImpl_WriteDirEntry(This,
1506 /****************************************************************************
1510 * Find and read the element of a storage with the given name.
1512 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1513 const OLECHAR *name, DirEntry *data)
1515 DirRef currentEntry;
1517 /* Read the storage entry to find the root of the tree. */
1518 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1520 currentEntry = data->dirRootEntry;
1522 while (currentEntry != DIRENTRY_NULL)
1526 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1528 cmp = entryNameCmp(name, data->name);
1535 currentEntry = data->leftChild;
1538 currentEntry = data->rightChild;
1541 return currentEntry;
1544 /****************************************************************************
1548 * Find and read the binary tree parent of the element with the given name.
1550 * If there is no such element, find a place where it could be inserted and
1551 * return STG_E_FILENOTFOUND.
1553 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1554 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1560 /* Read the storage entry to find the root of the tree. */
1561 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1563 *parentEntry = storageEntry;
1564 *relation = DIRENTRY_RELATION_DIR;
1566 childEntry = parentData->dirRootEntry;
1568 while (childEntry != DIRENTRY_NULL)
1572 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1574 cmp = entryNameCmp(childName, childData.name);
1582 *parentData = childData;
1583 *parentEntry = childEntry;
1584 *relation = DIRENTRY_RELATION_PREVIOUS;
1586 childEntry = parentData->leftChild;
1591 *parentData = childData;
1592 *parentEntry = childEntry;
1593 *relation = DIRENTRY_RELATION_NEXT;
1595 childEntry = parentData->rightChild;
1599 if (childEntry == DIRENTRY_NULL)
1600 return STG_E_FILENOTFOUND;
1606 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1607 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1608 SNB snbExclude, IStorage *pstgDest);
1610 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
1611 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1612 SNB snbExclude, IStorage *pstgDest)
1618 IStream *pstrChild, *pstrTmp;
1621 if (srcEntry == DIRENTRY_NULL)
1624 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1631 WCHAR **snb = snbExclude;
1633 while ( *snb != NULL && !skip )
1635 if ( lstrcmpW(data.name, *snb) == 0 )
1643 if (data.stgType == STGTY_STORAGE && !skip_storage)
1646 * create a new storage in destination storage
1648 hr = IStorage_CreateStorage( pstgDest, data.name,
1649 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1654 * if it already exist, don't create a new one use this one
1656 if (hr == STG_E_FILEALREADYEXISTS)
1658 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1659 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1660 NULL, 0, &pstgTmp );
1665 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1666 skip_stream, NULL, pstgTmp );
1668 IStorage_Release(pstgTmp);
1671 else if (data.stgType == STGTY_STREAM && !skip_stream)
1674 * create a new stream in destination storage. If the stream already
1675 * exist, it will be deleted and a new one will be created.
1677 hr = IStorage_CreateStream( pstgDest, data.name,
1678 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1682 * open child stream storage. This operation must succeed even if the
1683 * stream is already open, so we use internal functions to do it.
1687 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1689 IStream_AddRef(pstrChild);
1697 * Get the size of the source stream
1699 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1702 * Set the size of the destination stream.
1704 IStream_SetSize(pstrTmp, strStat.cbSize);
1709 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1712 IStream_Release( pstrChild );
1715 IStream_Release( pstrTmp );
1721 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1722 skip_stream, snbExclude, pstgDest );
1725 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1726 skip_stream, snbExclude, pstgDest );
1731 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1732 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1733 SNB snbExclude, IStorage *pstgDest)
1738 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1741 hr = IStorage_SetClass( pstgDest, &data.clsid );
1744 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
1745 skip_stream, snbExclude, pstgDest );
1750 /*************************************************************************
1753 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1755 DWORD ciidExclude, /* [in] */
1756 const IID* rgiidExclude, /* [size_is][unique][in] */
1757 SNB snbExclude, /* [unique][in] */
1758 IStorage* pstgDest) /* [unique][in] */
1760 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1762 BOOL skip_storage = FALSE, skip_stream = FALSE;
1765 TRACE("(%p, %d, %p, %p, %p)\n",
1766 iface, ciidExclude, rgiidExclude,
1767 snbExclude, pstgDest);
1769 if ( pstgDest == 0 )
1770 return STG_E_INVALIDPOINTER;
1772 for(i = 0; i < ciidExclude; ++i)
1774 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1775 skip_storage = TRUE;
1776 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1779 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1784 /* Give up early if it looks like this would be infinitely recursive.
1785 * Oddly enough, this includes some cases that aren't really recursive, like
1786 * copying to a transacted child. */
1787 IStorage *pstgDestAncestor = pstgDest;
1788 IStorage *pstgDestAncestorChild = NULL;
1790 /* Go up the chain from the destination until we find the source storage. */
1791 while (pstgDestAncestor != iface) {
1792 pstgDestAncestorChild = pstgDest;
1794 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
1796 TransactedSnapshotImpl *impl = (TransactedSnapshotImpl*) pstgDestAncestor;
1798 pstgDestAncestor = (IStorage*)impl->transactedParent;
1800 else if (pstgDestAncestor->lpVtbl == &Storage32InternalImpl_Vtbl)
1802 StorageInternalImpl *impl = (StorageInternalImpl*) pstgDestAncestor;
1804 pstgDestAncestor = (IStorage*)impl->parentStorage;
1810 if (pstgDestAncestor == iface)
1814 if (pstgDestAncestorChild && snbExclude)
1816 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
1818 WCHAR **snb = snbExclude;
1820 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
1822 while ( *snb != NULL && fail )
1824 if ( lstrcmpW(data.name, *snb) == 0 )
1831 return STG_E_ACCESSDENIED;
1835 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
1836 skip_storage, skip_stream, snbExclude, pstgDest );
1839 /*************************************************************************
1840 * MoveElementTo (IStorage)
1842 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1844 const OLECHAR *pwcsName, /* [string][in] */
1845 IStorage *pstgDest, /* [unique][in] */
1846 const OLECHAR *pwcsNewName,/* [string][in] */
1847 DWORD grfFlags) /* [in] */
1849 FIXME("(%p %s %p %s %u): stub\n", iface,
1850 debugstr_w(pwcsName), pstgDest,
1851 debugstr_w(pwcsNewName), grfFlags);
1855 /*************************************************************************
1858 * Ensures that any changes made to a storage object open in transacted mode
1859 * are reflected in the parent storage
1861 * In a non-transacted mode, this ensures all cached writes are completed.
1863 static HRESULT WINAPI StorageImpl_Commit(
1865 DWORD grfCommitFlags)/* [in] */
1867 StorageBaseImpl* const base=(StorageBaseImpl*)iface;
1868 TRACE("(%p %d)\n", iface, grfCommitFlags);
1869 return StorageBaseImpl_Flush(base);
1872 /*************************************************************************
1875 * Discard all changes that have been made since the last commit operation
1877 static HRESULT WINAPI StorageImpl_Revert(
1880 TRACE("(%p)\n", iface);
1884 /*************************************************************************
1885 * DestroyElement (IStorage)
1887 * Strategy: This implementation is built this way for simplicity not for speed.
1888 * I always delete the topmost element of the enumeration and adjust
1889 * the deleted element pointer all the time. This takes longer to
1890 * do but allow to reinvoke DestroyElement whenever we encounter a
1891 * storage object. The optimisation resides in the usage of another
1892 * enumeration strategy that would give all the leaves of a storage
1893 * first. (postfix order)
1895 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1897 const OLECHAR *pwcsName)/* [string][in] */
1899 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1902 DirEntry entryToDelete;
1903 DirRef entryToDeleteRef;
1906 iface, debugstr_w(pwcsName));
1909 return STG_E_INVALIDPOINTER;
1912 return STG_E_REVERTED;
1914 if ( !(This->openFlags & STGM_TRANSACTED) &&
1915 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1916 return STG_E_ACCESSDENIED;
1918 entryToDeleteRef = findElement(
1920 This->storageDirEntry,
1924 if ( entryToDeleteRef == DIRENTRY_NULL )
1926 return STG_E_FILENOTFOUND;
1929 if ( entryToDelete.stgType == STGTY_STORAGE )
1931 hr = deleteStorageContents(
1936 else if ( entryToDelete.stgType == STGTY_STREAM )
1938 hr = deleteStreamContents(
1948 * Remove the entry from its parent storage
1950 hr = removeFromTree(
1952 This->storageDirEntry,
1956 * Invalidate the entry
1959 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1962 hr = StorageBaseImpl_Flush(This);
1968 /******************************************************************************
1969 * Internal stream list handlers
1972 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1974 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1975 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1978 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1980 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1981 list_remove(&(strm->StrmListEntry));
1984 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1986 StgStreamImpl *strm;
1988 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1990 if (strm->dirEntry == streamEntry)
1999 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
2001 StorageInternalImpl *childstg;
2003 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
2005 if (childstg->base.storageDirEntry == storageEntry)
2014 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2016 struct list *cur, *cur2;
2017 StgStreamImpl *strm=NULL;
2018 StorageInternalImpl *childstg=NULL;
2020 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2021 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2022 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2023 strm->parentStorage = NULL;
2027 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2028 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2029 StorageBaseImpl_Invalidate( &childstg->base );
2032 if (stg->transactedChild)
2034 StorageBaseImpl_Invalidate(stg->transactedChild);
2036 stg->transactedChild = NULL;
2041 /*********************************************************************
2045 * Delete the contents of a storage entry.
2048 static HRESULT deleteStorageContents(
2049 StorageBaseImpl *parentStorage,
2050 DirRef indexToDelete,
2051 DirEntry entryDataToDelete)
2053 IEnumSTATSTG *elements = 0;
2054 IStorage *childStorage = 0;
2055 STATSTG currentElement;
2057 HRESULT destroyHr = S_OK;
2058 StorageInternalImpl *stg, *stg2;
2060 /* Invalidate any open storage objects. */
2061 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2063 if (stg->base.storageDirEntry == indexToDelete)
2065 StorageBaseImpl_Invalidate(&stg->base);
2070 * Open the storage and enumerate it
2072 hr = StorageBaseImpl_OpenStorage(
2073 (IStorage*)parentStorage,
2074 entryDataToDelete.name,
2076 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2087 * Enumerate the elements
2089 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2094 * Obtain the next element
2096 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
2099 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2101 CoTaskMemFree(currentElement.pwcsName);
2105 * We need to Reset the enumeration every time because we delete elements
2106 * and the enumeration could be invalid
2108 IEnumSTATSTG_Reset(elements);
2110 } while ((hr == S_OK) && (destroyHr == S_OK));
2112 IStorage_Release(childStorage);
2113 IEnumSTATSTG_Release(elements);
2118 /*********************************************************************
2122 * Perform the deletion of a stream's data
2125 static HRESULT deleteStreamContents(
2126 StorageBaseImpl *parentStorage,
2127 DirRef indexToDelete,
2128 DirEntry entryDataToDelete)
2132 ULARGE_INTEGER size;
2133 StgStreamImpl *strm, *strm2;
2135 /* Invalidate any open stream objects. */
2136 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2138 if (strm->dirEntry == indexToDelete)
2140 TRACE("Stream deleted %p\n", strm);
2141 strm->parentStorage = NULL;
2142 list_remove(&strm->StrmListEntry);
2146 size.u.HighPart = 0;
2149 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2150 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2160 hr = IStream_SetSize(pis, size);
2168 * Release the stream object.
2170 IStream_Release(pis);
2175 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2179 case DIRENTRY_RELATION_PREVIOUS:
2180 entry->leftChild = new_target;
2182 case DIRENTRY_RELATION_NEXT:
2183 entry->rightChild = new_target;
2185 case DIRENTRY_RELATION_DIR:
2186 entry->dirRootEntry = new_target;
2193 /*************************************************************************
2197 * This method removes a directory entry from its parent storage tree without
2198 * freeing any resources attached to it.
2200 static HRESULT removeFromTree(
2201 StorageBaseImpl *This,
2202 DirRef parentStorageIndex,
2203 DirRef deletedIndex)
2206 DirEntry entryToDelete;
2207 DirEntry parentEntry;
2208 DirRef parentEntryRef;
2209 ULONG typeOfRelation;
2211 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2217 * Find the element that links to the one we want to delete.
2219 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2220 &parentEntry, &parentEntryRef, &typeOfRelation);
2225 if (entryToDelete.leftChild != DIRENTRY_NULL)
2228 * Replace the deleted entry with its left child
2230 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2232 hr = StorageBaseImpl_WriteDirEntry(
2241 if (entryToDelete.rightChild != DIRENTRY_NULL)
2244 * We need to reinsert the right child somewhere. We already know it and
2245 * its children are greater than everything in the left tree, so we
2246 * insert it at the rightmost point in the left tree.
2248 DirRef newRightChildParent = entryToDelete.leftChild;
2249 DirEntry newRightChildParentEntry;
2253 hr = StorageBaseImpl_ReadDirEntry(
2255 newRightChildParent,
2256 &newRightChildParentEntry);
2262 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2263 newRightChildParent = newRightChildParentEntry.rightChild;
2264 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2266 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2268 hr = StorageBaseImpl_WriteDirEntry(
2270 newRightChildParent,
2271 &newRightChildParentEntry);
2281 * Replace the deleted entry with its right child
2283 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2285 hr = StorageBaseImpl_WriteDirEntry(
2299 /******************************************************************************
2300 * SetElementTimes (IStorage)
2302 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2304 const OLECHAR *pwcsName,/* [string][in] */
2305 const FILETIME *pctime, /* [in] */
2306 const FILETIME *patime, /* [in] */
2307 const FILETIME *pmtime) /* [in] */
2309 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2313 /******************************************************************************
2314 * SetStateBits (IStorage)
2316 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2318 DWORD grfStateBits,/* [in] */
2319 DWORD grfMask) /* [in] */
2321 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2324 return STG_E_REVERTED;
2326 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2330 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2331 DirRef index, const DirEntry *data)
2333 StorageImpl *This = (StorageImpl*)base;
2334 return StorageImpl_WriteDirEntry(This, index, data);
2337 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2338 DirRef index, DirEntry *data)
2340 StorageImpl *This = (StorageImpl*)base;
2341 return StorageImpl_ReadDirEntry(This, index, data);
2344 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2348 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2350 if (!This->blockChainCache[i])
2352 return &This->blockChainCache[i];
2356 i = This->blockChainToEvict;
2358 BlockChainStream_Destroy(This->blockChainCache[i]);
2359 This->blockChainCache[i] = NULL;
2361 This->blockChainToEvict++;
2362 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2363 This->blockChainToEvict = 0;
2365 return &This->blockChainCache[i];
2368 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2371 int i, free_index=-1;
2373 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2375 if (!This->blockChainCache[i])
2377 if (free_index == -1) free_index = i;
2379 else if (This->blockChainCache[i]->ownerDirEntry == index)
2381 return &This->blockChainCache[i];
2385 if (free_index == -1)
2387 free_index = This->blockChainToEvict;
2389 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2390 This->blockChainCache[free_index] = NULL;
2392 This->blockChainToEvict++;
2393 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2394 This->blockChainToEvict = 0;
2397 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2398 return &This->blockChainCache[free_index];
2401 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2405 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2407 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2409 BlockChainStream_Destroy(This->blockChainCache[i]);
2410 This->blockChainCache[i] = NULL;
2416 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2417 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2419 StorageImpl *This = (StorageImpl*)base;
2424 hr = StorageImpl_ReadDirEntry(This, index, &data);
2425 if (FAILED(hr)) return hr;
2427 if (data.size.QuadPart == 0)
2433 if (offset.QuadPart + size > data.size.QuadPart)
2435 bytesToRead = data.size.QuadPart - offset.QuadPart;
2442 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2444 SmallBlockChainStream *stream;
2446 stream = SmallBlockChainStream_Construct(This, NULL, index);
2447 if (!stream) return E_OUTOFMEMORY;
2449 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2451 SmallBlockChainStream_Destroy(stream);
2457 BlockChainStream *stream = NULL;
2459 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2460 if (!stream) return E_OUTOFMEMORY;
2462 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2468 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2469 ULARGE_INTEGER newsize)
2471 StorageImpl *This = (StorageImpl*)base;
2474 SmallBlockChainStream *smallblock=NULL;
2475 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2477 hr = StorageImpl_ReadDirEntry(This, index, &data);
2478 if (FAILED(hr)) return hr;
2480 /* In simple mode keep the stream size above the small block limit */
2481 if (This->base.openFlags & STGM_SIMPLE)
2482 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2484 if (data.size.QuadPart == newsize.QuadPart)
2487 /* Create a block chain object of the appropriate type */
2488 if (data.size.QuadPart == 0)
2490 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2492 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2493 if (!smallblock) return E_OUTOFMEMORY;
2497 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2498 bigblock = *pbigblock;
2499 if (!bigblock) return E_OUTOFMEMORY;
2502 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2504 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2505 if (!smallblock) return E_OUTOFMEMORY;
2509 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2510 bigblock = *pbigblock;
2511 if (!bigblock) return E_OUTOFMEMORY;
2514 /* Change the block chain type if necessary. */
2515 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2517 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2520 SmallBlockChainStream_Destroy(smallblock);
2524 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2525 *pbigblock = bigblock;
2527 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2529 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
2534 /* Set the size of the block chain. */
2537 SmallBlockChainStream_SetSize(smallblock, newsize);
2538 SmallBlockChainStream_Destroy(smallblock);
2542 BlockChainStream_SetSize(bigblock, newsize);
2545 /* Set the size in the directory entry. */
2546 hr = StorageImpl_ReadDirEntry(This, index, &data);
2549 data.size = newsize;
2551 hr = StorageImpl_WriteDirEntry(This, index, &data);
2556 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2557 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2559 StorageImpl *This = (StorageImpl*)base;
2562 ULARGE_INTEGER newSize;
2564 hr = StorageImpl_ReadDirEntry(This, index, &data);
2565 if (FAILED(hr)) return hr;
2567 /* Grow the stream if necessary */
2568 newSize.QuadPart = 0;
2569 newSize.QuadPart = offset.QuadPart + size;
2571 if (newSize.QuadPart > data.size.QuadPart)
2573 hr = StorageImpl_StreamSetSize(base, index, newSize);
2577 hr = StorageImpl_ReadDirEntry(This, index, &data);
2578 if (FAILED(hr)) return hr;
2581 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2583 SmallBlockChainStream *stream;
2585 stream = SmallBlockChainStream_Construct(This, NULL, index);
2586 if (!stream) return E_OUTOFMEMORY;
2588 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2590 SmallBlockChainStream_Destroy(stream);
2596 BlockChainStream *stream;
2598 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2599 if (!stream) return E_OUTOFMEMORY;
2601 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2607 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2610 StorageImpl *This = (StorageImpl*)base;
2611 DirEntry dst_data, src_data;
2614 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2617 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2621 StorageImpl_DeleteCachedBlockChainStream(This, src);
2622 dst_data.startingBlock = src_data.startingBlock;
2623 dst_data.size = src_data.size;
2625 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2631 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2633 StorageImpl *This = (StorageImpl*) iface;
2637 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2639 *result = statstg.pwcsName;
2645 * Virtual function table for the IStorage32Impl class.
2647 static const IStorageVtbl Storage32Impl_Vtbl =
2649 StorageBaseImpl_QueryInterface,
2650 StorageBaseImpl_AddRef,
2651 StorageBaseImpl_Release,
2652 StorageBaseImpl_CreateStream,
2653 StorageBaseImpl_OpenStream,
2654 StorageBaseImpl_CreateStorage,
2655 StorageBaseImpl_OpenStorage,
2656 StorageBaseImpl_CopyTo,
2657 StorageBaseImpl_MoveElementTo,
2660 StorageBaseImpl_EnumElements,
2661 StorageBaseImpl_DestroyElement,
2662 StorageBaseImpl_RenameElement,
2663 StorageBaseImpl_SetElementTimes,
2664 StorageBaseImpl_SetClass,
2665 StorageBaseImpl_SetStateBits,
2666 StorageBaseImpl_Stat
2669 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2671 StorageImpl_Destroy,
2672 StorageImpl_Invalidate,
2674 StorageImpl_GetFilename,
2675 StorageImpl_CreateDirEntry,
2676 StorageImpl_BaseWriteDirEntry,
2677 StorageImpl_BaseReadDirEntry,
2678 StorageImpl_DestroyDirEntry,
2679 StorageImpl_StreamReadAt,
2680 StorageImpl_StreamWriteAt,
2681 StorageImpl_StreamSetSize,
2682 StorageImpl_StreamLink
2685 static HRESULT StorageImpl_Construct(
2693 StorageImpl** result)
2697 DirEntry currentEntry;
2698 DirRef currentEntryRef;
2700 if ( FAILED( validateSTGM(openFlags) ))
2701 return STG_E_INVALIDFLAG;
2703 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2705 return E_OUTOFMEMORY;
2707 memset(This, 0, sizeof(StorageImpl));
2709 list_init(&This->base.strmHead);
2711 list_init(&This->base.storageHead);
2713 This->base.lpVtbl = &Storage32Impl_Vtbl;
2714 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2715 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2716 This->base.openFlags = (openFlags & ~STGM_CREATE);
2718 This->base.create = create;
2720 This->base.reverted = 0;
2723 * Initialize the big block cache.
2725 This->bigBlockSize = sector_size;
2726 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2728 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2731 This->lockBytes = pLkbyt;
2732 ILockBytes_AddRef(pLkbyt);
2740 ULARGE_INTEGER size;
2741 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2744 * Initialize all header variables:
2745 * - The big block depot consists of one block and it is at block 0
2746 * - The directory table starts at block 1
2747 * - There is no small block depot
2749 memset( This->bigBlockDepotStart,
2751 sizeof(This->bigBlockDepotStart));
2753 This->bigBlockDepotCount = 1;
2754 This->bigBlockDepotStart[0] = 0;
2755 This->rootStartBlock = 1;
2756 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2757 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2758 if (sector_size == 4096)
2759 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2761 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2762 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2763 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2764 This->extBigBlockDepotCount = 0;
2766 StorageImpl_SaveFileHeader(This);
2769 * Add one block for the big block depot and one block for the directory table
2771 size.u.HighPart = 0;
2772 size.u.LowPart = This->bigBlockSize * 3;
2773 ILockBytes_SetSize(This->lockBytes, size);
2776 * Initialize the big block depot
2778 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2779 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2780 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2781 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2786 * Load the header for the file.
2788 hr = StorageImpl_LoadFileHeader(This);
2797 * There is no block depot cached yet.
2799 This->indexBlockDepotCached = 0xFFFFFFFF;
2800 This->indexExtBlockDepotCached = 0xFFFFFFFF;
2803 * Start searching for free blocks with block 0.
2805 This->prevFreeBlock = 0;
2807 This->firstFreeSmallBlock = 0;
2809 /* Read the extended big block depot locations. */
2810 if (This->extBigBlockDepotCount != 0)
2812 ULONG current_block = This->extBigBlockDepotStart;
2813 ULONG cache_size = This->extBigBlockDepotCount * 2;
2816 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
2817 if (!This->extBigBlockDepotLocations)
2823 This->extBigBlockDepotLocationsSize = cache_size;
2825 for (i=0; i<This->extBigBlockDepotCount; i++)
2827 if (current_block == BLOCK_END_OF_CHAIN)
2829 WARN("File has too few extended big block depot blocks.\n");
2830 hr = STG_E_DOCFILECORRUPT;
2833 This->extBigBlockDepotLocations[i] = current_block;
2834 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
2839 This->extBigBlockDepotLocations = NULL;
2840 This->extBigBlockDepotLocationsSize = 0;
2844 * Create the block chain abstractions.
2846 if(!(This->rootBlockChain =
2847 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2849 hr = STG_E_READFAULT;
2853 if(!(This->smallBlockDepotChain =
2854 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2857 hr = STG_E_READFAULT;
2862 * Write the root storage entry (memory only)
2868 * Initialize the directory table
2870 memset(&rootEntry, 0, sizeof(rootEntry));
2871 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2872 sizeof(rootEntry.name)/sizeof(WCHAR) );
2873 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2874 rootEntry.stgType = STGTY_ROOT;
2875 rootEntry.leftChild = DIRENTRY_NULL;
2876 rootEntry.rightChild = DIRENTRY_NULL;
2877 rootEntry.dirRootEntry = DIRENTRY_NULL;
2878 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2879 rootEntry.size.u.HighPart = 0;
2880 rootEntry.size.u.LowPart = 0;
2882 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2886 * Find the ID of the root storage.
2888 currentEntryRef = 0;
2892 hr = StorageImpl_ReadDirEntry(
2899 if ( (currentEntry.sizeOfNameString != 0 ) &&
2900 (currentEntry.stgType == STGTY_ROOT) )
2902 This->base.storageDirEntry = currentEntryRef;
2908 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2912 hr = STG_E_READFAULT;
2917 * Create the block chain abstraction for the small block root chain.
2919 if(!(This->smallBlockRootChain =
2920 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2922 hr = STG_E_READFAULT;
2928 IStorage_Release((IStorage*)This);
2933 StorageImpl_Flush((StorageBaseImpl*)This);
2940 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2942 StorageImpl *This = (StorageImpl*) iface;
2944 StorageBaseImpl_DeleteAll(&This->base);
2946 This->base.reverted = 1;
2949 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2951 StorageImpl *This = (StorageImpl*) iface;
2953 TRACE("(%p)\n", This);
2955 StorageImpl_Flush(iface);
2957 StorageImpl_Invalidate(iface);
2959 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
2961 BlockChainStream_Destroy(This->smallBlockRootChain);
2962 BlockChainStream_Destroy(This->rootBlockChain);
2963 BlockChainStream_Destroy(This->smallBlockDepotChain);
2965 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2966 BlockChainStream_Destroy(This->blockChainCache[i]);
2968 if (This->lockBytes)
2969 ILockBytes_Release(This->lockBytes);
2970 HeapFree(GetProcessHeap(), 0, This);
2973 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface)
2975 StorageImpl *This = (StorageImpl*) iface;
2978 TRACE("(%p)\n", This);
2980 hr = BlockChainStream_Flush(This->smallBlockRootChain);
2983 hr = BlockChainStream_Flush(This->rootBlockChain);
2986 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
2988 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
2989 if (This->blockChainCache[i])
2990 hr = BlockChainStream_Flush(This->blockChainCache[i]);
2993 hr = ILockBytes_Flush(This->lockBytes);
2998 /******************************************************************************
2999 * Storage32Impl_GetNextFreeBigBlock
3001 * Returns the index of the next free big block.
3002 * If the big block depot is filled, this method will enlarge it.
3005 static ULONG StorageImpl_GetNextFreeBigBlock(
3008 ULONG depotBlockIndexPos;
3009 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3011 ULONG depotBlockOffset;
3012 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3013 ULONG nextBlockIndex = BLOCK_SPECIAL;
3015 ULONG freeBlock = BLOCK_UNUSED;
3016 ULARGE_INTEGER neededSize;
3019 depotIndex = This->prevFreeBlock / blocksPerDepot;
3020 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
3023 * Scan the entire big block depot until we find a block marked free
3025 while (nextBlockIndex != BLOCK_UNUSED)
3027 if (depotIndex < COUNT_BBDEPOTINHEADER)
3029 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
3032 * Grow the primary depot.
3034 if (depotBlockIndexPos == BLOCK_UNUSED)
3036 depotBlockIndexPos = depotIndex*blocksPerDepot;
3039 * Add a block depot.
3041 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3042 This->bigBlockDepotCount++;
3043 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3046 * Flag it as a block depot.
3048 StorageImpl_SetNextBlockInChain(This,
3052 /* Save new header information.
3054 StorageImpl_SaveFileHeader(This);
3059 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3061 if (depotBlockIndexPos == BLOCK_UNUSED)
3064 * Grow the extended depot.
3066 ULONG extIndex = BLOCK_UNUSED;
3067 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3068 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3070 if (extBlockOffset == 0)
3072 /* We need an extended block.
3074 extIndex = Storage32Impl_AddExtBlockDepot(This);
3075 This->extBigBlockDepotCount++;
3076 depotBlockIndexPos = extIndex + 1;
3079 depotBlockIndexPos = depotIndex * blocksPerDepot;
3082 * Add a block depot and mark it in the extended block.
3084 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3085 This->bigBlockDepotCount++;
3086 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3088 /* Flag the block depot.
3090 StorageImpl_SetNextBlockInChain(This,
3094 /* If necessary, flag the extended depot block.
3096 if (extIndex != BLOCK_UNUSED)
3097 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3099 /* Save header information.
3101 StorageImpl_SaveFileHeader(This);
3105 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3109 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3110 ( nextBlockIndex != BLOCK_UNUSED))
3112 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3114 if (nextBlockIndex == BLOCK_UNUSED)
3116 freeBlock = (depotIndex * blocksPerDepot) +
3117 (depotBlockOffset/sizeof(ULONG));
3120 depotBlockOffset += sizeof(ULONG);
3125 depotBlockOffset = 0;
3129 * make sure that the block physically exists before using it
3131 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3133 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3135 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3136 ILockBytes_SetSize(This->lockBytes, neededSize);
3138 This->prevFreeBlock = freeBlock;
3143 /******************************************************************************
3144 * Storage32Impl_AddBlockDepot
3146 * This will create a depot block, essentially it is a block initialized
3149 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3151 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3154 * Initialize blocks as free
3156 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3157 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3160 /******************************************************************************
3161 * Storage32Impl_GetExtDepotBlock
3163 * Returns the index of the block that corresponds to the specified depot
3164 * index. This method is only for depot indexes equal or greater than
3165 * COUNT_BBDEPOTINHEADER.
3167 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3169 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3170 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3171 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3172 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3173 ULONG blockIndex = BLOCK_UNUSED;
3174 ULONG extBlockIndex;
3175 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3176 int index, num_blocks;
3178 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3180 if (extBlockCount >= This->extBigBlockDepotCount)
3181 return BLOCK_UNUSED;
3183 if (This->indexExtBlockDepotCached != extBlockCount)
3185 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3187 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer);
3189 num_blocks = This->bigBlockSize / 4;
3191 for (index = 0; index < num_blocks; index++)
3193 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3194 This->extBlockDepotCached[index] = blockIndex;
3197 This->indexExtBlockDepotCached = extBlockCount;
3200 blockIndex = This->extBlockDepotCached[extBlockOffset];
3205 /******************************************************************************
3206 * Storage32Impl_SetExtDepotBlock
3208 * Associates the specified block index to the specified depot index.
3209 * This method is only for depot indexes equal or greater than
3210 * COUNT_BBDEPOTINHEADER.
3212 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3214 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3215 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3216 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3217 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3218 ULONG extBlockIndex;
3220 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3222 assert(extBlockCount < This->extBigBlockDepotCount);
3224 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3226 if (extBlockIndex != BLOCK_UNUSED)
3228 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3229 extBlockOffset * sizeof(ULONG),
3233 if (This->indexExtBlockDepotCached == extBlockCount)
3235 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3239 /******************************************************************************
3240 * Storage32Impl_AddExtBlockDepot
3242 * Creates an extended depot block.
3244 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3246 ULONG numExtBlocks = This->extBigBlockDepotCount;
3247 ULONG nextExtBlock = This->extBigBlockDepotStart;
3248 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3249 ULONG index = BLOCK_UNUSED;
3250 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3251 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3252 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3254 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3255 blocksPerDepotBlock;
3257 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3260 * The first extended block.
3262 This->extBigBlockDepotStart = index;
3267 * Find the last existing extended block.
3269 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3272 * Add the new extended block to the chain.
3274 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3279 * Initialize this block.
3281 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3282 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3284 /* Add the block to our cache. */
3285 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3287 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3288 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3290 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3291 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3293 This->extBigBlockDepotLocations = new_cache;
3294 This->extBigBlockDepotLocationsSize = new_cache_size;
3296 This->extBigBlockDepotLocations[numExtBlocks] = index;
3301 /******************************************************************************
3302 * Storage32Impl_FreeBigBlock
3304 * This method will flag the specified block as free in the big block depot.
3306 static void StorageImpl_FreeBigBlock(
3310 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3312 if (blockIndex < This->prevFreeBlock)
3313 This->prevFreeBlock = blockIndex;
3316 /************************************************************************
3317 * Storage32Impl_GetNextBlockInChain
3319 * This method will retrieve the block index of the next big block in
3322 * Params: This - Pointer to the Storage object.
3323 * blockIndex - Index of the block to retrieve the chain
3325 * nextBlockIndex - receives the return value.
3327 * Returns: This method returns the index of the next block in the chain.
3328 * It will return the constants:
3329 * BLOCK_SPECIAL - If the block given was not part of a
3331 * BLOCK_END_OF_CHAIN - If the block given was the last in
3333 * BLOCK_UNUSED - If the block given was not past of a chain
3335 * BLOCK_EXTBBDEPOT - This block is part of the extended
3338 * See Windows documentation for more details on IStorage methods.
3340 static HRESULT StorageImpl_GetNextBlockInChain(
3343 ULONG* nextBlockIndex)
3345 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3346 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3347 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3348 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3350 ULONG depotBlockIndexPos;
3351 int index, num_blocks;
3353 *nextBlockIndex = BLOCK_SPECIAL;
3355 if(depotBlockCount >= This->bigBlockDepotCount)
3357 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3358 This->bigBlockDepotCount);
3359 return STG_E_READFAULT;
3363 * Cache the currently accessed depot block.
3365 if (depotBlockCount != This->indexBlockDepotCached)
3367 This->indexBlockDepotCached = depotBlockCount;
3369 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3371 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3376 * We have to look in the extended depot.
3378 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3381 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3384 return STG_E_READFAULT;
3386 num_blocks = This->bigBlockSize / 4;
3388 for (index = 0; index < num_blocks; index++)
3390 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3391 This->blockDepotCached[index] = *nextBlockIndex;
3395 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3400 /******************************************************************************
3401 * Storage32Impl_GetNextExtendedBlock
3403 * Given an extended block this method will return the next extended block.
3406 * The last ULONG of an extended block is the block index of the next
3407 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3411 * - The index of the next extended block
3412 * - BLOCK_UNUSED: there is no next extended block.
3413 * - Any other return values denotes failure.
3415 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3417 ULONG nextBlockIndex = BLOCK_SPECIAL;
3418 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3420 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3423 return nextBlockIndex;
3426 /******************************************************************************
3427 * Storage32Impl_SetNextBlockInChain
3429 * This method will write the index of the specified block's next block
3430 * in the big block depot.
3432 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3435 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3436 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3437 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3440 static void StorageImpl_SetNextBlockInChain(
3445 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3446 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3447 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3448 ULONG depotBlockIndexPos;
3450 assert(depotBlockCount < This->bigBlockDepotCount);
3451 assert(blockIndex != nextBlock);
3453 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3455 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3460 * We have to look in the extended depot.
3462 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3465 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3468 * Update the cached block depot, if necessary.
3470 if (depotBlockCount == This->indexBlockDepotCached)
3472 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3476 /******************************************************************************
3477 * Storage32Impl_LoadFileHeader
3479 * This method will read in the file header
3481 static HRESULT StorageImpl_LoadFileHeader(
3485 BYTE headerBigBlock[HEADER_SIZE];
3487 ULARGE_INTEGER offset;
3492 * Get a pointer to the big block of data containing the header.
3494 offset.u.HighPart = 0;
3495 offset.u.LowPart = 0;
3496 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3497 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3498 hr = STG_E_FILENOTFOUND;
3501 * Extract the information from the header.
3506 * Check for the "magic number" signature and return an error if it is not
3509 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3511 return STG_E_OLDFORMAT;
3514 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3516 return STG_E_INVALIDHEADER;
3519 StorageUtl_ReadWord(
3521 OFFSET_BIGBLOCKSIZEBITS,
3522 &This->bigBlockSizeBits);
3524 StorageUtl_ReadWord(
3526 OFFSET_SMALLBLOCKSIZEBITS,
3527 &This->smallBlockSizeBits);
3529 StorageUtl_ReadDWord(
3531 OFFSET_BBDEPOTCOUNT,
3532 &This->bigBlockDepotCount);
3534 StorageUtl_ReadDWord(
3536 OFFSET_ROOTSTARTBLOCK,
3537 &This->rootStartBlock);
3539 StorageUtl_ReadDWord(
3541 OFFSET_SMALLBLOCKLIMIT,
3542 &This->smallBlockLimit);
3544 StorageUtl_ReadDWord(
3546 OFFSET_SBDEPOTSTART,
3547 &This->smallBlockDepotStart);
3549 StorageUtl_ReadDWord(
3551 OFFSET_EXTBBDEPOTSTART,
3552 &This->extBigBlockDepotStart);
3554 StorageUtl_ReadDWord(
3556 OFFSET_EXTBBDEPOTCOUNT,
3557 &This->extBigBlockDepotCount);
3559 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3561 StorageUtl_ReadDWord(
3563 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3564 &(This->bigBlockDepotStart[index]));
3568 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3570 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3571 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3574 * Right now, the code is making some assumptions about the size of the
3575 * blocks, just make sure they are what we're expecting.
3577 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3578 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3579 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3581 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3582 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3583 hr = STG_E_INVALIDHEADER;
3592 /******************************************************************************
3593 * Storage32Impl_SaveFileHeader
3595 * This method will save to the file the header
3597 static void StorageImpl_SaveFileHeader(
3600 BYTE headerBigBlock[HEADER_SIZE];
3603 ULARGE_INTEGER offset;
3604 DWORD bytes_read, bytes_written;
3605 DWORD major_version, dirsectorcount;
3608 * Get a pointer to the big block of data containing the header.
3610 offset.u.HighPart = 0;
3611 offset.u.LowPart = 0;
3612 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3613 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3614 hr = STG_E_FILENOTFOUND;
3616 if (This->bigBlockSizeBits == 0x9)
3618 else if (This->bigBlockSizeBits == 0xc)
3622 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3627 * If the block read failed, the file is probably new.
3632 * Initialize for all unknown fields.
3634 memset(headerBigBlock, 0, HEADER_SIZE);
3637 * Initialize the magic number.
3639 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3643 * Write the information to the header.
3645 StorageUtl_WriteWord(
3647 OFFSET_MINORVERSION,
3650 StorageUtl_WriteWord(
3652 OFFSET_MAJORVERSION,
3655 StorageUtl_WriteWord(
3657 OFFSET_BYTEORDERMARKER,
3660 StorageUtl_WriteWord(
3662 OFFSET_BIGBLOCKSIZEBITS,
3663 This->bigBlockSizeBits);
3665 StorageUtl_WriteWord(
3667 OFFSET_SMALLBLOCKSIZEBITS,
3668 This->smallBlockSizeBits);
3670 if (major_version >= 4)
3672 if (This->rootBlockChain)
3673 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3675 /* This file is being created, and it will start out with one block. */
3679 /* This field must be 0 in versions older than 4 */
3682 StorageUtl_WriteDWord(
3684 OFFSET_DIRSECTORCOUNT,
3687 StorageUtl_WriteDWord(
3689 OFFSET_BBDEPOTCOUNT,
3690 This->bigBlockDepotCount);
3692 StorageUtl_WriteDWord(
3694 OFFSET_ROOTSTARTBLOCK,
3695 This->rootStartBlock);
3697 StorageUtl_WriteDWord(
3699 OFFSET_SMALLBLOCKLIMIT,
3700 This->smallBlockLimit);
3702 StorageUtl_WriteDWord(
3704 OFFSET_SBDEPOTSTART,
3705 This->smallBlockDepotStart);
3707 StorageUtl_WriteDWord(
3709 OFFSET_SBDEPOTCOUNT,
3710 This->smallBlockDepotChain ?
3711 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3713 StorageUtl_WriteDWord(
3715 OFFSET_EXTBBDEPOTSTART,
3716 This->extBigBlockDepotStart);
3718 StorageUtl_WriteDWord(
3720 OFFSET_EXTBBDEPOTCOUNT,
3721 This->extBigBlockDepotCount);
3723 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3725 StorageUtl_WriteDWord(
3727 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3728 (This->bigBlockDepotStart[index]));
3732 * Write the big block back to the file.
3734 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3737 /******************************************************************************
3738 * StorageImpl_ReadRawDirEntry
3740 * This method will read the raw data from a directory entry in the file.
3742 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3744 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3746 ULARGE_INTEGER offset;
3750 offset.u.HighPart = 0;
3751 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3753 hr = BlockChainStream_ReadAt(
3754 This->rootBlockChain,
3760 if (bytesRead != RAW_DIRENTRY_SIZE)
3761 return STG_E_READFAULT;
3766 /******************************************************************************
3767 * StorageImpl_WriteRawDirEntry
3769 * This method will write the raw data from a directory entry in the file.
3771 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3773 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3775 ULARGE_INTEGER offset;
3779 offset.u.HighPart = 0;
3780 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3782 hr = BlockChainStream_WriteAt(
3783 This->rootBlockChain,
3792 /******************************************************************************
3795 * Update raw directory entry data from the fields in newData.
3797 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3799 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3801 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3804 buffer + OFFSET_PS_NAME,
3806 DIRENTRY_NAME_BUFFER_LEN );
3808 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3810 StorageUtl_WriteWord(
3812 OFFSET_PS_NAMELENGTH,
3813 newData->sizeOfNameString);
3815 StorageUtl_WriteDWord(
3817 OFFSET_PS_LEFTCHILD,
3818 newData->leftChild);
3820 StorageUtl_WriteDWord(
3822 OFFSET_PS_RIGHTCHILD,
3823 newData->rightChild);
3825 StorageUtl_WriteDWord(
3828 newData->dirRootEntry);
3830 StorageUtl_WriteGUID(
3835 StorageUtl_WriteDWord(
3838 newData->ctime.dwLowDateTime);
3840 StorageUtl_WriteDWord(
3842 OFFSET_PS_CTIMEHIGH,
3843 newData->ctime.dwHighDateTime);
3845 StorageUtl_WriteDWord(
3848 newData->mtime.dwLowDateTime);
3850 StorageUtl_WriteDWord(
3852 OFFSET_PS_MTIMEHIGH,
3853 newData->ctime.dwHighDateTime);
3855 StorageUtl_WriteDWord(
3857 OFFSET_PS_STARTBLOCK,
3858 newData->startingBlock);
3860 StorageUtl_WriteDWord(
3863 newData->size.u.LowPart);
3866 /******************************************************************************
3867 * Storage32Impl_ReadDirEntry
3869 * This method will read the specified directory entry.
3871 HRESULT StorageImpl_ReadDirEntry(
3876 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3879 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3881 if (SUCCEEDED(readRes))
3883 memset(buffer->name, 0, sizeof(buffer->name));
3886 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3887 DIRENTRY_NAME_BUFFER_LEN );
3888 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3890 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3892 StorageUtl_ReadWord(
3894 OFFSET_PS_NAMELENGTH,
3895 &buffer->sizeOfNameString);
3897 StorageUtl_ReadDWord(
3899 OFFSET_PS_LEFTCHILD,
3900 &buffer->leftChild);
3902 StorageUtl_ReadDWord(
3904 OFFSET_PS_RIGHTCHILD,
3905 &buffer->rightChild);
3907 StorageUtl_ReadDWord(
3910 &buffer->dirRootEntry);
3912 StorageUtl_ReadGUID(
3917 StorageUtl_ReadDWord(
3920 &buffer->ctime.dwLowDateTime);
3922 StorageUtl_ReadDWord(
3924 OFFSET_PS_CTIMEHIGH,
3925 &buffer->ctime.dwHighDateTime);
3927 StorageUtl_ReadDWord(
3930 &buffer->mtime.dwLowDateTime);
3932 StorageUtl_ReadDWord(
3934 OFFSET_PS_MTIMEHIGH,
3935 &buffer->mtime.dwHighDateTime);
3937 StorageUtl_ReadDWord(
3939 OFFSET_PS_STARTBLOCK,
3940 &buffer->startingBlock);
3942 StorageUtl_ReadDWord(
3945 &buffer->size.u.LowPart);
3947 buffer->size.u.HighPart = 0;
3953 /*********************************************************************
3954 * Write the specified directory entry to the file
3956 HRESULT StorageImpl_WriteDirEntry(
3959 const DirEntry* buffer)
3961 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3964 UpdateRawDirEntry(currentEntry, buffer);
3966 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3970 static BOOL StorageImpl_ReadBigBlock(
3975 ULARGE_INTEGER ulOffset;
3978 ulOffset.u.HighPart = 0;
3979 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3981 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3983 if (read && read < This->bigBlockSize)
3985 /* File ends during this block; fill the rest with 0's. */
3986 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3992 static BOOL StorageImpl_ReadDWordFromBigBlock(
3998 ULARGE_INTEGER ulOffset;
4002 ulOffset.u.HighPart = 0;
4003 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4004 ulOffset.u.LowPart += offset;
4006 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
4007 *value = lendian32toh(tmp);
4008 return (read == sizeof(DWORD));
4011 static BOOL StorageImpl_WriteBigBlock(
4016 ULARGE_INTEGER ulOffset;
4019 ulOffset.u.HighPart = 0;
4020 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4022 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
4023 return (wrote == This->bigBlockSize);
4026 static BOOL StorageImpl_WriteDWordToBigBlock(
4032 ULARGE_INTEGER ulOffset;
4035 ulOffset.u.HighPart = 0;
4036 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4037 ulOffset.u.LowPart += offset;
4039 value = htole32(value);
4040 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4041 return (wrote == sizeof(DWORD));
4044 /******************************************************************************
4045 * Storage32Impl_SmallBlocksToBigBlocks
4047 * This method will convert a small block chain to a big block chain.
4048 * The small block chain will be destroyed.
4050 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4052 SmallBlockChainStream** ppsbChain)
4054 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4055 ULARGE_INTEGER size, offset;
4056 ULONG cbRead, cbWritten;
4057 ULARGE_INTEGER cbTotalRead;
4058 DirRef streamEntryRef;
4059 HRESULT resWrite = S_OK;
4061 DirEntry streamEntry;
4063 BlockChainStream *bbTempChain = NULL;
4064 BlockChainStream *bigBlockChain = NULL;
4067 * Create a temporary big block chain that doesn't have
4068 * an associated directory entry. This temporary chain will be
4069 * used to copy data from small blocks to big blocks.
4071 bbTempChain = BlockChainStream_Construct(This,
4074 if(!bbTempChain) return NULL;
4076 * Grow the big block chain.
4078 size = SmallBlockChainStream_GetSize(*ppsbChain);
4079 BlockChainStream_SetSize(bbTempChain, size);
4082 * Copy the contents of the small block chain to the big block chain
4083 * by small block size increments.
4085 offset.u.LowPart = 0;
4086 offset.u.HighPart = 0;
4087 cbTotalRead.QuadPart = 0;
4089 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4092 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4094 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4097 if (FAILED(resRead))
4102 cbTotalRead.QuadPart += cbRead;
4104 resWrite = BlockChainStream_WriteAt(bbTempChain,
4110 if (FAILED(resWrite))
4113 offset.u.LowPart += cbRead;
4117 resRead = STG_E_READFAULT;
4120 } while (cbTotalRead.QuadPart < size.QuadPart);
4121 HeapFree(GetProcessHeap(),0,buffer);
4123 size.u.HighPart = 0;
4126 if (FAILED(resRead) || FAILED(resWrite))
4128 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4129 BlockChainStream_SetSize(bbTempChain, size);
4130 BlockChainStream_Destroy(bbTempChain);
4135 * Destroy the small block chain.
4137 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4138 SmallBlockChainStream_SetSize(*ppsbChain, size);
4139 SmallBlockChainStream_Destroy(*ppsbChain);
4143 * Change the directory entry. This chain is now a big block chain
4144 * and it doesn't reside in the small blocks chain anymore.
4146 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4148 streamEntry.startingBlock = bbHeadOfChain;
4150 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4153 * Destroy the temporary entryless big block chain.
4154 * Create a new big block chain associated with this entry.
4156 BlockChainStream_Destroy(bbTempChain);
4157 bigBlockChain = BlockChainStream_Construct(This,
4161 return bigBlockChain;
4164 /******************************************************************************
4165 * Storage32Impl_BigBlocksToSmallBlocks
4167 * This method will convert a big block chain to a small block chain.
4168 * The big block chain will be destroyed on success.
4170 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4172 BlockChainStream** ppbbChain,
4173 ULARGE_INTEGER newSize)
4175 ULARGE_INTEGER size, offset, cbTotalRead;
4176 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4177 DirRef streamEntryRef;
4178 HRESULT resWrite = S_OK, resRead = S_OK;
4179 DirEntry streamEntry;
4181 SmallBlockChainStream* sbTempChain;
4183 TRACE("%p %p\n", This, ppbbChain);
4185 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4191 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4192 size = BlockChainStream_GetSize(*ppbbChain);
4193 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4195 offset.u.HighPart = 0;
4196 offset.u.LowPart = 0;
4197 cbTotalRead.QuadPart = 0;
4198 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4199 while(cbTotalRead.QuadPart < size.QuadPart)
4201 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4202 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4210 cbTotalRead.QuadPart += cbRead;
4212 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4213 cbRead, buffer, &cbWritten);
4215 if(FAILED(resWrite))
4218 offset.u.LowPart += cbRead;
4222 resRead = STG_E_READFAULT;
4226 HeapFree(GetProcessHeap(), 0, buffer);
4228 size.u.HighPart = 0;
4231 if(FAILED(resRead) || FAILED(resWrite))
4233 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4234 SmallBlockChainStream_SetSize(sbTempChain, size);
4235 SmallBlockChainStream_Destroy(sbTempChain);
4239 /* destroy the original big block chain */
4240 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4241 BlockChainStream_SetSize(*ppbbChain, size);
4242 BlockChainStream_Destroy(*ppbbChain);
4245 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4246 streamEntry.startingBlock = sbHeadOfChain;
4247 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4249 SmallBlockChainStream_Destroy(sbTempChain);
4250 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4253 static HRESULT StorageBaseImpl_CopyStream(
4254 StorageBaseImpl *dst, DirRef dst_entry,
4255 StorageBaseImpl *src, DirRef src_entry)
4260 ULARGE_INTEGER bytes_copied;
4261 ULONG bytestocopy, bytesread, byteswritten;
4263 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4267 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4269 bytes_copied.QuadPart = 0;
4270 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4272 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4274 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4276 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4279 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4280 data, &byteswritten);
4283 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4284 bytes_copied.QuadPart += byteswritten;
4292 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4294 DirRef result=This->firstFreeEntry;
4296 while (result < This->entries_size && This->entries[result].inuse)
4299 if (result == This->entries_size)
4301 ULONG new_size = This->entries_size * 2;
4302 TransactedDirEntry *new_entries;
4304 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4305 if (!new_entries) return DIRENTRY_NULL;
4307 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4308 HeapFree(GetProcessHeap(), 0, This->entries);
4310 This->entries = new_entries;
4311 This->entries_size = new_size;
4314 This->entries[result].inuse = 1;
4316 This->firstFreeEntry = result+1;
4321 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4322 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4324 DirRef stubEntryRef;
4325 TransactedDirEntry *entry;
4327 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4329 if (stubEntryRef != DIRENTRY_NULL)
4331 entry = &This->entries[stubEntryRef];
4333 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4338 return stubEntryRef;
4341 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4342 TransactedSnapshotImpl *This, DirRef entry)
4347 if (!This->entries[entry].read)
4349 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4350 This->entries[entry].transactedParentEntry,
4353 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4355 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4357 if (data.leftChild == DIRENTRY_NULL)
4361 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4363 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4365 if (data.rightChild == DIRENTRY_NULL)
4369 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4371 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4373 if (data.dirRootEntry == DIRENTRY_NULL)
4379 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4380 This->entries[entry].read = 1;
4387 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4388 TransactedSnapshotImpl *This, DirRef entry)
4392 if (!This->entries[entry].stream_dirty)
4394 DirEntry new_entrydata;
4396 memset(&new_entrydata, 0, sizeof(DirEntry));
4397 new_entrydata.name[0] = 'S';
4398 new_entrydata.sizeOfNameString = 1;
4399 new_entrydata.stgType = STGTY_STREAM;
4400 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4401 new_entrydata.leftChild = DIRENTRY_NULL;
4402 new_entrydata.rightChild = DIRENTRY_NULL;
4403 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4405 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4406 &This->entries[entry].stream_entry);
4408 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4410 hr = StorageBaseImpl_CopyStream(
4411 This->scratch, This->entries[entry].stream_entry,
4412 This->transactedParent, This->entries[entry].transactedParentEntry);
4415 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4419 This->entries[entry].stream_dirty = 1;
4421 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4423 /* Since this entry is modified, and we aren't using its stream data, we
4424 * no longer care about the original entry. */
4426 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4428 if (delete_ref != DIRENTRY_NULL)
4429 This->entries[delete_ref].deleted = 1;
4431 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4438 /* Find the first entry in a depth-first traversal. */
4439 static DirRef TransactedSnapshotImpl_FindFirstChild(
4440 TransactedSnapshotImpl* This, DirRef parent)
4442 DirRef cursor, prev;
4443 TransactedDirEntry *entry;
4446 entry = &This->entries[cursor];
4449 if (entry->data.leftChild != DIRENTRY_NULL)
4452 cursor = entry->data.leftChild;
4453 entry = &This->entries[cursor];
4454 entry->parent = prev;
4456 else if (entry->data.rightChild != DIRENTRY_NULL)
4459 cursor = entry->data.rightChild;
4460 entry = &This->entries[cursor];
4461 entry->parent = prev;
4463 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4466 cursor = entry->data.dirRootEntry;
4467 entry = &This->entries[cursor];
4468 entry->parent = prev;
4477 /* Find the next entry in a depth-first traversal. */
4478 static DirRef TransactedSnapshotImpl_FindNextChild(
4479 TransactedSnapshotImpl* This, DirRef current)
4482 TransactedDirEntry *parent_entry;
4484 parent = This->entries[current].parent;
4485 parent_entry = &This->entries[parent];
4487 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4489 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4491 This->entries[parent_entry->data.rightChild].parent = parent;
4492 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4495 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4497 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4498 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4505 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4506 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4507 TransactedSnapshotImpl* This, DirRef entry)
4509 return entry != DIRENTRY_NULL &&
4510 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4513 /* Destroy the entries created by CopyTree. */
4514 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4515 TransactedSnapshotImpl* This, DirRef stop)
4518 TransactedDirEntry *entry;
4519 ULARGE_INTEGER zero;
4523 if (!This->entries[This->base.storageDirEntry].read)
4526 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4528 if (cursor == DIRENTRY_NULL)
4531 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4533 while (cursor != DIRENTRY_NULL && cursor != stop)
4535 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4537 entry = &This->entries[cursor];
4539 if (entry->stream_dirty)
4540 StorageBaseImpl_StreamSetSize(This->transactedParent,
4541 entry->newTransactedParentEntry, zero);
4543 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4544 entry->newTransactedParentEntry);
4546 entry->newTransactedParentEntry = entry->transactedParentEntry;
4549 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4553 /* Make a copy of our edited tree that we can use in the parent. */
4554 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4557 TransactedDirEntry *entry;
4560 cursor = This->base.storageDirEntry;
4561 entry = &This->entries[cursor];
4562 entry->parent = DIRENTRY_NULL;
4563 entry->newTransactedParentEntry = entry->transactedParentEntry;
4565 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4568 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4570 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4571 entry = &This->entries[cursor];
4573 while (cursor != DIRENTRY_NULL)
4575 /* Make a copy of this entry in the transacted parent. */
4577 (!entry->dirty && !entry->stream_dirty &&
4578 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4579 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4580 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4581 entry->newTransactedParentEntry = entry->transactedParentEntry;
4586 memcpy(&newData, &entry->data, sizeof(DirEntry));
4588 newData.size.QuadPart = 0;
4589 newData.startingBlock = BLOCK_END_OF_CHAIN;
4591 if (newData.leftChild != DIRENTRY_NULL)
4592 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4594 if (newData.rightChild != DIRENTRY_NULL)
4595 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4597 if (newData.dirRootEntry != DIRENTRY_NULL)
4598 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4600 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4601 &entry->newTransactedParentEntry);
4604 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4608 if (entry->stream_dirty)
4610 hr = StorageBaseImpl_CopyStream(
4611 This->transactedParent, entry->newTransactedParentEntry,
4612 This->scratch, entry->stream_entry);
4614 else if (entry->data.size.QuadPart)
4616 hr = StorageBaseImpl_StreamLink(
4617 This->transactedParent, entry->newTransactedParentEntry,
4618 entry->transactedParentEntry);
4623 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4624 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4629 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4630 entry = &This->entries[cursor];
4636 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4638 DWORD grfCommitFlags) /* [in] */
4640 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4641 TransactedDirEntry *root_entry;
4642 DirRef i, dir_root_ref;
4644 ULARGE_INTEGER zero;
4649 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4651 /* Cannot commit a read-only transacted storage */
4652 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4653 return STG_E_ACCESSDENIED;
4655 /* To prevent data loss, we create the new structure in the file before we
4656 * delete the old one, so that in case of errors the old data is intact. We
4657 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4658 * needed in the rare situation where we have just enough free disk space to
4659 * overwrite the existing data. */
4661 root_entry = &This->entries[This->base.storageDirEntry];
4663 if (!root_entry->read)
4666 hr = TransactedSnapshotImpl_CopyTree(This);
4667 if (FAILED(hr)) return hr;
4669 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4670 dir_root_ref = DIRENTRY_NULL;
4672 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4674 hr = StorageBaseImpl_Flush(This->transactedParent);
4676 /* Update the storage to use the new data in one step. */
4678 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4679 root_entry->transactedParentEntry, &data);
4683 data.dirRootEntry = dir_root_ref;
4684 data.clsid = root_entry->data.clsid;
4685 data.ctime = root_entry->data.ctime;
4686 data.mtime = root_entry->data.mtime;
4688 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4689 root_entry->transactedParentEntry, &data);
4692 /* Try to flush after updating the root storage, but if the flush fails, keep
4693 * going, on the theory that it'll either succeed later or the subsequent
4694 * writes will fail. */
4695 StorageBaseImpl_Flush(This->transactedParent);
4699 /* Destroy the old now-orphaned data. */
4700 for (i=0; i<This->entries_size; i++)
4702 TransactedDirEntry *entry = &This->entries[i];
4707 StorageBaseImpl_StreamSetSize(This->transactedParent,
4708 entry->transactedParentEntry, zero);
4709 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4710 entry->transactedParentEntry);
4711 memset(entry, 0, sizeof(TransactedDirEntry));
4712 This->firstFreeEntry = min(i, This->firstFreeEntry);
4714 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4716 if (entry->transactedParentEntry != DIRENTRY_NULL)
4717 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4718 entry->transactedParentEntry);
4719 if (entry->stream_dirty)
4721 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4722 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4723 entry->stream_dirty = 0;
4726 entry->transactedParentEntry = entry->newTransactedParentEntry;
4733 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4737 hr = StorageBaseImpl_Flush(This->transactedParent);
4742 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4745 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4746 ULARGE_INTEGER zero;
4749 TRACE("(%p)\n", iface);
4751 /* Destroy the open objects. */
4752 StorageBaseImpl_DeleteAll(&This->base);
4754 /* Clear out the scratch file. */
4756 for (i=0; i<This->entries_size; i++)
4758 if (This->entries[i].stream_dirty)
4760 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4763 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4767 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4769 This->firstFreeEntry = 0;
4770 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4775 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4777 if (!This->reverted)
4779 TRACE("Storage invalidated (stg=%p)\n", This);
4783 StorageBaseImpl_DeleteAll(This);
4787 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4789 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4791 TransactedSnapshotImpl_Revert((IStorage*)iface);
4793 IStorage_Release((IStorage*)This->transactedParent);
4795 IStorage_Release((IStorage*)This->scratch);
4797 HeapFree(GetProcessHeap(), 0, This->entries);
4799 HeapFree(GetProcessHeap(), 0, This);
4802 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4804 /* We only need to flush when committing. */
4808 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4810 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4812 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4815 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4816 const DirEntry *newData, DirRef *index)
4818 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4820 TransactedDirEntry *new_entry;
4822 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4823 if (new_ref == DIRENTRY_NULL)
4824 return E_OUTOFMEMORY;
4826 new_entry = &This->entries[new_ref];
4828 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4829 new_entry->read = 1;
4830 new_entry->dirty = 1;
4831 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4835 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4840 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4841 DirRef index, const DirEntry *data)
4843 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4846 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4848 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4849 if (FAILED(hr)) return hr;
4851 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4853 if (index != This->base.storageDirEntry)
4855 This->entries[index].dirty = 1;
4857 if (data->size.QuadPart == 0 &&
4858 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4860 /* Since this entry is modified, and we aren't using its stream data, we
4861 * no longer care about the original entry. */
4863 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4865 if (delete_ref != DIRENTRY_NULL)
4866 This->entries[delete_ref].deleted = 1;
4868 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4875 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4876 DirRef index, DirEntry *data)
4878 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4881 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4882 if (FAILED(hr)) return hr;
4884 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4886 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4891 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4894 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4896 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4897 This->entries[index].data.size.QuadPart != 0)
4899 /* If we deleted this entry while it has stream data. We must have left the
4900 * data because some other entry is using it, and we need to leave the
4901 * original entry alone. */
4902 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4903 This->firstFreeEntry = min(index, This->firstFreeEntry);
4907 This->entries[index].deleted = 1;
4913 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4914 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4916 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4918 if (This->entries[index].stream_dirty)
4920 return StorageBaseImpl_StreamReadAt(This->scratch,
4921 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4923 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4925 /* This stream doesn't live in the parent, and we haven't allocated storage
4932 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4933 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4937 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4938 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4940 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4943 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4944 if (FAILED(hr)) return hr;
4946 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4947 if (FAILED(hr)) return hr;
4949 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4950 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4952 if (SUCCEEDED(hr) && size != 0)
4953 This->entries[index].data.size.QuadPart = max(
4954 This->entries[index].data.size.QuadPart,
4955 offset.QuadPart + size);
4960 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4961 DirRef index, ULARGE_INTEGER newsize)
4963 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4966 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4967 if (FAILED(hr)) return hr;
4969 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4972 if (newsize.QuadPart == 0)
4974 /* Destroy any parent references or entries in the scratch file. */
4975 if (This->entries[index].stream_dirty)
4977 ULARGE_INTEGER zero;
4979 StorageBaseImpl_StreamSetSize(This->scratch,
4980 This->entries[index].stream_entry, zero);
4981 StorageBaseImpl_DestroyDirEntry(This->scratch,
4982 This->entries[index].stream_entry);
4983 This->entries[index].stream_dirty = 0;
4985 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4988 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4990 if (delete_ref != DIRENTRY_NULL)
4991 This->entries[delete_ref].deleted = 1;
4993 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4998 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4999 if (FAILED(hr)) return hr;
5001 hr = StorageBaseImpl_StreamSetSize(This->scratch,
5002 This->entries[index].stream_entry, newsize);
5006 This->entries[index].data.size = newsize;
5011 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
5012 DirRef dst, DirRef src)
5014 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5016 TransactedDirEntry *dst_entry, *src_entry;
5018 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
5019 if (FAILED(hr)) return hr;
5021 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
5022 if (FAILED(hr)) return hr;
5024 dst_entry = &This->entries[dst];
5025 src_entry = &This->entries[src];
5027 dst_entry->stream_dirty = src_entry->stream_dirty;
5028 dst_entry->stream_entry = src_entry->stream_entry;
5029 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
5030 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
5031 dst_entry->data.size = src_entry->data.size;
5036 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5038 StorageBaseImpl_QueryInterface,
5039 StorageBaseImpl_AddRef,
5040 StorageBaseImpl_Release,
5041 StorageBaseImpl_CreateStream,
5042 StorageBaseImpl_OpenStream,
5043 StorageBaseImpl_CreateStorage,
5044 StorageBaseImpl_OpenStorage,
5045 StorageBaseImpl_CopyTo,
5046 StorageBaseImpl_MoveElementTo,
5047 TransactedSnapshotImpl_Commit,
5048 TransactedSnapshotImpl_Revert,
5049 StorageBaseImpl_EnumElements,
5050 StorageBaseImpl_DestroyElement,
5051 StorageBaseImpl_RenameElement,
5052 StorageBaseImpl_SetElementTimes,
5053 StorageBaseImpl_SetClass,
5054 StorageBaseImpl_SetStateBits,
5055 StorageBaseImpl_Stat
5058 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5060 TransactedSnapshotImpl_Destroy,
5061 TransactedSnapshotImpl_Invalidate,
5062 TransactedSnapshotImpl_Flush,
5063 TransactedSnapshotImpl_GetFilename,
5064 TransactedSnapshotImpl_CreateDirEntry,
5065 TransactedSnapshotImpl_WriteDirEntry,
5066 TransactedSnapshotImpl_ReadDirEntry,
5067 TransactedSnapshotImpl_DestroyDirEntry,
5068 TransactedSnapshotImpl_StreamReadAt,
5069 TransactedSnapshotImpl_StreamWriteAt,
5070 TransactedSnapshotImpl_StreamSetSize,
5071 TransactedSnapshotImpl_StreamLink
5074 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5075 TransactedSnapshotImpl** result)
5079 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5082 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5084 /* This is OK because the property set storage functions use the IStorage functions. */
5085 (*result)->base.pssVtbl = parentStorage->pssVtbl;
5087 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5089 list_init(&(*result)->base.strmHead);
5091 list_init(&(*result)->base.storageHead);
5093 (*result)->base.ref = 1;
5095 (*result)->base.openFlags = parentStorage->openFlags;
5097 /* Create a new temporary storage to act as the scratch file. */
5098 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
5099 0, (IStorage**)&(*result)->scratch);
5103 ULONG num_entries = 20;
5105 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5107 (*result)->entries_size = num_entries;
5109 (*result)->firstFreeEntry = 0;
5111 if ((*result)->entries)
5113 /* parentStorage already has 1 reference, which we take over here. */
5114 (*result)->transactedParent = parentStorage;
5116 parentStorage->transactedChild = (StorageBaseImpl*)*result;
5118 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5122 IStorage_Release((IStorage*)(*result)->scratch);
5128 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
5133 return E_OUTOFMEMORY;
5136 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5137 StorageBaseImpl** result)
5141 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5143 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5146 return TransactedSnapshotImpl_Construct(parentStorage,
5147 (TransactedSnapshotImpl**)result);
5150 static HRESULT Storage_Construct(
5158 StorageBaseImpl** result)
5160 StorageImpl *newStorage;
5161 StorageBaseImpl *newTransactedStorage;
5164 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5165 if (FAILED(hr)) goto end;
5167 if (openFlags & STGM_TRANSACTED)
5169 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5171 IStorage_Release((IStorage*)newStorage);
5173 *result = newTransactedStorage;
5176 *result = &newStorage->base;
5182 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5184 StorageInternalImpl* This = (StorageInternalImpl*) base;
5186 if (!This->base.reverted)
5188 TRACE("Storage invalidated (stg=%p)\n", This);
5190 This->base.reverted = 1;
5192 This->parentStorage = NULL;
5194 StorageBaseImpl_DeleteAll(&This->base);
5196 list_remove(&This->ParentListEntry);
5200 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5202 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5204 StorageInternalImpl_Invalidate(&This->base);
5206 HeapFree(GetProcessHeap(), 0, This);
5209 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5211 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5213 return StorageBaseImpl_Flush(This->parentStorage);
5216 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5218 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5220 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5223 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5224 const DirEntry *newData, DirRef *index)
5226 StorageInternalImpl* This = (StorageInternalImpl*) base;
5228 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5232 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5233 DirRef index, const DirEntry *data)
5235 StorageInternalImpl* This = (StorageInternalImpl*) base;
5237 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5241 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5242 DirRef index, DirEntry *data)
5244 StorageInternalImpl* This = (StorageInternalImpl*) base;
5246 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5250 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5253 StorageInternalImpl* This = (StorageInternalImpl*) base;
5255 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5259 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5260 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5262 StorageInternalImpl* This = (StorageInternalImpl*) base;
5264 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5265 index, offset, size, buffer, bytesRead);
5268 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5269 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5271 StorageInternalImpl* This = (StorageInternalImpl*) base;
5273 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5274 index, offset, size, buffer, bytesWritten);
5277 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5278 DirRef index, ULARGE_INTEGER newsize)
5280 StorageInternalImpl* This = (StorageInternalImpl*) base;
5282 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5286 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5287 DirRef dst, DirRef src)
5289 StorageInternalImpl* This = (StorageInternalImpl*) base;
5291 return StorageBaseImpl_StreamLink(This->parentStorage,
5295 /******************************************************************************
5297 ** Storage32InternalImpl_Commit
5300 static HRESULT WINAPI StorageInternalImpl_Commit(
5302 DWORD grfCommitFlags) /* [in] */
5304 StorageBaseImpl* base = (StorageBaseImpl*) iface;
5305 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5306 return StorageBaseImpl_Flush(base);
5309 /******************************************************************************
5311 ** Storage32InternalImpl_Revert
5314 static HRESULT WINAPI StorageInternalImpl_Revert(
5317 FIXME("(%p): stub\n", iface);
5321 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5323 IStorage_Release((IStorage*)This->parentStorage);
5324 HeapFree(GetProcessHeap(), 0, This);
5327 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5328 IEnumSTATSTG* iface,
5332 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5335 return E_INVALIDARG;
5339 if (IsEqualGUID(&IID_IUnknown, riid) ||
5340 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5343 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
5347 return E_NOINTERFACE;
5350 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5351 IEnumSTATSTG* iface)
5353 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5354 return InterlockedIncrement(&This->ref);
5357 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5358 IEnumSTATSTG* iface)
5360 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5364 newRef = InterlockedDecrement(&This->ref);
5368 IEnumSTATSTGImpl_Destroy(This);
5374 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5375 IEnumSTATSTGImpl* This,
5378 DirRef result = DIRENTRY_NULL;
5382 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5384 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5385 This->parentStorage->storageDirEntry, &entry);
5386 searchNode = entry.dirRootEntry;
5388 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5390 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5394 LONG diff = entryNameCmp( entry.name, This->name);
5398 searchNode = entry.rightChild;
5402 result = searchNode;
5403 memcpy(result_name, entry.name, sizeof(result_name));
5404 searchNode = entry.leftChild;
5412 if (result != DIRENTRY_NULL)
5413 memcpy(This->name, result_name, sizeof(result_name));
5419 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5420 IEnumSTATSTG* iface,
5423 ULONG* pceltFetched)
5425 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5427 DirEntry currentEntry;
5428 STATSTG* currentReturnStruct = rgelt;
5429 ULONG objectFetched = 0;
5430 DirRef currentSearchNode;
5433 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5434 return E_INVALIDARG;
5436 if (This->parentStorage->reverted)
5437 return STG_E_REVERTED;
5440 * To avoid the special case, get another pointer to a ULONG value if
5441 * the caller didn't supply one.
5443 if (pceltFetched==0)
5444 pceltFetched = &objectFetched;
5447 * Start the iteration, we will iterate until we hit the end of the
5448 * linked list or until we hit the number of items to iterate through
5452 while ( *pceltFetched < celt )
5454 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
5456 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5460 * Read the entry from the storage.
5462 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5467 * Copy the information to the return buffer.
5469 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5470 currentReturnStruct,
5475 * Step to the next item in the iteration
5478 currentReturnStruct++;
5481 if (SUCCEEDED(hr) && *pceltFetched != celt)
5488 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5489 IEnumSTATSTG* iface,
5492 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5494 ULONG objectFetched = 0;
5495 DirRef currentSearchNode;
5498 if (This->parentStorage->reverted)
5499 return STG_E_REVERTED;
5501 while ( (objectFetched < celt) )
5503 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
5505 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5511 if (SUCCEEDED(hr) && objectFetched != celt)
5517 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5518 IEnumSTATSTG* iface)
5520 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5522 if (This->parentStorage->reverted)
5523 return STG_E_REVERTED;
5530 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5531 IEnumSTATSTG* iface,
5532 IEnumSTATSTG** ppenum)
5534 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5536 IEnumSTATSTGImpl* newClone;
5538 if (This->parentStorage->reverted)
5539 return STG_E_REVERTED;
5542 * Perform a sanity check on the parameters.
5545 return E_INVALIDARG;
5547 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5548 This->storageDirEntry);
5552 * The new clone enumeration must point to the same current node as
5555 memcpy(newClone->name, This->name, sizeof(newClone->name));
5557 *ppenum = &newClone->IEnumSTATSTG_iface;
5560 * Don't forget to nail down a reference to the clone before
5563 IEnumSTATSTGImpl_AddRef(*ppenum);
5569 * Virtual function table for the IEnumSTATSTGImpl class.
5571 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5573 IEnumSTATSTGImpl_QueryInterface,
5574 IEnumSTATSTGImpl_AddRef,
5575 IEnumSTATSTGImpl_Release,
5576 IEnumSTATSTGImpl_Next,
5577 IEnumSTATSTGImpl_Skip,
5578 IEnumSTATSTGImpl_Reset,
5579 IEnumSTATSTGImpl_Clone
5582 /******************************************************************************
5583 ** IEnumSTATSTGImpl implementation
5586 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5587 StorageBaseImpl* parentStorage,
5588 DirRef storageDirEntry)
5590 IEnumSTATSTGImpl* newEnumeration;
5592 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5594 if (newEnumeration!=0)
5597 * Set-up the virtual function table and reference count.
5599 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5600 newEnumeration->ref = 0;
5603 * We want to nail-down the reference to the storage in case the
5604 * enumeration out-lives the storage in the client application.
5606 newEnumeration->parentStorage = parentStorage;
5607 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
5609 newEnumeration->storageDirEntry = storageDirEntry;
5612 * Make sure the current node of the iterator is the first one.
5614 IEnumSTATSTGImpl_Reset(&newEnumeration->IEnumSTATSTG_iface);
5617 return newEnumeration;
5621 * Virtual function table for the Storage32InternalImpl class.
5623 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5625 StorageBaseImpl_QueryInterface,
5626 StorageBaseImpl_AddRef,
5627 StorageBaseImpl_Release,
5628 StorageBaseImpl_CreateStream,
5629 StorageBaseImpl_OpenStream,
5630 StorageBaseImpl_CreateStorage,
5631 StorageBaseImpl_OpenStorage,
5632 StorageBaseImpl_CopyTo,
5633 StorageBaseImpl_MoveElementTo,
5634 StorageInternalImpl_Commit,
5635 StorageInternalImpl_Revert,
5636 StorageBaseImpl_EnumElements,
5637 StorageBaseImpl_DestroyElement,
5638 StorageBaseImpl_RenameElement,
5639 StorageBaseImpl_SetElementTimes,
5640 StorageBaseImpl_SetClass,
5641 StorageBaseImpl_SetStateBits,
5642 StorageBaseImpl_Stat
5645 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5647 StorageInternalImpl_Destroy,
5648 StorageInternalImpl_Invalidate,
5649 StorageInternalImpl_Flush,
5650 StorageInternalImpl_GetFilename,
5651 StorageInternalImpl_CreateDirEntry,
5652 StorageInternalImpl_WriteDirEntry,
5653 StorageInternalImpl_ReadDirEntry,
5654 StorageInternalImpl_DestroyDirEntry,
5655 StorageInternalImpl_StreamReadAt,
5656 StorageInternalImpl_StreamWriteAt,
5657 StorageInternalImpl_StreamSetSize,
5658 StorageInternalImpl_StreamLink
5661 /******************************************************************************
5662 ** Storage32InternalImpl implementation
5665 static StorageInternalImpl* StorageInternalImpl_Construct(
5666 StorageBaseImpl* parentStorage,
5668 DirRef storageDirEntry)
5670 StorageInternalImpl* newStorage;
5672 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5676 list_init(&newStorage->base.strmHead);
5678 list_init(&newStorage->base.storageHead);
5681 * Initialize the virtual function table.
5683 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
5684 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
5685 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5686 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5688 newStorage->base.reverted = 0;
5690 newStorage->base.ref = 1;
5692 newStorage->parentStorage = parentStorage;
5695 * Keep a reference to the directory entry of this storage
5697 newStorage->base.storageDirEntry = storageDirEntry;
5699 newStorage->base.create = 0;
5707 /******************************************************************************
5708 ** StorageUtl implementation
5711 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5715 memcpy(&tmp, buffer+offset, sizeof(WORD));
5716 *value = lendian16toh(tmp);
5719 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5721 value = htole16(value);
5722 memcpy(buffer+offset, &value, sizeof(WORD));
5725 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5729 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5730 *value = lendian32toh(tmp);
5733 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5735 value = htole32(value);
5736 memcpy(buffer+offset, &value, sizeof(DWORD));
5739 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5740 ULARGE_INTEGER* value)
5742 #ifdef WORDS_BIGENDIAN
5745 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5746 value->u.LowPart = htole32(tmp.u.HighPart);
5747 value->u.HighPart = htole32(tmp.u.LowPart);
5749 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5753 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5754 const ULARGE_INTEGER *value)
5756 #ifdef WORDS_BIGENDIAN
5759 tmp.u.LowPart = htole32(value->u.HighPart);
5760 tmp.u.HighPart = htole32(value->u.LowPart);
5761 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5763 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5767 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5769 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5770 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5771 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5773 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5776 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5778 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5779 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5780 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5782 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5785 void StorageUtl_CopyDirEntryToSTATSTG(
5786 StorageBaseImpl* storage,
5787 STATSTG* destination,
5788 const DirEntry* source,
5792 * The copy of the string occurs only when the flag is not set
5794 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5796 /* Use the filename for the root storage. */
5797 destination->pwcsName = 0;
5798 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5800 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5801 (source->name[0] == 0) )
5803 destination->pwcsName = 0;
5807 destination->pwcsName =
5808 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5810 strcpyW(destination->pwcsName, source->name);
5813 switch (source->stgType)
5817 destination->type = STGTY_STORAGE;
5820 destination->type = STGTY_STREAM;
5823 destination->type = STGTY_STREAM;
5827 destination->cbSize = source->size;
5829 currentReturnStruct->mtime = {0}; TODO
5830 currentReturnStruct->ctime = {0};
5831 currentReturnStruct->atime = {0};
5833 destination->grfMode = 0;
5834 destination->grfLocksSupported = 0;
5835 destination->clsid = source->clsid;
5836 destination->grfStateBits = 0;
5837 destination->reserved = 0;
5840 /******************************************************************************
5841 ** BlockChainStream implementation
5844 /* Read and save the index of all blocks in this stream. */
5845 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5847 ULONG next_sector, next_offset;
5849 struct BlockChainRun *last_run;
5851 if (This->indexCacheLen == 0)
5855 next_sector = BlockChainStream_GetHeadOfChain(This);
5859 last_run = &This->indexCache[This->indexCacheLen-1];
5860 next_offset = last_run->lastOffset+1;
5861 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5862 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5864 if (FAILED(hr)) return hr;
5867 while (next_sector != BLOCK_END_OF_CHAIN)
5869 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5871 /* Add the current block to the cache. */
5872 if (This->indexCacheSize == 0)
5874 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5875 if (!This->indexCache) return E_OUTOFMEMORY;
5876 This->indexCacheSize = 16;
5878 else if (This->indexCacheSize == This->indexCacheLen)
5880 struct BlockChainRun *new_cache;
5883 new_size = This->indexCacheSize * 2;
5884 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5885 if (!new_cache) return E_OUTOFMEMORY;
5886 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5888 HeapFree(GetProcessHeap(), 0, This->indexCache);
5889 This->indexCache = new_cache;
5890 This->indexCacheSize = new_size;
5893 This->indexCacheLen++;
5894 last_run = &This->indexCache[This->indexCacheLen-1];
5895 last_run->firstSector = next_sector;
5896 last_run->firstOffset = next_offset;
5899 last_run->lastOffset = next_offset;
5901 /* Find the next block. */
5903 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5904 if (FAILED(hr)) return hr;
5907 if (This->indexCacheLen)
5909 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5910 This->numBlocks = last_run->lastOffset+1;
5914 This->tailIndex = BLOCK_END_OF_CHAIN;
5915 This->numBlocks = 0;
5921 /* Locate the nth block in this stream. */
5922 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5924 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5925 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5927 if (offset >= This->numBlocks)
5928 return BLOCK_END_OF_CHAIN;
5930 while (min_run < max_run)
5932 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5933 if (offset < This->indexCache[run_to_check].firstOffset)
5935 max_offset = This->indexCache[run_to_check].firstOffset-1;
5936 max_run = run_to_check-1;
5938 else if (offset > This->indexCache[run_to_check].lastOffset)
5940 min_offset = This->indexCache[run_to_check].lastOffset+1;
5941 min_run = run_to_check+1;
5944 /* Block is in this run. */
5945 min_run = max_run = run_to_check;
5948 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5951 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
5952 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
5954 BlockChainBlock *result=NULL;
5958 if (This->cachedBlocks[i].index == index)
5960 *sector = This->cachedBlocks[i].sector;
5961 *block = &This->cachedBlocks[i];
5965 *sector = BlockChainStream_GetSectorOfOffset(This, index);
5966 if (*sector == BLOCK_END_OF_CHAIN)
5967 return STG_E_DOCFILECORRUPT;
5971 if (This->cachedBlocks[0].index == 0xffffffff)
5972 result = &This->cachedBlocks[0];
5973 else if (This->cachedBlocks[1].index == 0xffffffff)
5974 result = &This->cachedBlocks[1];
5977 result = &This->cachedBlocks[This->blockToEvict++];
5978 if (This->blockToEvict == 2)
5979 This->blockToEvict = 0;
5984 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
5985 return STG_E_WRITEFAULT;
5990 result->index = index;
5991 result->sector = *sector;
5998 BlockChainStream* BlockChainStream_Construct(
5999 StorageImpl* parentStorage,
6000 ULONG* headOfStreamPlaceHolder,
6003 BlockChainStream* newStream;
6005 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
6007 newStream->parentStorage = parentStorage;
6008 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6009 newStream->ownerDirEntry = dirEntry;
6010 newStream->indexCache = NULL;
6011 newStream->indexCacheLen = 0;
6012 newStream->indexCacheSize = 0;
6013 newStream->cachedBlocks[0].index = 0xffffffff;
6014 newStream->cachedBlocks[0].dirty = 0;
6015 newStream->cachedBlocks[1].index = 0xffffffff;
6016 newStream->cachedBlocks[1].dirty = 0;
6017 newStream->blockToEvict = 0;
6019 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
6021 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
6022 HeapFree(GetProcessHeap(), 0, newStream);
6029 HRESULT BlockChainStream_Flush(BlockChainStream* This)
6032 if (!This) return S_OK;
6035 if (This->cachedBlocks[i].dirty)
6037 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6038 This->cachedBlocks[i].dirty = 0;
6040 return STG_E_WRITEFAULT;
6046 void BlockChainStream_Destroy(BlockChainStream* This)
6050 BlockChainStream_Flush(This);
6051 HeapFree(GetProcessHeap(), 0, This->indexCache);
6053 HeapFree(GetProcessHeap(), 0, This);
6056 /******************************************************************************
6057 * BlockChainStream_GetHeadOfChain
6059 * Returns the head of this stream chain.
6060 * Some special chains don't have directory entries, their heads are kept in
6061 * This->headOfStreamPlaceHolder.
6064 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6066 DirEntry chainEntry;
6069 if (This->headOfStreamPlaceHolder != 0)
6070 return *(This->headOfStreamPlaceHolder);
6072 if (This->ownerDirEntry != DIRENTRY_NULL)
6074 hr = StorageImpl_ReadDirEntry(
6075 This->parentStorage,
6076 This->ownerDirEntry,
6081 return chainEntry.startingBlock;
6085 return BLOCK_END_OF_CHAIN;
6088 /******************************************************************************
6089 * BlockChainStream_GetCount
6091 * Returns the number of blocks that comprises this chain.
6092 * This is not the size of the stream as the last block may not be full!
6094 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6096 return This->numBlocks;
6099 /******************************************************************************
6100 * BlockChainStream_ReadAt
6102 * Reads a specified number of bytes from this chain at the specified offset.
6103 * bytesRead may be NULL.
6104 * Failure will be returned if the specified number of bytes has not been read.
6106 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6107 ULARGE_INTEGER offset,
6112 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6113 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6114 ULONG bytesToReadInBuffer;
6117 ULARGE_INTEGER stream_size;
6119 BlockChainBlock *cachedBlock;
6121 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6124 * Find the first block in the stream that contains part of the buffer.
6126 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6130 stream_size = BlockChainStream_GetSize(This);
6131 if (stream_size.QuadPart > offset.QuadPart)
6132 size = min(stream_size.QuadPart - offset.QuadPart, size);
6137 * Start reading the buffer.
6139 bufferWalker = buffer;
6143 ULARGE_INTEGER ulOffset;
6147 * Calculate how many bytes we can copy from this big block.
6149 bytesToReadInBuffer =
6150 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6152 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6159 /* Not in cache, and we're going to read past the end of the block. */
6160 ulOffset.u.HighPart = 0;
6161 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6164 StorageImpl_ReadAt(This->parentStorage,
6167 bytesToReadInBuffer,
6172 if (!cachedBlock->read)
6174 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6175 return STG_E_READFAULT;
6177 cachedBlock->read = 1;
6180 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6181 bytesReadAt = bytesToReadInBuffer;
6184 blockNoInSequence++;
6185 bufferWalker += bytesReadAt;
6186 size -= bytesReadAt;
6187 *bytesRead += bytesReadAt;
6188 offsetInBlock = 0; /* There is no offset on the next block */
6190 if (bytesToReadInBuffer != bytesReadAt)
6197 /******************************************************************************
6198 * BlockChainStream_WriteAt
6200 * Writes the specified number of bytes to this chain at the specified offset.
6201 * Will fail if not all specified number of bytes have been written.
6203 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6204 ULARGE_INTEGER offset,
6207 ULONG* bytesWritten)
6209 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6210 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6213 const BYTE* bufferWalker;
6215 BlockChainBlock *cachedBlock;
6218 bufferWalker = buffer;
6222 ULARGE_INTEGER ulOffset;
6223 DWORD bytesWrittenAt;
6226 * Calculate how many bytes we can copy to this big block.
6229 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6231 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6233 /* BlockChainStream_SetSize should have already been called to ensure we have
6234 * enough blocks in the chain to write into */
6237 ERR("not enough blocks in chain to write data\n");
6243 /* Not in cache, and we're going to write past the end of the block. */
6244 ulOffset.u.HighPart = 0;
6245 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6248 StorageImpl_WriteAt(This->parentStorage,
6256 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6258 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6259 return STG_E_READFAULT;
6262 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6263 bytesWrittenAt = bytesToWrite;
6264 cachedBlock->read = 1;
6265 cachedBlock->dirty = 1;
6268 blockNoInSequence++;
6269 bufferWalker += bytesWrittenAt;
6270 size -= bytesWrittenAt;
6271 *bytesWritten += bytesWrittenAt;
6272 offsetInBlock = 0; /* There is no offset on the next block */
6274 if (bytesWrittenAt != bytesToWrite)
6278 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6281 /******************************************************************************
6282 * BlockChainStream_Shrink
6284 * Shrinks this chain in the big block depot.
6286 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6287 ULARGE_INTEGER newSize)
6294 * Figure out how many blocks are needed to contain the new size
6296 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6298 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6304 * Go to the new end of chain
6306 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6308 /* Mark the new end of chain */
6309 StorageImpl_SetNextBlockInChain(
6310 This->parentStorage,
6312 BLOCK_END_OF_CHAIN);
6314 This->tailIndex = blockIndex;
6318 if (This->headOfStreamPlaceHolder != 0)
6320 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6324 DirEntry chainEntry;
6325 assert(This->ownerDirEntry != DIRENTRY_NULL);
6327 StorageImpl_ReadDirEntry(
6328 This->parentStorage,
6329 This->ownerDirEntry,
6332 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6334 StorageImpl_WriteDirEntry(
6335 This->parentStorage,
6336 This->ownerDirEntry,
6340 This->tailIndex = BLOCK_END_OF_CHAIN;
6343 This->numBlocks = numBlocks;
6346 * Mark the extra blocks as free
6348 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6350 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6351 StorageImpl_FreeBigBlock(This->parentStorage,
6352 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6353 if (last_run->lastOffset == last_run->firstOffset)
6354 This->indexCacheLen--;
6356 last_run->lastOffset--;
6360 * Reset the last accessed block cache.
6364 if (This->cachedBlocks[i].index >= numBlocks)
6366 This->cachedBlocks[i].index = 0xffffffff;
6367 This->cachedBlocks[i].dirty = 0;
6374 /******************************************************************************
6375 * BlockChainStream_Enlarge
6377 * Grows this chain in the big block depot.
6379 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6380 ULARGE_INTEGER newSize)
6382 ULONG blockIndex, currentBlock;
6384 ULONG oldNumBlocks = 0;
6386 blockIndex = BlockChainStream_GetHeadOfChain(This);
6389 * Empty chain. Create the head.
6391 if (blockIndex == BLOCK_END_OF_CHAIN)
6393 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6394 StorageImpl_SetNextBlockInChain(This->parentStorage,
6396 BLOCK_END_OF_CHAIN);
6398 if (This->headOfStreamPlaceHolder != 0)
6400 *(This->headOfStreamPlaceHolder) = blockIndex;
6404 DirEntry chainEntry;
6405 assert(This->ownerDirEntry != DIRENTRY_NULL);
6407 StorageImpl_ReadDirEntry(
6408 This->parentStorage,
6409 This->ownerDirEntry,
6412 chainEntry.startingBlock = blockIndex;
6414 StorageImpl_WriteDirEntry(
6415 This->parentStorage,
6416 This->ownerDirEntry,
6420 This->tailIndex = blockIndex;
6421 This->numBlocks = 1;
6425 * Figure out how many blocks are needed to contain this stream
6427 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6429 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6433 * Go to the current end of chain
6435 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6437 currentBlock = blockIndex;
6439 while (blockIndex != BLOCK_END_OF_CHAIN)
6442 currentBlock = blockIndex;
6444 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6449 This->tailIndex = currentBlock;
6452 currentBlock = This->tailIndex;
6453 oldNumBlocks = This->numBlocks;
6456 * Add new blocks to the chain
6458 if (oldNumBlocks < newNumBlocks)
6460 while (oldNumBlocks < newNumBlocks)
6462 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6464 StorageImpl_SetNextBlockInChain(
6465 This->parentStorage,
6469 StorageImpl_SetNextBlockInChain(
6470 This->parentStorage,
6472 BLOCK_END_OF_CHAIN);
6474 currentBlock = blockIndex;
6478 This->tailIndex = blockIndex;
6479 This->numBlocks = newNumBlocks;
6482 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6488 /******************************************************************************
6489 * BlockChainStream_SetSize
6491 * Sets the size of this stream. The big block depot will be updated.
6492 * The file will grow if we grow the chain.
6494 * TODO: Free the actual blocks in the file when we shrink the chain.
6495 * Currently, the blocks are still in the file. So the file size
6496 * doesn't shrink even if we shrink streams.
6498 BOOL BlockChainStream_SetSize(
6499 BlockChainStream* This,
6500 ULARGE_INTEGER newSize)
6502 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6504 if (newSize.u.LowPart == size.u.LowPart)
6507 if (newSize.u.LowPart < size.u.LowPart)
6509 BlockChainStream_Shrink(This, newSize);
6513 BlockChainStream_Enlarge(This, newSize);
6519 /******************************************************************************
6520 * BlockChainStream_GetSize
6522 * Returns the size of this chain.
6523 * Will return the block count if this chain doesn't have a directory entry.
6525 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6527 DirEntry chainEntry;
6529 if(This->headOfStreamPlaceHolder == NULL)
6532 * This chain has a directory entry so use the size value from there.
6534 StorageImpl_ReadDirEntry(
6535 This->parentStorage,
6536 This->ownerDirEntry,
6539 return chainEntry.size;
6544 * this chain is a chain that does not have a directory entry, figure out the
6545 * size by making the product number of used blocks times the
6548 ULARGE_INTEGER result;
6549 result.u.HighPart = 0;
6552 BlockChainStream_GetCount(This) *
6553 This->parentStorage->bigBlockSize;
6559 /******************************************************************************
6560 ** SmallBlockChainStream implementation
6563 SmallBlockChainStream* SmallBlockChainStream_Construct(
6564 StorageImpl* parentStorage,
6565 ULONG* headOfStreamPlaceHolder,
6568 SmallBlockChainStream* newStream;
6570 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6572 newStream->parentStorage = parentStorage;
6573 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6574 newStream->ownerDirEntry = dirEntry;
6579 void SmallBlockChainStream_Destroy(
6580 SmallBlockChainStream* This)
6582 HeapFree(GetProcessHeap(), 0, This);
6585 /******************************************************************************
6586 * SmallBlockChainStream_GetHeadOfChain
6588 * Returns the head of this chain of small blocks.
6590 static ULONG SmallBlockChainStream_GetHeadOfChain(
6591 SmallBlockChainStream* This)
6593 DirEntry chainEntry;
6596 if (This->headOfStreamPlaceHolder != NULL)
6597 return *(This->headOfStreamPlaceHolder);
6599 if (This->ownerDirEntry)
6601 hr = StorageImpl_ReadDirEntry(
6602 This->parentStorage,
6603 This->ownerDirEntry,
6608 return chainEntry.startingBlock;
6613 return BLOCK_END_OF_CHAIN;
6616 /******************************************************************************
6617 * SmallBlockChainStream_GetNextBlockInChain
6619 * Returns the index of the next small block in this chain.
6622 * - BLOCK_END_OF_CHAIN: end of this chain
6623 * - BLOCK_UNUSED: small block 'blockIndex' is free
6625 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6626 SmallBlockChainStream* This,
6628 ULONG* nextBlockInChain)
6630 ULARGE_INTEGER offsetOfBlockInDepot;
6635 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6637 offsetOfBlockInDepot.u.HighPart = 0;
6638 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6641 * Read those bytes in the buffer from the small block file.
6643 res = BlockChainStream_ReadAt(
6644 This->parentStorage->smallBlockDepotChain,
6645 offsetOfBlockInDepot,
6650 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6651 res = STG_E_READFAULT;
6655 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6662 /******************************************************************************
6663 * SmallBlockChainStream_SetNextBlockInChain
6665 * Writes the index of the next block of the specified block in the small
6667 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6668 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6670 static void SmallBlockChainStream_SetNextBlockInChain(
6671 SmallBlockChainStream* This,
6675 ULARGE_INTEGER offsetOfBlockInDepot;
6679 offsetOfBlockInDepot.u.HighPart = 0;
6680 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6682 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6685 * Read those bytes in the buffer from the small block file.
6687 BlockChainStream_WriteAt(
6688 This->parentStorage->smallBlockDepotChain,
6689 offsetOfBlockInDepot,
6695 /******************************************************************************
6696 * SmallBlockChainStream_FreeBlock
6698 * Flag small block 'blockIndex' as free in the small block depot.
6700 static void SmallBlockChainStream_FreeBlock(
6701 SmallBlockChainStream* This,
6704 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6707 /******************************************************************************
6708 * SmallBlockChainStream_GetNextFreeBlock
6710 * Returns the index of a free small block. The small block depot will be
6711 * enlarged if necessary. The small block chain will also be enlarged if
6714 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6715 SmallBlockChainStream* This)
6717 ULARGE_INTEGER offsetOfBlockInDepot;
6720 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6721 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6723 ULONG smallBlocksPerBigBlock;
6725 ULONG blocksRequired;
6726 ULARGE_INTEGER old_size, size_required;
6728 offsetOfBlockInDepot.u.HighPart = 0;
6731 * Scan the small block depot for a free block
6733 while (nextBlockIndex != BLOCK_UNUSED)
6735 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6737 res = BlockChainStream_ReadAt(
6738 This->parentStorage->smallBlockDepotChain,
6739 offsetOfBlockInDepot,
6745 * If we run out of space for the small block depot, enlarge it
6747 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6749 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6751 if (nextBlockIndex != BLOCK_UNUSED)
6757 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6759 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6760 ULARGE_INTEGER newSize, offset;
6763 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6764 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6767 * Initialize all the small blocks to free
6769 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6770 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6771 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6772 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6774 StorageImpl_SaveFileHeader(This->parentStorage);
6778 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6780 smallBlocksPerBigBlock =
6781 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6784 * Verify if we have to allocate big blocks to contain small blocks
6786 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6788 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6790 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6792 if (size_required.QuadPart > old_size.QuadPart)
6794 BlockChainStream_SetSize(
6795 This->parentStorage->smallBlockRootChain,
6798 StorageImpl_ReadDirEntry(
6799 This->parentStorage,
6800 This->parentStorage->base.storageDirEntry,
6803 rootEntry.size = size_required;
6805 StorageImpl_WriteDirEntry(
6806 This->parentStorage,
6807 This->parentStorage->base.storageDirEntry,
6814 /******************************************************************************
6815 * SmallBlockChainStream_ReadAt
6817 * Reads a specified number of bytes from this chain at the specified offset.
6818 * bytesRead may be NULL.
6819 * Failure will be returned if the specified number of bytes has not been read.
6821 HRESULT SmallBlockChainStream_ReadAt(
6822 SmallBlockChainStream* This,
6823 ULARGE_INTEGER offset,
6829 ULARGE_INTEGER offsetInBigBlockFile;
6830 ULONG blockNoInSequence =
6831 offset.u.LowPart / This->parentStorage->smallBlockSize;
6833 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6834 ULONG bytesToReadInBuffer;
6836 ULONG bytesReadFromBigBlockFile;
6838 ULARGE_INTEGER stream_size;
6841 * This should never happen on a small block file.
6843 assert(offset.u.HighPart==0);
6847 stream_size = SmallBlockChainStream_GetSize(This);
6848 if (stream_size.QuadPart > offset.QuadPart)
6849 size = min(stream_size.QuadPart - offset.QuadPart, size);
6854 * Find the first block in the stream that contains part of the buffer.
6856 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6858 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6860 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6863 blockNoInSequence--;
6867 * Start reading the buffer.
6869 bufferWalker = buffer;
6871 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6874 * Calculate how many bytes we can copy from this small block.
6876 bytesToReadInBuffer =
6877 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6880 * Calculate the offset of the small block in the small block file.
6882 offsetInBigBlockFile.u.HighPart = 0;
6883 offsetInBigBlockFile.u.LowPart =
6884 blockIndex * This->parentStorage->smallBlockSize;
6886 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6889 * Read those bytes in the buffer from the small block file.
6890 * The small block has already been identified so it shouldn't fail
6891 * unless the file is corrupt.
6893 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6894 offsetInBigBlockFile,
6895 bytesToReadInBuffer,
6897 &bytesReadFromBigBlockFile);
6902 if (!bytesReadFromBigBlockFile)
6903 return STG_E_DOCFILECORRUPT;
6906 * Step to the next big block.
6908 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6910 return STG_E_DOCFILECORRUPT;
6912 bufferWalker += bytesReadFromBigBlockFile;
6913 size -= bytesReadFromBigBlockFile;
6914 *bytesRead += bytesReadFromBigBlockFile;
6915 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6921 /******************************************************************************
6922 * SmallBlockChainStream_WriteAt
6924 * Writes the specified number of bytes to this chain at the specified offset.
6925 * Will fail if not all specified number of bytes have been written.
6927 HRESULT SmallBlockChainStream_WriteAt(
6928 SmallBlockChainStream* This,
6929 ULARGE_INTEGER offset,
6932 ULONG* bytesWritten)
6934 ULARGE_INTEGER offsetInBigBlockFile;
6935 ULONG blockNoInSequence =
6936 offset.u.LowPart / This->parentStorage->smallBlockSize;
6938 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6939 ULONG bytesToWriteInBuffer;
6941 ULONG bytesWrittenToBigBlockFile;
6942 const BYTE* bufferWalker;
6946 * This should never happen on a small block file.
6948 assert(offset.u.HighPart==0);
6951 * Find the first block in the stream that contains part of the buffer.
6953 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6955 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6957 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6958 return STG_E_DOCFILECORRUPT;
6959 blockNoInSequence--;
6963 * Start writing the buffer.
6966 bufferWalker = buffer;
6967 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6970 * Calculate how many bytes we can copy to this small block.
6972 bytesToWriteInBuffer =
6973 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6976 * Calculate the offset of the small block in the small block file.
6978 offsetInBigBlockFile.u.HighPart = 0;
6979 offsetInBigBlockFile.u.LowPart =
6980 blockIndex * This->parentStorage->smallBlockSize;
6982 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6985 * Write those bytes in the buffer to the small block file.
6987 res = BlockChainStream_WriteAt(
6988 This->parentStorage->smallBlockRootChain,
6989 offsetInBigBlockFile,
6990 bytesToWriteInBuffer,
6992 &bytesWrittenToBigBlockFile);
6997 * Step to the next big block.
6999 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7002 bufferWalker += bytesWrittenToBigBlockFile;
7003 size -= bytesWrittenToBigBlockFile;
7004 *bytesWritten += bytesWrittenToBigBlockFile;
7005 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7008 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7011 /******************************************************************************
7012 * SmallBlockChainStream_Shrink
7014 * Shrinks this chain in the small block depot.
7016 static BOOL SmallBlockChainStream_Shrink(
7017 SmallBlockChainStream* This,
7018 ULARGE_INTEGER newSize)
7020 ULONG blockIndex, extraBlock;
7024 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7026 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7029 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7032 * Go to the new end of chain
7034 while (count < numBlocks)
7036 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7043 * If the count is 0, we have a special case, the head of the chain was
7048 DirEntry chainEntry;
7050 StorageImpl_ReadDirEntry(This->parentStorage,
7051 This->ownerDirEntry,
7054 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7056 StorageImpl_WriteDirEntry(This->parentStorage,
7057 This->ownerDirEntry,
7061 * We start freeing the chain at the head block.
7063 extraBlock = blockIndex;
7067 /* Get the next block before marking the new end */
7068 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7072 /* Mark the new end of chain */
7073 SmallBlockChainStream_SetNextBlockInChain(
7076 BLOCK_END_OF_CHAIN);
7080 * Mark the extra blocks as free
7082 while (extraBlock != BLOCK_END_OF_CHAIN)
7084 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7087 SmallBlockChainStream_FreeBlock(This, extraBlock);
7088 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7089 extraBlock = blockIndex;
7095 /******************************************************************************
7096 * SmallBlockChainStream_Enlarge
7098 * Grows this chain in the small block depot.
7100 static BOOL SmallBlockChainStream_Enlarge(
7101 SmallBlockChainStream* This,
7102 ULARGE_INTEGER newSize)
7104 ULONG blockIndex, currentBlock;
7106 ULONG oldNumBlocks = 0;
7108 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7111 * Empty chain. Create the head.
7113 if (blockIndex == BLOCK_END_OF_CHAIN)
7115 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7116 SmallBlockChainStream_SetNextBlockInChain(
7119 BLOCK_END_OF_CHAIN);
7121 if (This->headOfStreamPlaceHolder != NULL)
7123 *(This->headOfStreamPlaceHolder) = blockIndex;
7127 DirEntry chainEntry;
7129 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7132 chainEntry.startingBlock = blockIndex;
7134 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7139 currentBlock = blockIndex;
7142 * Figure out how many blocks are needed to contain this stream
7144 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7146 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7150 * Go to the current end of chain
7152 while (blockIndex != BLOCK_END_OF_CHAIN)
7155 currentBlock = blockIndex;
7156 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7161 * Add new blocks to the chain
7163 while (oldNumBlocks < newNumBlocks)
7165 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7166 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7168 SmallBlockChainStream_SetNextBlockInChain(
7171 BLOCK_END_OF_CHAIN);
7173 currentBlock = blockIndex;
7180 /******************************************************************************
7181 * SmallBlockChainStream_SetSize
7183 * Sets the size of this stream.
7184 * The file will grow if we grow the chain.
7186 * TODO: Free the actual blocks in the file when we shrink the chain.
7187 * Currently, the blocks are still in the file. So the file size
7188 * doesn't shrink even if we shrink streams.
7190 BOOL SmallBlockChainStream_SetSize(
7191 SmallBlockChainStream* This,
7192 ULARGE_INTEGER newSize)
7194 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7196 if (newSize.u.LowPart == size.u.LowPart)
7199 if (newSize.u.LowPart < size.u.LowPart)
7201 SmallBlockChainStream_Shrink(This, newSize);
7205 SmallBlockChainStream_Enlarge(This, newSize);
7211 /******************************************************************************
7212 * SmallBlockChainStream_GetCount
7214 * Returns the number of small blocks that comprises this chain.
7215 * This is not the size of the stream as the last block may not be full!
7218 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7223 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7225 while(blockIndex != BLOCK_END_OF_CHAIN)
7229 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7230 blockIndex, &blockIndex)))
7237 /******************************************************************************
7238 * SmallBlockChainStream_GetSize
7240 * Returns the size of this chain.
7242 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7244 DirEntry chainEntry;
7246 if(This->headOfStreamPlaceHolder != NULL)
7248 ULARGE_INTEGER result;
7249 result.u.HighPart = 0;
7251 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7252 This->parentStorage->smallBlockSize;
7257 StorageImpl_ReadDirEntry(
7258 This->parentStorage,
7259 This->ownerDirEntry,
7262 return chainEntry.size;
7265 static HRESULT create_storagefile(
7269 STGOPTIONS* pStgOptions,
7273 StorageBaseImpl* newStorage = 0;
7274 HANDLE hFile = INVALID_HANDLE_VALUE;
7275 HRESULT hr = STG_E_INVALIDFLAG;
7279 DWORD fileAttributes;
7280 WCHAR tempFileName[MAX_PATH];
7283 return STG_E_INVALIDPOINTER;
7285 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7286 return STG_E_INVALIDPARAMETER;
7288 /* if no share mode given then DENY_NONE is the default */
7289 if (STGM_SHARE_MODE(grfMode) == 0)
7290 grfMode |= STGM_SHARE_DENY_NONE;
7292 if ( FAILED( validateSTGM(grfMode) ))
7295 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7296 switch(STGM_ACCESS_MODE(grfMode))
7299 case STGM_READWRITE:
7305 /* in direct mode, can only use SHARE_EXCLUSIVE */
7306 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7309 /* but in transacted mode, any share mode is valid */
7312 * Generate a unique name.
7316 WCHAR tempPath[MAX_PATH];
7317 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7319 memset(tempPath, 0, sizeof(tempPath));
7320 memset(tempFileName, 0, sizeof(tempFileName));
7322 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7325 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7326 pwcsName = tempFileName;
7329 hr = STG_E_INSUFFICIENTMEMORY;
7333 creationMode = TRUNCATE_EXISTING;
7337 creationMode = GetCreationModeFromSTGM(grfMode);
7341 * Interpret the STGM value grfMode
7343 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7344 accessMode = GetAccessModeFromSTGM(grfMode);
7346 if (grfMode & STGM_DELETEONRELEASE)
7347 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7349 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7351 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7355 FIXME("Storage share mode not implemented.\n");
7360 hFile = CreateFileW(pwcsName,
7368 if (hFile == INVALID_HANDLE_VALUE)
7370 if(GetLastError() == ERROR_FILE_EXISTS)
7371 hr = STG_E_FILEALREADYEXISTS;
7378 * Allocate and initialize the new IStorage32object.
7380 hr = Storage_Construct(
7387 pStgOptions->ulSectorSize,
7395 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
7397 IStorage_Release((IStorage*)newStorage);
7400 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7405 /******************************************************************************
7406 * StgCreateDocfile [OLE32.@]
7407 * Creates a new compound file storage object
7410 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7411 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7412 * reserved [ ?] unused?, usually 0
7413 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7416 * S_OK if the file was successfully created
7417 * some STG_E_ value if error
7419 * if pwcsName is NULL, create file with new unique name
7420 * the function can returns
7421 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7424 HRESULT WINAPI StgCreateDocfile(
7428 IStorage **ppstgOpen)
7430 STGOPTIONS stgoptions = {1, 0, 512};
7432 TRACE("(%s, %x, %d, %p)\n",
7433 debugstr_w(pwcsName), grfMode,
7434 reserved, ppstgOpen);
7437 return STG_E_INVALIDPOINTER;
7439 return STG_E_INVALIDPARAMETER;
7441 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7444 /******************************************************************************
7445 * StgCreateStorageEx [OLE32.@]
7447 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7449 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7450 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7452 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7454 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7455 return STG_E_INVALIDPARAMETER;
7458 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7460 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7461 return STG_E_INVALIDPARAMETER;
7464 if (stgfmt == STGFMT_FILE)
7466 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7467 return STG_E_INVALIDPARAMETER;
7470 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7472 STGOPTIONS defaultOptions = {1, 0, 512};
7474 if (!pStgOptions) pStgOptions = &defaultOptions;
7475 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7479 ERR("Invalid stgfmt argument\n");
7480 return STG_E_INVALIDPARAMETER;
7483 /******************************************************************************
7484 * StgCreatePropSetStg [OLE32.@]
7486 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7487 IPropertySetStorage **ppPropSetStg)
7491 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
7493 hr = STG_E_INVALIDPARAMETER;
7495 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
7496 (void**)ppPropSetStg);
7500 /******************************************************************************
7501 * StgOpenStorageEx [OLE32.@]
7503 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7505 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7506 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7508 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7510 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7511 return STG_E_INVALIDPARAMETER;
7517 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7518 return STG_E_INVALIDPARAMETER;
7520 case STGFMT_STORAGE:
7523 case STGFMT_DOCFILE:
7524 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7526 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7527 return STG_E_INVALIDPARAMETER;
7529 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7533 WARN("STGFMT_ANY assuming storage\n");
7537 return STG_E_INVALIDPARAMETER;
7540 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7544 /******************************************************************************
7545 * StgOpenStorage [OLE32.@]
7547 HRESULT WINAPI StgOpenStorage(
7548 const OLECHAR *pwcsName,
7549 IStorage *pstgPriority,
7553 IStorage **ppstgOpen)
7555 StorageBaseImpl* newStorage = 0;
7561 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7562 debugstr_w(pwcsName), pstgPriority, grfMode,
7563 snbExclude, reserved, ppstgOpen);
7567 hr = STG_E_INVALIDNAME;
7573 hr = STG_E_INVALIDPOINTER;
7579 hr = STG_E_INVALIDPARAMETER;
7583 if (grfMode & STGM_PRIORITY)
7585 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7586 return STG_E_INVALIDFLAG;
7587 if (grfMode & STGM_DELETEONRELEASE)
7588 return STG_E_INVALIDFUNCTION;
7589 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7590 return STG_E_INVALIDFLAG;
7591 grfMode &= ~0xf0; /* remove the existing sharing mode */
7592 grfMode |= STGM_SHARE_DENY_NONE;
7594 /* STGM_PRIORITY stops other IStorage objects on the same file from
7595 * committing until the STGM_PRIORITY IStorage is closed. it also
7596 * stops non-transacted mode StgOpenStorage calls with write access from
7597 * succeeding. obviously, both of these cannot be achieved through just
7598 * file share flags */
7599 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7603 * Validate the sharing mode
7605 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7606 switch(STGM_SHARE_MODE(grfMode))
7608 case STGM_SHARE_EXCLUSIVE:
7609 case STGM_SHARE_DENY_WRITE:
7612 hr = STG_E_INVALIDFLAG;
7616 if ( FAILED( validateSTGM(grfMode) ) ||
7617 (grfMode&STGM_CREATE))
7619 hr = STG_E_INVALIDFLAG;
7623 /* shared reading requires transacted mode */
7624 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7625 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7626 !(grfMode&STGM_TRANSACTED) )
7628 hr = STG_E_INVALIDFLAG;
7633 * Interpret the STGM value grfMode
7635 shareMode = GetShareModeFromSTGM(grfMode);
7636 accessMode = GetAccessModeFromSTGM(grfMode);
7640 hFile = CreateFileW( pwcsName,
7645 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7648 if (hFile==INVALID_HANDLE_VALUE)
7650 DWORD last_error = GetLastError();
7656 case ERROR_FILE_NOT_FOUND:
7657 hr = STG_E_FILENOTFOUND;
7660 case ERROR_PATH_NOT_FOUND:
7661 hr = STG_E_PATHNOTFOUND;
7664 case ERROR_ACCESS_DENIED:
7665 case ERROR_WRITE_PROTECT:
7666 hr = STG_E_ACCESSDENIED;
7669 case ERROR_SHARING_VIOLATION:
7670 hr = STG_E_SHAREVIOLATION;
7681 * Refuse to open the file if it's too small to be a structured storage file
7682 * FIXME: verify the file when reading instead of here
7684 if (GetFileSize(hFile, NULL) < 0x100)
7687 hr = STG_E_FILEALREADYEXISTS;
7692 * Allocate and initialize the new IStorage32object.
7694 hr = Storage_Construct(
7707 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7709 if(hr == STG_E_INVALIDHEADER)
7710 hr = STG_E_FILEALREADYEXISTS;
7715 * Get an "out" pointer for the caller.
7717 *ppstgOpen = (IStorage*)newStorage;
7720 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7724 /******************************************************************************
7725 * StgCreateDocfileOnILockBytes [OLE32.@]
7727 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7731 IStorage** ppstgOpen)
7733 StorageBaseImpl* newStorage = 0;
7736 if ((ppstgOpen == 0) || (plkbyt == 0))
7737 return STG_E_INVALIDPOINTER;
7740 * Allocate and initialize the new IStorage object.
7742 hr = Storage_Construct(
7758 * Get an "out" pointer for the caller.
7760 *ppstgOpen = (IStorage*)newStorage;
7765 /******************************************************************************
7766 * StgOpenStorageOnILockBytes [OLE32.@]
7768 HRESULT WINAPI StgOpenStorageOnILockBytes(
7770 IStorage *pstgPriority,
7774 IStorage **ppstgOpen)
7776 StorageBaseImpl* newStorage = 0;
7779 if ((plkbyt == 0) || (ppstgOpen == 0))
7780 return STG_E_INVALIDPOINTER;
7782 if ( FAILED( validateSTGM(grfMode) ))
7783 return STG_E_INVALIDFLAG;
7788 * Allocate and initialize the new IStorage object.
7790 hr = Storage_Construct(
7806 * Get an "out" pointer for the caller.
7808 *ppstgOpen = (IStorage*)newStorage;
7813 /******************************************************************************
7814 * StgSetTimes [ole32.@]
7815 * StgSetTimes [OLE32.@]
7819 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7820 FILETIME const *patime, FILETIME const *pmtime)
7822 IStorage *stg = NULL;
7825 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7827 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7831 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7832 IStorage_Release(stg);
7838 /******************************************************************************
7839 * StgIsStorageILockBytes [OLE32.@]
7841 * Determines if the ILockBytes contains a storage object.
7843 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7846 ULARGE_INTEGER offset;
7848 offset.u.HighPart = 0;
7849 offset.u.LowPart = 0;
7851 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
7853 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
7859 /******************************************************************************
7860 * WriteClassStg [OLE32.@]
7862 * This method will store the specified CLSID in the specified storage object
7864 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7869 return E_INVALIDARG;
7872 return STG_E_INVALIDPOINTER;
7874 hRes = IStorage_SetClass(pStg, rclsid);
7879 /***********************************************************************
7880 * ReadClassStg (OLE32.@)
7882 * This method reads the CLSID previously written to a storage object with
7883 * the WriteClassStg.
7886 * pstg [I] IStorage pointer
7887 * pclsid [O] Pointer to where the CLSID is written
7891 * Failure: HRESULT code.
7893 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7898 TRACE("(%p, %p)\n", pstg, pclsid);
7900 if(!pstg || !pclsid)
7901 return E_INVALIDARG;
7904 * read a STATSTG structure (contains the clsid) from the storage
7906 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7909 *pclsid=pstatstg.clsid;
7914 /***********************************************************************
7915 * OleLoadFromStream (OLE32.@)
7917 * This function loads an object from stream
7919 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7923 LPPERSISTSTREAM xstm;
7925 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7927 res=ReadClassStm(pStm,&clsid);
7930 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7933 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7935 IUnknown_Release((IUnknown*)*ppvObj);
7938 res=IPersistStream_Load(xstm,pStm);
7939 IPersistStream_Release(xstm);
7940 /* FIXME: all refcounts ok at this point? I think they should be:
7943 * xstm : 0 (released)
7948 /***********************************************************************
7949 * OleSaveToStream (OLE32.@)
7951 * This function saves an object with the IPersistStream interface on it
7952 * to the specified stream.
7954 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7960 TRACE("(%p,%p)\n",pPStm,pStm);
7962 res=IPersistStream_GetClassID(pPStm,&clsid);
7964 if (SUCCEEDED(res)){
7966 res=WriteClassStm(pStm,&clsid);
7970 res=IPersistStream_Save(pPStm,pStm,TRUE);
7973 TRACE("Finished Save\n");
7977 /****************************************************************************
7978 * This method validate a STGM parameter that can contain the values below
7980 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7981 * The stgm values contained in 0xffff0000 are bitmasks.
7983 * STGM_DIRECT 0x00000000
7984 * STGM_TRANSACTED 0x00010000
7985 * STGM_SIMPLE 0x08000000
7987 * STGM_READ 0x00000000
7988 * STGM_WRITE 0x00000001
7989 * STGM_READWRITE 0x00000002
7991 * STGM_SHARE_DENY_NONE 0x00000040
7992 * STGM_SHARE_DENY_READ 0x00000030
7993 * STGM_SHARE_DENY_WRITE 0x00000020
7994 * STGM_SHARE_EXCLUSIVE 0x00000010
7996 * STGM_PRIORITY 0x00040000
7997 * STGM_DELETEONRELEASE 0x04000000
7999 * STGM_CREATE 0x00001000
8000 * STGM_CONVERT 0x00020000
8001 * STGM_FAILIFTHERE 0x00000000
8003 * STGM_NOSCRATCH 0x00100000
8004 * STGM_NOSNAPSHOT 0x00200000
8006 static HRESULT validateSTGM(DWORD stgm)
8008 DWORD access = STGM_ACCESS_MODE(stgm);
8009 DWORD share = STGM_SHARE_MODE(stgm);
8010 DWORD create = STGM_CREATE_MODE(stgm);
8012 if (stgm&~STGM_KNOWN_FLAGS)
8014 ERR("unknown flags %08x\n", stgm);
8022 case STGM_READWRITE:
8030 case STGM_SHARE_DENY_NONE:
8031 case STGM_SHARE_DENY_READ:
8032 case STGM_SHARE_DENY_WRITE:
8033 case STGM_SHARE_EXCLUSIVE:
8042 case STGM_FAILIFTHERE:
8049 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8051 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8055 * STGM_CREATE | STGM_CONVERT
8056 * if both are false, STGM_FAILIFTHERE is set to TRUE
8058 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8062 * STGM_NOSCRATCH requires STGM_TRANSACTED
8064 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8068 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8069 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8071 if ( (stgm & STGM_NOSNAPSHOT) &&
8072 (!(stgm & STGM_TRANSACTED) ||
8073 share == STGM_SHARE_EXCLUSIVE ||
8074 share == STGM_SHARE_DENY_WRITE) )
8080 /****************************************************************************
8081 * GetShareModeFromSTGM
8083 * This method will return a share mode flag from a STGM value.
8084 * The STGM value is assumed valid.
8086 static DWORD GetShareModeFromSTGM(DWORD stgm)
8088 switch (STGM_SHARE_MODE(stgm))
8090 case STGM_SHARE_DENY_NONE:
8091 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8092 case STGM_SHARE_DENY_READ:
8093 return FILE_SHARE_WRITE;
8094 case STGM_SHARE_DENY_WRITE:
8095 return FILE_SHARE_READ;
8096 case STGM_SHARE_EXCLUSIVE:
8099 ERR("Invalid share mode!\n");
8104 /****************************************************************************
8105 * GetAccessModeFromSTGM
8107 * This method will return an access mode flag from a STGM value.
8108 * The STGM value is assumed valid.
8110 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8112 switch (STGM_ACCESS_MODE(stgm))
8115 return GENERIC_READ;
8117 case STGM_READWRITE:
8118 return GENERIC_READ | GENERIC_WRITE;
8120 ERR("Invalid access mode!\n");
8125 /****************************************************************************
8126 * GetCreationModeFromSTGM
8128 * This method will return a creation mode flag from a STGM value.
8129 * The STGM value is assumed valid.
8131 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8133 switch(STGM_CREATE_MODE(stgm))
8136 return CREATE_ALWAYS;
8138 FIXME("STGM_CONVERT not implemented!\n");
8140 case STGM_FAILIFTHERE:
8143 ERR("Invalid create mode!\n");
8149 /*************************************************************************
8150 * OLECONVERT_LoadOLE10 [Internal]
8152 * Loads the OLE10 STREAM to memory
8155 * pOleStream [I] The OLESTREAM
8156 * pData [I] Data Structure for the OLESTREAM Data
8160 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8161 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8164 * This function is used by OleConvertOLESTREAMToIStorage only.
8166 * Memory allocated for pData must be freed by the caller
8168 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8171 HRESULT hRes = S_OK;
8175 pData->pData = NULL;
8176 pData->pstrOleObjFileName = NULL;
8178 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8181 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8182 if(dwSize != sizeof(pData->dwOleID))
8184 hRes = CONVERT10_E_OLESTREAM_GET;
8186 else if(pData->dwOleID != OLESTREAM_ID)
8188 hRes = CONVERT10_E_OLESTREAM_FMT;
8199 /* Get the TypeID... more info needed for this field */
8200 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8201 if(dwSize != sizeof(pData->dwTypeID))
8203 hRes = CONVERT10_E_OLESTREAM_GET;
8208 if(pData->dwTypeID != 0)
8210 /* Get the length of the OleTypeName */
8211 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8212 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8214 hRes = CONVERT10_E_OLESTREAM_GET;
8219 if(pData->dwOleTypeNameLength > 0)
8221 /* Get the OleTypeName */
8222 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8223 if(dwSize != pData->dwOleTypeNameLength)
8225 hRes = CONVERT10_E_OLESTREAM_GET;
8231 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8232 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8234 hRes = CONVERT10_E_OLESTREAM_GET;
8238 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8239 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8240 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8241 if(pData->pstrOleObjFileName)
8243 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8244 if(dwSize != pData->dwOleObjFileNameLength)
8246 hRes = CONVERT10_E_OLESTREAM_GET;
8250 hRes = CONVERT10_E_OLESTREAM_GET;
8255 /* Get the Width of the Metafile */
8256 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8257 if(dwSize != sizeof(pData->dwMetaFileWidth))
8259 hRes = CONVERT10_E_OLESTREAM_GET;
8263 /* Get the Height of the Metafile */
8264 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8265 if(dwSize != sizeof(pData->dwMetaFileHeight))
8267 hRes = CONVERT10_E_OLESTREAM_GET;
8273 /* Get the Length of the Data */
8274 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8275 if(dwSize != sizeof(pData->dwDataLength))
8277 hRes = CONVERT10_E_OLESTREAM_GET;
8281 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8283 if(!bStrem1) /* if it is a second OLE stream data */
8285 pData->dwDataLength -= 8;
8286 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8287 if(dwSize != sizeof(pData->strUnknown))
8289 hRes = CONVERT10_E_OLESTREAM_GET;
8295 if(pData->dwDataLength > 0)
8297 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8299 /* Get Data (ex. IStorage, Metafile, or BMP) */
8302 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8303 if(dwSize != pData->dwDataLength)
8305 hRes = CONVERT10_E_OLESTREAM_GET;
8310 hRes = CONVERT10_E_OLESTREAM_GET;
8319 /*************************************************************************
8320 * OLECONVERT_SaveOLE10 [Internal]
8322 * Saves the OLE10 STREAM From memory
8325 * pData [I] Data Structure for the OLESTREAM Data
8326 * pOleStream [I] The OLESTREAM to save
8330 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8333 * This function is used by OleConvertIStorageToOLESTREAM only.
8336 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8339 HRESULT hRes = S_OK;
8343 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8344 if(dwSize != sizeof(pData->dwOleID))
8346 hRes = CONVERT10_E_OLESTREAM_PUT;
8351 /* Set the TypeID */
8352 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8353 if(dwSize != sizeof(pData->dwTypeID))
8355 hRes = CONVERT10_E_OLESTREAM_PUT;
8359 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8361 /* Set the Length of the OleTypeName */
8362 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8363 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8365 hRes = CONVERT10_E_OLESTREAM_PUT;
8370 if(pData->dwOleTypeNameLength > 0)
8372 /* Set the OleTypeName */
8373 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8374 if(dwSize != pData->dwOleTypeNameLength)
8376 hRes = CONVERT10_E_OLESTREAM_PUT;
8383 /* Set the width of the Metafile */
8384 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8385 if(dwSize != sizeof(pData->dwMetaFileWidth))
8387 hRes = CONVERT10_E_OLESTREAM_PUT;
8393 /* Set the height of the Metafile */
8394 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8395 if(dwSize != sizeof(pData->dwMetaFileHeight))
8397 hRes = CONVERT10_E_OLESTREAM_PUT;
8403 /* Set the length of the Data */
8404 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8405 if(dwSize != sizeof(pData->dwDataLength))
8407 hRes = CONVERT10_E_OLESTREAM_PUT;
8413 if(pData->dwDataLength > 0)
8415 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8416 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8417 if(dwSize != pData->dwDataLength)
8419 hRes = CONVERT10_E_OLESTREAM_PUT;
8427 /*************************************************************************
8428 * OLECONVERT_GetOLE20FromOLE10[Internal]
8430 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8431 * opens it, and copies the content to the dest IStorage for
8432 * OleConvertOLESTREAMToIStorage
8436 * pDestStorage [I] The IStorage to copy the data to
8437 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8438 * nBufferLength [I] The size of the buffer
8447 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8451 IStorage *pTempStorage;
8452 DWORD dwNumOfBytesWritten;
8453 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8454 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8456 /* Create a temp File */
8457 GetTempPathW(MAX_PATH, wstrTempDir);
8458 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8459 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8461 if(hFile != INVALID_HANDLE_VALUE)
8463 /* Write IStorage Data to File */
8464 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8467 /* Open and copy temp storage to the Dest Storage */
8468 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8471 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8472 IStorage_Release(pTempStorage);
8474 DeleteFileW(wstrTempFile);
8479 /*************************************************************************
8480 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8482 * Saves the OLE10 STREAM From memory
8485 * pStorage [I] The Src IStorage to copy
8486 * pData [I] The Dest Memory to write to.
8489 * The size in bytes allocated for pData
8492 * Memory allocated for pData must be freed by the caller
8494 * Used by OleConvertIStorageToOLESTREAM only.
8497 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8501 DWORD nDataLength = 0;
8502 IStorage *pTempStorage;
8503 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8504 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8508 /* Create temp Storage */
8509 GetTempPathW(MAX_PATH, wstrTempDir);
8510 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8511 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8515 /* Copy Src Storage to the Temp Storage */
8516 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8517 IStorage_Release(pTempStorage);
8519 /* Open Temp Storage as a file and copy to memory */
8520 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8521 if(hFile != INVALID_HANDLE_VALUE)
8523 nDataLength = GetFileSize(hFile, NULL);
8524 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8525 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8528 DeleteFileW(wstrTempFile);
8533 /*************************************************************************
8534 * OLECONVERT_CreateOleStream [Internal]
8536 * Creates the "\001OLE" stream in the IStorage if necessary.
8539 * pStorage [I] Dest storage to create the stream in
8545 * This function is used by OleConvertOLESTREAMToIStorage only.
8547 * This stream is still unknown, MS Word seems to have extra data
8548 * but since the data is stored in the OLESTREAM there should be
8549 * no need to recreate the stream. If the stream is manually
8550 * deleted it will create it with this default data.
8553 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8557 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8558 BYTE pOleStreamHeader [] =
8560 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8561 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8562 0x00, 0x00, 0x00, 0x00
8565 /* Create stream if not present */
8566 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8567 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8571 /* Write default Data */
8572 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8573 IStream_Release(pStream);
8577 /* write a string to a stream, preceded by its length */
8578 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8585 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8586 r = IStream_Write( stm, &len, sizeof(len), NULL);
8591 str = CoTaskMemAlloc( len );
8592 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8593 r = IStream_Write( stm, str, len, NULL);
8594 CoTaskMemFree( str );
8598 /* read a string preceded by its length from a stream */
8599 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8602 DWORD len, count = 0;
8606 r = IStream_Read( stm, &len, sizeof(len), &count );
8609 if( count != sizeof(len) )
8610 return E_OUTOFMEMORY;
8612 TRACE("%d bytes\n",len);
8614 str = CoTaskMemAlloc( len );
8616 return E_OUTOFMEMORY;
8618 r = IStream_Read( stm, str, len, &count );
8623 CoTaskMemFree( str );
8624 return E_OUTOFMEMORY;
8627 TRACE("Read string %s\n",debugstr_an(str,len));
8629 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8630 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8632 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8633 CoTaskMemFree( str );
8641 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8642 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8646 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8648 static const BYTE unknown1[12] =
8649 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8650 0xFF, 0xFF, 0xFF, 0xFF};
8651 static const BYTE unknown2[16] =
8652 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8653 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8655 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8656 debugstr_w(lpszUserType), debugstr_w(szClipName),
8657 debugstr_w(szProgIDName));
8659 /* Create a CompObj stream */
8660 r = IStorage_CreateStream(pstg, szwStreamName,
8661 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8665 /* Write CompObj Structure to stream */
8666 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8668 if( SUCCEEDED( r ) )
8669 r = WriteClassStm( pstm, clsid );
8671 if( SUCCEEDED( r ) )
8672 r = STREAM_WriteString( pstm, lpszUserType );
8673 if( SUCCEEDED( r ) )
8674 r = STREAM_WriteString( pstm, szClipName );
8675 if( SUCCEEDED( r ) )
8676 r = STREAM_WriteString( pstm, szProgIDName );
8677 if( SUCCEEDED( r ) )
8678 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8680 IStream_Release( pstm );
8685 /***********************************************************************
8686 * WriteFmtUserTypeStg (OLE32.@)
8688 HRESULT WINAPI WriteFmtUserTypeStg(
8689 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8692 WCHAR szwClipName[0x40];
8693 CLSID clsid = CLSID_NULL;
8694 LPWSTR wstrProgID = NULL;
8697 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8699 /* get the clipboard format name */
8700 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8703 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8705 /* FIXME: There's room to save a CLSID and its ProgID, but
8706 the CLSID is not looked up in the registry and in all the
8707 tests I wrote it was CLSID_NULL. Where does it come from?
8710 /* get the real program ID. This may fail, but that's fine */
8711 ProgIDFromCLSID(&clsid, &wstrProgID);
8713 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8715 r = STORAGE_WriteCompObj( pstg, &clsid,
8716 lpszUserType, szwClipName, wstrProgID );
8718 CoTaskMemFree(wstrProgID);
8724 /******************************************************************************
8725 * ReadFmtUserTypeStg [OLE32.@]
8727 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8731 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8732 unsigned char unknown1[12];
8733 unsigned char unknown2[16];
8735 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8738 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8740 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8741 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8744 WARN("Failed to open stream r = %08x\n", r);
8748 /* read the various parts of the structure */
8749 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8750 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8752 r = ReadClassStm( stm, &clsid );
8756 r = STREAM_ReadString( stm, &szCLSIDName );
8760 r = STREAM_ReadString( stm, &szOleTypeName );
8764 r = STREAM_ReadString( stm, &szProgIDName );
8768 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8769 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8772 /* ok, success... now we just need to store what we found */
8774 *pcf = RegisterClipboardFormatW( szOleTypeName );
8775 CoTaskMemFree( szOleTypeName );
8777 if( lplpszUserType )
8778 *lplpszUserType = szCLSIDName;
8779 CoTaskMemFree( szProgIDName );
8782 IStream_Release( stm );
8788 /*************************************************************************
8789 * OLECONVERT_CreateCompObjStream [Internal]
8791 * Creates a "\001CompObj" is the destination IStorage if necessary.
8794 * pStorage [I] The dest IStorage to create the CompObj Stream
8796 * strOleTypeName [I] The ProgID
8800 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8803 * This function is used by OleConvertOLESTREAMToIStorage only.
8805 * The stream data is stored in the OLESTREAM and there should be
8806 * no need to recreate the stream. If the stream is manually
8807 * deleted it will attempt to create it by querying the registry.
8811 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8814 HRESULT hStorageRes, hRes = S_OK;
8815 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8816 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8817 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8819 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8820 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8822 /* Initialize the CompObj structure */
8823 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8824 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8825 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8828 /* Create a CompObj stream if it doesn't exist */
8829 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8830 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8831 if(hStorageRes == S_OK)
8833 /* copy the OleTypeName to the compobj struct */
8834 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8835 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8837 /* copy the OleTypeName to the compobj struct */
8838 /* Note: in the test made, these were Identical */
8839 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8840 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8843 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8844 bufferW, OLESTREAM_MAX_STR_LEN );
8845 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8851 /* Get the CLSID Default Name from the Registry */
8852 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8853 if(hErr == ERROR_SUCCESS)
8855 char strTemp[OLESTREAM_MAX_STR_LEN];
8856 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8857 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8858 if(hErr == ERROR_SUCCESS)
8860 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8866 /* Write CompObj Structure to stream */
8867 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8869 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8871 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8872 if(IStorageCompObj.dwCLSIDNameLength > 0)
8874 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8876 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8877 if(IStorageCompObj.dwOleTypeNameLength > 0)
8879 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8881 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8882 if(IStorageCompObj.dwProgIDNameLength > 0)
8884 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8886 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8887 IStream_Release(pStream);
8893 /*************************************************************************
8894 * OLECONVERT_CreateOlePresStream[Internal]
8896 * Creates the "\002OlePres000" Stream with the Metafile data
8899 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8900 * dwExtentX [I] Width of the Metafile
8901 * dwExtentY [I] Height of the Metafile
8902 * pData [I] Metafile data
8903 * dwDataLength [I] Size of the Metafile data
8907 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8910 * This function is used by OleConvertOLESTREAMToIStorage only.
8913 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8917 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8918 BYTE pOlePresStreamHeader [] =
8920 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8921 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8922 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8923 0x00, 0x00, 0x00, 0x00
8926 BYTE pOlePresStreamHeaderEmpty [] =
8928 0x00, 0x00, 0x00, 0x00,
8929 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8930 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8931 0x00, 0x00, 0x00, 0x00
8934 /* Create the OlePres000 Stream */
8935 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8936 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8941 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8943 memset(&OlePres, 0, sizeof(OlePres));
8944 /* Do we have any metafile data to save */
8945 if(dwDataLength > 0)
8947 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8948 nHeaderSize = sizeof(pOlePresStreamHeader);
8952 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8953 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8955 /* Set width and height of the metafile */
8956 OlePres.dwExtentX = dwExtentX;
8957 OlePres.dwExtentY = -dwExtentY;
8959 /* Set Data and Length */
8960 if(dwDataLength > sizeof(METAFILEPICT16))
8962 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8963 OlePres.pData = &(pData[8]);
8965 /* Save OlePres000 Data to Stream */
8966 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8967 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8968 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8969 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8970 if(OlePres.dwSize > 0)
8972 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8974 IStream_Release(pStream);
8978 /*************************************************************************
8979 * OLECONVERT_CreateOle10NativeStream [Internal]
8981 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8984 * pStorage [I] Dest storage to create the stream in
8985 * pData [I] Ole10 Native Data (ex. bmp)
8986 * dwDataLength [I] Size of the Ole10 Native Data
8992 * This function is used by OleConvertOLESTREAMToIStorage only.
8994 * Might need to verify the data and return appropriate error message
8997 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
9001 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9003 /* Create the Ole10Native Stream */
9004 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9005 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9009 /* Write info to stream */
9010 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9011 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9012 IStream_Release(pStream);
9017 /*************************************************************************
9018 * OLECONVERT_GetOLE10ProgID [Internal]
9020 * Finds the ProgID (or OleTypeID) from the IStorage
9023 * pStorage [I] The Src IStorage to get the ProgID
9024 * strProgID [I] the ProgID string to get
9025 * dwSize [I] the size of the string
9029 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9032 * This function is used by OleConvertIStorageToOLESTREAM only.
9036 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9040 LARGE_INTEGER iSeekPos;
9041 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9042 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9044 /* Open the CompObj Stream */
9045 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9046 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9050 /*Get the OleType from the CompObj Stream */
9051 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9052 iSeekPos.u.HighPart = 0;
9054 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9055 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9056 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9057 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9058 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9059 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9060 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9062 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9065 IStream_Read(pStream, strProgID, *dwSize, NULL);
9067 IStream_Release(pStream);
9072 LPOLESTR wstrProgID;
9074 /* Get the OleType from the registry */
9075 REFCLSID clsid = &(stat.clsid);
9076 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9077 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9080 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9087 /*************************************************************************
9088 * OLECONVERT_GetOle10PresData [Internal]
9090 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9093 * pStorage [I] Src IStroage
9094 * pOleStream [I] Dest OleStream Mem Struct
9100 * This function is used by OleConvertIStorageToOLESTREAM only.
9102 * Memory allocated for pData must be freed by the caller
9106 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9111 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9113 /* Initialize Default data for OLESTREAM */
9114 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9115 pOleStreamData[0].dwTypeID = 2;
9116 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9117 pOleStreamData[1].dwTypeID = 0;
9118 pOleStreamData[0].dwMetaFileWidth = 0;
9119 pOleStreamData[0].dwMetaFileHeight = 0;
9120 pOleStreamData[0].pData = NULL;
9121 pOleStreamData[1].pData = NULL;
9123 /* Open Ole10Native Stream */
9124 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9125 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9129 /* Read Size and Data */
9130 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9131 if(pOleStreamData->dwDataLength > 0)
9133 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9134 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9136 IStream_Release(pStream);
9142 /*************************************************************************
9143 * OLECONVERT_GetOle20PresData[Internal]
9145 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9148 * pStorage [I] Src IStroage
9149 * pOleStreamData [I] Dest OleStream Mem Struct
9155 * This function is used by OleConvertIStorageToOLESTREAM only.
9157 * Memory allocated for pData must be freed by the caller
9159 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9163 OLECONVERT_ISTORAGE_OLEPRES olePress;
9164 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9166 /* Initialize Default data for OLESTREAM */
9167 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9168 pOleStreamData[0].dwTypeID = 2;
9169 pOleStreamData[0].dwMetaFileWidth = 0;
9170 pOleStreamData[0].dwMetaFileHeight = 0;
9171 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9172 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9173 pOleStreamData[1].dwTypeID = 0;
9174 pOleStreamData[1].dwOleTypeNameLength = 0;
9175 pOleStreamData[1].strOleTypeName[0] = 0;
9176 pOleStreamData[1].dwMetaFileWidth = 0;
9177 pOleStreamData[1].dwMetaFileHeight = 0;
9178 pOleStreamData[1].pData = NULL;
9179 pOleStreamData[1].dwDataLength = 0;
9182 /* Open OlePress000 stream */
9183 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9184 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9187 LARGE_INTEGER iSeekPos;
9188 METAFILEPICT16 MetaFilePict;
9189 static const char strMetafilePictName[] = "METAFILEPICT";
9191 /* Set the TypeID for a Metafile */
9192 pOleStreamData[1].dwTypeID = 5;
9194 /* Set the OleTypeName to Metafile */
9195 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9196 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9198 iSeekPos.u.HighPart = 0;
9199 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9201 /* Get Presentation Data */
9202 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9203 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9204 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9205 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9207 /*Set width and Height */
9208 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9209 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9210 if(olePress.dwSize > 0)
9213 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9215 /* Set MetaFilePict struct */
9216 MetaFilePict.mm = 8;
9217 MetaFilePict.xExt = olePress.dwExtentX;
9218 MetaFilePict.yExt = olePress.dwExtentY;
9219 MetaFilePict.hMF = 0;
9221 /* Get Metafile Data */
9222 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9223 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9224 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9226 IStream_Release(pStream);
9230 /*************************************************************************
9231 * OleConvertOLESTREAMToIStorage [OLE32.@]
9236 * DVTARGETDEVICE parameter is not handled
9237 * Still unsure of some mem fields for OLE 10 Stream
9238 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9239 * and "\001OLE" streams
9242 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9243 LPOLESTREAM pOleStream,
9245 const DVTARGETDEVICE* ptd)
9249 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9251 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9253 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9257 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9260 if(pstg == NULL || pOleStream == NULL)
9262 hRes = E_INVALIDARG;
9267 /* Load the OLESTREAM to Memory */
9268 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9273 /* Load the OLESTREAM to Memory (part 2)*/
9274 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9280 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9282 /* Do we have the IStorage Data in the OLESTREAM */
9283 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9285 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9286 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9290 /* It must be an original OLE 1.0 source */
9291 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9296 /* It must be an original OLE 1.0 source */
9297 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9300 /* Create CompObj Stream if necessary */
9301 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9304 /*Create the Ole Stream if necessary */
9305 OLECONVERT_CreateOleStream(pstg);
9310 /* Free allocated memory */
9311 for(i=0; i < 2; i++)
9313 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9314 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9315 pOleStreamData[i].pstrOleObjFileName = NULL;
9320 /*************************************************************************
9321 * OleConvertIStorageToOLESTREAM [OLE32.@]
9328 * Still unsure of some mem fields for OLE 10 Stream
9329 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9330 * and "\001OLE" streams.
9333 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9335 LPOLESTREAM pOleStream)
9338 HRESULT hRes = S_OK;
9340 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9341 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9343 TRACE("%p %p\n", pstg, pOleStream);
9345 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9347 if(pstg == NULL || pOleStream == NULL)
9349 hRes = E_INVALIDARG;
9353 /* Get the ProgID */
9354 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9355 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9359 /* Was it originally Ole10 */
9360 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9363 IStream_Release(pStream);
9364 /* Get Presentation Data for Ole10Native */
9365 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9369 /* Get Presentation Data (OLE20) */
9370 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9373 /* Save OLESTREAM */
9374 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9377 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9382 /* Free allocated memory */
9383 for(i=0; i < 2; i++)
9385 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9391 /***********************************************************************
9392 * GetConvertStg (OLE32.@)
9394 HRESULT WINAPI GetConvertStg(IStorage *stg) {
9395 FIXME("unimplemented stub!\n");
9399 /******************************************************************************
9400 * StgIsStorageFile [OLE32.@]
9401 * Verify if the file contains a storage object
9407 * S_OK if file has magic bytes as a storage object
9408 * S_FALSE if file is not storage
9411 StgIsStorageFile(LPCOLESTR fn)
9417 TRACE("%s\n", debugstr_w(fn));
9418 hf = CreateFileW(fn, GENERIC_READ,
9419 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9420 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9422 if (hf == INVALID_HANDLE_VALUE)
9423 return STG_E_FILENOTFOUND;
9425 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9427 WARN(" unable to read file\n");
9434 if (bytes_read != 8) {
9435 TRACE(" too short\n");
9439 if (!memcmp(magic,STORAGE_magic,8)) {
9444 TRACE(" -> Invalid header.\n");
9448 /***********************************************************************
9449 * WriteClassStm (OLE32.@)
9451 * Writes a CLSID to a stream.
9454 * pStm [I] Stream to write to.
9455 * rclsid [I] CLSID to write.
9459 * Failure: HRESULT code.
9461 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9463 TRACE("(%p,%p)\n",pStm,rclsid);
9465 if (!pStm || !rclsid)
9466 return E_INVALIDARG;
9468 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9471 /***********************************************************************
9472 * ReadClassStm (OLE32.@)
9474 * Reads a CLSID from a stream.
9477 * pStm [I] Stream to read from.
9478 * rclsid [O] CLSID to read.
9482 * Failure: HRESULT code.
9484 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9489 TRACE("(%p,%p)\n",pStm,pclsid);
9491 if (!pStm || !pclsid)
9492 return E_INVALIDARG;
9494 /* clear the output args */
9495 *pclsid = CLSID_NULL;
9497 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
9502 if (nbByte != sizeof(CLSID))
9503 return STG_E_READFAULT;