2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootEntryName[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base;
82 * Entry in the parent's stream tracking list
84 struct list ParentListEntry;
86 StorageBaseImpl *parentStorage;
88 typedef struct StorageInternalImpl StorageInternalImpl;
90 /* Method definitions for the Storage32InternalImpl class. */
91 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
92 DWORD openFlags, DirRef storageDirEntry);
93 static void StorageImpl_Destroy(StorageBaseImpl* iface);
94 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
95 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
96 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
97 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
98 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
99 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
100 static void StorageImpl_SaveFileHeader(StorageImpl* This);
102 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
103 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
104 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
105 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
106 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
108 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
109 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
110 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
112 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
113 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
114 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
115 ULONG blockIndex, ULONG offset, DWORD value);
116 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
117 ULONG blockIndex, ULONG offset, DWORD* value);
119 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
120 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
122 typedef struct TransactedDirEntry
124 /* If applicable, a reference to the original DirEntry in the transacted
125 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
126 DirRef transactedParentEntry;
128 /* True if this entry is being used. */
131 /* True if data is up to date. */
134 /* True if this entry has been modified. */
137 /* True if this entry's stream has been modified. */
140 /* True if this entry has been deleted in the transacted storage, but the
141 * delete has not yet been committed. */
144 /* If this entry's stream has been modified, a reference to where the stream
145 * is stored in the snapshot file. */
148 /* This directory entry's data, including any changes that have been made. */
151 /* A reference to the parent of this node. This is only valid while we are
152 * committing changes. */
155 /* A reference to a newly-created entry in the transacted parent. This is
156 * always equal to transactedParentEntry except when committing changes. */
157 DirRef newTransactedParentEntry;
158 } TransactedDirEntry;
160 /****************************************************************************
161 * Transacted storage object.
163 typedef struct TransactedSnapshotImpl
165 struct StorageBaseImpl base;
168 * Modified streams are temporarily saved to the scratch file.
170 StorageBaseImpl *scratch;
172 /* The directory structure is kept here, so that we can track how these
173 * entries relate to those in the parent storage. */
174 TransactedDirEntry *entries;
176 ULONG firstFreeEntry;
179 * Changes are committed to the transacted parent.
181 StorageBaseImpl *transactedParent;
182 } TransactedSnapshotImpl;
184 /* Generic function to create a transacted wrapper for a direct storage object. */
185 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
187 /* OLESTREAM memory structure to use for Get and Put Routines */
188 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
193 DWORD dwOleTypeNameLength;
194 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
195 CHAR *pstrOleObjFileName;
196 DWORD dwOleObjFileNameLength;
197 DWORD dwMetaFileWidth;
198 DWORD dwMetaFileHeight;
199 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
202 }OLECONVERT_OLESTREAM_DATA;
204 /* CompObj Stream structure */
205 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
210 DWORD dwCLSIDNameLength;
211 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
212 DWORD dwOleTypeNameLength;
213 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
214 DWORD dwProgIDNameLength;
215 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
217 }OLECONVERT_ISTORAGE_COMPOBJ;
220 /* Ole Presentation Stream structure */
221 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
229 }OLECONVERT_ISTORAGE_OLEPRES;
233 /***********************************************************************
234 * Forward declaration of internal functions used by the method DestroyElement
236 static HRESULT deleteStorageContents(
237 StorageBaseImpl *parentStorage,
238 DirRef indexToDelete,
239 DirEntry entryDataToDelete);
241 static HRESULT deleteStreamContents(
242 StorageBaseImpl *parentStorage,
243 DirRef indexToDelete,
244 DirEntry entryDataToDelete);
246 static HRESULT removeFromTree(
247 StorageBaseImpl *This,
248 DirRef parentStorageIndex,
249 DirRef deletedIndex);
251 /***********************************************************************
252 * Declaration of the functions used to manipulate DirEntry
255 static HRESULT insertIntoTree(
256 StorageBaseImpl *This,
257 DirRef parentStorageIndex,
258 DirRef newEntryIndex);
260 static LONG entryNameCmp(
261 const OLECHAR *name1,
262 const OLECHAR *name2);
264 static DirRef findElement(
265 StorageBaseImpl *storage,
270 static HRESULT findTreeParent(
271 StorageBaseImpl *storage,
273 const OLECHAR *childName,
274 DirEntry *parentData,
278 /***********************************************************************
279 * Declaration of miscellaneous functions...
281 static HRESULT validateSTGM(DWORD stgmValue);
283 static DWORD GetShareModeFromSTGM(DWORD stgm);
284 static DWORD GetAccessModeFromSTGM(DWORD stgm);
285 static DWORD GetCreationModeFromSTGM(DWORD stgm);
287 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
290 /****************************************************************************
291 * IEnumSTATSTGImpl definitions.
293 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
294 * This class allows iterating through the content of a storage and to find
295 * specific items inside it.
297 struct IEnumSTATSTGImpl
299 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
300 * since we want to cast this in an IEnumSTATSTG pointer */
302 LONG ref; /* Reference count */
303 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
304 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
306 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
310 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
311 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
313 /************************************************************************
317 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
319 return (index+1) * This->bigBlockSize;
322 /************************************************************************
323 ** Storage32BaseImpl implementation
325 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
326 ULARGE_INTEGER offset,
331 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
334 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
335 ULARGE_INTEGER offset,
340 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
343 /************************************************************************
344 * Storage32BaseImpl_QueryInterface (IUnknown)
346 * This method implements the common QueryInterface for all IStorage32
347 * implementations contained in this file.
349 * See Windows documentation for more details on IUnknown methods.
351 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
356 StorageBaseImpl *This = (StorageBaseImpl *)iface;
358 if ( (This==0) || (ppvObject==0) )
363 if (IsEqualGUID(&IID_IUnknown, riid) ||
364 IsEqualGUID(&IID_IStorage, riid))
368 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
370 *ppvObject = &This->pssVtbl;
374 return E_NOINTERFACE;
376 IStorage_AddRef(iface);
381 /************************************************************************
382 * Storage32BaseImpl_AddRef (IUnknown)
384 * This method implements the common AddRef for all IStorage32
385 * implementations contained in this file.
387 * See Windows documentation for more details on IUnknown methods.
389 static ULONG WINAPI StorageBaseImpl_AddRef(
392 StorageBaseImpl *This = (StorageBaseImpl *)iface;
393 ULONG ref = InterlockedIncrement(&This->ref);
395 TRACE("(%p) AddRef to %d\n", This, ref);
400 /************************************************************************
401 * Storage32BaseImpl_Release (IUnknown)
403 * This method implements the common Release for all IStorage32
404 * implementations contained in this file.
406 * See Windows documentation for more details on IUnknown methods.
408 static ULONG WINAPI StorageBaseImpl_Release(
411 StorageBaseImpl *This = (StorageBaseImpl *)iface;
413 ULONG ref = InterlockedDecrement(&This->ref);
415 TRACE("(%p) ReleaseRef to %d\n", This, ref);
420 * Since we are using a system of base-classes, we want to call the
421 * destructor of the appropriate derived class. To do this, we are
422 * using virtual functions to implement the destructor.
424 StorageBaseImpl_Destroy(This);
430 /************************************************************************
431 * Storage32BaseImpl_OpenStream (IStorage)
433 * This method will open the specified stream object from the current storage.
435 * See Windows documentation for more details on IStorage methods.
437 static HRESULT WINAPI StorageBaseImpl_OpenStream(
439 const OLECHAR* pwcsName, /* [string][in] */
440 void* reserved1, /* [unique][in] */
441 DWORD grfMode, /* [in] */
442 DWORD reserved2, /* [in] */
443 IStream** ppstm) /* [out] */
445 StorageBaseImpl *This = (StorageBaseImpl *)iface;
446 StgStreamImpl* newStream;
447 DirEntry currentEntry;
448 DirRef streamEntryRef;
449 HRESULT res = STG_E_UNKNOWN;
451 TRACE("(%p, %s, %p, %x, %d, %p)\n",
452 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
454 if ( (pwcsName==NULL) || (ppstm==0) )
462 if ( FAILED( validateSTGM(grfMode) ) ||
463 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
465 res = STG_E_INVALIDFLAG;
472 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
474 res = STG_E_INVALIDFUNCTION;
480 res = STG_E_REVERTED;
485 * Check that we're compatible with the parent's storage mode, but
486 * only if we are not in transacted mode
488 if(!(This->openFlags & STGM_TRANSACTED)) {
489 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
491 res = STG_E_INVALIDFLAG;
497 * Search for the element with the given name
499 streamEntryRef = findElement(
501 This->storageDirEntry,
506 * If it was found, construct the stream object and return a pointer to it.
508 if ( (streamEntryRef!=DIRENTRY_NULL) &&
509 (currentEntry.stgType==STGTY_STREAM) )
511 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
513 /* A single stream cannot be opened a second time. */
514 res = STG_E_ACCESSDENIED;
518 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
522 newStream->grfMode = grfMode;
523 *ppstm = (IStream*)newStream;
525 IStream_AddRef(*ppstm);
535 res = STG_E_FILENOTFOUND;
539 TRACE("<-- IStream %p\n", *ppstm);
540 TRACE("<-- %08x\n", res);
544 /************************************************************************
545 * Storage32BaseImpl_OpenStorage (IStorage)
547 * This method will open a new storage object from the current storage.
549 * See Windows documentation for more details on IStorage methods.
551 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
553 const OLECHAR* pwcsName, /* [string][unique][in] */
554 IStorage* pstgPriority, /* [unique][in] */
555 DWORD grfMode, /* [in] */
556 SNB snbExclude, /* [unique][in] */
557 DWORD reserved, /* [in] */
558 IStorage** ppstg) /* [out] */
560 StorageBaseImpl *This = (StorageBaseImpl *)iface;
561 StorageInternalImpl* newStorage;
562 StorageBaseImpl* newTransactedStorage;
563 DirEntry currentEntry;
564 DirRef storageEntryRef;
565 HRESULT res = STG_E_UNKNOWN;
567 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
568 iface, debugstr_w(pwcsName), pstgPriority,
569 grfMode, snbExclude, reserved, ppstg);
571 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
577 if (This->openFlags & STGM_SIMPLE)
579 res = STG_E_INVALIDFUNCTION;
584 if (snbExclude != NULL)
586 res = STG_E_INVALIDPARAMETER;
590 if ( FAILED( validateSTGM(grfMode) ))
592 res = STG_E_INVALIDFLAG;
599 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
600 (grfMode & STGM_DELETEONRELEASE) ||
601 (grfMode & STGM_PRIORITY) )
603 res = STG_E_INVALIDFUNCTION;
608 return STG_E_REVERTED;
611 * Check that we're compatible with the parent's storage mode,
612 * but only if we are not transacted
614 if(!(This->openFlags & STGM_TRANSACTED)) {
615 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
617 res = STG_E_ACCESSDENIED;
624 storageEntryRef = findElement(
626 This->storageDirEntry,
630 if ( (storageEntryRef!=DIRENTRY_NULL) &&
631 (currentEntry.stgType==STGTY_STORAGE) )
633 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
635 /* A single storage cannot be opened a second time. */
636 res = STG_E_ACCESSDENIED;
640 newStorage = StorageInternalImpl_Construct(
647 if (grfMode & STGM_TRANSACTED)
649 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
653 HeapFree(GetProcessHeap(), 0, newStorage);
657 *ppstg = (IStorage*)newTransactedStorage;
661 *ppstg = (IStorage*)newStorage;
664 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
670 res = STG_E_INSUFFICIENTMEMORY;
674 res = STG_E_FILENOTFOUND;
677 TRACE("<-- %08x\n", res);
681 /************************************************************************
682 * Storage32BaseImpl_EnumElements (IStorage)
684 * This method will create an enumerator object that can be used to
685 * retrieve information about all the elements in the storage object.
687 * See Windows documentation for more details on IStorage methods.
689 static HRESULT WINAPI StorageBaseImpl_EnumElements(
691 DWORD reserved1, /* [in] */
692 void* reserved2, /* [size_is][unique][in] */
693 DWORD reserved3, /* [in] */
694 IEnumSTATSTG** ppenum) /* [out] */
696 StorageBaseImpl *This = (StorageBaseImpl *)iface;
697 IEnumSTATSTGImpl* newEnum;
699 TRACE("(%p, %d, %p, %d, %p)\n",
700 iface, reserved1, reserved2, reserved3, ppenum);
702 if ( (This==0) || (ppenum==0))
706 return STG_E_REVERTED;
708 newEnum = IEnumSTATSTGImpl_Construct(
710 This->storageDirEntry);
714 *ppenum = (IEnumSTATSTG*)newEnum;
716 IEnumSTATSTG_AddRef(*ppenum);
721 return E_OUTOFMEMORY;
724 /************************************************************************
725 * Storage32BaseImpl_Stat (IStorage)
727 * This method will retrieve information about this storage object.
729 * See Windows documentation for more details on IStorage methods.
731 static HRESULT WINAPI StorageBaseImpl_Stat(
733 STATSTG* pstatstg, /* [out] */
734 DWORD grfStatFlag) /* [in] */
736 StorageBaseImpl *This = (StorageBaseImpl *)iface;
737 DirEntry currentEntry;
738 HRESULT res = STG_E_UNKNOWN;
740 TRACE("(%p, %p, %x)\n",
741 iface, pstatstg, grfStatFlag);
743 if ( (This==0) || (pstatstg==0))
751 res = STG_E_REVERTED;
755 res = StorageBaseImpl_ReadDirEntry(
757 This->storageDirEntry,
762 StorageUtl_CopyDirEntryToSTATSTG(
768 pstatstg->grfMode = This->openFlags;
769 pstatstg->grfStateBits = This->stateBits;
775 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);
777 TRACE("<-- %08x\n", res);
781 /************************************************************************
782 * Storage32BaseImpl_RenameElement (IStorage)
784 * This method will rename the specified element.
786 * See Windows documentation for more details on IStorage methods.
788 static HRESULT WINAPI StorageBaseImpl_RenameElement(
790 const OLECHAR* pwcsOldName, /* [in] */
791 const OLECHAR* pwcsNewName) /* [in] */
793 StorageBaseImpl *This = (StorageBaseImpl *)iface;
794 DirEntry currentEntry;
795 DirRef currentEntryRef;
797 TRACE("(%p, %s, %s)\n",
798 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
801 return STG_E_REVERTED;
803 currentEntryRef = findElement(This,
804 This->storageDirEntry,
808 if (currentEntryRef != DIRENTRY_NULL)
811 * There is already an element with the new name
813 return STG_E_FILEALREADYEXISTS;
817 * Search for the old element name
819 currentEntryRef = findElement(This,
820 This->storageDirEntry,
824 if (currentEntryRef != DIRENTRY_NULL)
826 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
827 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
829 WARN("Element is already open; cannot rename.\n");
830 return STG_E_ACCESSDENIED;
833 /* Remove the element from its current position in the tree */
834 removeFromTree(This, This->storageDirEntry,
837 /* Change the name of the element */
838 strcpyW(currentEntry.name, pwcsNewName);
840 /* Delete any sibling links */
841 currentEntry.leftChild = DIRENTRY_NULL;
842 currentEntry.rightChild = DIRENTRY_NULL;
844 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
847 /* Insert the element in a new position in the tree */
848 insertIntoTree(This, This->storageDirEntry,
854 * There is no element with the old name
856 return STG_E_FILENOTFOUND;
862 /************************************************************************
863 * Storage32BaseImpl_CreateStream (IStorage)
865 * This method will create a stream object within this storage
867 * See Windows documentation for more details on IStorage methods.
869 static HRESULT WINAPI StorageBaseImpl_CreateStream(
871 const OLECHAR* pwcsName, /* [string][in] */
872 DWORD grfMode, /* [in] */
873 DWORD reserved1, /* [in] */
874 DWORD reserved2, /* [in] */
875 IStream** ppstm) /* [out] */
877 StorageBaseImpl *This = (StorageBaseImpl *)iface;
878 StgStreamImpl* newStream;
879 DirEntry currentEntry, newStreamEntry;
880 DirRef currentEntryRef, newStreamEntryRef;
883 TRACE("(%p, %s, %x, %d, %d, %p)\n",
884 iface, debugstr_w(pwcsName), grfMode,
885 reserved1, reserved2, ppstm);
888 return STG_E_INVALIDPOINTER;
891 return STG_E_INVALIDNAME;
893 if (reserved1 || reserved2)
894 return STG_E_INVALIDPARAMETER;
896 if ( FAILED( validateSTGM(grfMode) ))
897 return STG_E_INVALIDFLAG;
899 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
900 return STG_E_INVALIDFLAG;
903 return STG_E_REVERTED;
908 if ((grfMode & STGM_DELETEONRELEASE) ||
909 (grfMode & STGM_TRANSACTED))
910 return STG_E_INVALIDFUNCTION;
913 * Don't worry about permissions in transacted mode, as we can always write
914 * changes; we just can't always commit them.
916 if(!(This->openFlags & STGM_TRANSACTED)) {
917 /* Can't create a stream on read-only storage */
918 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
919 return STG_E_ACCESSDENIED;
921 /* Can't create a stream with greater access than the parent. */
922 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
923 return STG_E_ACCESSDENIED;
926 if(This->openFlags & STGM_SIMPLE)
927 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
931 currentEntryRef = findElement(This,
932 This->storageDirEntry,
936 if (currentEntryRef != DIRENTRY_NULL)
939 * An element with this name already exists
941 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
943 IStorage_DestroyElement(iface, pwcsName);
946 return STG_E_FILEALREADYEXISTS;
950 * memset the empty entry
952 memset(&newStreamEntry, 0, sizeof(DirEntry));
954 newStreamEntry.sizeOfNameString =
955 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
957 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
958 return STG_E_INVALIDNAME;
960 strcpyW(newStreamEntry.name, pwcsName);
962 newStreamEntry.stgType = STGTY_STREAM;
963 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
964 newStreamEntry.size.u.LowPart = 0;
965 newStreamEntry.size.u.HighPart = 0;
967 newStreamEntry.leftChild = DIRENTRY_NULL;
968 newStreamEntry.rightChild = DIRENTRY_NULL;
969 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
971 /* call CoFileTime to get the current time
976 /* newStreamEntry.clsid */
979 * Create an entry with the new data
981 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
986 * Insert the new entry in the parent storage's tree.
990 This->storageDirEntry,
994 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
999 * Open the stream to return it.
1001 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1005 *ppstm = (IStream*)newStream;
1007 IStream_AddRef(*ppstm);
1011 return STG_E_INSUFFICIENTMEMORY;
1017 /************************************************************************
1018 * Storage32BaseImpl_SetClass (IStorage)
1020 * This method will write the specified CLSID in the directory entry of this
1023 * See Windows documentation for more details on IStorage methods.
1025 static HRESULT WINAPI StorageBaseImpl_SetClass(
1027 REFCLSID clsid) /* [in] */
1029 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1031 DirEntry currentEntry;
1033 TRACE("(%p, %p)\n", iface, clsid);
1036 return STG_E_REVERTED;
1038 hRes = StorageBaseImpl_ReadDirEntry(This,
1039 This->storageDirEntry,
1041 if (SUCCEEDED(hRes))
1043 currentEntry.clsid = *clsid;
1045 hRes = StorageBaseImpl_WriteDirEntry(This,
1046 This->storageDirEntry,
1053 /************************************************************************
1054 ** Storage32Impl implementation
1057 /************************************************************************
1058 * Storage32BaseImpl_CreateStorage (IStorage)
1060 * This method will create the storage object within the provided storage.
1062 * See Windows documentation for more details on IStorage methods.
1064 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1066 const OLECHAR *pwcsName, /* [string][in] */
1067 DWORD grfMode, /* [in] */
1068 DWORD reserved1, /* [in] */
1069 DWORD reserved2, /* [in] */
1070 IStorage **ppstg) /* [out] */
1072 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1074 DirEntry currentEntry;
1076 DirRef currentEntryRef;
1080 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1081 iface, debugstr_w(pwcsName), grfMode,
1082 reserved1, reserved2, ppstg);
1085 return STG_E_INVALIDPOINTER;
1087 if (This->openFlags & STGM_SIMPLE)
1089 return STG_E_INVALIDFUNCTION;
1093 return STG_E_INVALIDNAME;
1097 if ( FAILED( validateSTGM(grfMode) ) ||
1098 (grfMode & STGM_DELETEONRELEASE) )
1100 WARN("bad grfMode: 0x%x\n", grfMode);
1101 return STG_E_INVALIDFLAG;
1105 return STG_E_REVERTED;
1108 * Check that we're compatible with the parent's storage mode
1110 if ( !(This->openFlags & STGM_TRANSACTED) &&
1111 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1113 WARN("access denied\n");
1114 return STG_E_ACCESSDENIED;
1117 currentEntryRef = findElement(This,
1118 This->storageDirEntry,
1122 if (currentEntryRef != DIRENTRY_NULL)
1125 * An element with this name already exists
1127 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1128 ((This->openFlags & STGM_TRANSACTED) ||
1129 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1131 hr = IStorage_DestroyElement(iface, pwcsName);
1137 WARN("file already exists\n");
1138 return STG_E_FILEALREADYEXISTS;
1141 else if (!(This->openFlags & STGM_TRANSACTED) &&
1142 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1144 WARN("read-only storage\n");
1145 return STG_E_ACCESSDENIED;
1148 memset(&newEntry, 0, sizeof(DirEntry));
1150 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1152 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1154 FIXME("name too long\n");
1155 return STG_E_INVALIDNAME;
1158 strcpyW(newEntry.name, pwcsName);
1160 newEntry.stgType = STGTY_STORAGE;
1161 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1162 newEntry.size.u.LowPart = 0;
1163 newEntry.size.u.HighPart = 0;
1165 newEntry.leftChild = DIRENTRY_NULL;
1166 newEntry.rightChild = DIRENTRY_NULL;
1167 newEntry.dirRootEntry = DIRENTRY_NULL;
1169 /* call CoFileTime to get the current time
1174 /* newEntry.clsid */
1177 * Create a new directory entry for the storage
1179 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1184 * Insert the new directory entry into the parent storage's tree
1186 hr = insertIntoTree(
1188 This->storageDirEntry,
1192 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1197 * Open it to get a pointer to return.
1199 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1201 if( (hr != S_OK) || (*ppstg == NULL))
1211 /***************************************************************************
1215 * Reserve a directory entry in the file and initialize it.
1217 static HRESULT StorageImpl_CreateDirEntry(
1218 StorageBaseImpl *base,
1219 const DirEntry *newData,
1222 StorageImpl *storage = (StorageImpl*)base;
1223 ULONG currentEntryIndex = 0;
1224 ULONG newEntryIndex = DIRENTRY_NULL;
1226 BYTE currentData[RAW_DIRENTRY_SIZE];
1227 WORD sizeOfNameString;
1231 hr = StorageImpl_ReadRawDirEntry(storage,
1237 StorageUtl_ReadWord(
1239 OFFSET_PS_NAMELENGTH,
1242 if (sizeOfNameString == 0)
1245 * The entry exists and is available, we found it.
1247 newEntryIndex = currentEntryIndex;
1253 * We exhausted the directory entries, we will create more space below
1255 newEntryIndex = currentEntryIndex;
1257 currentEntryIndex++;
1259 } while (newEntryIndex == DIRENTRY_NULL);
1262 * grow the directory stream
1266 BYTE emptyData[RAW_DIRENTRY_SIZE];
1267 ULARGE_INTEGER newSize;
1269 ULONG lastEntry = 0;
1270 ULONG blockCount = 0;
1273 * obtain the new count of blocks in the directory stream
1275 blockCount = BlockChainStream_GetCount(
1276 storage->rootBlockChain)+1;
1279 * initialize the size used by the directory stream
1281 newSize.u.HighPart = 0;
1282 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1285 * add a block to the directory stream
1287 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1290 * memset the empty entry in order to initialize the unused newly
1293 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1298 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1301 entryIndex = newEntryIndex + 1;
1302 entryIndex < lastEntry;
1305 StorageImpl_WriteRawDirEntry(
1311 StorageImpl_SaveFileHeader(storage);
1314 UpdateRawDirEntry(currentData, newData);
1316 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1319 *index = newEntryIndex;
1324 /***************************************************************************
1328 * Mark a directory entry in the file as free.
1330 static HRESULT StorageImpl_DestroyDirEntry(
1331 StorageBaseImpl *base,
1335 BYTE emptyData[RAW_DIRENTRY_SIZE];
1336 StorageImpl *storage = (StorageImpl*)base;
1338 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1340 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1346 /****************************************************************************
1350 * Case insensitive comparison of DirEntry.name by first considering
1353 * Returns <0 when name1 < name2
1354 * >0 when name1 > name2
1355 * 0 when name1 == name2
1357 static LONG entryNameCmp(
1358 const OLECHAR *name1,
1359 const OLECHAR *name2)
1361 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1363 while (diff == 0 && *name1 != 0)
1366 * We compare the string themselves only when they are of the same length
1368 diff = toupperW(*name1++) - toupperW(*name2++);
1374 /****************************************************************************
1378 * Add a directory entry to a storage
1380 static HRESULT insertIntoTree(
1381 StorageBaseImpl *This,
1382 DirRef parentStorageIndex,
1383 DirRef newEntryIndex)
1385 DirEntry currentEntry;
1389 * Read the inserted entry
1391 StorageBaseImpl_ReadDirEntry(This,
1396 * Read the storage entry
1398 StorageBaseImpl_ReadDirEntry(This,
1402 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1405 * The root storage contains some element, therefore, start the research
1406 * for the appropriate location.
1409 DirRef current, next, previous, currentEntryId;
1412 * Keep a reference to the root of the storage's element tree
1414 currentEntryId = currentEntry.dirRootEntry;
1419 StorageBaseImpl_ReadDirEntry(This,
1420 currentEntry.dirRootEntry,
1423 previous = currentEntry.leftChild;
1424 next = currentEntry.rightChild;
1425 current = currentEntryId;
1429 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1433 if (previous != DIRENTRY_NULL)
1435 StorageBaseImpl_ReadDirEntry(This,
1442 currentEntry.leftChild = newEntryIndex;
1443 StorageBaseImpl_WriteDirEntry(This,
1451 if (next != DIRENTRY_NULL)
1453 StorageBaseImpl_ReadDirEntry(This,
1460 currentEntry.rightChild = newEntryIndex;
1461 StorageBaseImpl_WriteDirEntry(This,
1470 * Trying to insert an item with the same name in the
1471 * subtree structure.
1473 return STG_E_FILEALREADYEXISTS;
1476 previous = currentEntry.leftChild;
1477 next = currentEntry.rightChild;
1483 * The storage is empty, make the new entry the root of its element tree
1485 currentEntry.dirRootEntry = newEntryIndex;
1486 StorageBaseImpl_WriteDirEntry(This,
1494 /****************************************************************************
1498 * Find and read the element of a storage with the given name.
1500 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1501 const OLECHAR *name, DirEntry *data)
1503 DirRef currentEntry;
1505 /* Read the storage entry to find the root of the tree. */
1506 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1508 currentEntry = data->dirRootEntry;
1510 while (currentEntry != DIRENTRY_NULL)
1514 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1516 cmp = entryNameCmp(name, data->name);
1523 currentEntry = data->leftChild;
1526 currentEntry = data->rightChild;
1529 return currentEntry;
1532 /****************************************************************************
1536 * Find and read the binary tree parent of the element with the given name.
1538 * If there is no such element, find a place where it could be inserted and
1539 * return STG_E_FILENOTFOUND.
1541 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1542 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1548 /* Read the storage entry to find the root of the tree. */
1549 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1551 *parentEntry = storageEntry;
1552 *relation = DIRENTRY_RELATION_DIR;
1554 childEntry = parentData->dirRootEntry;
1556 while (childEntry != DIRENTRY_NULL)
1560 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1562 cmp = entryNameCmp(childName, childData.name);
1570 *parentData = childData;
1571 *parentEntry = childEntry;
1572 *relation = DIRENTRY_RELATION_PREVIOUS;
1574 childEntry = parentData->leftChild;
1579 *parentData = childData;
1580 *parentEntry = childEntry;
1581 *relation = DIRENTRY_RELATION_NEXT;
1583 childEntry = parentData->rightChild;
1587 if (childEntry == DIRENTRY_NULL)
1588 return STG_E_FILENOTFOUND;
1594 /*************************************************************************
1597 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1599 DWORD ciidExclude, /* [in] */
1600 const IID* rgiidExclude, /* [size_is][unique][in] */
1601 SNB snbExclude, /* [unique][in] */
1602 IStorage* pstgDest) /* [unique][in] */
1604 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1606 IEnumSTATSTG *elements = 0;
1607 STATSTG curElement, strStat;
1609 IStorage *pstgTmp, *pstgChild;
1610 IStream *pstrTmp, *pstrChild;
1613 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1616 TRACE("(%p, %d, %p, %p, %p)\n",
1617 iface, ciidExclude, rgiidExclude,
1618 snbExclude, pstgDest);
1620 if ( pstgDest == 0 )
1621 return STG_E_INVALIDPOINTER;
1624 * Enumerate the elements
1626 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1634 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1635 IStorage_SetClass( pstgDest, &curElement.clsid );
1637 for(i = 0; i < ciidExclude; ++i)
1639 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1640 skip_storage = TRUE;
1641 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1644 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1650 * Obtain the next element
1652 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1654 if ( hr == S_FALSE )
1656 hr = S_OK; /* done, every element has been copied */
1662 WCHAR **snb = snbExclude;
1664 while ( *snb != NULL && !skip )
1666 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1675 if (curElement.type == STGTY_STORAGE)
1681 * open child source storage
1683 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1684 STGM_READ|STGM_SHARE_EXCLUSIVE,
1685 NULL, 0, &pstgChild );
1691 * create a new storage in destination storage
1693 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1694 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1698 * if it already exist, don't create a new one use this one
1700 if (hr == STG_E_FILEALREADYEXISTS)
1702 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1703 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1704 NULL, 0, &pstgTmp );
1710 * do the copy recursively
1712 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1715 IStorage_Release( pstgTmp );
1718 IStorage_Release( pstgChild );
1720 else if (curElement.type == STGTY_STREAM)
1726 * create a new stream in destination storage. If the stream already
1727 * exist, it will be deleted and a new one will be created.
1729 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1730 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1737 * open child stream storage. This operation must succeed even if the
1738 * stream is already open, so we use internal functions to do it.
1740 srcEntryRef = findElement( This, This->storageDirEntry, curElement.pwcsName,
1744 ERR("source stream not found\n");
1745 hr = STG_E_DOCFILECORRUPT;
1750 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntryRef);
1752 IStream_AddRef(pstrChild);
1760 * Get the size of the source stream
1762 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1765 * Set the size of the destination stream.
1767 IStream_SetSize(pstrTmp, strStat.cbSize);
1772 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1775 IStream_Release( pstrChild );
1778 IStream_Release( pstrTmp );
1782 WARN("unknown element type: %d\n", curElement.type);
1786 CoTaskMemFree(curElement.pwcsName);
1787 } while (hr == S_OK);
1792 IEnumSTATSTG_Release(elements);
1797 /*************************************************************************
1798 * MoveElementTo (IStorage)
1800 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1802 const OLECHAR *pwcsName, /* [string][in] */
1803 IStorage *pstgDest, /* [unique][in] */
1804 const OLECHAR *pwcsNewName,/* [string][in] */
1805 DWORD grfFlags) /* [in] */
1807 FIXME("(%p %s %p %s %u): stub\n", iface,
1808 debugstr_w(pwcsName), pstgDest,
1809 debugstr_w(pwcsNewName), grfFlags);
1813 /*************************************************************************
1816 * Ensures that any changes made to a storage object open in transacted mode
1817 * are reflected in the parent storage
1819 * In a non-transacted mode, this ensures all cached writes are completed.
1821 static HRESULT WINAPI StorageImpl_Commit(
1823 DWORD grfCommitFlags)/* [in] */
1825 StorageBaseImpl* const base=(StorageBaseImpl*)iface;
1826 TRACE("(%p %d)\n", iface, grfCommitFlags);
1827 return StorageBaseImpl_Flush(base);
1830 /*************************************************************************
1833 * Discard all changes that have been made since the last commit operation
1835 static HRESULT WINAPI StorageImpl_Revert(
1838 TRACE("(%p)\n", iface);
1842 /*************************************************************************
1843 * DestroyElement (IStorage)
1845 * Strategy: This implementation is built this way for simplicity not for speed.
1846 * I always delete the topmost element of the enumeration and adjust
1847 * the deleted element pointer all the time. This takes longer to
1848 * do but allow to reinvoke DestroyElement whenever we encounter a
1849 * storage object. The optimisation resides in the usage of another
1850 * enumeration strategy that would give all the leaves of a storage
1851 * first. (postfix order)
1853 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1855 const OLECHAR *pwcsName)/* [string][in] */
1857 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1860 DirEntry entryToDelete;
1861 DirRef entryToDeleteRef;
1864 iface, debugstr_w(pwcsName));
1867 return STG_E_INVALIDPOINTER;
1870 return STG_E_REVERTED;
1872 if ( !(This->openFlags & STGM_TRANSACTED) &&
1873 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1874 return STG_E_ACCESSDENIED;
1876 entryToDeleteRef = findElement(
1878 This->storageDirEntry,
1882 if ( entryToDeleteRef == DIRENTRY_NULL )
1884 return STG_E_FILENOTFOUND;
1887 if ( entryToDelete.stgType == STGTY_STORAGE )
1889 hr = deleteStorageContents(
1894 else if ( entryToDelete.stgType == STGTY_STREAM )
1896 hr = deleteStreamContents(
1906 * Remove the entry from its parent storage
1908 hr = removeFromTree(
1910 This->storageDirEntry,
1914 * Invalidate the entry
1917 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1923 /******************************************************************************
1924 * Internal stream list handlers
1927 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1929 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1930 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1933 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1935 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1936 list_remove(&(strm->StrmListEntry));
1939 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1941 StgStreamImpl *strm;
1943 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1945 if (strm->dirEntry == streamEntry)
1954 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1956 StorageInternalImpl *childstg;
1958 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1960 if (childstg->base.storageDirEntry == storageEntry)
1969 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1971 struct list *cur, *cur2;
1972 StgStreamImpl *strm=NULL;
1973 StorageInternalImpl *childstg=NULL;
1975 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1976 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1977 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1978 strm->parentStorage = NULL;
1982 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1983 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1984 StorageBaseImpl_Invalidate( &childstg->base );
1987 if (stg->transactedChild)
1989 StorageBaseImpl_Invalidate(stg->transactedChild);
1991 stg->transactedChild = NULL;
1996 /*********************************************************************
2000 * Delete the contents of a storage entry.
2003 static HRESULT deleteStorageContents(
2004 StorageBaseImpl *parentStorage,
2005 DirRef indexToDelete,
2006 DirEntry entryDataToDelete)
2008 IEnumSTATSTG *elements = 0;
2009 IStorage *childStorage = 0;
2010 STATSTG currentElement;
2012 HRESULT destroyHr = S_OK;
2013 StorageInternalImpl *stg, *stg2;
2015 /* Invalidate any open storage objects. */
2016 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2018 if (stg->base.storageDirEntry == indexToDelete)
2020 StorageBaseImpl_Invalidate(&stg->base);
2025 * Open the storage and enumerate it
2027 hr = StorageBaseImpl_OpenStorage(
2028 (IStorage*)parentStorage,
2029 entryDataToDelete.name,
2031 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2042 * Enumerate the elements
2044 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2049 * Obtain the next element
2051 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
2054 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2056 CoTaskMemFree(currentElement.pwcsName);
2060 * We need to Reset the enumeration every time because we delete elements
2061 * and the enumeration could be invalid
2063 IEnumSTATSTG_Reset(elements);
2065 } while ((hr == S_OK) && (destroyHr == S_OK));
2067 IStorage_Release(childStorage);
2068 IEnumSTATSTG_Release(elements);
2073 /*********************************************************************
2077 * Perform the deletion of a stream's data
2080 static HRESULT deleteStreamContents(
2081 StorageBaseImpl *parentStorage,
2082 DirRef indexToDelete,
2083 DirEntry entryDataToDelete)
2087 ULARGE_INTEGER size;
2088 StgStreamImpl *strm, *strm2;
2090 /* Invalidate any open stream objects. */
2091 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2093 if (strm->dirEntry == indexToDelete)
2095 TRACE("Stream deleted %p\n", strm);
2096 strm->parentStorage = NULL;
2097 list_remove(&strm->StrmListEntry);
2101 size.u.HighPart = 0;
2104 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2105 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2115 hr = IStream_SetSize(pis, size);
2123 * Release the stream object.
2125 IStream_Release(pis);
2130 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2134 case DIRENTRY_RELATION_PREVIOUS:
2135 entry->leftChild = new_target;
2137 case DIRENTRY_RELATION_NEXT:
2138 entry->rightChild = new_target;
2140 case DIRENTRY_RELATION_DIR:
2141 entry->dirRootEntry = new_target;
2148 /*************************************************************************
2152 * This method removes a directory entry from its parent storage tree without
2153 * freeing any resources attached to it.
2155 static HRESULT removeFromTree(
2156 StorageBaseImpl *This,
2157 DirRef parentStorageIndex,
2158 DirRef deletedIndex)
2161 DirEntry entryToDelete;
2162 DirEntry parentEntry;
2163 DirRef parentEntryRef;
2164 ULONG typeOfRelation;
2166 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2172 * Find the element that links to the one we want to delete.
2174 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2175 &parentEntry, &parentEntryRef, &typeOfRelation);
2180 if (entryToDelete.leftChild != DIRENTRY_NULL)
2183 * Replace the deleted entry with its left child
2185 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2187 hr = StorageBaseImpl_WriteDirEntry(
2196 if (entryToDelete.rightChild != DIRENTRY_NULL)
2199 * We need to reinsert the right child somewhere. We already know it and
2200 * its children are greater than everything in the left tree, so we
2201 * insert it at the rightmost point in the left tree.
2203 DirRef newRightChildParent = entryToDelete.leftChild;
2204 DirEntry newRightChildParentEntry;
2208 hr = StorageBaseImpl_ReadDirEntry(
2210 newRightChildParent,
2211 &newRightChildParentEntry);
2217 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2218 newRightChildParent = newRightChildParentEntry.rightChild;
2219 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2221 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2223 hr = StorageBaseImpl_WriteDirEntry(
2225 newRightChildParent,
2226 &newRightChildParentEntry);
2236 * Replace the deleted entry with its right child
2238 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2240 hr = StorageBaseImpl_WriteDirEntry(
2254 /******************************************************************************
2255 * SetElementTimes (IStorage)
2257 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2259 const OLECHAR *pwcsName,/* [string][in] */
2260 const FILETIME *pctime, /* [in] */
2261 const FILETIME *patime, /* [in] */
2262 const FILETIME *pmtime) /* [in] */
2264 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2268 /******************************************************************************
2269 * SetStateBits (IStorage)
2271 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2273 DWORD grfStateBits,/* [in] */
2274 DWORD grfMask) /* [in] */
2276 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2279 return STG_E_REVERTED;
2281 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2285 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2286 DirRef index, const DirEntry *data)
2288 StorageImpl *This = (StorageImpl*)base;
2289 return StorageImpl_WriteDirEntry(This, index, data);
2292 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2293 DirRef index, DirEntry *data)
2295 StorageImpl *This = (StorageImpl*)base;
2296 return StorageImpl_ReadDirEntry(This, index, data);
2299 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2303 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2305 if (!This->blockChainCache[i])
2307 return &This->blockChainCache[i];
2311 i = This->blockChainToEvict;
2313 BlockChainStream_Destroy(This->blockChainCache[i]);
2314 This->blockChainCache[i] = NULL;
2316 This->blockChainToEvict++;
2317 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2318 This->blockChainToEvict = 0;
2320 return &This->blockChainCache[i];
2323 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2326 int i, free_index=-1;
2328 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2330 if (!This->blockChainCache[i])
2332 if (free_index == -1) free_index = i;
2334 else if (This->blockChainCache[i]->ownerDirEntry == index)
2336 return &This->blockChainCache[i];
2340 if (free_index == -1)
2342 free_index = This->blockChainToEvict;
2344 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2345 This->blockChainCache[free_index] = NULL;
2347 This->blockChainToEvict++;
2348 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2349 This->blockChainToEvict = 0;
2352 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2353 return &This->blockChainCache[free_index];
2356 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2360 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2362 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2364 BlockChainStream_Destroy(This->blockChainCache[i]);
2365 This->blockChainCache[i] = NULL;
2371 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2372 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2374 StorageImpl *This = (StorageImpl*)base;
2379 hr = StorageImpl_ReadDirEntry(This, index, &data);
2380 if (FAILED(hr)) return hr;
2382 if (data.size.QuadPart == 0)
2388 if (offset.QuadPart + size > data.size.QuadPart)
2390 bytesToRead = data.size.QuadPart - offset.QuadPart;
2397 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2399 SmallBlockChainStream *stream;
2401 stream = SmallBlockChainStream_Construct(This, NULL, index);
2402 if (!stream) return E_OUTOFMEMORY;
2404 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2406 SmallBlockChainStream_Destroy(stream);
2412 BlockChainStream *stream = NULL;
2414 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2415 if (!stream) return E_OUTOFMEMORY;
2417 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2423 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2424 ULARGE_INTEGER newsize)
2426 StorageImpl *This = (StorageImpl*)base;
2429 SmallBlockChainStream *smallblock=NULL;
2430 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2432 hr = StorageImpl_ReadDirEntry(This, index, &data);
2433 if (FAILED(hr)) return hr;
2435 /* In simple mode keep the stream size above the small block limit */
2436 if (This->base.openFlags & STGM_SIMPLE)
2437 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2439 if (data.size.QuadPart == newsize.QuadPart)
2442 /* Create a block chain object of the appropriate type */
2443 if (data.size.QuadPart == 0)
2445 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2447 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2448 if (!smallblock) return E_OUTOFMEMORY;
2452 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2453 bigblock = *pbigblock;
2454 if (!bigblock) return E_OUTOFMEMORY;
2457 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2459 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2460 if (!smallblock) return E_OUTOFMEMORY;
2464 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2465 bigblock = *pbigblock;
2466 if (!bigblock) return E_OUTOFMEMORY;
2469 /* Change the block chain type if necessary. */
2470 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2472 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2475 SmallBlockChainStream_Destroy(smallblock);
2479 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2480 *pbigblock = bigblock;
2482 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2484 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2489 /* Set the size of the block chain. */
2492 SmallBlockChainStream_SetSize(smallblock, newsize);
2493 SmallBlockChainStream_Destroy(smallblock);
2497 BlockChainStream_SetSize(bigblock, newsize);
2500 /* Set the size in the directory entry. */
2501 hr = StorageImpl_ReadDirEntry(This, index, &data);
2504 data.size = newsize;
2506 hr = StorageImpl_WriteDirEntry(This, index, &data);
2511 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2512 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2514 StorageImpl *This = (StorageImpl*)base;
2517 ULARGE_INTEGER newSize;
2519 hr = StorageImpl_ReadDirEntry(This, index, &data);
2520 if (FAILED(hr)) return hr;
2522 /* Grow the stream if necessary */
2523 newSize.QuadPart = 0;
2524 newSize.QuadPart = offset.QuadPart + size;
2526 if (newSize.QuadPart > data.size.QuadPart)
2528 hr = StorageImpl_StreamSetSize(base, index, newSize);
2532 hr = StorageImpl_ReadDirEntry(This, index, &data);
2533 if (FAILED(hr)) return hr;
2536 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2538 SmallBlockChainStream *stream;
2540 stream = SmallBlockChainStream_Construct(This, NULL, index);
2541 if (!stream) return E_OUTOFMEMORY;
2543 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2545 SmallBlockChainStream_Destroy(stream);
2551 BlockChainStream *stream;
2553 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2554 if (!stream) return E_OUTOFMEMORY;
2556 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2562 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2565 StorageImpl *This = (StorageImpl*)base;
2566 DirEntry dst_data, src_data;
2569 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2572 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2576 StorageImpl_DeleteCachedBlockChainStream(This, src);
2577 dst_data.startingBlock = src_data.startingBlock;
2578 dst_data.size = src_data.size;
2580 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2586 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2588 StorageImpl *This = (StorageImpl*) iface;
2592 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2594 *result = statstg.pwcsName;
2600 * Virtual function table for the IStorage32Impl class.
2602 static const IStorageVtbl Storage32Impl_Vtbl =
2604 StorageBaseImpl_QueryInterface,
2605 StorageBaseImpl_AddRef,
2606 StorageBaseImpl_Release,
2607 StorageBaseImpl_CreateStream,
2608 StorageBaseImpl_OpenStream,
2609 StorageBaseImpl_CreateStorage,
2610 StorageBaseImpl_OpenStorage,
2611 StorageBaseImpl_CopyTo,
2612 StorageBaseImpl_MoveElementTo,
2615 StorageBaseImpl_EnumElements,
2616 StorageBaseImpl_DestroyElement,
2617 StorageBaseImpl_RenameElement,
2618 StorageBaseImpl_SetElementTimes,
2619 StorageBaseImpl_SetClass,
2620 StorageBaseImpl_SetStateBits,
2621 StorageBaseImpl_Stat
2624 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2626 StorageImpl_Destroy,
2627 StorageImpl_Invalidate,
2629 StorageImpl_GetFilename,
2630 StorageImpl_CreateDirEntry,
2631 StorageImpl_BaseWriteDirEntry,
2632 StorageImpl_BaseReadDirEntry,
2633 StorageImpl_DestroyDirEntry,
2634 StorageImpl_StreamReadAt,
2635 StorageImpl_StreamWriteAt,
2636 StorageImpl_StreamSetSize,
2637 StorageImpl_StreamLink
2640 static HRESULT StorageImpl_Construct(
2648 StorageImpl** result)
2652 DirEntry currentEntry;
2653 DirRef currentEntryRef;
2655 if ( FAILED( validateSTGM(openFlags) ))
2656 return STG_E_INVALIDFLAG;
2658 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2660 return E_OUTOFMEMORY;
2662 memset(This, 0, sizeof(StorageImpl));
2664 list_init(&This->base.strmHead);
2666 list_init(&This->base.storageHead);
2668 This->base.lpVtbl = &Storage32Impl_Vtbl;
2669 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2670 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2671 This->base.openFlags = (openFlags & ~STGM_CREATE);
2673 This->base.create = create;
2675 This->base.reverted = 0;
2678 * Initialize the big block cache.
2680 This->bigBlockSize = sector_size;
2681 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2683 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2686 This->lockBytes = pLkbyt;
2687 ILockBytes_AddRef(pLkbyt);
2695 ULARGE_INTEGER size;
2696 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2699 * Initialize all header variables:
2700 * - The big block depot consists of one block and it is at block 0
2701 * - The directory table starts at block 1
2702 * - There is no small block depot
2704 memset( This->bigBlockDepotStart,
2706 sizeof(This->bigBlockDepotStart));
2708 This->bigBlockDepotCount = 1;
2709 This->bigBlockDepotStart[0] = 0;
2710 This->rootStartBlock = 1;
2711 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2712 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2713 if (sector_size == 4096)
2714 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2716 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2717 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2718 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2719 This->extBigBlockDepotCount = 0;
2721 StorageImpl_SaveFileHeader(This);
2724 * Add one block for the big block depot and one block for the directory table
2726 size.u.HighPart = 0;
2727 size.u.LowPart = This->bigBlockSize * 3;
2728 ILockBytes_SetSize(This->lockBytes, size);
2731 * Initialize the big block depot
2733 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2734 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2735 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2736 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2741 * Load the header for the file.
2743 hr = StorageImpl_LoadFileHeader(This);
2752 * There is no block depot cached yet.
2754 This->indexBlockDepotCached = 0xFFFFFFFF;
2757 * Start searching for free blocks with block 0.
2759 This->prevFreeBlock = 0;
2761 This->firstFreeSmallBlock = 0;
2764 * Create the block chain abstractions.
2766 if(!(This->rootBlockChain =
2767 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2769 hr = STG_E_READFAULT;
2773 if(!(This->smallBlockDepotChain =
2774 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2777 hr = STG_E_READFAULT;
2782 * Write the root storage entry (memory only)
2788 * Initialize the directory table
2790 memset(&rootEntry, 0, sizeof(rootEntry));
2791 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2792 sizeof(rootEntry.name)/sizeof(WCHAR) );
2793 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2794 rootEntry.stgType = STGTY_ROOT;
2795 rootEntry.leftChild = DIRENTRY_NULL;
2796 rootEntry.rightChild = DIRENTRY_NULL;
2797 rootEntry.dirRootEntry = DIRENTRY_NULL;
2798 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2799 rootEntry.size.u.HighPart = 0;
2800 rootEntry.size.u.LowPart = 0;
2802 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2806 * Find the ID of the root storage.
2808 currentEntryRef = 0;
2812 hr = StorageImpl_ReadDirEntry(
2819 if ( (currentEntry.sizeOfNameString != 0 ) &&
2820 (currentEntry.stgType == STGTY_ROOT) )
2822 This->base.storageDirEntry = currentEntryRef;
2828 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2832 hr = STG_E_READFAULT;
2837 * Create the block chain abstraction for the small block root chain.
2839 if(!(This->smallBlockRootChain =
2840 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2842 hr = STG_E_READFAULT;
2848 IStorage_Release((IStorage*)This);
2857 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2859 StorageImpl *This = (StorageImpl*) iface;
2861 StorageBaseImpl_DeleteAll(&This->base);
2863 This->base.reverted = 1;
2866 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2868 StorageImpl *This = (StorageImpl*) iface;
2870 TRACE("(%p)\n", This);
2872 StorageImpl_Flush(iface);
2874 StorageImpl_Invalidate(iface);
2876 BlockChainStream_Destroy(This->smallBlockRootChain);
2877 BlockChainStream_Destroy(This->rootBlockChain);
2878 BlockChainStream_Destroy(This->smallBlockDepotChain);
2880 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2881 BlockChainStream_Destroy(This->blockChainCache[i]);
2883 if (This->lockBytes)
2884 ILockBytes_Release(This->lockBytes);
2885 HeapFree(GetProcessHeap(), 0, This);
2888 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface)
2890 StorageImpl *This = (StorageImpl*) iface;
2892 return ILockBytes_Flush(This->lockBytes);
2895 /******************************************************************************
2896 * Storage32Impl_GetNextFreeBigBlock
2898 * Returns the index of the next free big block.
2899 * If the big block depot is filled, this method will enlarge it.
2902 static ULONG StorageImpl_GetNextFreeBigBlock(
2905 ULONG depotBlockIndexPos;
2906 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
2908 ULONG depotBlockOffset;
2909 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2910 ULONG nextBlockIndex = BLOCK_SPECIAL;
2912 ULONG freeBlock = BLOCK_UNUSED;
2913 ULARGE_INTEGER neededSize;
2916 depotIndex = This->prevFreeBlock / blocksPerDepot;
2917 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2920 * Scan the entire big block depot until we find a block marked free
2922 while (nextBlockIndex != BLOCK_UNUSED)
2924 if (depotIndex < COUNT_BBDEPOTINHEADER)
2926 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2929 * Grow the primary depot.
2931 if (depotBlockIndexPos == BLOCK_UNUSED)
2933 depotBlockIndexPos = depotIndex*blocksPerDepot;
2936 * Add a block depot.
2938 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2939 This->bigBlockDepotCount++;
2940 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2943 * Flag it as a block depot.
2945 StorageImpl_SetNextBlockInChain(This,
2949 /* Save new header information.
2951 StorageImpl_SaveFileHeader(This);
2956 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2958 if (depotBlockIndexPos == BLOCK_UNUSED)
2961 * Grow the extended depot.
2963 ULONG extIndex = BLOCK_UNUSED;
2964 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2965 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2967 if (extBlockOffset == 0)
2969 /* We need an extended block.
2971 extIndex = Storage32Impl_AddExtBlockDepot(This);
2972 This->extBigBlockDepotCount++;
2973 depotBlockIndexPos = extIndex + 1;
2976 depotBlockIndexPos = depotIndex * blocksPerDepot;
2979 * Add a block depot and mark it in the extended block.
2981 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2982 This->bigBlockDepotCount++;
2983 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2985 /* Flag the block depot.
2987 StorageImpl_SetNextBlockInChain(This,
2991 /* If necessary, flag the extended depot block.
2993 if (extIndex != BLOCK_UNUSED)
2994 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2996 /* Save header information.
2998 StorageImpl_SaveFileHeader(This);
3002 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3006 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3007 ( nextBlockIndex != BLOCK_UNUSED))
3009 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3011 if (nextBlockIndex == BLOCK_UNUSED)
3013 freeBlock = (depotIndex * blocksPerDepot) +
3014 (depotBlockOffset/sizeof(ULONG));
3017 depotBlockOffset += sizeof(ULONG);
3022 depotBlockOffset = 0;
3026 * make sure that the block physically exists before using it
3028 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3030 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3032 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3033 ILockBytes_SetSize(This->lockBytes, neededSize);
3035 This->prevFreeBlock = freeBlock;
3040 /******************************************************************************
3041 * Storage32Impl_AddBlockDepot
3043 * This will create a depot block, essentially it is a block initialized
3046 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3048 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3051 * Initialize blocks as free
3053 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3054 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3057 /******************************************************************************
3058 * Storage32Impl_GetExtDepotBlock
3060 * Returns the index of the block that corresponds to the specified depot
3061 * index. This method is only for depot indexes equal or greater than
3062 * COUNT_BBDEPOTINHEADER.
3064 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3066 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3067 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3068 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3069 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3070 ULONG blockIndex = BLOCK_UNUSED;
3071 ULONG extBlockIndex = This->extBigBlockDepotStart;
3073 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3075 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
3076 return BLOCK_UNUSED;
3078 while (extBlockCount > 0)
3080 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3084 if (extBlockIndex != BLOCK_UNUSED)
3085 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3086 extBlockOffset * sizeof(ULONG), &blockIndex);
3091 /******************************************************************************
3092 * Storage32Impl_SetExtDepotBlock
3094 * Associates the specified block index to the specified depot index.
3095 * This method is only for depot indexes equal or greater than
3096 * COUNT_BBDEPOTINHEADER.
3098 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3100 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3101 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3102 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3103 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3104 ULONG extBlockIndex = This->extBigBlockDepotStart;
3106 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3108 while (extBlockCount > 0)
3110 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3114 if (extBlockIndex != BLOCK_UNUSED)
3116 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3117 extBlockOffset * sizeof(ULONG),
3122 /******************************************************************************
3123 * Storage32Impl_AddExtBlockDepot
3125 * Creates an extended depot block.
3127 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3129 ULONG numExtBlocks = This->extBigBlockDepotCount;
3130 ULONG nextExtBlock = This->extBigBlockDepotStart;
3131 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3132 ULONG index = BLOCK_UNUSED;
3133 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3134 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3135 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3137 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3138 blocksPerDepotBlock;
3140 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3143 * The first extended block.
3145 This->extBigBlockDepotStart = index;
3151 * Follow the chain to the last one.
3153 for (i = 0; i < (numExtBlocks - 1); i++)
3155 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3159 * Add the new extended block to the chain.
3161 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3166 * Initialize this block.
3168 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3169 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3174 /******************************************************************************
3175 * Storage32Impl_FreeBigBlock
3177 * This method will flag the specified block as free in the big block depot.
3179 static void StorageImpl_FreeBigBlock(
3183 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3185 if (blockIndex < This->prevFreeBlock)
3186 This->prevFreeBlock = blockIndex;
3189 /************************************************************************
3190 * Storage32Impl_GetNextBlockInChain
3192 * This method will retrieve the block index of the next big block in
3195 * Params: This - Pointer to the Storage object.
3196 * blockIndex - Index of the block to retrieve the chain
3198 * nextBlockIndex - receives the return value.
3200 * Returns: This method returns the index of the next block in the chain.
3201 * It will return the constants:
3202 * BLOCK_SPECIAL - If the block given was not part of a
3204 * BLOCK_END_OF_CHAIN - If the block given was the last in
3206 * BLOCK_UNUSED - If the block given was not past of a chain
3208 * BLOCK_EXTBBDEPOT - This block is part of the extended
3211 * See Windows documentation for more details on IStorage methods.
3213 static HRESULT StorageImpl_GetNextBlockInChain(
3216 ULONG* nextBlockIndex)
3218 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3219 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3220 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3221 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3223 ULONG depotBlockIndexPos;
3224 int index, num_blocks;
3226 *nextBlockIndex = BLOCK_SPECIAL;
3228 if(depotBlockCount >= This->bigBlockDepotCount)
3230 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3231 This->bigBlockDepotCount);
3232 return STG_E_READFAULT;
3236 * Cache the currently accessed depot block.
3238 if (depotBlockCount != This->indexBlockDepotCached)
3240 This->indexBlockDepotCached = depotBlockCount;
3242 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3244 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3249 * We have to look in the extended depot.
3251 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3254 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3257 return STG_E_READFAULT;
3259 num_blocks = This->bigBlockSize / 4;
3261 for (index = 0; index < num_blocks; index++)
3263 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3264 This->blockDepotCached[index] = *nextBlockIndex;
3268 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3273 /******************************************************************************
3274 * Storage32Impl_GetNextExtendedBlock
3276 * Given an extended block this method will return the next extended block.
3279 * The last ULONG of an extended block is the block index of the next
3280 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3284 * - The index of the next extended block
3285 * - BLOCK_UNUSED: there is no next extended block.
3286 * - Any other return values denotes failure.
3288 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3290 ULONG nextBlockIndex = BLOCK_SPECIAL;
3291 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3293 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3296 return nextBlockIndex;
3299 /******************************************************************************
3300 * Storage32Impl_SetNextBlockInChain
3302 * This method will write the index of the specified block's next block
3303 * in the big block depot.
3305 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3308 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3309 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3310 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3313 static void StorageImpl_SetNextBlockInChain(
3318 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3319 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3320 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3321 ULONG depotBlockIndexPos;
3323 assert(depotBlockCount < This->bigBlockDepotCount);
3324 assert(blockIndex != nextBlock);
3326 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3328 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3333 * We have to look in the extended depot.
3335 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3338 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3341 * Update the cached block depot, if necessary.
3343 if (depotBlockCount == This->indexBlockDepotCached)
3345 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3349 /******************************************************************************
3350 * Storage32Impl_LoadFileHeader
3352 * This method will read in the file header
3354 static HRESULT StorageImpl_LoadFileHeader(
3358 BYTE headerBigBlock[HEADER_SIZE];
3360 ULARGE_INTEGER offset;
3365 * Get a pointer to the big block of data containing the header.
3367 offset.u.HighPart = 0;
3368 offset.u.LowPart = 0;
3369 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3370 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3371 hr = STG_E_FILENOTFOUND;
3374 * Extract the information from the header.
3379 * Check for the "magic number" signature and return an error if it is not
3382 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3384 return STG_E_OLDFORMAT;
3387 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3389 return STG_E_INVALIDHEADER;
3392 StorageUtl_ReadWord(
3394 OFFSET_BIGBLOCKSIZEBITS,
3395 &This->bigBlockSizeBits);
3397 StorageUtl_ReadWord(
3399 OFFSET_SMALLBLOCKSIZEBITS,
3400 &This->smallBlockSizeBits);
3402 StorageUtl_ReadDWord(
3404 OFFSET_BBDEPOTCOUNT,
3405 &This->bigBlockDepotCount);
3407 StorageUtl_ReadDWord(
3409 OFFSET_ROOTSTARTBLOCK,
3410 &This->rootStartBlock);
3412 StorageUtl_ReadDWord(
3414 OFFSET_SMALLBLOCKLIMIT,
3415 &This->smallBlockLimit);
3417 StorageUtl_ReadDWord(
3419 OFFSET_SBDEPOTSTART,
3420 &This->smallBlockDepotStart);
3422 StorageUtl_ReadDWord(
3424 OFFSET_EXTBBDEPOTSTART,
3425 &This->extBigBlockDepotStart);
3427 StorageUtl_ReadDWord(
3429 OFFSET_EXTBBDEPOTCOUNT,
3430 &This->extBigBlockDepotCount);
3432 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3434 StorageUtl_ReadDWord(
3436 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3437 &(This->bigBlockDepotStart[index]));
3441 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3443 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3444 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3447 * Right now, the code is making some assumptions about the size of the
3448 * blocks, just make sure they are what we're expecting.
3450 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3451 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3452 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3454 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3455 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3456 hr = STG_E_INVALIDHEADER;
3465 /******************************************************************************
3466 * Storage32Impl_SaveFileHeader
3468 * This method will save to the file the header
3470 static void StorageImpl_SaveFileHeader(
3473 BYTE headerBigBlock[HEADER_SIZE];
3476 ULARGE_INTEGER offset;
3477 DWORD bytes_read, bytes_written;
3478 DWORD major_version, dirsectorcount;
3481 * Get a pointer to the big block of data containing the header.
3483 offset.u.HighPart = 0;
3484 offset.u.LowPart = 0;
3485 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3486 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3487 hr = STG_E_FILENOTFOUND;
3489 if (This->bigBlockSizeBits == 0x9)
3491 else if (This->bigBlockSizeBits == 0xc)
3495 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3500 * If the block read failed, the file is probably new.
3505 * Initialize for all unknown fields.
3507 memset(headerBigBlock, 0, HEADER_SIZE);
3510 * Initialize the magic number.
3512 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3516 * Write the information to the header.
3518 StorageUtl_WriteWord(
3520 OFFSET_MINORVERSION,
3523 StorageUtl_WriteWord(
3525 OFFSET_MAJORVERSION,
3528 StorageUtl_WriteWord(
3530 OFFSET_BYTEORDERMARKER,
3533 StorageUtl_WriteWord(
3535 OFFSET_BIGBLOCKSIZEBITS,
3536 This->bigBlockSizeBits);
3538 StorageUtl_WriteWord(
3540 OFFSET_SMALLBLOCKSIZEBITS,
3541 This->smallBlockSizeBits);
3543 if (major_version >= 4)
3545 if (This->rootBlockChain)
3546 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3548 /* This file is being created, and it will start out with one block. */
3552 /* This field must be 0 in versions older than 4 */
3555 StorageUtl_WriteDWord(
3557 OFFSET_DIRSECTORCOUNT,
3560 StorageUtl_WriteDWord(
3562 OFFSET_BBDEPOTCOUNT,
3563 This->bigBlockDepotCount);
3565 StorageUtl_WriteDWord(
3567 OFFSET_ROOTSTARTBLOCK,
3568 This->rootStartBlock);
3570 StorageUtl_WriteDWord(
3572 OFFSET_SMALLBLOCKLIMIT,
3573 This->smallBlockLimit);
3575 StorageUtl_WriteDWord(
3577 OFFSET_SBDEPOTSTART,
3578 This->smallBlockDepotStart);
3580 StorageUtl_WriteDWord(
3582 OFFSET_SBDEPOTCOUNT,
3583 This->smallBlockDepotChain ?
3584 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3586 StorageUtl_WriteDWord(
3588 OFFSET_EXTBBDEPOTSTART,
3589 This->extBigBlockDepotStart);
3591 StorageUtl_WriteDWord(
3593 OFFSET_EXTBBDEPOTCOUNT,
3594 This->extBigBlockDepotCount);
3596 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3598 StorageUtl_WriteDWord(
3600 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3601 (This->bigBlockDepotStart[index]));
3605 * Write the big block back to the file.
3607 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3610 /******************************************************************************
3611 * StorageImpl_ReadRawDirEntry
3613 * This method will read the raw data from a directory entry in the file.
3615 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3617 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3619 ULARGE_INTEGER offset;
3623 offset.u.HighPart = 0;
3624 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3626 hr = BlockChainStream_ReadAt(
3627 This->rootBlockChain,
3633 if (bytesRead != RAW_DIRENTRY_SIZE)
3634 return STG_E_READFAULT;
3639 /******************************************************************************
3640 * StorageImpl_WriteRawDirEntry
3642 * This method will write the raw data from a directory entry in the file.
3644 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3646 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3648 ULARGE_INTEGER offset;
3652 offset.u.HighPart = 0;
3653 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3655 hr = BlockChainStream_WriteAt(
3656 This->rootBlockChain,
3665 /******************************************************************************
3668 * Update raw directory entry data from the fields in newData.
3670 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3672 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3674 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3677 buffer + OFFSET_PS_NAME,
3679 DIRENTRY_NAME_BUFFER_LEN );
3681 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3683 StorageUtl_WriteWord(
3685 OFFSET_PS_NAMELENGTH,
3686 newData->sizeOfNameString);
3688 StorageUtl_WriteDWord(
3690 OFFSET_PS_LEFTCHILD,
3691 newData->leftChild);
3693 StorageUtl_WriteDWord(
3695 OFFSET_PS_RIGHTCHILD,
3696 newData->rightChild);
3698 StorageUtl_WriteDWord(
3701 newData->dirRootEntry);
3703 StorageUtl_WriteGUID(
3708 StorageUtl_WriteDWord(
3711 newData->ctime.dwLowDateTime);
3713 StorageUtl_WriteDWord(
3715 OFFSET_PS_CTIMEHIGH,
3716 newData->ctime.dwHighDateTime);
3718 StorageUtl_WriteDWord(
3721 newData->mtime.dwLowDateTime);
3723 StorageUtl_WriteDWord(
3725 OFFSET_PS_MTIMEHIGH,
3726 newData->ctime.dwHighDateTime);
3728 StorageUtl_WriteDWord(
3730 OFFSET_PS_STARTBLOCK,
3731 newData->startingBlock);
3733 StorageUtl_WriteDWord(
3736 newData->size.u.LowPart);
3739 /******************************************************************************
3740 * Storage32Impl_ReadDirEntry
3742 * This method will read the specified directory entry.
3744 HRESULT StorageImpl_ReadDirEntry(
3749 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3752 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3754 if (SUCCEEDED(readRes))
3756 memset(buffer->name, 0, sizeof(buffer->name));
3759 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3760 DIRENTRY_NAME_BUFFER_LEN );
3761 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3763 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3765 StorageUtl_ReadWord(
3767 OFFSET_PS_NAMELENGTH,
3768 &buffer->sizeOfNameString);
3770 StorageUtl_ReadDWord(
3772 OFFSET_PS_LEFTCHILD,
3773 &buffer->leftChild);
3775 StorageUtl_ReadDWord(
3777 OFFSET_PS_RIGHTCHILD,
3778 &buffer->rightChild);
3780 StorageUtl_ReadDWord(
3783 &buffer->dirRootEntry);
3785 StorageUtl_ReadGUID(
3790 StorageUtl_ReadDWord(
3793 &buffer->ctime.dwLowDateTime);
3795 StorageUtl_ReadDWord(
3797 OFFSET_PS_CTIMEHIGH,
3798 &buffer->ctime.dwHighDateTime);
3800 StorageUtl_ReadDWord(
3803 &buffer->mtime.dwLowDateTime);
3805 StorageUtl_ReadDWord(
3807 OFFSET_PS_MTIMEHIGH,
3808 &buffer->mtime.dwHighDateTime);
3810 StorageUtl_ReadDWord(
3812 OFFSET_PS_STARTBLOCK,
3813 &buffer->startingBlock);
3815 StorageUtl_ReadDWord(
3818 &buffer->size.u.LowPart);
3820 buffer->size.u.HighPart = 0;
3826 /*********************************************************************
3827 * Write the specified directory entry to the file
3829 HRESULT StorageImpl_WriteDirEntry(
3832 const DirEntry* buffer)
3834 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3837 UpdateRawDirEntry(currentEntry, buffer);
3839 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3843 static BOOL StorageImpl_ReadBigBlock(
3848 ULARGE_INTEGER ulOffset;
3851 ulOffset.u.HighPart = 0;
3852 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3854 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3855 return (read == This->bigBlockSize);
3858 static BOOL StorageImpl_ReadDWordFromBigBlock(
3864 ULARGE_INTEGER ulOffset;
3868 ulOffset.u.HighPart = 0;
3869 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3870 ulOffset.u.LowPart += offset;
3872 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3873 *value = lendian32toh(tmp);
3874 return (read == sizeof(DWORD));
3877 static BOOL StorageImpl_WriteBigBlock(
3882 ULARGE_INTEGER ulOffset;
3885 ulOffset.u.HighPart = 0;
3886 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3888 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3889 return (wrote == This->bigBlockSize);
3892 static BOOL StorageImpl_WriteDWordToBigBlock(
3898 ULARGE_INTEGER ulOffset;
3901 ulOffset.u.HighPart = 0;
3902 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3903 ulOffset.u.LowPart += offset;
3905 value = htole32(value);
3906 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3907 return (wrote == sizeof(DWORD));
3910 /******************************************************************************
3911 * Storage32Impl_SmallBlocksToBigBlocks
3913 * This method will convert a small block chain to a big block chain.
3914 * The small block chain will be destroyed.
3916 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3918 SmallBlockChainStream** ppsbChain)
3920 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3921 ULARGE_INTEGER size, offset;
3922 ULONG cbRead, cbWritten;
3923 ULARGE_INTEGER cbTotalRead;
3924 DirRef streamEntryRef;
3925 HRESULT resWrite = S_OK;
3927 DirEntry streamEntry;
3929 BlockChainStream *bbTempChain = NULL;
3930 BlockChainStream *bigBlockChain = NULL;
3933 * Create a temporary big block chain that doesn't have
3934 * an associated directory entry. This temporary chain will be
3935 * used to copy data from small blocks to big blocks.
3937 bbTempChain = BlockChainStream_Construct(This,
3940 if(!bbTempChain) return NULL;
3942 * Grow the big block chain.
3944 size = SmallBlockChainStream_GetSize(*ppsbChain);
3945 BlockChainStream_SetSize(bbTempChain, size);
3948 * Copy the contents of the small block chain to the big block chain
3949 * by small block size increments.
3951 offset.u.LowPart = 0;
3952 offset.u.HighPart = 0;
3953 cbTotalRead.QuadPart = 0;
3955 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3958 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3960 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3963 if (FAILED(resRead))
3968 cbTotalRead.QuadPart += cbRead;
3970 resWrite = BlockChainStream_WriteAt(bbTempChain,
3976 if (FAILED(resWrite))
3979 offset.u.LowPart += cbRead;
3983 resRead = STG_E_READFAULT;
3986 } while (cbTotalRead.QuadPart < size.QuadPart);
3987 HeapFree(GetProcessHeap(),0,buffer);
3989 size.u.HighPart = 0;
3992 if (FAILED(resRead) || FAILED(resWrite))
3994 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3995 BlockChainStream_SetSize(bbTempChain, size);
3996 BlockChainStream_Destroy(bbTempChain);
4001 * Destroy the small block chain.
4003 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4004 SmallBlockChainStream_SetSize(*ppsbChain, size);
4005 SmallBlockChainStream_Destroy(*ppsbChain);
4009 * Change the directory entry. This chain is now a big block chain
4010 * and it doesn't reside in the small blocks chain anymore.
4012 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4014 streamEntry.startingBlock = bbHeadOfChain;
4016 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4019 * Destroy the temporary entryless big block chain.
4020 * Create a new big block chain associated with this entry.
4022 BlockChainStream_Destroy(bbTempChain);
4023 bigBlockChain = BlockChainStream_Construct(This,
4027 return bigBlockChain;
4030 /******************************************************************************
4031 * Storage32Impl_BigBlocksToSmallBlocks
4033 * This method will convert a big block chain to a small block chain.
4034 * The big block chain will be destroyed on success.
4036 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4038 BlockChainStream** ppbbChain)
4040 ULARGE_INTEGER size, offset, cbTotalRead;
4041 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4042 DirRef streamEntryRef;
4043 HRESULT resWrite = S_OK, resRead;
4044 DirEntry streamEntry;
4046 SmallBlockChainStream* sbTempChain;
4048 TRACE("%p %p\n", This, ppbbChain);
4050 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4056 size = BlockChainStream_GetSize(*ppbbChain);
4057 SmallBlockChainStream_SetSize(sbTempChain, size);
4059 offset.u.HighPart = 0;
4060 offset.u.LowPart = 0;
4061 cbTotalRead.QuadPart = 0;
4062 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4065 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4066 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4074 cbTotalRead.QuadPart += cbRead;
4076 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4077 cbRead, buffer, &cbWritten);
4079 if(FAILED(resWrite))
4082 offset.u.LowPart += cbRead;
4086 resRead = STG_E_READFAULT;
4089 }while(cbTotalRead.QuadPart < size.QuadPart);
4090 HeapFree(GetProcessHeap(), 0, buffer);
4092 size.u.HighPart = 0;
4095 if(FAILED(resRead) || FAILED(resWrite))
4097 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4098 SmallBlockChainStream_SetSize(sbTempChain, size);
4099 SmallBlockChainStream_Destroy(sbTempChain);
4103 /* destroy the original big block chain */
4104 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4105 BlockChainStream_SetSize(*ppbbChain, size);
4106 BlockChainStream_Destroy(*ppbbChain);
4109 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4110 streamEntry.startingBlock = sbHeadOfChain;
4111 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4113 SmallBlockChainStream_Destroy(sbTempChain);
4114 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4117 static HRESULT StorageBaseImpl_CopyStream(
4118 StorageBaseImpl *dst, DirRef dst_entry,
4119 StorageBaseImpl *src, DirRef src_entry)
4124 ULARGE_INTEGER bytes_copied;
4125 ULONG bytestocopy, bytesread, byteswritten;
4127 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4131 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4133 bytes_copied.QuadPart = 0;
4134 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4136 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4138 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4140 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4143 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4144 data, &byteswritten);
4147 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4148 bytes_copied.QuadPart += byteswritten;
4156 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4158 DirRef result=This->firstFreeEntry;
4160 while (result < This->entries_size && This->entries[result].inuse)
4163 if (result == This->entries_size)
4165 ULONG new_size = This->entries_size * 2;
4166 TransactedDirEntry *new_entries;
4168 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4169 if (!new_entries) return DIRENTRY_NULL;
4171 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4172 HeapFree(GetProcessHeap(), 0, This->entries);
4174 This->entries = new_entries;
4175 This->entries_size = new_size;
4178 This->entries[result].inuse = 1;
4180 This->firstFreeEntry = result+1;
4185 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4186 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4188 DirRef stubEntryRef;
4189 TransactedDirEntry *entry;
4191 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4193 if (stubEntryRef != DIRENTRY_NULL)
4195 entry = &This->entries[stubEntryRef];
4197 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4202 return stubEntryRef;
4205 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4206 TransactedSnapshotImpl *This, DirRef entry)
4211 if (!This->entries[entry].read)
4213 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4214 This->entries[entry].transactedParentEntry,
4217 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4219 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4221 if (data.leftChild == DIRENTRY_NULL)
4225 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4227 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4229 if (data.rightChild == DIRENTRY_NULL)
4233 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4235 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4237 if (data.dirRootEntry == DIRENTRY_NULL)
4243 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4244 This->entries[entry].read = 1;
4251 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4252 TransactedSnapshotImpl *This, DirRef entry)
4256 if (!This->entries[entry].stream_dirty)
4258 DirEntry new_entrydata;
4260 memset(&new_entrydata, 0, sizeof(DirEntry));
4261 new_entrydata.name[0] = 'S';
4262 new_entrydata.sizeOfNameString = 1;
4263 new_entrydata.stgType = STGTY_STREAM;
4264 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4265 new_entrydata.leftChild = DIRENTRY_NULL;
4266 new_entrydata.rightChild = DIRENTRY_NULL;
4267 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4269 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4270 &This->entries[entry].stream_entry);
4272 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4274 hr = StorageBaseImpl_CopyStream(
4275 This->scratch, This->entries[entry].stream_entry,
4276 This->transactedParent, This->entries[entry].transactedParentEntry);
4279 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4283 This->entries[entry].stream_dirty = 1;
4285 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4287 /* Since this entry is modified, and we aren't using its stream data, we
4288 * no longer care about the original entry. */
4290 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4292 if (delete_ref != DIRENTRY_NULL)
4293 This->entries[delete_ref].deleted = 1;
4295 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4302 /* Find the first entry in a depth-first traversal. */
4303 static DirRef TransactedSnapshotImpl_FindFirstChild(
4304 TransactedSnapshotImpl* This, DirRef parent)
4306 DirRef cursor, prev;
4307 TransactedDirEntry *entry;
4310 entry = &This->entries[cursor];
4313 if (entry->data.leftChild != DIRENTRY_NULL)
4316 cursor = entry->data.leftChild;
4317 entry = &This->entries[cursor];
4318 entry->parent = prev;
4320 else if (entry->data.rightChild != DIRENTRY_NULL)
4323 cursor = entry->data.rightChild;
4324 entry = &This->entries[cursor];
4325 entry->parent = prev;
4327 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4330 cursor = entry->data.dirRootEntry;
4331 entry = &This->entries[cursor];
4332 entry->parent = prev;
4341 /* Find the next entry in a depth-first traversal. */
4342 static DirRef TransactedSnapshotImpl_FindNextChild(
4343 TransactedSnapshotImpl* This, DirRef current)
4346 TransactedDirEntry *parent_entry;
4348 parent = This->entries[current].parent;
4349 parent_entry = &This->entries[parent];
4351 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4353 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4355 This->entries[parent_entry->data.rightChild].parent = parent;
4356 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4359 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4361 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4362 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4369 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4370 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4371 TransactedSnapshotImpl* This, DirRef entry)
4373 return entry != DIRENTRY_NULL &&
4374 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4377 /* Destroy the entries created by CopyTree. */
4378 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4379 TransactedSnapshotImpl* This, DirRef stop)
4382 TransactedDirEntry *entry;
4383 ULARGE_INTEGER zero;
4387 if (!This->entries[This->base.storageDirEntry].read)
4390 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4392 if (cursor == DIRENTRY_NULL)
4395 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4397 while (cursor != DIRENTRY_NULL && cursor != stop)
4399 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4401 entry = &This->entries[cursor];
4403 if (entry->stream_dirty)
4404 StorageBaseImpl_StreamSetSize(This->transactedParent,
4405 entry->newTransactedParentEntry, zero);
4407 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4408 entry->newTransactedParentEntry);
4410 entry->newTransactedParentEntry = entry->transactedParentEntry;
4413 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4417 /* Make a copy of our edited tree that we can use in the parent. */
4418 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4421 TransactedDirEntry *entry;
4424 cursor = This->base.storageDirEntry;
4425 entry = &This->entries[cursor];
4426 entry->parent = DIRENTRY_NULL;
4427 entry->newTransactedParentEntry = entry->transactedParentEntry;
4429 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4432 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4434 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4435 entry = &This->entries[cursor];
4437 while (cursor != DIRENTRY_NULL)
4439 /* Make a copy of this entry in the transacted parent. */
4441 (!entry->dirty && !entry->stream_dirty &&
4442 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4443 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4444 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4445 entry->newTransactedParentEntry = entry->transactedParentEntry;
4450 memcpy(&newData, &entry->data, sizeof(DirEntry));
4452 newData.size.QuadPart = 0;
4453 newData.startingBlock = BLOCK_END_OF_CHAIN;
4455 if (newData.leftChild != DIRENTRY_NULL)
4456 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4458 if (newData.rightChild != DIRENTRY_NULL)
4459 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4461 if (newData.dirRootEntry != DIRENTRY_NULL)
4462 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4464 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4465 &entry->newTransactedParentEntry);
4468 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4472 if (entry->stream_dirty)
4474 hr = StorageBaseImpl_CopyStream(
4475 This->transactedParent, entry->newTransactedParentEntry,
4476 This->scratch, entry->stream_entry);
4478 else if (entry->data.size.QuadPart)
4480 hr = StorageBaseImpl_StreamLink(
4481 This->transactedParent, entry->newTransactedParentEntry,
4482 entry->transactedParentEntry);
4487 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4488 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4493 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4494 entry = &This->entries[cursor];
4500 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4502 DWORD grfCommitFlags) /* [in] */
4504 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4505 TransactedDirEntry *root_entry;
4506 DirRef i, dir_root_ref;
4508 ULARGE_INTEGER zero;
4513 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4515 /* Cannot commit a read-only transacted storage */
4516 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4517 return STG_E_ACCESSDENIED;
4519 /* To prevent data loss, we create the new structure in the file before we
4520 * delete the old one, so that in case of errors the old data is intact. We
4521 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4522 * needed in the rare situation where we have just enough free disk space to
4523 * overwrite the existing data. */
4525 root_entry = &This->entries[This->base.storageDirEntry];
4527 if (!root_entry->read)
4530 hr = TransactedSnapshotImpl_CopyTree(This);
4531 if (FAILED(hr)) return hr;
4533 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4534 dir_root_ref = DIRENTRY_NULL;
4536 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4538 hr = StorageBaseImpl_Flush(This->transactedParent);
4540 /* Update the storage to use the new data in one step. */
4542 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4543 root_entry->transactedParentEntry, &data);
4547 data.dirRootEntry = dir_root_ref;
4548 data.clsid = root_entry->data.clsid;
4549 data.ctime = root_entry->data.ctime;
4550 data.mtime = root_entry->data.mtime;
4552 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4553 root_entry->transactedParentEntry, &data);
4556 /* Try to flush after updating the root storage, but if the flush fails, keep
4557 * going, on the theory that it'll either succeed later or the subsequent
4558 * writes will fail. */
4559 StorageBaseImpl_Flush(This->transactedParent);
4563 /* Destroy the old now-orphaned data. */
4564 for (i=0; i<This->entries_size; i++)
4566 TransactedDirEntry *entry = &This->entries[i];
4571 StorageBaseImpl_StreamSetSize(This->transactedParent,
4572 entry->transactedParentEntry, zero);
4573 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4574 entry->transactedParentEntry);
4575 memset(entry, 0, sizeof(TransactedDirEntry));
4576 This->firstFreeEntry = min(i, This->firstFreeEntry);
4578 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4580 if (entry->transactedParentEntry != DIRENTRY_NULL)
4581 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4582 entry->transactedParentEntry);
4583 if (entry->stream_dirty)
4585 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4586 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4587 entry->stream_dirty = 0;
4590 entry->transactedParentEntry = entry->newTransactedParentEntry;
4597 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4601 hr = StorageBaseImpl_Flush(This->transactedParent);
4606 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4609 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4610 ULARGE_INTEGER zero;
4613 TRACE("(%p)\n", iface);
4615 /* Destroy the open objects. */
4616 StorageBaseImpl_DeleteAll(&This->base);
4618 /* Clear out the scratch file. */
4620 for (i=0; i<This->entries_size; i++)
4622 if (This->entries[i].stream_dirty)
4624 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4627 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4631 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4633 This->firstFreeEntry = 0;
4634 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4639 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4641 if (!This->reverted)
4643 TRACE("Storage invalidated (stg=%p)\n", This);
4647 StorageBaseImpl_DeleteAll(This);
4651 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4653 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4655 TransactedSnapshotImpl_Revert((IStorage*)iface);
4657 IStorage_Release((IStorage*)This->transactedParent);
4659 IStorage_Release((IStorage*)This->scratch);
4661 HeapFree(GetProcessHeap(), 0, This->entries);
4663 HeapFree(GetProcessHeap(), 0, This);
4666 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4668 /* We only need to flush when committing. */
4672 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4674 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4676 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4679 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4680 const DirEntry *newData, DirRef *index)
4682 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4684 TransactedDirEntry *new_entry;
4686 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4687 if (new_ref == DIRENTRY_NULL)
4688 return E_OUTOFMEMORY;
4690 new_entry = &This->entries[new_ref];
4692 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4693 new_entry->read = 1;
4694 new_entry->dirty = 1;
4695 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4699 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4704 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4705 DirRef index, const DirEntry *data)
4707 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4710 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4712 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4713 if (FAILED(hr)) return hr;
4715 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4717 if (index != This->base.storageDirEntry)
4719 This->entries[index].dirty = 1;
4721 if (data->size.QuadPart == 0 &&
4722 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4724 /* Since this entry is modified, and we aren't using its stream data, we
4725 * no longer care about the original entry. */
4727 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4729 if (delete_ref != DIRENTRY_NULL)
4730 This->entries[delete_ref].deleted = 1;
4732 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4739 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4740 DirRef index, DirEntry *data)
4742 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4745 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4746 if (FAILED(hr)) return hr;
4748 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4750 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4755 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4758 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4760 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4761 This->entries[index].data.size.QuadPart != 0)
4763 /* If we deleted this entry while it has stream data. We must have left the
4764 * data because some other entry is using it, and we need to leave the
4765 * original entry alone. */
4766 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4767 This->firstFreeEntry = min(index, This->firstFreeEntry);
4771 This->entries[index].deleted = 1;
4777 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4778 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4780 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4782 if (This->entries[index].stream_dirty)
4784 return StorageBaseImpl_StreamReadAt(This->scratch,
4785 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4787 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4789 /* This stream doesn't live in the parent, and we haven't allocated storage
4796 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4797 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4801 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4802 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4804 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4807 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4808 if (FAILED(hr)) return hr;
4810 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4811 if (FAILED(hr)) return hr;
4813 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4814 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4816 if (SUCCEEDED(hr) && size != 0)
4817 This->entries[index].data.size.QuadPart = max(
4818 This->entries[index].data.size.QuadPart,
4819 offset.QuadPart + size);
4824 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4825 DirRef index, ULARGE_INTEGER newsize)
4827 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4830 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4831 if (FAILED(hr)) return hr;
4833 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4836 if (newsize.QuadPart == 0)
4838 /* Destroy any parent references or entries in the scratch file. */
4839 if (This->entries[index].stream_dirty)
4841 ULARGE_INTEGER zero;
4843 StorageBaseImpl_StreamSetSize(This->scratch,
4844 This->entries[index].stream_entry, zero);
4845 StorageBaseImpl_DestroyDirEntry(This->scratch,
4846 This->entries[index].stream_entry);
4847 This->entries[index].stream_dirty = 0;
4849 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4852 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4854 if (delete_ref != DIRENTRY_NULL)
4855 This->entries[delete_ref].deleted = 1;
4857 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4862 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4863 if (FAILED(hr)) return hr;
4865 hr = StorageBaseImpl_StreamSetSize(This->scratch,
4866 This->entries[index].stream_entry, newsize);
4870 This->entries[index].data.size = newsize;
4875 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
4876 DirRef dst, DirRef src)
4878 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4880 TransactedDirEntry *dst_entry, *src_entry;
4882 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
4883 if (FAILED(hr)) return hr;
4885 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
4886 if (FAILED(hr)) return hr;
4888 dst_entry = &This->entries[dst];
4889 src_entry = &This->entries[src];
4891 dst_entry->stream_dirty = src_entry->stream_dirty;
4892 dst_entry->stream_entry = src_entry->stream_entry;
4893 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
4894 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
4895 dst_entry->data.size = src_entry->data.size;
4900 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4902 StorageBaseImpl_QueryInterface,
4903 StorageBaseImpl_AddRef,
4904 StorageBaseImpl_Release,
4905 StorageBaseImpl_CreateStream,
4906 StorageBaseImpl_OpenStream,
4907 StorageBaseImpl_CreateStorage,
4908 StorageBaseImpl_OpenStorage,
4909 StorageBaseImpl_CopyTo,
4910 StorageBaseImpl_MoveElementTo,
4911 TransactedSnapshotImpl_Commit,
4912 TransactedSnapshotImpl_Revert,
4913 StorageBaseImpl_EnumElements,
4914 StorageBaseImpl_DestroyElement,
4915 StorageBaseImpl_RenameElement,
4916 StorageBaseImpl_SetElementTimes,
4917 StorageBaseImpl_SetClass,
4918 StorageBaseImpl_SetStateBits,
4919 StorageBaseImpl_Stat
4922 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4924 TransactedSnapshotImpl_Destroy,
4925 TransactedSnapshotImpl_Invalidate,
4926 TransactedSnapshotImpl_Flush,
4927 TransactedSnapshotImpl_GetFilename,
4928 TransactedSnapshotImpl_CreateDirEntry,
4929 TransactedSnapshotImpl_WriteDirEntry,
4930 TransactedSnapshotImpl_ReadDirEntry,
4931 TransactedSnapshotImpl_DestroyDirEntry,
4932 TransactedSnapshotImpl_StreamReadAt,
4933 TransactedSnapshotImpl_StreamWriteAt,
4934 TransactedSnapshotImpl_StreamSetSize,
4935 TransactedSnapshotImpl_StreamLink
4938 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4939 TransactedSnapshotImpl** result)
4943 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4946 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4948 /* This is OK because the property set storage functions use the IStorage functions. */
4949 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4951 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4953 list_init(&(*result)->base.strmHead);
4955 list_init(&(*result)->base.storageHead);
4957 (*result)->base.ref = 1;
4959 (*result)->base.openFlags = parentStorage->openFlags;
4961 /* Create a new temporary storage to act as the scratch file. */
4962 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
4963 0, (IStorage**)&(*result)->scratch);
4967 ULONG num_entries = 20;
4969 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
4971 (*result)->entries_size = num_entries;
4973 (*result)->firstFreeEntry = 0;
4975 if ((*result)->entries)
4977 /* parentStorage already has 1 reference, which we take over here. */
4978 (*result)->transactedParent = parentStorage;
4980 parentStorage->transactedChild = (StorageBaseImpl*)*result;
4982 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
4986 IStorage_Release((IStorage*)(*result)->scratch);
4992 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4997 return E_OUTOFMEMORY;
5000 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5001 StorageBaseImpl** result)
5005 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5007 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5010 return TransactedSnapshotImpl_Construct(parentStorage,
5011 (TransactedSnapshotImpl**)result);
5014 static HRESULT Storage_Construct(
5022 StorageBaseImpl** result)
5024 StorageImpl *newStorage;
5025 StorageBaseImpl *newTransactedStorage;
5028 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5029 if (FAILED(hr)) goto end;
5031 if (openFlags & STGM_TRANSACTED)
5033 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5035 IStorage_Release((IStorage*)newStorage);
5037 *result = newTransactedStorage;
5040 *result = &newStorage->base;
5046 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5048 StorageInternalImpl* This = (StorageInternalImpl*) base;
5050 if (!This->base.reverted)
5052 TRACE("Storage invalidated (stg=%p)\n", This);
5054 This->base.reverted = 1;
5056 This->parentStorage = NULL;
5058 StorageBaseImpl_DeleteAll(&This->base);
5060 list_remove(&This->ParentListEntry);
5064 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5066 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5068 StorageInternalImpl_Invalidate(&This->base);
5070 HeapFree(GetProcessHeap(), 0, This);
5073 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5075 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5077 return StorageBaseImpl_Flush(This->parentStorage);
5080 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5082 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5084 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5087 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5088 const DirEntry *newData, DirRef *index)
5090 StorageInternalImpl* This = (StorageInternalImpl*) base;
5092 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5096 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5097 DirRef index, const DirEntry *data)
5099 StorageInternalImpl* This = (StorageInternalImpl*) base;
5101 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5105 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5106 DirRef index, DirEntry *data)
5108 StorageInternalImpl* This = (StorageInternalImpl*) base;
5110 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5114 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5117 StorageInternalImpl* This = (StorageInternalImpl*) base;
5119 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5123 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5124 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5126 StorageInternalImpl* This = (StorageInternalImpl*) base;
5128 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5129 index, offset, size, buffer, bytesRead);
5132 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5133 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5135 StorageInternalImpl* This = (StorageInternalImpl*) base;
5137 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5138 index, offset, size, buffer, bytesWritten);
5141 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5142 DirRef index, ULARGE_INTEGER newsize)
5144 StorageInternalImpl* This = (StorageInternalImpl*) base;
5146 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5150 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5151 DirRef dst, DirRef src)
5153 StorageInternalImpl* This = (StorageInternalImpl*) base;
5155 return StorageBaseImpl_StreamLink(This->parentStorage,
5159 /******************************************************************************
5161 ** Storage32InternalImpl_Commit
5164 static HRESULT WINAPI StorageInternalImpl_Commit(
5166 DWORD grfCommitFlags) /* [in] */
5168 StorageBaseImpl* base = (StorageBaseImpl*) iface;
5169 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5170 return StorageBaseImpl_Flush(base);
5173 /******************************************************************************
5175 ** Storage32InternalImpl_Revert
5178 static HRESULT WINAPI StorageInternalImpl_Revert(
5181 FIXME("(%p): stub\n", iface);
5185 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5187 IStorage_Release((IStorage*)This->parentStorage);
5188 HeapFree(GetProcessHeap(), 0, This);
5191 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5192 IEnumSTATSTG* iface,
5196 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5199 return E_INVALIDARG;
5203 if (IsEqualGUID(&IID_IUnknown, riid) ||
5204 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5207 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
5211 return E_NOINTERFACE;
5214 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5215 IEnumSTATSTG* iface)
5217 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5218 return InterlockedIncrement(&This->ref);
5221 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5222 IEnumSTATSTG* iface)
5224 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5228 newRef = InterlockedDecrement(&This->ref);
5232 IEnumSTATSTGImpl_Destroy(This);
5238 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5239 IEnumSTATSTGImpl* This,
5242 DirRef result = DIRENTRY_NULL;
5246 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5248 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5249 This->parentStorage->storageDirEntry, &entry);
5250 searchNode = entry.dirRootEntry;
5252 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5254 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5258 LONG diff = entryNameCmp( entry.name, This->name);
5262 searchNode = entry.rightChild;
5266 result = searchNode;
5267 memcpy(result_name, entry.name, sizeof(result_name));
5268 searchNode = entry.leftChild;
5276 if (result != DIRENTRY_NULL)
5277 memcpy(This->name, result_name, sizeof(result_name));
5283 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5284 IEnumSTATSTG* iface,
5287 ULONG* pceltFetched)
5289 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5291 DirEntry currentEntry;
5292 STATSTG* currentReturnStruct = rgelt;
5293 ULONG objectFetched = 0;
5294 DirRef currentSearchNode;
5297 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5298 return E_INVALIDARG;
5300 if (This->parentStorage->reverted)
5301 return STG_E_REVERTED;
5304 * To avoid the special case, get another pointer to a ULONG value if
5305 * the caller didn't supply one.
5307 if (pceltFetched==0)
5308 pceltFetched = &objectFetched;
5311 * Start the iteration, we will iterate until we hit the end of the
5312 * linked list or until we hit the number of items to iterate through
5316 while ( *pceltFetched < celt )
5318 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
5320 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5324 * Read the entry from the storage.
5326 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5331 * Copy the information to the return buffer.
5333 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5334 currentReturnStruct,
5339 * Step to the next item in the iteration
5342 currentReturnStruct++;
5345 if (SUCCEEDED(hr) && *pceltFetched != celt)
5352 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5353 IEnumSTATSTG* iface,
5356 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5358 ULONG objectFetched = 0;
5359 DirRef currentSearchNode;
5362 if (This->parentStorage->reverted)
5363 return STG_E_REVERTED;
5365 while ( (objectFetched < celt) )
5367 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
5369 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5375 if (SUCCEEDED(hr) && objectFetched != celt)
5381 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5382 IEnumSTATSTG* iface)
5384 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5386 if (This->parentStorage->reverted)
5387 return STG_E_REVERTED;
5394 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5395 IEnumSTATSTG* iface,
5396 IEnumSTATSTG** ppenum)
5398 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5400 IEnumSTATSTGImpl* newClone;
5402 if (This->parentStorage->reverted)
5403 return STG_E_REVERTED;
5406 * Perform a sanity check on the parameters.
5409 return E_INVALIDARG;
5411 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5412 This->storageDirEntry);
5416 * The new clone enumeration must point to the same current node as
5419 memcpy(newClone->name, This->name, sizeof(newClone->name));
5421 *ppenum = (IEnumSTATSTG*)newClone;
5424 * Don't forget to nail down a reference to the clone before
5427 IEnumSTATSTGImpl_AddRef(*ppenum);
5433 * Virtual function table for the IEnumSTATSTGImpl class.
5435 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5437 IEnumSTATSTGImpl_QueryInterface,
5438 IEnumSTATSTGImpl_AddRef,
5439 IEnumSTATSTGImpl_Release,
5440 IEnumSTATSTGImpl_Next,
5441 IEnumSTATSTGImpl_Skip,
5442 IEnumSTATSTGImpl_Reset,
5443 IEnumSTATSTGImpl_Clone
5446 /******************************************************************************
5447 ** IEnumSTATSTGImpl implementation
5450 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5451 StorageBaseImpl* parentStorage,
5452 DirRef storageDirEntry)
5454 IEnumSTATSTGImpl* newEnumeration;
5456 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5458 if (newEnumeration!=0)
5461 * Set-up the virtual function table and reference count.
5463 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5464 newEnumeration->ref = 0;
5467 * We want to nail-down the reference to the storage in case the
5468 * enumeration out-lives the storage in the client application.
5470 newEnumeration->parentStorage = parentStorage;
5471 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
5473 newEnumeration->storageDirEntry = storageDirEntry;
5476 * Make sure the current node of the iterator is the first one.
5478 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
5481 return newEnumeration;
5485 * Virtual function table for the Storage32InternalImpl class.
5487 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5489 StorageBaseImpl_QueryInterface,
5490 StorageBaseImpl_AddRef,
5491 StorageBaseImpl_Release,
5492 StorageBaseImpl_CreateStream,
5493 StorageBaseImpl_OpenStream,
5494 StorageBaseImpl_CreateStorage,
5495 StorageBaseImpl_OpenStorage,
5496 StorageBaseImpl_CopyTo,
5497 StorageBaseImpl_MoveElementTo,
5498 StorageInternalImpl_Commit,
5499 StorageInternalImpl_Revert,
5500 StorageBaseImpl_EnumElements,
5501 StorageBaseImpl_DestroyElement,
5502 StorageBaseImpl_RenameElement,
5503 StorageBaseImpl_SetElementTimes,
5504 StorageBaseImpl_SetClass,
5505 StorageBaseImpl_SetStateBits,
5506 StorageBaseImpl_Stat
5509 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5511 StorageInternalImpl_Destroy,
5512 StorageInternalImpl_Invalidate,
5513 StorageInternalImpl_Flush,
5514 StorageInternalImpl_GetFilename,
5515 StorageInternalImpl_CreateDirEntry,
5516 StorageInternalImpl_WriteDirEntry,
5517 StorageInternalImpl_ReadDirEntry,
5518 StorageInternalImpl_DestroyDirEntry,
5519 StorageInternalImpl_StreamReadAt,
5520 StorageInternalImpl_StreamWriteAt,
5521 StorageInternalImpl_StreamSetSize,
5522 StorageInternalImpl_StreamLink
5525 /******************************************************************************
5526 ** Storage32InternalImpl implementation
5529 static StorageInternalImpl* StorageInternalImpl_Construct(
5530 StorageBaseImpl* parentStorage,
5532 DirRef storageDirEntry)
5534 StorageInternalImpl* newStorage;
5536 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5540 list_init(&newStorage->base.strmHead);
5542 list_init(&newStorage->base.storageHead);
5545 * Initialize the virtual function table.
5547 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
5548 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
5549 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5550 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5552 newStorage->base.reverted = 0;
5554 newStorage->base.ref = 1;
5556 newStorage->parentStorage = parentStorage;
5559 * Keep a reference to the directory entry of this storage
5561 newStorage->base.storageDirEntry = storageDirEntry;
5563 newStorage->base.create = 0;
5571 /******************************************************************************
5572 ** StorageUtl implementation
5575 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5579 memcpy(&tmp, buffer+offset, sizeof(WORD));
5580 *value = lendian16toh(tmp);
5583 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5585 value = htole16(value);
5586 memcpy(buffer+offset, &value, sizeof(WORD));
5589 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5593 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5594 *value = lendian32toh(tmp);
5597 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5599 value = htole32(value);
5600 memcpy(buffer+offset, &value, sizeof(DWORD));
5603 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5604 ULARGE_INTEGER* value)
5606 #ifdef WORDS_BIGENDIAN
5609 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5610 value->u.LowPart = htole32(tmp.u.HighPart);
5611 value->u.HighPart = htole32(tmp.u.LowPart);
5613 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5617 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5618 const ULARGE_INTEGER *value)
5620 #ifdef WORDS_BIGENDIAN
5623 tmp.u.LowPart = htole32(value->u.HighPart);
5624 tmp.u.HighPart = htole32(value->u.LowPart);
5625 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5627 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5631 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5633 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5634 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5635 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5637 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5640 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5642 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5643 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5644 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5646 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5649 void StorageUtl_CopyDirEntryToSTATSTG(
5650 StorageBaseImpl* storage,
5651 STATSTG* destination,
5652 const DirEntry* source,
5656 * The copy of the string occurs only when the flag is not set
5658 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5660 /* Use the filename for the root storage. */
5661 destination->pwcsName = 0;
5662 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5664 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5665 (source->name[0] == 0) )
5667 destination->pwcsName = 0;
5671 destination->pwcsName =
5672 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5674 strcpyW(destination->pwcsName, source->name);
5677 switch (source->stgType)
5681 destination->type = STGTY_STORAGE;
5684 destination->type = STGTY_STREAM;
5687 destination->type = STGTY_STREAM;
5691 destination->cbSize = source->size;
5693 currentReturnStruct->mtime = {0}; TODO
5694 currentReturnStruct->ctime = {0};
5695 currentReturnStruct->atime = {0};
5697 destination->grfMode = 0;
5698 destination->grfLocksSupported = 0;
5699 destination->clsid = source->clsid;
5700 destination->grfStateBits = 0;
5701 destination->reserved = 0;
5704 /******************************************************************************
5705 ** BlockChainStream implementation
5708 /* Read and save the index of all blocks in this stream. */
5709 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5711 ULONG next_sector, next_offset;
5713 struct BlockChainRun *last_run;
5715 if (This->indexCacheLen == 0)
5719 next_sector = BlockChainStream_GetHeadOfChain(This);
5723 last_run = &This->indexCache[This->indexCacheLen-1];
5724 next_offset = last_run->lastOffset+1;
5725 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5726 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5728 if (FAILED(hr)) return hr;
5731 while (next_sector != BLOCK_END_OF_CHAIN)
5733 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5735 /* Add the current block to the cache. */
5736 if (This->indexCacheSize == 0)
5738 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5739 if (!This->indexCache) return E_OUTOFMEMORY;
5740 This->indexCacheSize = 16;
5742 else if (This->indexCacheSize == This->indexCacheLen)
5744 struct BlockChainRun *new_cache;
5747 new_size = This->indexCacheSize * 2;
5748 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5749 if (!new_cache) return E_OUTOFMEMORY;
5750 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5752 HeapFree(GetProcessHeap(), 0, This->indexCache);
5753 This->indexCache = new_cache;
5754 This->indexCacheSize = new_size;
5757 This->indexCacheLen++;
5758 last_run = &This->indexCache[This->indexCacheLen-1];
5759 last_run->firstSector = next_sector;
5760 last_run->firstOffset = next_offset;
5763 last_run->lastOffset = next_offset;
5765 /* Find the next block. */
5767 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5768 if (FAILED(hr)) return hr;
5771 if (This->indexCacheLen)
5773 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5774 This->numBlocks = last_run->lastOffset+1;
5778 This->tailIndex = BLOCK_END_OF_CHAIN;
5779 This->numBlocks = 0;
5785 /* Locate the nth block in this stream. */
5786 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5788 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5789 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5791 if (offset >= This->numBlocks)
5792 return BLOCK_END_OF_CHAIN;
5794 while (min_run < max_run)
5796 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5797 if (offset < This->indexCache[run_to_check].firstOffset)
5799 max_offset = This->indexCache[run_to_check].firstOffset-1;
5800 max_run = run_to_check-1;
5802 else if (offset > This->indexCache[run_to_check].lastOffset)
5804 min_offset = This->indexCache[run_to_check].lastOffset+1;
5805 min_run = run_to_check+1;
5808 /* Block is in this run. */
5809 min_run = max_run = run_to_check;
5812 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5815 BlockChainStream* BlockChainStream_Construct(
5816 StorageImpl* parentStorage,
5817 ULONG* headOfStreamPlaceHolder,
5820 BlockChainStream* newStream;
5822 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5824 newStream->parentStorage = parentStorage;
5825 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5826 newStream->ownerDirEntry = dirEntry;
5827 newStream->indexCache = NULL;
5828 newStream->indexCacheLen = 0;
5829 newStream->indexCacheSize = 0;
5831 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
5833 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
5834 HeapFree(GetProcessHeap(), 0, newStream);
5841 void BlockChainStream_Destroy(BlockChainStream* This)
5844 HeapFree(GetProcessHeap(), 0, This->indexCache);
5845 HeapFree(GetProcessHeap(), 0, This);
5848 /******************************************************************************
5849 * BlockChainStream_GetHeadOfChain
5851 * Returns the head of this stream chain.
5852 * Some special chains don't have directory entries, their heads are kept in
5853 * This->headOfStreamPlaceHolder.
5856 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5858 DirEntry chainEntry;
5861 if (This->headOfStreamPlaceHolder != 0)
5862 return *(This->headOfStreamPlaceHolder);
5864 if (This->ownerDirEntry != DIRENTRY_NULL)
5866 hr = StorageImpl_ReadDirEntry(
5867 This->parentStorage,
5868 This->ownerDirEntry,
5873 return chainEntry.startingBlock;
5877 return BLOCK_END_OF_CHAIN;
5880 /******************************************************************************
5881 * BlockChainStream_GetCount
5883 * Returns the number of blocks that comprises this chain.
5884 * This is not the size of the stream as the last block may not be full!
5886 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5888 return This->numBlocks;
5891 /******************************************************************************
5892 * BlockChainStream_ReadAt
5894 * Reads a specified number of bytes from this chain at the specified offset.
5895 * bytesRead may be NULL.
5896 * Failure will be returned if the specified number of bytes has not been read.
5898 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5899 ULARGE_INTEGER offset,
5904 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5905 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5906 ULONG bytesToReadInBuffer;
5909 ULARGE_INTEGER stream_size;
5911 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5914 * Find the first block in the stream that contains part of the buffer.
5916 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
5920 stream_size = BlockChainStream_GetSize(This);
5921 if (stream_size.QuadPart > offset.QuadPart)
5922 size = min(stream_size.QuadPart - offset.QuadPart, size);
5927 * Start reading the buffer.
5929 bufferWalker = buffer;
5931 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5933 ULARGE_INTEGER ulOffset;
5936 * Calculate how many bytes we can copy from this big block.
5938 bytesToReadInBuffer =
5939 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5941 TRACE("block %i\n",blockIndex);
5942 ulOffset.u.HighPart = 0;
5943 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
5946 StorageImpl_ReadAt(This->parentStorage,
5949 bytesToReadInBuffer,
5952 * Step to the next big block.
5954 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5955 return STG_E_DOCFILECORRUPT;
5957 bufferWalker += bytesReadAt;
5958 size -= bytesReadAt;
5959 *bytesRead += bytesReadAt;
5960 offsetInBlock = 0; /* There is no offset on the next block */
5962 if (bytesToReadInBuffer != bytesReadAt)
5969 /******************************************************************************
5970 * BlockChainStream_WriteAt
5972 * Writes the specified number of bytes to this chain at the specified offset.
5973 * Will fail if not all specified number of bytes have been written.
5975 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5976 ULARGE_INTEGER offset,
5979 ULONG* bytesWritten)
5981 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5982 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5985 const BYTE* bufferWalker;
5988 * Find the first block in the stream that contains part of the buffer.
5990 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
5992 /* BlockChainStream_SetSize should have already been called to ensure we have
5993 * enough blocks in the chain to write into */
5994 if (blockIndex == BLOCK_END_OF_CHAIN)
5996 ERR("not enough blocks in chain to write data\n");
5997 return STG_E_DOCFILECORRUPT;
6001 bufferWalker = buffer;
6003 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6005 ULARGE_INTEGER ulOffset;
6006 DWORD bytesWrittenAt;
6008 * Calculate how many bytes we can copy from this big block.
6011 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6013 TRACE("block %i\n",blockIndex);
6014 ulOffset.u.HighPart = 0;
6015 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6018 StorageImpl_WriteAt(This->parentStorage,
6025 * Step to the next big block.
6027 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
6029 return STG_E_DOCFILECORRUPT;
6031 bufferWalker += bytesWrittenAt;
6032 size -= bytesWrittenAt;
6033 *bytesWritten += bytesWrittenAt;
6034 offsetInBlock = 0; /* There is no offset on the next block */
6036 if (bytesWrittenAt != bytesToWrite)
6040 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6043 /******************************************************************************
6044 * BlockChainStream_Shrink
6046 * Shrinks this chain in the big block depot.
6048 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6049 ULARGE_INTEGER newSize)
6055 * Figure out how many blocks are needed to contain the new size
6057 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6059 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6065 * Go to the new end of chain
6067 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6069 /* Mark the new end of chain */
6070 StorageImpl_SetNextBlockInChain(
6071 This->parentStorage,
6073 BLOCK_END_OF_CHAIN);
6075 This->tailIndex = blockIndex;
6079 if (This->headOfStreamPlaceHolder != 0)
6081 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6085 DirEntry chainEntry;
6086 assert(This->ownerDirEntry != DIRENTRY_NULL);
6088 StorageImpl_ReadDirEntry(
6089 This->parentStorage,
6090 This->ownerDirEntry,
6093 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6095 StorageImpl_WriteDirEntry(
6096 This->parentStorage,
6097 This->ownerDirEntry,
6101 This->tailIndex = BLOCK_END_OF_CHAIN;
6104 This->numBlocks = numBlocks;
6107 * Mark the extra blocks as free
6109 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6111 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6112 StorageImpl_FreeBigBlock(This->parentStorage,
6113 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6114 if (last_run->lastOffset == last_run->firstOffset)
6115 This->indexCacheLen--;
6117 last_run->lastOffset--;
6123 /******************************************************************************
6124 * BlockChainStream_Enlarge
6126 * Grows this chain in the big block depot.
6128 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6129 ULARGE_INTEGER newSize)
6131 ULONG blockIndex, currentBlock;
6133 ULONG oldNumBlocks = 0;
6135 blockIndex = BlockChainStream_GetHeadOfChain(This);
6138 * Empty chain. Create the head.
6140 if (blockIndex == BLOCK_END_OF_CHAIN)
6142 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6143 StorageImpl_SetNextBlockInChain(This->parentStorage,
6145 BLOCK_END_OF_CHAIN);
6147 if (This->headOfStreamPlaceHolder != 0)
6149 *(This->headOfStreamPlaceHolder) = blockIndex;
6153 DirEntry chainEntry;
6154 assert(This->ownerDirEntry != DIRENTRY_NULL);
6156 StorageImpl_ReadDirEntry(
6157 This->parentStorage,
6158 This->ownerDirEntry,
6161 chainEntry.startingBlock = blockIndex;
6163 StorageImpl_WriteDirEntry(
6164 This->parentStorage,
6165 This->ownerDirEntry,
6169 This->tailIndex = blockIndex;
6170 This->numBlocks = 1;
6174 * Figure out how many blocks are needed to contain this stream
6176 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6178 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6182 * Go to the current end of chain
6184 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6186 currentBlock = blockIndex;
6188 while (blockIndex != BLOCK_END_OF_CHAIN)
6191 currentBlock = blockIndex;
6193 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6198 This->tailIndex = currentBlock;
6201 currentBlock = This->tailIndex;
6202 oldNumBlocks = This->numBlocks;
6205 * Add new blocks to the chain
6207 if (oldNumBlocks < newNumBlocks)
6209 while (oldNumBlocks < newNumBlocks)
6211 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6213 StorageImpl_SetNextBlockInChain(
6214 This->parentStorage,
6218 StorageImpl_SetNextBlockInChain(
6219 This->parentStorage,
6221 BLOCK_END_OF_CHAIN);
6223 currentBlock = blockIndex;
6227 This->tailIndex = blockIndex;
6228 This->numBlocks = newNumBlocks;
6231 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6237 /******************************************************************************
6238 * BlockChainStream_SetSize
6240 * Sets the size of this stream. The big block depot will be updated.
6241 * The file will grow if we grow the chain.
6243 * TODO: Free the actual blocks in the file when we shrink the chain.
6244 * Currently, the blocks are still in the file. So the file size
6245 * doesn't shrink even if we shrink streams.
6247 BOOL BlockChainStream_SetSize(
6248 BlockChainStream* This,
6249 ULARGE_INTEGER newSize)
6251 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6253 if (newSize.u.LowPart == size.u.LowPart)
6256 if (newSize.u.LowPart < size.u.LowPart)
6258 BlockChainStream_Shrink(This, newSize);
6262 BlockChainStream_Enlarge(This, newSize);
6268 /******************************************************************************
6269 * BlockChainStream_GetSize
6271 * Returns the size of this chain.
6272 * Will return the block count if this chain doesn't have a directory entry.
6274 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6276 DirEntry chainEntry;
6278 if(This->headOfStreamPlaceHolder == NULL)
6281 * This chain has a directory entry so use the size value from there.
6283 StorageImpl_ReadDirEntry(
6284 This->parentStorage,
6285 This->ownerDirEntry,
6288 return chainEntry.size;
6293 * this chain is a chain that does not have a directory entry, figure out the
6294 * size by making the product number of used blocks times the
6297 ULARGE_INTEGER result;
6298 result.u.HighPart = 0;
6301 BlockChainStream_GetCount(This) *
6302 This->parentStorage->bigBlockSize;
6308 /******************************************************************************
6309 ** SmallBlockChainStream implementation
6312 SmallBlockChainStream* SmallBlockChainStream_Construct(
6313 StorageImpl* parentStorage,
6314 ULONG* headOfStreamPlaceHolder,
6317 SmallBlockChainStream* newStream;
6319 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6321 newStream->parentStorage = parentStorage;
6322 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6323 newStream->ownerDirEntry = dirEntry;
6328 void SmallBlockChainStream_Destroy(
6329 SmallBlockChainStream* This)
6331 HeapFree(GetProcessHeap(), 0, This);
6334 /******************************************************************************
6335 * SmallBlockChainStream_GetHeadOfChain
6337 * Returns the head of this chain of small blocks.
6339 static ULONG SmallBlockChainStream_GetHeadOfChain(
6340 SmallBlockChainStream* This)
6342 DirEntry chainEntry;
6345 if (This->headOfStreamPlaceHolder != NULL)
6346 return *(This->headOfStreamPlaceHolder);
6348 if (This->ownerDirEntry)
6350 hr = StorageImpl_ReadDirEntry(
6351 This->parentStorage,
6352 This->ownerDirEntry,
6357 return chainEntry.startingBlock;
6362 return BLOCK_END_OF_CHAIN;
6365 /******************************************************************************
6366 * SmallBlockChainStream_GetNextBlockInChain
6368 * Returns the index of the next small block in this chain.
6371 * - BLOCK_END_OF_CHAIN: end of this chain
6372 * - BLOCK_UNUSED: small block 'blockIndex' is free
6374 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6375 SmallBlockChainStream* This,
6377 ULONG* nextBlockInChain)
6379 ULARGE_INTEGER offsetOfBlockInDepot;
6384 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6386 offsetOfBlockInDepot.u.HighPart = 0;
6387 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6390 * Read those bytes in the buffer from the small block file.
6392 res = BlockChainStream_ReadAt(
6393 This->parentStorage->smallBlockDepotChain,
6394 offsetOfBlockInDepot,
6399 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6400 res = STG_E_READFAULT;
6404 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6411 /******************************************************************************
6412 * SmallBlockChainStream_SetNextBlockInChain
6414 * Writes the index of the next block of the specified block in the small
6416 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6417 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6419 static void SmallBlockChainStream_SetNextBlockInChain(
6420 SmallBlockChainStream* This,
6424 ULARGE_INTEGER offsetOfBlockInDepot;
6428 offsetOfBlockInDepot.u.HighPart = 0;
6429 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6431 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6434 * Read those bytes in the buffer from the small block file.
6436 BlockChainStream_WriteAt(
6437 This->parentStorage->smallBlockDepotChain,
6438 offsetOfBlockInDepot,
6444 /******************************************************************************
6445 * SmallBlockChainStream_FreeBlock
6447 * Flag small block 'blockIndex' as free in the small block depot.
6449 static void SmallBlockChainStream_FreeBlock(
6450 SmallBlockChainStream* This,
6453 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6456 /******************************************************************************
6457 * SmallBlockChainStream_GetNextFreeBlock
6459 * Returns the index of a free small block. The small block depot will be
6460 * enlarged if necessary. The small block chain will also be enlarged if
6463 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6464 SmallBlockChainStream* This)
6466 ULARGE_INTEGER offsetOfBlockInDepot;
6469 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6470 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6472 ULONG smallBlocksPerBigBlock;
6474 ULONG blocksRequired;
6475 ULARGE_INTEGER old_size, size_required;
6477 offsetOfBlockInDepot.u.HighPart = 0;
6480 * Scan the small block depot for a free block
6482 while (nextBlockIndex != BLOCK_UNUSED)
6484 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6486 res = BlockChainStream_ReadAt(
6487 This->parentStorage->smallBlockDepotChain,
6488 offsetOfBlockInDepot,
6494 * If we run out of space for the small block depot, enlarge it
6496 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6498 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6500 if (nextBlockIndex != BLOCK_UNUSED)
6506 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6508 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6509 ULARGE_INTEGER newSize, offset;
6512 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6513 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6516 * Initialize all the small blocks to free
6518 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6519 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6520 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6521 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6523 StorageImpl_SaveFileHeader(This->parentStorage);
6527 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6529 smallBlocksPerBigBlock =
6530 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6533 * Verify if we have to allocate big blocks to contain small blocks
6535 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6537 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6539 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6541 if (size_required.QuadPart > old_size.QuadPart)
6543 BlockChainStream_SetSize(
6544 This->parentStorage->smallBlockRootChain,
6547 StorageImpl_ReadDirEntry(
6548 This->parentStorage,
6549 This->parentStorage->base.storageDirEntry,
6552 rootEntry.size = size_required;
6554 StorageImpl_WriteDirEntry(
6555 This->parentStorage,
6556 This->parentStorage->base.storageDirEntry,
6563 /******************************************************************************
6564 * SmallBlockChainStream_ReadAt
6566 * Reads a specified number of bytes from this chain at the specified offset.
6567 * bytesRead may be NULL.
6568 * Failure will be returned if the specified number of bytes has not been read.
6570 HRESULT SmallBlockChainStream_ReadAt(
6571 SmallBlockChainStream* This,
6572 ULARGE_INTEGER offset,
6578 ULARGE_INTEGER offsetInBigBlockFile;
6579 ULONG blockNoInSequence =
6580 offset.u.LowPart / This->parentStorage->smallBlockSize;
6582 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6583 ULONG bytesToReadInBuffer;
6585 ULONG bytesReadFromBigBlockFile;
6587 ULARGE_INTEGER stream_size;
6590 * This should never happen on a small block file.
6592 assert(offset.u.HighPart==0);
6596 stream_size = SmallBlockChainStream_GetSize(This);
6597 if (stream_size.QuadPart > offset.QuadPart)
6598 size = min(stream_size.QuadPart - offset.QuadPart, size);
6603 * Find the first block in the stream that contains part of the buffer.
6605 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6607 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6609 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6612 blockNoInSequence--;
6616 * Start reading the buffer.
6618 bufferWalker = buffer;
6620 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6623 * Calculate how many bytes we can copy from this small block.
6625 bytesToReadInBuffer =
6626 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6629 * Calculate the offset of the small block in the small block file.
6631 offsetInBigBlockFile.u.HighPart = 0;
6632 offsetInBigBlockFile.u.LowPart =
6633 blockIndex * This->parentStorage->smallBlockSize;
6635 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6638 * Read those bytes in the buffer from the small block file.
6639 * The small block has already been identified so it shouldn't fail
6640 * unless the file is corrupt.
6642 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6643 offsetInBigBlockFile,
6644 bytesToReadInBuffer,
6646 &bytesReadFromBigBlockFile);
6651 if (!bytesReadFromBigBlockFile)
6652 return STG_E_DOCFILECORRUPT;
6655 * Step to the next big block.
6657 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6659 return STG_E_DOCFILECORRUPT;
6661 bufferWalker += bytesReadFromBigBlockFile;
6662 size -= bytesReadFromBigBlockFile;
6663 *bytesRead += bytesReadFromBigBlockFile;
6664 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6670 /******************************************************************************
6671 * SmallBlockChainStream_WriteAt
6673 * Writes the specified number of bytes to this chain at the specified offset.
6674 * Will fail if not all specified number of bytes have been written.
6676 HRESULT SmallBlockChainStream_WriteAt(
6677 SmallBlockChainStream* This,
6678 ULARGE_INTEGER offset,
6681 ULONG* bytesWritten)
6683 ULARGE_INTEGER offsetInBigBlockFile;
6684 ULONG blockNoInSequence =
6685 offset.u.LowPart / This->parentStorage->smallBlockSize;
6687 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6688 ULONG bytesToWriteInBuffer;
6690 ULONG bytesWrittenToBigBlockFile;
6691 const BYTE* bufferWalker;
6695 * This should never happen on a small block file.
6697 assert(offset.u.HighPart==0);
6700 * Find the first block in the stream that contains part of the buffer.
6702 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6704 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6706 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6707 return STG_E_DOCFILECORRUPT;
6708 blockNoInSequence--;
6712 * Start writing the buffer.
6715 bufferWalker = buffer;
6716 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6719 * Calculate how many bytes we can copy to this small block.
6721 bytesToWriteInBuffer =
6722 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6725 * Calculate the offset of the small block in the small block file.
6727 offsetInBigBlockFile.u.HighPart = 0;
6728 offsetInBigBlockFile.u.LowPart =
6729 blockIndex * This->parentStorage->smallBlockSize;
6731 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6734 * Write those bytes in the buffer to the small block file.
6736 res = BlockChainStream_WriteAt(
6737 This->parentStorage->smallBlockRootChain,
6738 offsetInBigBlockFile,
6739 bytesToWriteInBuffer,
6741 &bytesWrittenToBigBlockFile);
6746 * Step to the next big block.
6748 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6751 bufferWalker += bytesWrittenToBigBlockFile;
6752 size -= bytesWrittenToBigBlockFile;
6753 *bytesWritten += bytesWrittenToBigBlockFile;
6754 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6757 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6760 /******************************************************************************
6761 * SmallBlockChainStream_Shrink
6763 * Shrinks this chain in the small block depot.
6765 static BOOL SmallBlockChainStream_Shrink(
6766 SmallBlockChainStream* This,
6767 ULARGE_INTEGER newSize)
6769 ULONG blockIndex, extraBlock;
6773 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6775 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6778 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6781 * Go to the new end of chain
6783 while (count < numBlocks)
6785 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6792 * If the count is 0, we have a special case, the head of the chain was
6797 DirEntry chainEntry;
6799 StorageImpl_ReadDirEntry(This->parentStorage,
6800 This->ownerDirEntry,
6803 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6805 StorageImpl_WriteDirEntry(This->parentStorage,
6806 This->ownerDirEntry,
6810 * We start freeing the chain at the head block.
6812 extraBlock = blockIndex;
6816 /* Get the next block before marking the new end */
6817 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6821 /* Mark the new end of chain */
6822 SmallBlockChainStream_SetNextBlockInChain(
6825 BLOCK_END_OF_CHAIN);
6829 * Mark the extra blocks as free
6831 while (extraBlock != BLOCK_END_OF_CHAIN)
6833 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6836 SmallBlockChainStream_FreeBlock(This, extraBlock);
6837 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
6838 extraBlock = blockIndex;
6844 /******************************************************************************
6845 * SmallBlockChainStream_Enlarge
6847 * Grows this chain in the small block depot.
6849 static BOOL SmallBlockChainStream_Enlarge(
6850 SmallBlockChainStream* This,
6851 ULARGE_INTEGER newSize)
6853 ULONG blockIndex, currentBlock;
6855 ULONG oldNumBlocks = 0;
6857 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6860 * Empty chain. Create the head.
6862 if (blockIndex == BLOCK_END_OF_CHAIN)
6864 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6865 SmallBlockChainStream_SetNextBlockInChain(
6868 BLOCK_END_OF_CHAIN);
6870 if (This->headOfStreamPlaceHolder != NULL)
6872 *(This->headOfStreamPlaceHolder) = blockIndex;
6876 DirEntry chainEntry;
6878 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6881 chainEntry.startingBlock = blockIndex;
6883 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6888 currentBlock = blockIndex;
6891 * Figure out how many blocks are needed to contain this stream
6893 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6895 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6899 * Go to the current end of chain
6901 while (blockIndex != BLOCK_END_OF_CHAIN)
6904 currentBlock = blockIndex;
6905 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6910 * Add new blocks to the chain
6912 while (oldNumBlocks < newNumBlocks)
6914 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6915 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6917 SmallBlockChainStream_SetNextBlockInChain(
6920 BLOCK_END_OF_CHAIN);
6922 currentBlock = blockIndex;
6929 /******************************************************************************
6930 * SmallBlockChainStream_SetSize
6932 * Sets the size of this stream.
6933 * The file will grow if we grow the chain.
6935 * TODO: Free the actual blocks in the file when we shrink the chain.
6936 * Currently, the blocks are still in the file. So the file size
6937 * doesn't shrink even if we shrink streams.
6939 BOOL SmallBlockChainStream_SetSize(
6940 SmallBlockChainStream* This,
6941 ULARGE_INTEGER newSize)
6943 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6945 if (newSize.u.LowPart == size.u.LowPart)
6948 if (newSize.u.LowPart < size.u.LowPart)
6950 SmallBlockChainStream_Shrink(This, newSize);
6954 SmallBlockChainStream_Enlarge(This, newSize);
6960 /******************************************************************************
6961 * SmallBlockChainStream_GetCount
6963 * Returns the number of small blocks that comprises this chain.
6964 * This is not the size of the stream as the last block may not be full!
6967 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6972 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6974 while(blockIndex != BLOCK_END_OF_CHAIN)
6978 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6979 blockIndex, &blockIndex)))
6986 /******************************************************************************
6987 * SmallBlockChainStream_GetSize
6989 * Returns the size of this chain.
6991 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6993 DirEntry chainEntry;
6995 if(This->headOfStreamPlaceHolder != NULL)
6997 ULARGE_INTEGER result;
6998 result.u.HighPart = 0;
7000 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7001 This->parentStorage->smallBlockSize;
7006 StorageImpl_ReadDirEntry(
7007 This->parentStorage,
7008 This->ownerDirEntry,
7011 return chainEntry.size;
7014 static HRESULT create_storagefile(
7018 STGOPTIONS* pStgOptions,
7022 StorageBaseImpl* newStorage = 0;
7023 HANDLE hFile = INVALID_HANDLE_VALUE;
7024 HRESULT hr = STG_E_INVALIDFLAG;
7028 DWORD fileAttributes;
7029 WCHAR tempFileName[MAX_PATH];
7032 return STG_E_INVALIDPOINTER;
7034 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7035 return STG_E_INVALIDPARAMETER;
7037 /* if no share mode given then DENY_NONE is the default */
7038 if (STGM_SHARE_MODE(grfMode) == 0)
7039 grfMode |= STGM_SHARE_DENY_NONE;
7041 if ( FAILED( validateSTGM(grfMode) ))
7044 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7045 switch(STGM_ACCESS_MODE(grfMode))
7048 case STGM_READWRITE:
7054 /* in direct mode, can only use SHARE_EXCLUSIVE */
7055 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7058 /* but in transacted mode, any share mode is valid */
7061 * Generate a unique name.
7065 WCHAR tempPath[MAX_PATH];
7066 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7068 memset(tempPath, 0, sizeof(tempPath));
7069 memset(tempFileName, 0, sizeof(tempFileName));
7071 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7074 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7075 pwcsName = tempFileName;
7078 hr = STG_E_INSUFFICIENTMEMORY;
7082 creationMode = TRUNCATE_EXISTING;
7086 creationMode = GetCreationModeFromSTGM(grfMode);
7090 * Interpret the STGM value grfMode
7092 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7093 accessMode = GetAccessModeFromSTGM(grfMode);
7095 if (grfMode & STGM_DELETEONRELEASE)
7096 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7098 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7100 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7104 FIXME("Storage share mode not implemented.\n");
7109 hFile = CreateFileW(pwcsName,
7117 if (hFile == INVALID_HANDLE_VALUE)
7119 if(GetLastError() == ERROR_FILE_EXISTS)
7120 hr = STG_E_FILEALREADYEXISTS;
7127 * Allocate and initialize the new IStorage32object.
7129 hr = Storage_Construct(
7136 pStgOptions->ulSectorSize,
7144 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
7146 IStorage_Release((IStorage*)newStorage);
7149 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7154 /******************************************************************************
7155 * StgCreateDocfile [OLE32.@]
7156 * Creates a new compound file storage object
7159 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7160 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7161 * reserved [ ?] unused?, usually 0
7162 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7165 * S_OK if the file was successfully created
7166 * some STG_E_ value if error
7168 * if pwcsName is NULL, create file with new unique name
7169 * the function can returns
7170 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7173 HRESULT WINAPI StgCreateDocfile(
7177 IStorage **ppstgOpen)
7179 STGOPTIONS stgoptions = {1, 0, 512};
7181 TRACE("(%s, %x, %d, %p)\n",
7182 debugstr_w(pwcsName), grfMode,
7183 reserved, ppstgOpen);
7186 return STG_E_INVALIDPOINTER;
7188 return STG_E_INVALIDPARAMETER;
7190 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7193 /******************************************************************************
7194 * StgCreateStorageEx [OLE32.@]
7196 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7198 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7199 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7201 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7203 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7204 return STG_E_INVALIDPARAMETER;
7207 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7209 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7210 return STG_E_INVALIDPARAMETER;
7213 if (stgfmt == STGFMT_FILE)
7215 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7216 return STG_E_INVALIDPARAMETER;
7219 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7221 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7225 ERR("Invalid stgfmt argument\n");
7226 return STG_E_INVALIDPARAMETER;
7229 /******************************************************************************
7230 * StgCreatePropSetStg [OLE32.@]
7232 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7233 IPropertySetStorage **ppPropSetStg)
7237 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
7239 hr = STG_E_INVALIDPARAMETER;
7241 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
7242 (void**)ppPropSetStg);
7246 /******************************************************************************
7247 * StgOpenStorageEx [OLE32.@]
7249 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7251 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7252 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7254 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7256 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7257 return STG_E_INVALIDPARAMETER;
7263 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7264 return STG_E_INVALIDPARAMETER;
7266 case STGFMT_STORAGE:
7269 case STGFMT_DOCFILE:
7270 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7272 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7273 return STG_E_INVALIDPARAMETER;
7275 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7279 WARN("STGFMT_ANY assuming storage\n");
7283 return STG_E_INVALIDPARAMETER;
7286 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7290 /******************************************************************************
7291 * StgOpenStorage [OLE32.@]
7293 HRESULT WINAPI StgOpenStorage(
7294 const OLECHAR *pwcsName,
7295 IStorage *pstgPriority,
7299 IStorage **ppstgOpen)
7301 StorageBaseImpl* newStorage = 0;
7307 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7308 debugstr_w(pwcsName), pstgPriority, grfMode,
7309 snbExclude, reserved, ppstgOpen);
7313 hr = STG_E_INVALIDNAME;
7319 hr = STG_E_INVALIDPOINTER;
7325 hr = STG_E_INVALIDPARAMETER;
7329 if (grfMode & STGM_PRIORITY)
7331 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7332 return STG_E_INVALIDFLAG;
7333 if (grfMode & STGM_DELETEONRELEASE)
7334 return STG_E_INVALIDFUNCTION;
7335 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7336 return STG_E_INVALIDFLAG;
7337 grfMode &= ~0xf0; /* remove the existing sharing mode */
7338 grfMode |= STGM_SHARE_DENY_NONE;
7340 /* STGM_PRIORITY stops other IStorage objects on the same file from
7341 * committing until the STGM_PRIORITY IStorage is closed. it also
7342 * stops non-transacted mode StgOpenStorage calls with write access from
7343 * succeeding. obviously, both of these cannot be achieved through just
7344 * file share flags */
7345 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7349 * Validate the sharing mode
7351 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7352 switch(STGM_SHARE_MODE(grfMode))
7354 case STGM_SHARE_EXCLUSIVE:
7355 case STGM_SHARE_DENY_WRITE:
7358 hr = STG_E_INVALIDFLAG;
7362 if ( FAILED( validateSTGM(grfMode) ) ||
7363 (grfMode&STGM_CREATE))
7365 hr = STG_E_INVALIDFLAG;
7369 /* shared reading requires transacted mode */
7370 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7371 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7372 !(grfMode&STGM_TRANSACTED) )
7374 hr = STG_E_INVALIDFLAG;
7379 * Interpret the STGM value grfMode
7381 shareMode = GetShareModeFromSTGM(grfMode);
7382 accessMode = GetAccessModeFromSTGM(grfMode);
7386 hFile = CreateFileW( pwcsName,
7391 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7394 if (hFile==INVALID_HANDLE_VALUE)
7396 DWORD last_error = GetLastError();
7402 case ERROR_FILE_NOT_FOUND:
7403 hr = STG_E_FILENOTFOUND;
7406 case ERROR_PATH_NOT_FOUND:
7407 hr = STG_E_PATHNOTFOUND;
7410 case ERROR_ACCESS_DENIED:
7411 case ERROR_WRITE_PROTECT:
7412 hr = STG_E_ACCESSDENIED;
7415 case ERROR_SHARING_VIOLATION:
7416 hr = STG_E_SHAREVIOLATION;
7427 * Refuse to open the file if it's too small to be a structured storage file
7428 * FIXME: verify the file when reading instead of here
7430 if (GetFileSize(hFile, NULL) < 0x100)
7433 hr = STG_E_FILEALREADYEXISTS;
7438 * Allocate and initialize the new IStorage32object.
7440 hr = Storage_Construct(
7453 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7455 if(hr == STG_E_INVALIDHEADER)
7456 hr = STG_E_FILEALREADYEXISTS;
7461 * Get an "out" pointer for the caller.
7463 *ppstgOpen = (IStorage*)newStorage;
7466 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7470 /******************************************************************************
7471 * StgCreateDocfileOnILockBytes [OLE32.@]
7473 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7477 IStorage** ppstgOpen)
7479 StorageBaseImpl* newStorage = 0;
7482 if ((ppstgOpen == 0) || (plkbyt == 0))
7483 return STG_E_INVALIDPOINTER;
7486 * Allocate and initialize the new IStorage object.
7488 hr = Storage_Construct(
7504 * Get an "out" pointer for the caller.
7506 *ppstgOpen = (IStorage*)newStorage;
7511 /******************************************************************************
7512 * StgOpenStorageOnILockBytes [OLE32.@]
7514 HRESULT WINAPI StgOpenStorageOnILockBytes(
7516 IStorage *pstgPriority,
7520 IStorage **ppstgOpen)
7522 StorageBaseImpl* newStorage = 0;
7525 if ((plkbyt == 0) || (ppstgOpen == 0))
7526 return STG_E_INVALIDPOINTER;
7528 if ( FAILED( validateSTGM(grfMode) ))
7529 return STG_E_INVALIDFLAG;
7534 * Allocate and initialize the new IStorage object.
7536 hr = Storage_Construct(
7552 * Get an "out" pointer for the caller.
7554 *ppstgOpen = (IStorage*)newStorage;
7559 /******************************************************************************
7560 * StgSetTimes [ole32.@]
7561 * StgSetTimes [OLE32.@]
7565 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7566 FILETIME const *patime, FILETIME const *pmtime)
7568 IStorage *stg = NULL;
7571 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7573 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7577 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7578 IStorage_Release(stg);
7584 /******************************************************************************
7585 * StgIsStorageILockBytes [OLE32.@]
7587 * Determines if the ILockBytes contains a storage object.
7589 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7592 ULARGE_INTEGER offset;
7594 offset.u.HighPart = 0;
7595 offset.u.LowPart = 0;
7597 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
7599 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
7605 /******************************************************************************
7606 * WriteClassStg [OLE32.@]
7608 * This method will store the specified CLSID in the specified storage object
7610 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7615 return E_INVALIDARG;
7618 return STG_E_INVALIDPOINTER;
7620 hRes = IStorage_SetClass(pStg, rclsid);
7625 /***********************************************************************
7626 * ReadClassStg (OLE32.@)
7628 * This method reads the CLSID previously written to a storage object with
7629 * the WriteClassStg.
7632 * pstg [I] IStorage pointer
7633 * pclsid [O] Pointer to where the CLSID is written
7637 * Failure: HRESULT code.
7639 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7644 TRACE("(%p, %p)\n", pstg, pclsid);
7646 if(!pstg || !pclsid)
7647 return E_INVALIDARG;
7650 * read a STATSTG structure (contains the clsid) from the storage
7652 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7655 *pclsid=pstatstg.clsid;
7660 /***********************************************************************
7661 * OleLoadFromStream (OLE32.@)
7663 * This function loads an object from stream
7665 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7669 LPPERSISTSTREAM xstm;
7671 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7673 res=ReadClassStm(pStm,&clsid);
7676 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7679 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7681 IUnknown_Release((IUnknown*)*ppvObj);
7684 res=IPersistStream_Load(xstm,pStm);
7685 IPersistStream_Release(xstm);
7686 /* FIXME: all refcounts ok at this point? I think they should be:
7689 * xstm : 0 (released)
7694 /***********************************************************************
7695 * OleSaveToStream (OLE32.@)
7697 * This function saves an object with the IPersistStream interface on it
7698 * to the specified stream.
7700 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7706 TRACE("(%p,%p)\n",pPStm,pStm);
7708 res=IPersistStream_GetClassID(pPStm,&clsid);
7710 if (SUCCEEDED(res)){
7712 res=WriteClassStm(pStm,&clsid);
7716 res=IPersistStream_Save(pPStm,pStm,TRUE);
7719 TRACE("Finished Save\n");
7723 /****************************************************************************
7724 * This method validate a STGM parameter that can contain the values below
7726 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7727 * The stgm values contained in 0xffff0000 are bitmasks.
7729 * STGM_DIRECT 0x00000000
7730 * STGM_TRANSACTED 0x00010000
7731 * STGM_SIMPLE 0x08000000
7733 * STGM_READ 0x00000000
7734 * STGM_WRITE 0x00000001
7735 * STGM_READWRITE 0x00000002
7737 * STGM_SHARE_DENY_NONE 0x00000040
7738 * STGM_SHARE_DENY_READ 0x00000030
7739 * STGM_SHARE_DENY_WRITE 0x00000020
7740 * STGM_SHARE_EXCLUSIVE 0x00000010
7742 * STGM_PRIORITY 0x00040000
7743 * STGM_DELETEONRELEASE 0x04000000
7745 * STGM_CREATE 0x00001000
7746 * STGM_CONVERT 0x00020000
7747 * STGM_FAILIFTHERE 0x00000000
7749 * STGM_NOSCRATCH 0x00100000
7750 * STGM_NOSNAPSHOT 0x00200000
7752 static HRESULT validateSTGM(DWORD stgm)
7754 DWORD access = STGM_ACCESS_MODE(stgm);
7755 DWORD share = STGM_SHARE_MODE(stgm);
7756 DWORD create = STGM_CREATE_MODE(stgm);
7758 if (stgm&~STGM_KNOWN_FLAGS)
7760 ERR("unknown flags %08x\n", stgm);
7768 case STGM_READWRITE:
7776 case STGM_SHARE_DENY_NONE:
7777 case STGM_SHARE_DENY_READ:
7778 case STGM_SHARE_DENY_WRITE:
7779 case STGM_SHARE_EXCLUSIVE:
7788 case STGM_FAILIFTHERE:
7795 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7797 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7801 * STGM_CREATE | STGM_CONVERT
7802 * if both are false, STGM_FAILIFTHERE is set to TRUE
7804 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7808 * STGM_NOSCRATCH requires STGM_TRANSACTED
7810 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7814 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7815 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7817 if ( (stgm & STGM_NOSNAPSHOT) &&
7818 (!(stgm & STGM_TRANSACTED) ||
7819 share == STGM_SHARE_EXCLUSIVE ||
7820 share == STGM_SHARE_DENY_WRITE) )
7826 /****************************************************************************
7827 * GetShareModeFromSTGM
7829 * This method will return a share mode flag from a STGM value.
7830 * The STGM value is assumed valid.
7832 static DWORD GetShareModeFromSTGM(DWORD stgm)
7834 switch (STGM_SHARE_MODE(stgm))
7836 case STGM_SHARE_DENY_NONE:
7837 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7838 case STGM_SHARE_DENY_READ:
7839 return FILE_SHARE_WRITE;
7840 case STGM_SHARE_DENY_WRITE:
7841 return FILE_SHARE_READ;
7842 case STGM_SHARE_EXCLUSIVE:
7845 ERR("Invalid share mode!\n");
7850 /****************************************************************************
7851 * GetAccessModeFromSTGM
7853 * This method will return an access mode flag from a STGM value.
7854 * The STGM value is assumed valid.
7856 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7858 switch (STGM_ACCESS_MODE(stgm))
7861 return GENERIC_READ;
7863 case STGM_READWRITE:
7864 return GENERIC_READ | GENERIC_WRITE;
7866 ERR("Invalid access mode!\n");
7871 /****************************************************************************
7872 * GetCreationModeFromSTGM
7874 * This method will return a creation mode flag from a STGM value.
7875 * The STGM value is assumed valid.
7877 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7879 switch(STGM_CREATE_MODE(stgm))
7882 return CREATE_ALWAYS;
7884 FIXME("STGM_CONVERT not implemented!\n");
7886 case STGM_FAILIFTHERE:
7889 ERR("Invalid create mode!\n");
7895 /*************************************************************************
7896 * OLECONVERT_LoadOLE10 [Internal]
7898 * Loads the OLE10 STREAM to memory
7901 * pOleStream [I] The OLESTREAM
7902 * pData [I] Data Structure for the OLESTREAM Data
7906 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7907 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7910 * This function is used by OleConvertOLESTREAMToIStorage only.
7912 * Memory allocated for pData must be freed by the caller
7914 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7917 HRESULT hRes = S_OK;
7921 pData->pData = NULL;
7922 pData->pstrOleObjFileName = NULL;
7924 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7927 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7928 if(dwSize != sizeof(pData->dwOleID))
7930 hRes = CONVERT10_E_OLESTREAM_GET;
7932 else if(pData->dwOleID != OLESTREAM_ID)
7934 hRes = CONVERT10_E_OLESTREAM_FMT;
7945 /* Get the TypeID... more info needed for this field */
7946 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7947 if(dwSize != sizeof(pData->dwTypeID))
7949 hRes = CONVERT10_E_OLESTREAM_GET;
7954 if(pData->dwTypeID != 0)
7956 /* Get the length of the OleTypeName */
7957 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7958 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7960 hRes = CONVERT10_E_OLESTREAM_GET;
7965 if(pData->dwOleTypeNameLength > 0)
7967 /* Get the OleTypeName */
7968 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7969 if(dwSize != pData->dwOleTypeNameLength)
7971 hRes = CONVERT10_E_OLESTREAM_GET;
7977 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7978 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7980 hRes = CONVERT10_E_OLESTREAM_GET;
7984 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7985 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7986 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7987 if(pData->pstrOleObjFileName)
7989 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7990 if(dwSize != pData->dwOleObjFileNameLength)
7992 hRes = CONVERT10_E_OLESTREAM_GET;
7996 hRes = CONVERT10_E_OLESTREAM_GET;
8001 /* Get the Width of the Metafile */
8002 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8003 if(dwSize != sizeof(pData->dwMetaFileWidth))
8005 hRes = CONVERT10_E_OLESTREAM_GET;
8009 /* Get the Height of the Metafile */
8010 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8011 if(dwSize != sizeof(pData->dwMetaFileHeight))
8013 hRes = CONVERT10_E_OLESTREAM_GET;
8019 /* Get the Length of the Data */
8020 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8021 if(dwSize != sizeof(pData->dwDataLength))
8023 hRes = CONVERT10_E_OLESTREAM_GET;
8027 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8029 if(!bStrem1) /* if it is a second OLE stream data */
8031 pData->dwDataLength -= 8;
8032 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8033 if(dwSize != sizeof(pData->strUnknown))
8035 hRes = CONVERT10_E_OLESTREAM_GET;
8041 if(pData->dwDataLength > 0)
8043 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8045 /* Get Data (ex. IStorage, Metafile, or BMP) */
8048 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8049 if(dwSize != pData->dwDataLength)
8051 hRes = CONVERT10_E_OLESTREAM_GET;
8056 hRes = CONVERT10_E_OLESTREAM_GET;
8065 /*************************************************************************
8066 * OLECONVERT_SaveOLE10 [Internal]
8068 * Saves the OLE10 STREAM From memory
8071 * pData [I] Data Structure for the OLESTREAM Data
8072 * pOleStream [I] The OLESTREAM to save
8076 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8079 * This function is used by OleConvertIStorageToOLESTREAM only.
8082 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8085 HRESULT hRes = S_OK;
8089 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8090 if(dwSize != sizeof(pData->dwOleID))
8092 hRes = CONVERT10_E_OLESTREAM_PUT;
8097 /* Set the TypeID */
8098 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8099 if(dwSize != sizeof(pData->dwTypeID))
8101 hRes = CONVERT10_E_OLESTREAM_PUT;
8105 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8107 /* Set the Length of the OleTypeName */
8108 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8109 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8111 hRes = CONVERT10_E_OLESTREAM_PUT;
8116 if(pData->dwOleTypeNameLength > 0)
8118 /* Set the OleTypeName */
8119 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8120 if(dwSize != pData->dwOleTypeNameLength)
8122 hRes = CONVERT10_E_OLESTREAM_PUT;
8129 /* Set the width of the Metafile */
8130 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8131 if(dwSize != sizeof(pData->dwMetaFileWidth))
8133 hRes = CONVERT10_E_OLESTREAM_PUT;
8139 /* Set the height of the Metafile */
8140 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8141 if(dwSize != sizeof(pData->dwMetaFileHeight))
8143 hRes = CONVERT10_E_OLESTREAM_PUT;
8149 /* Set the length of the Data */
8150 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8151 if(dwSize != sizeof(pData->dwDataLength))
8153 hRes = CONVERT10_E_OLESTREAM_PUT;
8159 if(pData->dwDataLength > 0)
8161 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8162 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8163 if(dwSize != pData->dwDataLength)
8165 hRes = CONVERT10_E_OLESTREAM_PUT;
8173 /*************************************************************************
8174 * OLECONVERT_GetOLE20FromOLE10[Internal]
8176 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8177 * opens it, and copies the content to the dest IStorage for
8178 * OleConvertOLESTREAMToIStorage
8182 * pDestStorage [I] The IStorage to copy the data to
8183 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8184 * nBufferLength [I] The size of the buffer
8193 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8197 IStorage *pTempStorage;
8198 DWORD dwNumOfBytesWritten;
8199 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8200 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8202 /* Create a temp File */
8203 GetTempPathW(MAX_PATH, wstrTempDir);
8204 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8205 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8207 if(hFile != INVALID_HANDLE_VALUE)
8209 /* Write IStorage Data to File */
8210 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8213 /* Open and copy temp storage to the Dest Storage */
8214 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8217 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8218 IStorage_Release(pTempStorage);
8220 DeleteFileW(wstrTempFile);
8225 /*************************************************************************
8226 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8228 * Saves the OLE10 STREAM From memory
8231 * pStorage [I] The Src IStorage to copy
8232 * pData [I] The Dest Memory to write to.
8235 * The size in bytes allocated for pData
8238 * Memory allocated for pData must be freed by the caller
8240 * Used by OleConvertIStorageToOLESTREAM only.
8243 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8247 DWORD nDataLength = 0;
8248 IStorage *pTempStorage;
8249 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8250 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8254 /* Create temp Storage */
8255 GetTempPathW(MAX_PATH, wstrTempDir);
8256 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8257 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8261 /* Copy Src Storage to the Temp Storage */
8262 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8263 IStorage_Release(pTempStorage);
8265 /* Open Temp Storage as a file and copy to memory */
8266 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8267 if(hFile != INVALID_HANDLE_VALUE)
8269 nDataLength = GetFileSize(hFile, NULL);
8270 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8271 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8274 DeleteFileW(wstrTempFile);
8279 /*************************************************************************
8280 * OLECONVERT_CreateOleStream [Internal]
8282 * Creates the "\001OLE" stream in the IStorage if necessary.
8285 * pStorage [I] Dest storage to create the stream in
8291 * This function is used by OleConvertOLESTREAMToIStorage only.
8293 * This stream is still unknown, MS Word seems to have extra data
8294 * but since the data is stored in the OLESTREAM there should be
8295 * no need to recreate the stream. If the stream is manually
8296 * deleted it will create it with this default data.
8299 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8303 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8304 BYTE pOleStreamHeader [] =
8306 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8307 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8308 0x00, 0x00, 0x00, 0x00
8311 /* Create stream if not present */
8312 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8313 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8317 /* Write default Data */
8318 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8319 IStream_Release(pStream);
8323 /* write a string to a stream, preceded by its length */
8324 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8331 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8332 r = IStream_Write( stm, &len, sizeof(len), NULL);
8337 str = CoTaskMemAlloc( len );
8338 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8339 r = IStream_Write( stm, str, len, NULL);
8340 CoTaskMemFree( str );
8344 /* read a string preceded by its length from a stream */
8345 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8348 DWORD len, count = 0;
8352 r = IStream_Read( stm, &len, sizeof(len), &count );
8355 if( count != sizeof(len) )
8356 return E_OUTOFMEMORY;
8358 TRACE("%d bytes\n",len);
8360 str = CoTaskMemAlloc( len );
8362 return E_OUTOFMEMORY;
8364 r = IStream_Read( stm, str, len, &count );
8369 CoTaskMemFree( str );
8370 return E_OUTOFMEMORY;
8373 TRACE("Read string %s\n",debugstr_an(str,len));
8375 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8376 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8378 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8379 CoTaskMemFree( str );
8387 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8388 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8392 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8394 static const BYTE unknown1[12] =
8395 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8396 0xFF, 0xFF, 0xFF, 0xFF};
8397 static const BYTE unknown2[16] =
8398 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8399 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8401 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8402 debugstr_w(lpszUserType), debugstr_w(szClipName),
8403 debugstr_w(szProgIDName));
8405 /* Create a CompObj stream */
8406 r = IStorage_CreateStream(pstg, szwStreamName,
8407 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8411 /* Write CompObj Structure to stream */
8412 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8414 if( SUCCEEDED( r ) )
8415 r = WriteClassStm( pstm, clsid );
8417 if( SUCCEEDED( r ) )
8418 r = STREAM_WriteString( pstm, lpszUserType );
8419 if( SUCCEEDED( r ) )
8420 r = STREAM_WriteString( pstm, szClipName );
8421 if( SUCCEEDED( r ) )
8422 r = STREAM_WriteString( pstm, szProgIDName );
8423 if( SUCCEEDED( r ) )
8424 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8426 IStream_Release( pstm );
8431 /***********************************************************************
8432 * WriteFmtUserTypeStg (OLE32.@)
8434 HRESULT WINAPI WriteFmtUserTypeStg(
8435 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8438 WCHAR szwClipName[0x40];
8439 CLSID clsid = CLSID_NULL;
8440 LPWSTR wstrProgID = NULL;
8443 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8445 /* get the clipboard format name */
8446 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8449 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8451 /* FIXME: There's room to save a CLSID and its ProgID, but
8452 the CLSID is not looked up in the registry and in all the
8453 tests I wrote it was CLSID_NULL. Where does it come from?
8456 /* get the real program ID. This may fail, but that's fine */
8457 ProgIDFromCLSID(&clsid, &wstrProgID);
8459 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8461 r = STORAGE_WriteCompObj( pstg, &clsid,
8462 lpszUserType, szwClipName, wstrProgID );
8464 CoTaskMemFree(wstrProgID);
8470 /******************************************************************************
8471 * ReadFmtUserTypeStg [OLE32.@]
8473 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8477 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8478 unsigned char unknown1[12];
8479 unsigned char unknown2[16];
8481 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8484 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8486 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8487 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8490 WARN("Failed to open stream r = %08x\n", r);
8494 /* read the various parts of the structure */
8495 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8496 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8498 r = ReadClassStm( stm, &clsid );
8502 r = STREAM_ReadString( stm, &szCLSIDName );
8506 r = STREAM_ReadString( stm, &szOleTypeName );
8510 r = STREAM_ReadString( stm, &szProgIDName );
8514 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8515 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8518 /* ok, success... now we just need to store what we found */
8520 *pcf = RegisterClipboardFormatW( szOleTypeName );
8521 CoTaskMemFree( szOleTypeName );
8523 if( lplpszUserType )
8524 *lplpszUserType = szCLSIDName;
8525 CoTaskMemFree( szProgIDName );
8528 IStream_Release( stm );
8534 /*************************************************************************
8535 * OLECONVERT_CreateCompObjStream [Internal]
8537 * Creates a "\001CompObj" is the destination IStorage if necessary.
8540 * pStorage [I] The dest IStorage to create the CompObj Stream
8542 * strOleTypeName [I] The ProgID
8546 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8549 * This function is used by OleConvertOLESTREAMToIStorage only.
8551 * The stream data is stored in the OLESTREAM and there should be
8552 * no need to recreate the stream. If the stream is manually
8553 * deleted it will attempt to create it by querying the registry.
8557 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8560 HRESULT hStorageRes, hRes = S_OK;
8561 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8562 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8563 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8565 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8566 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8568 /* Initialize the CompObj structure */
8569 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8570 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8571 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8574 /* Create a CompObj stream if it doesn't exist */
8575 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8576 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8577 if(hStorageRes == S_OK)
8579 /* copy the OleTypeName to the compobj struct */
8580 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8581 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8583 /* copy the OleTypeName to the compobj struct */
8584 /* Note: in the test made, these were Identical */
8585 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8586 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8589 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8590 bufferW, OLESTREAM_MAX_STR_LEN );
8591 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8597 /* Get the CLSID Default Name from the Registry */
8598 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8599 if(hErr == ERROR_SUCCESS)
8601 char strTemp[OLESTREAM_MAX_STR_LEN];
8602 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8603 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8604 if(hErr == ERROR_SUCCESS)
8606 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8612 /* Write CompObj Structure to stream */
8613 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8615 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8617 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8618 if(IStorageCompObj.dwCLSIDNameLength > 0)
8620 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8622 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8623 if(IStorageCompObj.dwOleTypeNameLength > 0)
8625 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8627 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8628 if(IStorageCompObj.dwProgIDNameLength > 0)
8630 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8632 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8633 IStream_Release(pStream);
8639 /*************************************************************************
8640 * OLECONVERT_CreateOlePresStream[Internal]
8642 * Creates the "\002OlePres000" Stream with the Metafile data
8645 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8646 * dwExtentX [I] Width of the Metafile
8647 * dwExtentY [I] Height of the Metafile
8648 * pData [I] Metafile data
8649 * dwDataLength [I] Size of the Metafile data
8653 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8656 * This function is used by OleConvertOLESTREAMToIStorage only.
8659 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8663 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8664 BYTE pOlePresStreamHeader [] =
8666 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8667 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8668 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8669 0x00, 0x00, 0x00, 0x00
8672 BYTE pOlePresStreamHeaderEmpty [] =
8674 0x00, 0x00, 0x00, 0x00,
8675 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8676 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8677 0x00, 0x00, 0x00, 0x00
8680 /* Create the OlePres000 Stream */
8681 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8682 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8687 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8689 memset(&OlePres, 0, sizeof(OlePres));
8690 /* Do we have any metafile data to save */
8691 if(dwDataLength > 0)
8693 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8694 nHeaderSize = sizeof(pOlePresStreamHeader);
8698 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8699 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8701 /* Set width and height of the metafile */
8702 OlePres.dwExtentX = dwExtentX;
8703 OlePres.dwExtentY = -dwExtentY;
8705 /* Set Data and Length */
8706 if(dwDataLength > sizeof(METAFILEPICT16))
8708 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8709 OlePres.pData = &(pData[8]);
8711 /* Save OlePres000 Data to Stream */
8712 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8713 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8714 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8715 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8716 if(OlePres.dwSize > 0)
8718 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8720 IStream_Release(pStream);
8724 /*************************************************************************
8725 * OLECONVERT_CreateOle10NativeStream [Internal]
8727 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8730 * pStorage [I] Dest storage to create the stream in
8731 * pData [I] Ole10 Native Data (ex. bmp)
8732 * dwDataLength [I] Size of the Ole10 Native Data
8738 * This function is used by OleConvertOLESTREAMToIStorage only.
8740 * Might need to verify the data and return appropriate error message
8743 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8747 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8749 /* Create the Ole10Native Stream */
8750 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8751 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8755 /* Write info to stream */
8756 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8757 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8758 IStream_Release(pStream);
8763 /*************************************************************************
8764 * OLECONVERT_GetOLE10ProgID [Internal]
8766 * Finds the ProgID (or OleTypeID) from the IStorage
8769 * pStorage [I] The Src IStorage to get the ProgID
8770 * strProgID [I] the ProgID string to get
8771 * dwSize [I] the size of the string
8775 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8778 * This function is used by OleConvertIStorageToOLESTREAM only.
8782 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8786 LARGE_INTEGER iSeekPos;
8787 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8788 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8790 /* Open the CompObj Stream */
8791 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8792 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8796 /*Get the OleType from the CompObj Stream */
8797 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8798 iSeekPos.u.HighPart = 0;
8800 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8801 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8802 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8803 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8804 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8805 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8806 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8808 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8811 IStream_Read(pStream, strProgID, *dwSize, NULL);
8813 IStream_Release(pStream);
8818 LPOLESTR wstrProgID;
8820 /* Get the OleType from the registry */
8821 REFCLSID clsid = &(stat.clsid);
8822 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8823 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8826 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8833 /*************************************************************************
8834 * OLECONVERT_GetOle10PresData [Internal]
8836 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8839 * pStorage [I] Src IStroage
8840 * pOleStream [I] Dest OleStream Mem Struct
8846 * This function is used by OleConvertIStorageToOLESTREAM only.
8848 * Memory allocated for pData must be freed by the caller
8852 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8857 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8859 /* Initialize Default data for OLESTREAM */
8860 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8861 pOleStreamData[0].dwTypeID = 2;
8862 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8863 pOleStreamData[1].dwTypeID = 0;
8864 pOleStreamData[0].dwMetaFileWidth = 0;
8865 pOleStreamData[0].dwMetaFileHeight = 0;
8866 pOleStreamData[0].pData = NULL;
8867 pOleStreamData[1].pData = NULL;
8869 /* Open Ole10Native Stream */
8870 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8871 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8875 /* Read Size and Data */
8876 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8877 if(pOleStreamData->dwDataLength > 0)
8879 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8880 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8882 IStream_Release(pStream);
8888 /*************************************************************************
8889 * OLECONVERT_GetOle20PresData[Internal]
8891 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8894 * pStorage [I] Src IStroage
8895 * pOleStreamData [I] Dest OleStream Mem Struct
8901 * This function is used by OleConvertIStorageToOLESTREAM only.
8903 * Memory allocated for pData must be freed by the caller
8905 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8909 OLECONVERT_ISTORAGE_OLEPRES olePress;
8910 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8912 /* Initialize Default data for OLESTREAM */
8913 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8914 pOleStreamData[0].dwTypeID = 2;
8915 pOleStreamData[0].dwMetaFileWidth = 0;
8916 pOleStreamData[0].dwMetaFileHeight = 0;
8917 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8918 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8919 pOleStreamData[1].dwTypeID = 0;
8920 pOleStreamData[1].dwOleTypeNameLength = 0;
8921 pOleStreamData[1].strOleTypeName[0] = 0;
8922 pOleStreamData[1].dwMetaFileWidth = 0;
8923 pOleStreamData[1].dwMetaFileHeight = 0;
8924 pOleStreamData[1].pData = NULL;
8925 pOleStreamData[1].dwDataLength = 0;
8928 /* Open OlePress000 stream */
8929 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8930 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8933 LARGE_INTEGER iSeekPos;
8934 METAFILEPICT16 MetaFilePict;
8935 static const char strMetafilePictName[] = "METAFILEPICT";
8937 /* Set the TypeID for a Metafile */
8938 pOleStreamData[1].dwTypeID = 5;
8940 /* Set the OleTypeName to Metafile */
8941 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8942 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8944 iSeekPos.u.HighPart = 0;
8945 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8947 /* Get Presentation Data */
8948 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8949 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8950 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8951 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8953 /*Set width and Height */
8954 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8955 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8956 if(olePress.dwSize > 0)
8959 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8961 /* Set MetaFilePict struct */
8962 MetaFilePict.mm = 8;
8963 MetaFilePict.xExt = olePress.dwExtentX;
8964 MetaFilePict.yExt = olePress.dwExtentY;
8965 MetaFilePict.hMF = 0;
8967 /* Get Metafile Data */
8968 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8969 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8970 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8972 IStream_Release(pStream);
8976 /*************************************************************************
8977 * OleConvertOLESTREAMToIStorage [OLE32.@]
8982 * DVTARGETDEVICE parameter is not handled
8983 * Still unsure of some mem fields for OLE 10 Stream
8984 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8985 * and "\001OLE" streams
8988 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8989 LPOLESTREAM pOleStream,
8991 const DVTARGETDEVICE* ptd)
8995 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8997 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8999 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9003 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9006 if(pstg == NULL || pOleStream == NULL)
9008 hRes = E_INVALIDARG;
9013 /* Load the OLESTREAM to Memory */
9014 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9019 /* Load the OLESTREAM to Memory (part 2)*/
9020 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9026 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9028 /* Do we have the IStorage Data in the OLESTREAM */
9029 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9031 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9032 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9036 /* It must be an original OLE 1.0 source */
9037 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9042 /* It must be an original OLE 1.0 source */
9043 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9046 /* Create CompObj Stream if necessary */
9047 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9050 /*Create the Ole Stream if necessary */
9051 OLECONVERT_CreateOleStream(pstg);
9056 /* Free allocated memory */
9057 for(i=0; i < 2; i++)
9059 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9060 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9061 pOleStreamData[i].pstrOleObjFileName = NULL;
9066 /*************************************************************************
9067 * OleConvertIStorageToOLESTREAM [OLE32.@]
9074 * Still unsure of some mem fields for OLE 10 Stream
9075 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9076 * and "\001OLE" streams.
9079 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9081 LPOLESTREAM pOleStream)
9084 HRESULT hRes = S_OK;
9086 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9087 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9089 TRACE("%p %p\n", pstg, pOleStream);
9091 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9093 if(pstg == NULL || pOleStream == NULL)
9095 hRes = E_INVALIDARG;
9099 /* Get the ProgID */
9100 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9101 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9105 /* Was it originally Ole10 */
9106 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9109 IStream_Release(pStream);
9110 /* Get Presentation Data for Ole10Native */
9111 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9115 /* Get Presentation Data (OLE20) */
9116 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9119 /* Save OLESTREAM */
9120 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9123 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9128 /* Free allocated memory */
9129 for(i=0; i < 2; i++)
9131 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9137 /***********************************************************************
9138 * GetConvertStg (OLE32.@)
9140 HRESULT WINAPI GetConvertStg(IStorage *stg) {
9141 FIXME("unimplemented stub!\n");
9145 /******************************************************************************
9146 * StgIsStorageFile [OLE32.@]
9147 * Verify if the file contains a storage object
9153 * S_OK if file has magic bytes as a storage object
9154 * S_FALSE if file is not storage
9157 StgIsStorageFile(LPCOLESTR fn)
9163 TRACE("%s\n", debugstr_w(fn));
9164 hf = CreateFileW(fn, GENERIC_READ,
9165 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9166 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9168 if (hf == INVALID_HANDLE_VALUE)
9169 return STG_E_FILENOTFOUND;
9171 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9173 WARN(" unable to read file\n");
9180 if (bytes_read != 8) {
9181 TRACE(" too short\n");
9185 if (!memcmp(magic,STORAGE_magic,8)) {
9190 TRACE(" -> Invalid header.\n");
9194 /***********************************************************************
9195 * WriteClassStm (OLE32.@)
9197 * Writes a CLSID to a stream.
9200 * pStm [I] Stream to write to.
9201 * rclsid [I] CLSID to write.
9205 * Failure: HRESULT code.
9207 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9209 TRACE("(%p,%p)\n",pStm,rclsid);
9211 if (!pStm || !rclsid)
9212 return E_INVALIDARG;
9214 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9217 /***********************************************************************
9218 * ReadClassStm (OLE32.@)
9220 * Reads a CLSID from a stream.
9223 * pStm [I] Stream to read from.
9224 * rclsid [O] CLSID to read.
9228 * Failure: HRESULT code.
9230 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9235 TRACE("(%p,%p)\n",pStm,pclsid);
9237 if (!pStm || !pclsid)
9238 return E_INVALIDARG;
9240 /* clear the output args */
9241 *pclsid = CLSID_NULL;
9243 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
9248 if (nbByte != sizeof(CLSID))
9249 return STG_E_READFAULT;