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 IEnumSTATSTG IEnumSTATSTG_iface;
301 LONG ref; /* Reference count */
302 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
303 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
305 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
308 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
310 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
314 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
315 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
317 /************************************************************************
321 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
323 return (index+1) * This->bigBlockSize;
326 /************************************************************************
327 ** Storage32BaseImpl implementation
329 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
330 ULARGE_INTEGER offset,
335 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
338 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
339 ULARGE_INTEGER offset,
344 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
347 /************************************************************************
348 * Storage32BaseImpl_QueryInterface (IUnknown)
350 * This method implements the common QueryInterface for all IStorage32
351 * implementations contained in this file.
353 * See Windows documentation for more details on IUnknown methods.
355 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
360 StorageBaseImpl *This = (StorageBaseImpl *)iface;
362 if ( (This==0) || (ppvObject==0) )
367 if (IsEqualGUID(&IID_IUnknown, riid) ||
368 IsEqualGUID(&IID_IStorage, riid))
372 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
374 *ppvObject = &This->pssVtbl;
378 return E_NOINTERFACE;
380 IStorage_AddRef(iface);
385 /************************************************************************
386 * Storage32BaseImpl_AddRef (IUnknown)
388 * This method implements the common AddRef for all IStorage32
389 * implementations contained in this file.
391 * See Windows documentation for more details on IUnknown methods.
393 static ULONG WINAPI StorageBaseImpl_AddRef(
396 StorageBaseImpl *This = (StorageBaseImpl *)iface;
397 ULONG ref = InterlockedIncrement(&This->ref);
399 TRACE("(%p) AddRef to %d\n", This, ref);
404 /************************************************************************
405 * Storage32BaseImpl_Release (IUnknown)
407 * This method implements the common Release for all IStorage32
408 * implementations contained in this file.
410 * See Windows documentation for more details on IUnknown methods.
412 static ULONG WINAPI StorageBaseImpl_Release(
415 StorageBaseImpl *This = (StorageBaseImpl *)iface;
417 ULONG ref = InterlockedDecrement(&This->ref);
419 TRACE("(%p) ReleaseRef to %d\n", This, ref);
424 * Since we are using a system of base-classes, we want to call the
425 * destructor of the appropriate derived class. To do this, we are
426 * using virtual functions to implement the destructor.
428 StorageBaseImpl_Destroy(This);
434 /************************************************************************
435 * Storage32BaseImpl_OpenStream (IStorage)
437 * This method will open the specified stream object from the current storage.
439 * See Windows documentation for more details on IStorage methods.
441 static HRESULT WINAPI StorageBaseImpl_OpenStream(
443 const OLECHAR* pwcsName, /* [string][in] */
444 void* reserved1, /* [unique][in] */
445 DWORD grfMode, /* [in] */
446 DWORD reserved2, /* [in] */
447 IStream** ppstm) /* [out] */
449 StorageBaseImpl *This = (StorageBaseImpl *)iface;
450 StgStreamImpl* newStream;
451 DirEntry currentEntry;
452 DirRef streamEntryRef;
453 HRESULT res = STG_E_UNKNOWN;
455 TRACE("(%p, %s, %p, %x, %d, %p)\n",
456 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
458 if ( (pwcsName==NULL) || (ppstm==0) )
466 if ( FAILED( validateSTGM(grfMode) ) ||
467 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
469 res = STG_E_INVALIDFLAG;
476 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
478 res = STG_E_INVALIDFUNCTION;
484 res = STG_E_REVERTED;
489 * Check that we're compatible with the parent's storage mode, but
490 * only if we are not in transacted mode
492 if(!(This->openFlags & STGM_TRANSACTED)) {
493 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
495 res = STG_E_INVALIDFLAG;
501 * Search for the element with the given name
503 streamEntryRef = findElement(
505 This->storageDirEntry,
510 * If it was found, construct the stream object and return a pointer to it.
512 if ( (streamEntryRef!=DIRENTRY_NULL) &&
513 (currentEntry.stgType==STGTY_STREAM) )
515 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
517 /* A single stream cannot be opened a second time. */
518 res = STG_E_ACCESSDENIED;
522 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
526 newStream->grfMode = grfMode;
527 *ppstm = (IStream*)newStream;
529 IStream_AddRef(*ppstm);
539 res = STG_E_FILENOTFOUND;
543 TRACE("<-- IStream %p\n", *ppstm);
544 TRACE("<-- %08x\n", res);
548 /************************************************************************
549 * Storage32BaseImpl_OpenStorage (IStorage)
551 * This method will open a new storage object from the current storage.
553 * See Windows documentation for more details on IStorage methods.
555 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
557 const OLECHAR* pwcsName, /* [string][unique][in] */
558 IStorage* pstgPriority, /* [unique][in] */
559 DWORD grfMode, /* [in] */
560 SNB snbExclude, /* [unique][in] */
561 DWORD reserved, /* [in] */
562 IStorage** ppstg) /* [out] */
564 StorageBaseImpl *This = (StorageBaseImpl *)iface;
565 StorageInternalImpl* newStorage;
566 StorageBaseImpl* newTransactedStorage;
567 DirEntry currentEntry;
568 DirRef storageEntryRef;
569 HRESULT res = STG_E_UNKNOWN;
571 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
572 iface, debugstr_w(pwcsName), pstgPriority,
573 grfMode, snbExclude, reserved, ppstg);
575 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
581 if (This->openFlags & STGM_SIMPLE)
583 res = STG_E_INVALIDFUNCTION;
588 if (snbExclude != NULL)
590 res = STG_E_INVALIDPARAMETER;
594 if ( FAILED( validateSTGM(grfMode) ))
596 res = STG_E_INVALIDFLAG;
603 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
604 (grfMode & STGM_DELETEONRELEASE) ||
605 (grfMode & STGM_PRIORITY) )
607 res = STG_E_INVALIDFUNCTION;
612 return STG_E_REVERTED;
615 * Check that we're compatible with the parent's storage mode,
616 * but only if we are not transacted
618 if(!(This->openFlags & STGM_TRANSACTED)) {
619 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
621 res = STG_E_ACCESSDENIED;
628 storageEntryRef = findElement(
630 This->storageDirEntry,
634 if ( (storageEntryRef!=DIRENTRY_NULL) &&
635 (currentEntry.stgType==STGTY_STORAGE) )
637 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
639 /* A single storage cannot be opened a second time. */
640 res = STG_E_ACCESSDENIED;
644 newStorage = StorageInternalImpl_Construct(
651 if (grfMode & STGM_TRANSACTED)
653 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
657 HeapFree(GetProcessHeap(), 0, newStorage);
661 *ppstg = (IStorage*)newTransactedStorage;
665 *ppstg = (IStorage*)newStorage;
668 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
674 res = STG_E_INSUFFICIENTMEMORY;
678 res = STG_E_FILENOTFOUND;
681 TRACE("<-- %08x\n", res);
685 /************************************************************************
686 * Storage32BaseImpl_EnumElements (IStorage)
688 * This method will create an enumerator object that can be used to
689 * retrieve information about all the elements in the storage object.
691 * See Windows documentation for more details on IStorage methods.
693 static HRESULT WINAPI StorageBaseImpl_EnumElements(
695 DWORD reserved1, /* [in] */
696 void* reserved2, /* [size_is][unique][in] */
697 DWORD reserved3, /* [in] */
698 IEnumSTATSTG** ppenum) /* [out] */
700 StorageBaseImpl *This = (StorageBaseImpl *)iface;
701 IEnumSTATSTGImpl* newEnum;
703 TRACE("(%p, %d, %p, %d, %p)\n",
704 iface, reserved1, reserved2, reserved3, ppenum);
706 if ( (This==0) || (ppenum==0))
710 return STG_E_REVERTED;
712 newEnum = IEnumSTATSTGImpl_Construct(
714 This->storageDirEntry);
718 *ppenum = &newEnum->IEnumSTATSTG_iface;
720 IEnumSTATSTG_AddRef(*ppenum);
725 return E_OUTOFMEMORY;
728 /************************************************************************
729 * Storage32BaseImpl_Stat (IStorage)
731 * This method will retrieve information about this storage object.
733 * See Windows documentation for more details on IStorage methods.
735 static HRESULT WINAPI StorageBaseImpl_Stat(
737 STATSTG* pstatstg, /* [out] */
738 DWORD grfStatFlag) /* [in] */
740 StorageBaseImpl *This = (StorageBaseImpl *)iface;
741 DirEntry currentEntry;
742 HRESULT res = STG_E_UNKNOWN;
744 TRACE("(%p, %p, %x)\n",
745 iface, pstatstg, grfStatFlag);
747 if ( (This==0) || (pstatstg==0))
755 res = STG_E_REVERTED;
759 res = StorageBaseImpl_ReadDirEntry(
761 This->storageDirEntry,
766 StorageUtl_CopyDirEntryToSTATSTG(
772 pstatstg->grfMode = This->openFlags;
773 pstatstg->grfStateBits = This->stateBits;
779 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);
781 TRACE("<-- %08x\n", res);
785 /************************************************************************
786 * Storage32BaseImpl_RenameElement (IStorage)
788 * This method will rename the specified element.
790 * See Windows documentation for more details on IStorage methods.
792 static HRESULT WINAPI StorageBaseImpl_RenameElement(
794 const OLECHAR* pwcsOldName, /* [in] */
795 const OLECHAR* pwcsNewName) /* [in] */
797 StorageBaseImpl *This = (StorageBaseImpl *)iface;
798 DirEntry currentEntry;
799 DirRef currentEntryRef;
801 TRACE("(%p, %s, %s)\n",
802 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
805 return STG_E_REVERTED;
807 currentEntryRef = findElement(This,
808 This->storageDirEntry,
812 if (currentEntryRef != DIRENTRY_NULL)
815 * There is already an element with the new name
817 return STG_E_FILEALREADYEXISTS;
821 * Search for the old element name
823 currentEntryRef = findElement(This,
824 This->storageDirEntry,
828 if (currentEntryRef != DIRENTRY_NULL)
830 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
831 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
833 WARN("Element is already open; cannot rename.\n");
834 return STG_E_ACCESSDENIED;
837 /* Remove the element from its current position in the tree */
838 removeFromTree(This, This->storageDirEntry,
841 /* Change the name of the element */
842 strcpyW(currentEntry.name, pwcsNewName);
844 /* Delete any sibling links */
845 currentEntry.leftChild = DIRENTRY_NULL;
846 currentEntry.rightChild = DIRENTRY_NULL;
848 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
851 /* Insert the element in a new position in the tree */
852 insertIntoTree(This, This->storageDirEntry,
858 * There is no element with the old name
860 return STG_E_FILENOTFOUND;
863 return StorageBaseImpl_Flush(This);
866 /************************************************************************
867 * Storage32BaseImpl_CreateStream (IStorage)
869 * This method will create a stream object within this storage
871 * See Windows documentation for more details on IStorage methods.
873 static HRESULT WINAPI StorageBaseImpl_CreateStream(
875 const OLECHAR* pwcsName, /* [string][in] */
876 DWORD grfMode, /* [in] */
877 DWORD reserved1, /* [in] */
878 DWORD reserved2, /* [in] */
879 IStream** ppstm) /* [out] */
881 StorageBaseImpl *This = (StorageBaseImpl *)iface;
882 StgStreamImpl* newStream;
883 DirEntry currentEntry, newStreamEntry;
884 DirRef currentEntryRef, newStreamEntryRef;
887 TRACE("(%p, %s, %x, %d, %d, %p)\n",
888 iface, debugstr_w(pwcsName), grfMode,
889 reserved1, reserved2, ppstm);
892 return STG_E_INVALIDPOINTER;
895 return STG_E_INVALIDNAME;
897 if (reserved1 || reserved2)
898 return STG_E_INVALIDPARAMETER;
900 if ( FAILED( validateSTGM(grfMode) ))
901 return STG_E_INVALIDFLAG;
903 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
904 return STG_E_INVALIDFLAG;
907 return STG_E_REVERTED;
912 if ((grfMode & STGM_DELETEONRELEASE) ||
913 (grfMode & STGM_TRANSACTED))
914 return STG_E_INVALIDFUNCTION;
917 * Don't worry about permissions in transacted mode, as we can always write
918 * changes; we just can't always commit them.
920 if(!(This->openFlags & STGM_TRANSACTED)) {
921 /* Can't create a stream on read-only storage */
922 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
923 return STG_E_ACCESSDENIED;
925 /* Can't create a stream with greater access than the parent. */
926 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
927 return STG_E_ACCESSDENIED;
930 if(This->openFlags & STGM_SIMPLE)
931 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
935 currentEntryRef = findElement(This,
936 This->storageDirEntry,
940 if (currentEntryRef != DIRENTRY_NULL)
943 * An element with this name already exists
945 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
947 IStorage_DestroyElement(iface, pwcsName);
950 return STG_E_FILEALREADYEXISTS;
954 * memset the empty entry
956 memset(&newStreamEntry, 0, sizeof(DirEntry));
958 newStreamEntry.sizeOfNameString =
959 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
961 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
962 return STG_E_INVALIDNAME;
964 strcpyW(newStreamEntry.name, pwcsName);
966 newStreamEntry.stgType = STGTY_STREAM;
967 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
968 newStreamEntry.size.u.LowPart = 0;
969 newStreamEntry.size.u.HighPart = 0;
971 newStreamEntry.leftChild = DIRENTRY_NULL;
972 newStreamEntry.rightChild = DIRENTRY_NULL;
973 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
975 /* call CoFileTime to get the current time
980 /* newStreamEntry.clsid */
983 * Create an entry with the new data
985 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
990 * Insert the new entry in the parent storage's tree.
994 This->storageDirEntry,
998 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
1003 * Open the stream to return it.
1005 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1009 *ppstm = (IStream*)newStream;
1011 IStream_AddRef(*ppstm);
1015 return STG_E_INSUFFICIENTMEMORY;
1018 return StorageBaseImpl_Flush(This);
1021 /************************************************************************
1022 * Storage32BaseImpl_SetClass (IStorage)
1024 * This method will write the specified CLSID in the directory entry of this
1027 * See Windows documentation for more details on IStorage methods.
1029 static HRESULT WINAPI StorageBaseImpl_SetClass(
1031 REFCLSID clsid) /* [in] */
1033 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1035 DirEntry currentEntry;
1037 TRACE("(%p, %p)\n", iface, clsid);
1040 return STG_E_REVERTED;
1042 hRes = StorageBaseImpl_ReadDirEntry(This,
1043 This->storageDirEntry,
1045 if (SUCCEEDED(hRes))
1047 currentEntry.clsid = *clsid;
1049 hRes = StorageBaseImpl_WriteDirEntry(This,
1050 This->storageDirEntry,
1054 if (SUCCEEDED(hRes))
1055 hRes = StorageBaseImpl_Flush(This);
1060 /************************************************************************
1061 ** Storage32Impl implementation
1064 /************************************************************************
1065 * Storage32BaseImpl_CreateStorage (IStorage)
1067 * This method will create the storage object within the provided storage.
1069 * See Windows documentation for more details on IStorage methods.
1071 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1073 const OLECHAR *pwcsName, /* [string][in] */
1074 DWORD grfMode, /* [in] */
1075 DWORD reserved1, /* [in] */
1076 DWORD reserved2, /* [in] */
1077 IStorage **ppstg) /* [out] */
1079 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1081 DirEntry currentEntry;
1083 DirRef currentEntryRef;
1087 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1088 iface, debugstr_w(pwcsName), grfMode,
1089 reserved1, reserved2, ppstg);
1092 return STG_E_INVALIDPOINTER;
1094 if (This->openFlags & STGM_SIMPLE)
1096 return STG_E_INVALIDFUNCTION;
1100 return STG_E_INVALIDNAME;
1104 if ( FAILED( validateSTGM(grfMode) ) ||
1105 (grfMode & STGM_DELETEONRELEASE) )
1107 WARN("bad grfMode: 0x%x\n", grfMode);
1108 return STG_E_INVALIDFLAG;
1112 return STG_E_REVERTED;
1115 * Check that we're compatible with the parent's storage mode
1117 if ( !(This->openFlags & STGM_TRANSACTED) &&
1118 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1120 WARN("access denied\n");
1121 return STG_E_ACCESSDENIED;
1124 currentEntryRef = findElement(This,
1125 This->storageDirEntry,
1129 if (currentEntryRef != DIRENTRY_NULL)
1132 * An element with this name already exists
1134 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1135 ((This->openFlags & STGM_TRANSACTED) ||
1136 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1138 hr = IStorage_DestroyElement(iface, pwcsName);
1144 WARN("file already exists\n");
1145 return STG_E_FILEALREADYEXISTS;
1148 else if (!(This->openFlags & STGM_TRANSACTED) &&
1149 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1151 WARN("read-only storage\n");
1152 return STG_E_ACCESSDENIED;
1155 memset(&newEntry, 0, sizeof(DirEntry));
1157 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1159 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1161 FIXME("name too long\n");
1162 return STG_E_INVALIDNAME;
1165 strcpyW(newEntry.name, pwcsName);
1167 newEntry.stgType = STGTY_STORAGE;
1168 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1169 newEntry.size.u.LowPart = 0;
1170 newEntry.size.u.HighPart = 0;
1172 newEntry.leftChild = DIRENTRY_NULL;
1173 newEntry.rightChild = DIRENTRY_NULL;
1174 newEntry.dirRootEntry = DIRENTRY_NULL;
1176 /* call CoFileTime to get the current time
1181 /* newEntry.clsid */
1184 * Create a new directory entry for the storage
1186 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1191 * Insert the new directory entry into the parent storage's tree
1193 hr = insertIntoTree(
1195 This->storageDirEntry,
1199 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1204 * Open it to get a pointer to return.
1206 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1208 if( (hr != S_OK) || (*ppstg == NULL))
1214 hr = StorageBaseImpl_Flush(This);
1220 /***************************************************************************
1224 * Reserve a directory entry in the file and initialize it.
1226 static HRESULT StorageImpl_CreateDirEntry(
1227 StorageBaseImpl *base,
1228 const DirEntry *newData,
1231 StorageImpl *storage = (StorageImpl*)base;
1232 ULONG currentEntryIndex = 0;
1233 ULONG newEntryIndex = DIRENTRY_NULL;
1235 BYTE currentData[RAW_DIRENTRY_SIZE];
1236 WORD sizeOfNameString;
1240 hr = StorageImpl_ReadRawDirEntry(storage,
1246 StorageUtl_ReadWord(
1248 OFFSET_PS_NAMELENGTH,
1251 if (sizeOfNameString == 0)
1254 * The entry exists and is available, we found it.
1256 newEntryIndex = currentEntryIndex;
1262 * We exhausted the directory entries, we will create more space below
1264 newEntryIndex = currentEntryIndex;
1266 currentEntryIndex++;
1268 } while (newEntryIndex == DIRENTRY_NULL);
1271 * grow the directory stream
1275 BYTE emptyData[RAW_DIRENTRY_SIZE];
1276 ULARGE_INTEGER newSize;
1278 ULONG lastEntry = 0;
1279 ULONG blockCount = 0;
1282 * obtain the new count of blocks in the directory stream
1284 blockCount = BlockChainStream_GetCount(
1285 storage->rootBlockChain)+1;
1288 * initialize the size used by the directory stream
1290 newSize.u.HighPart = 0;
1291 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1294 * add a block to the directory stream
1296 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1299 * memset the empty entry in order to initialize the unused newly
1302 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1307 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1310 entryIndex = newEntryIndex + 1;
1311 entryIndex < lastEntry;
1314 StorageImpl_WriteRawDirEntry(
1320 StorageImpl_SaveFileHeader(storage);
1323 UpdateRawDirEntry(currentData, newData);
1325 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1328 *index = newEntryIndex;
1333 /***************************************************************************
1337 * Mark a directory entry in the file as free.
1339 static HRESULT StorageImpl_DestroyDirEntry(
1340 StorageBaseImpl *base,
1344 BYTE emptyData[RAW_DIRENTRY_SIZE];
1345 StorageImpl *storage = (StorageImpl*)base;
1347 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1349 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1355 /****************************************************************************
1359 * Case insensitive comparison of DirEntry.name by first considering
1362 * Returns <0 when name1 < name2
1363 * >0 when name1 > name2
1364 * 0 when name1 == name2
1366 static LONG entryNameCmp(
1367 const OLECHAR *name1,
1368 const OLECHAR *name2)
1370 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1372 while (diff == 0 && *name1 != 0)
1375 * We compare the string themselves only when they are of the same length
1377 diff = toupperW(*name1++) - toupperW(*name2++);
1383 /****************************************************************************
1387 * Add a directory entry to a storage
1389 static HRESULT insertIntoTree(
1390 StorageBaseImpl *This,
1391 DirRef parentStorageIndex,
1392 DirRef newEntryIndex)
1394 DirEntry currentEntry;
1398 * Read the inserted entry
1400 StorageBaseImpl_ReadDirEntry(This,
1405 * Read the storage entry
1407 StorageBaseImpl_ReadDirEntry(This,
1411 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1414 * The root storage contains some element, therefore, start the research
1415 * for the appropriate location.
1418 DirRef current, next, previous, currentEntryId;
1421 * Keep a reference to the root of the storage's element tree
1423 currentEntryId = currentEntry.dirRootEntry;
1428 StorageBaseImpl_ReadDirEntry(This,
1429 currentEntry.dirRootEntry,
1432 previous = currentEntry.leftChild;
1433 next = currentEntry.rightChild;
1434 current = currentEntryId;
1438 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1442 if (previous != DIRENTRY_NULL)
1444 StorageBaseImpl_ReadDirEntry(This,
1451 currentEntry.leftChild = newEntryIndex;
1452 StorageBaseImpl_WriteDirEntry(This,
1460 if (next != DIRENTRY_NULL)
1462 StorageBaseImpl_ReadDirEntry(This,
1469 currentEntry.rightChild = newEntryIndex;
1470 StorageBaseImpl_WriteDirEntry(This,
1479 * Trying to insert an item with the same name in the
1480 * subtree structure.
1482 return STG_E_FILEALREADYEXISTS;
1485 previous = currentEntry.leftChild;
1486 next = currentEntry.rightChild;
1492 * The storage is empty, make the new entry the root of its element tree
1494 currentEntry.dirRootEntry = newEntryIndex;
1495 StorageBaseImpl_WriteDirEntry(This,
1503 /****************************************************************************
1507 * Find and read the element of a storage with the given name.
1509 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1510 const OLECHAR *name, DirEntry *data)
1512 DirRef currentEntry;
1514 /* Read the storage entry to find the root of the tree. */
1515 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1517 currentEntry = data->dirRootEntry;
1519 while (currentEntry != DIRENTRY_NULL)
1523 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1525 cmp = entryNameCmp(name, data->name);
1532 currentEntry = data->leftChild;
1535 currentEntry = data->rightChild;
1538 return currentEntry;
1541 /****************************************************************************
1545 * Find and read the binary tree parent of the element with the given name.
1547 * If there is no such element, find a place where it could be inserted and
1548 * return STG_E_FILENOTFOUND.
1550 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1551 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1557 /* Read the storage entry to find the root of the tree. */
1558 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1560 *parentEntry = storageEntry;
1561 *relation = DIRENTRY_RELATION_DIR;
1563 childEntry = parentData->dirRootEntry;
1565 while (childEntry != DIRENTRY_NULL)
1569 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1571 cmp = entryNameCmp(childName, childData.name);
1579 *parentData = childData;
1580 *parentEntry = childEntry;
1581 *relation = DIRENTRY_RELATION_PREVIOUS;
1583 childEntry = parentData->leftChild;
1588 *parentData = childData;
1589 *parentEntry = childEntry;
1590 *relation = DIRENTRY_RELATION_NEXT;
1592 childEntry = parentData->rightChild;
1596 if (childEntry == DIRENTRY_NULL)
1597 return STG_E_FILENOTFOUND;
1603 /*************************************************************************
1606 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1608 DWORD ciidExclude, /* [in] */
1609 const IID* rgiidExclude, /* [size_is][unique][in] */
1610 SNB snbExclude, /* [unique][in] */
1611 IStorage* pstgDest) /* [unique][in] */
1613 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1615 IEnumSTATSTG *elements = 0;
1616 STATSTG curElement, strStat;
1618 IStorage *pstgTmp, *pstgChild;
1619 IStream *pstrTmp, *pstrChild;
1622 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1625 TRACE("(%p, %d, %p, %p, %p)\n",
1626 iface, ciidExclude, rgiidExclude,
1627 snbExclude, pstgDest);
1629 if ( pstgDest == 0 )
1630 return STG_E_INVALIDPOINTER;
1633 * Enumerate the elements
1635 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1643 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1644 IStorage_SetClass( pstgDest, &curElement.clsid );
1646 for(i = 0; i < ciidExclude; ++i)
1648 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1649 skip_storage = TRUE;
1650 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1653 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1659 * Obtain the next element
1661 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1663 if ( hr == S_FALSE )
1665 hr = S_OK; /* done, every element has been copied */
1671 WCHAR **snb = snbExclude;
1673 while ( *snb != NULL && !skip )
1675 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1684 if (curElement.type == STGTY_STORAGE)
1690 * open child source storage
1692 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1693 STGM_READ|STGM_SHARE_EXCLUSIVE,
1694 NULL, 0, &pstgChild );
1700 * create a new storage in destination storage
1702 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1703 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1707 * if it already exist, don't create a new one use this one
1709 if (hr == STG_E_FILEALREADYEXISTS)
1711 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1712 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1713 NULL, 0, &pstgTmp );
1719 * do the copy recursively
1721 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1724 IStorage_Release( pstgTmp );
1727 IStorage_Release( pstgChild );
1729 else if (curElement.type == STGTY_STREAM)
1735 * create a new stream in destination storage. If the stream already
1736 * exist, it will be deleted and a new one will be created.
1738 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1739 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1746 * open child stream storage. This operation must succeed even if the
1747 * stream is already open, so we use internal functions to do it.
1749 srcEntryRef = findElement( This, This->storageDirEntry, curElement.pwcsName,
1753 ERR("source stream not found\n");
1754 hr = STG_E_DOCFILECORRUPT;
1759 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntryRef);
1761 IStream_AddRef(pstrChild);
1769 * Get the size of the source stream
1771 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1774 * Set the size of the destination stream.
1776 IStream_SetSize(pstrTmp, strStat.cbSize);
1781 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1784 IStream_Release( pstrChild );
1787 IStream_Release( pstrTmp );
1791 WARN("unknown element type: %d\n", curElement.type);
1795 CoTaskMemFree(curElement.pwcsName);
1796 } while (hr == S_OK);
1801 IEnumSTATSTG_Release(elements);
1806 /*************************************************************************
1807 * MoveElementTo (IStorage)
1809 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1811 const OLECHAR *pwcsName, /* [string][in] */
1812 IStorage *pstgDest, /* [unique][in] */
1813 const OLECHAR *pwcsNewName,/* [string][in] */
1814 DWORD grfFlags) /* [in] */
1816 FIXME("(%p %s %p %s %u): stub\n", iface,
1817 debugstr_w(pwcsName), pstgDest,
1818 debugstr_w(pwcsNewName), grfFlags);
1822 /*************************************************************************
1825 * Ensures that any changes made to a storage object open in transacted mode
1826 * are reflected in the parent storage
1828 * In a non-transacted mode, this ensures all cached writes are completed.
1830 static HRESULT WINAPI StorageImpl_Commit(
1832 DWORD grfCommitFlags)/* [in] */
1834 StorageBaseImpl* const base=(StorageBaseImpl*)iface;
1835 TRACE("(%p %d)\n", iface, grfCommitFlags);
1836 return StorageBaseImpl_Flush(base);
1839 /*************************************************************************
1842 * Discard all changes that have been made since the last commit operation
1844 static HRESULT WINAPI StorageImpl_Revert(
1847 TRACE("(%p)\n", iface);
1851 /*************************************************************************
1852 * DestroyElement (IStorage)
1854 * Strategy: This implementation is built this way for simplicity not for speed.
1855 * I always delete the topmost element of the enumeration and adjust
1856 * the deleted element pointer all the time. This takes longer to
1857 * do but allow to reinvoke DestroyElement whenever we encounter a
1858 * storage object. The optimisation resides in the usage of another
1859 * enumeration strategy that would give all the leaves of a storage
1860 * first. (postfix order)
1862 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1864 const OLECHAR *pwcsName)/* [string][in] */
1866 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1869 DirEntry entryToDelete;
1870 DirRef entryToDeleteRef;
1873 iface, debugstr_w(pwcsName));
1876 return STG_E_INVALIDPOINTER;
1879 return STG_E_REVERTED;
1881 if ( !(This->openFlags & STGM_TRANSACTED) &&
1882 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1883 return STG_E_ACCESSDENIED;
1885 entryToDeleteRef = findElement(
1887 This->storageDirEntry,
1891 if ( entryToDeleteRef == DIRENTRY_NULL )
1893 return STG_E_FILENOTFOUND;
1896 if ( entryToDelete.stgType == STGTY_STORAGE )
1898 hr = deleteStorageContents(
1903 else if ( entryToDelete.stgType == STGTY_STREAM )
1905 hr = deleteStreamContents(
1915 * Remove the entry from its parent storage
1917 hr = removeFromTree(
1919 This->storageDirEntry,
1923 * Invalidate the entry
1926 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1929 hr = StorageBaseImpl_Flush(This);
1935 /******************************************************************************
1936 * Internal stream list handlers
1939 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1941 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1942 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1945 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1947 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1948 list_remove(&(strm->StrmListEntry));
1951 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1953 StgStreamImpl *strm;
1955 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1957 if (strm->dirEntry == streamEntry)
1966 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1968 StorageInternalImpl *childstg;
1970 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1972 if (childstg->base.storageDirEntry == storageEntry)
1981 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1983 struct list *cur, *cur2;
1984 StgStreamImpl *strm=NULL;
1985 StorageInternalImpl *childstg=NULL;
1987 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1988 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1989 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1990 strm->parentStorage = NULL;
1994 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1995 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1996 StorageBaseImpl_Invalidate( &childstg->base );
1999 if (stg->transactedChild)
2001 StorageBaseImpl_Invalidate(stg->transactedChild);
2003 stg->transactedChild = NULL;
2008 /*********************************************************************
2012 * Delete the contents of a storage entry.
2015 static HRESULT deleteStorageContents(
2016 StorageBaseImpl *parentStorage,
2017 DirRef indexToDelete,
2018 DirEntry entryDataToDelete)
2020 IEnumSTATSTG *elements = 0;
2021 IStorage *childStorage = 0;
2022 STATSTG currentElement;
2024 HRESULT destroyHr = S_OK;
2025 StorageInternalImpl *stg, *stg2;
2027 /* Invalidate any open storage objects. */
2028 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2030 if (stg->base.storageDirEntry == indexToDelete)
2032 StorageBaseImpl_Invalidate(&stg->base);
2037 * Open the storage and enumerate it
2039 hr = StorageBaseImpl_OpenStorage(
2040 (IStorage*)parentStorage,
2041 entryDataToDelete.name,
2043 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2054 * Enumerate the elements
2056 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2061 * Obtain the next element
2063 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
2066 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2068 CoTaskMemFree(currentElement.pwcsName);
2072 * We need to Reset the enumeration every time because we delete elements
2073 * and the enumeration could be invalid
2075 IEnumSTATSTG_Reset(elements);
2077 } while ((hr == S_OK) && (destroyHr == S_OK));
2079 IStorage_Release(childStorage);
2080 IEnumSTATSTG_Release(elements);
2085 /*********************************************************************
2089 * Perform the deletion of a stream's data
2092 static HRESULT deleteStreamContents(
2093 StorageBaseImpl *parentStorage,
2094 DirRef indexToDelete,
2095 DirEntry entryDataToDelete)
2099 ULARGE_INTEGER size;
2100 StgStreamImpl *strm, *strm2;
2102 /* Invalidate any open stream objects. */
2103 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2105 if (strm->dirEntry == indexToDelete)
2107 TRACE("Stream deleted %p\n", strm);
2108 strm->parentStorage = NULL;
2109 list_remove(&strm->StrmListEntry);
2113 size.u.HighPart = 0;
2116 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2117 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2127 hr = IStream_SetSize(pis, size);
2135 * Release the stream object.
2137 IStream_Release(pis);
2142 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2146 case DIRENTRY_RELATION_PREVIOUS:
2147 entry->leftChild = new_target;
2149 case DIRENTRY_RELATION_NEXT:
2150 entry->rightChild = new_target;
2152 case DIRENTRY_RELATION_DIR:
2153 entry->dirRootEntry = new_target;
2160 /*************************************************************************
2164 * This method removes a directory entry from its parent storage tree without
2165 * freeing any resources attached to it.
2167 static HRESULT removeFromTree(
2168 StorageBaseImpl *This,
2169 DirRef parentStorageIndex,
2170 DirRef deletedIndex)
2173 DirEntry entryToDelete;
2174 DirEntry parentEntry;
2175 DirRef parentEntryRef;
2176 ULONG typeOfRelation;
2178 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2184 * Find the element that links to the one we want to delete.
2186 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2187 &parentEntry, &parentEntryRef, &typeOfRelation);
2192 if (entryToDelete.leftChild != DIRENTRY_NULL)
2195 * Replace the deleted entry with its left child
2197 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2199 hr = StorageBaseImpl_WriteDirEntry(
2208 if (entryToDelete.rightChild != DIRENTRY_NULL)
2211 * We need to reinsert the right child somewhere. We already know it and
2212 * its children are greater than everything in the left tree, so we
2213 * insert it at the rightmost point in the left tree.
2215 DirRef newRightChildParent = entryToDelete.leftChild;
2216 DirEntry newRightChildParentEntry;
2220 hr = StorageBaseImpl_ReadDirEntry(
2222 newRightChildParent,
2223 &newRightChildParentEntry);
2229 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2230 newRightChildParent = newRightChildParentEntry.rightChild;
2231 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2233 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2235 hr = StorageBaseImpl_WriteDirEntry(
2237 newRightChildParent,
2238 &newRightChildParentEntry);
2248 * Replace the deleted entry with its right child
2250 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2252 hr = StorageBaseImpl_WriteDirEntry(
2266 /******************************************************************************
2267 * SetElementTimes (IStorage)
2269 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2271 const OLECHAR *pwcsName,/* [string][in] */
2272 const FILETIME *pctime, /* [in] */
2273 const FILETIME *patime, /* [in] */
2274 const FILETIME *pmtime) /* [in] */
2276 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2280 /******************************************************************************
2281 * SetStateBits (IStorage)
2283 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2285 DWORD grfStateBits,/* [in] */
2286 DWORD grfMask) /* [in] */
2288 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2291 return STG_E_REVERTED;
2293 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2297 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2298 DirRef index, const DirEntry *data)
2300 StorageImpl *This = (StorageImpl*)base;
2301 return StorageImpl_WriteDirEntry(This, index, data);
2304 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2305 DirRef index, DirEntry *data)
2307 StorageImpl *This = (StorageImpl*)base;
2308 return StorageImpl_ReadDirEntry(This, index, data);
2311 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2315 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2317 if (!This->blockChainCache[i])
2319 return &This->blockChainCache[i];
2323 i = This->blockChainToEvict;
2325 BlockChainStream_Destroy(This->blockChainCache[i]);
2326 This->blockChainCache[i] = NULL;
2328 This->blockChainToEvict++;
2329 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2330 This->blockChainToEvict = 0;
2332 return &This->blockChainCache[i];
2335 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2338 int i, free_index=-1;
2340 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2342 if (!This->blockChainCache[i])
2344 if (free_index == -1) free_index = i;
2346 else if (This->blockChainCache[i]->ownerDirEntry == index)
2348 return &This->blockChainCache[i];
2352 if (free_index == -1)
2354 free_index = This->blockChainToEvict;
2356 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2357 This->blockChainCache[free_index] = NULL;
2359 This->blockChainToEvict++;
2360 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2361 This->blockChainToEvict = 0;
2364 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2365 return &This->blockChainCache[free_index];
2368 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2372 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2374 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2376 BlockChainStream_Destroy(This->blockChainCache[i]);
2377 This->blockChainCache[i] = NULL;
2383 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2384 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2386 StorageImpl *This = (StorageImpl*)base;
2391 hr = StorageImpl_ReadDirEntry(This, index, &data);
2392 if (FAILED(hr)) return hr;
2394 if (data.size.QuadPart == 0)
2400 if (offset.QuadPart + size > data.size.QuadPart)
2402 bytesToRead = data.size.QuadPart - offset.QuadPart;
2409 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2411 SmallBlockChainStream *stream;
2413 stream = SmallBlockChainStream_Construct(This, NULL, index);
2414 if (!stream) return E_OUTOFMEMORY;
2416 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2418 SmallBlockChainStream_Destroy(stream);
2424 BlockChainStream *stream = NULL;
2426 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2427 if (!stream) return E_OUTOFMEMORY;
2429 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2435 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2436 ULARGE_INTEGER newsize)
2438 StorageImpl *This = (StorageImpl*)base;
2441 SmallBlockChainStream *smallblock=NULL;
2442 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2444 hr = StorageImpl_ReadDirEntry(This, index, &data);
2445 if (FAILED(hr)) return hr;
2447 /* In simple mode keep the stream size above the small block limit */
2448 if (This->base.openFlags & STGM_SIMPLE)
2449 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2451 if (data.size.QuadPart == newsize.QuadPart)
2454 /* Create a block chain object of the appropriate type */
2455 if (data.size.QuadPart == 0)
2457 if (newsize.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 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2471 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2472 if (!smallblock) return E_OUTOFMEMORY;
2476 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2477 bigblock = *pbigblock;
2478 if (!bigblock) return E_OUTOFMEMORY;
2481 /* Change the block chain type if necessary. */
2482 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2484 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2487 SmallBlockChainStream_Destroy(smallblock);
2491 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2492 *pbigblock = bigblock;
2494 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2496 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
2501 /* Set the size of the block chain. */
2504 SmallBlockChainStream_SetSize(smallblock, newsize);
2505 SmallBlockChainStream_Destroy(smallblock);
2509 BlockChainStream_SetSize(bigblock, newsize);
2512 /* Set the size in the directory entry. */
2513 hr = StorageImpl_ReadDirEntry(This, index, &data);
2516 data.size = newsize;
2518 hr = StorageImpl_WriteDirEntry(This, index, &data);
2523 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2524 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2526 StorageImpl *This = (StorageImpl*)base;
2529 ULARGE_INTEGER newSize;
2531 hr = StorageImpl_ReadDirEntry(This, index, &data);
2532 if (FAILED(hr)) return hr;
2534 /* Grow the stream if necessary */
2535 newSize.QuadPart = 0;
2536 newSize.QuadPart = offset.QuadPart + size;
2538 if (newSize.QuadPart > data.size.QuadPart)
2540 hr = StorageImpl_StreamSetSize(base, index, newSize);
2544 hr = StorageImpl_ReadDirEntry(This, index, &data);
2545 if (FAILED(hr)) return hr;
2548 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2550 SmallBlockChainStream *stream;
2552 stream = SmallBlockChainStream_Construct(This, NULL, index);
2553 if (!stream) return E_OUTOFMEMORY;
2555 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2557 SmallBlockChainStream_Destroy(stream);
2563 BlockChainStream *stream;
2565 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2566 if (!stream) return E_OUTOFMEMORY;
2568 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2574 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2577 StorageImpl *This = (StorageImpl*)base;
2578 DirEntry dst_data, src_data;
2581 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2584 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2588 StorageImpl_DeleteCachedBlockChainStream(This, src);
2589 dst_data.startingBlock = src_data.startingBlock;
2590 dst_data.size = src_data.size;
2592 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2598 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2600 StorageImpl *This = (StorageImpl*) iface;
2604 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2606 *result = statstg.pwcsName;
2612 * Virtual function table for the IStorage32Impl class.
2614 static const IStorageVtbl Storage32Impl_Vtbl =
2616 StorageBaseImpl_QueryInterface,
2617 StorageBaseImpl_AddRef,
2618 StorageBaseImpl_Release,
2619 StorageBaseImpl_CreateStream,
2620 StorageBaseImpl_OpenStream,
2621 StorageBaseImpl_CreateStorage,
2622 StorageBaseImpl_OpenStorage,
2623 StorageBaseImpl_CopyTo,
2624 StorageBaseImpl_MoveElementTo,
2627 StorageBaseImpl_EnumElements,
2628 StorageBaseImpl_DestroyElement,
2629 StorageBaseImpl_RenameElement,
2630 StorageBaseImpl_SetElementTimes,
2631 StorageBaseImpl_SetClass,
2632 StorageBaseImpl_SetStateBits,
2633 StorageBaseImpl_Stat
2636 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2638 StorageImpl_Destroy,
2639 StorageImpl_Invalidate,
2641 StorageImpl_GetFilename,
2642 StorageImpl_CreateDirEntry,
2643 StorageImpl_BaseWriteDirEntry,
2644 StorageImpl_BaseReadDirEntry,
2645 StorageImpl_DestroyDirEntry,
2646 StorageImpl_StreamReadAt,
2647 StorageImpl_StreamWriteAt,
2648 StorageImpl_StreamSetSize,
2649 StorageImpl_StreamLink
2652 static HRESULT StorageImpl_Construct(
2660 StorageImpl** result)
2664 DirEntry currentEntry;
2665 DirRef currentEntryRef;
2667 if ( FAILED( validateSTGM(openFlags) ))
2668 return STG_E_INVALIDFLAG;
2670 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2672 return E_OUTOFMEMORY;
2674 memset(This, 0, sizeof(StorageImpl));
2676 list_init(&This->base.strmHead);
2678 list_init(&This->base.storageHead);
2680 This->base.lpVtbl = &Storage32Impl_Vtbl;
2681 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2682 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2683 This->base.openFlags = (openFlags & ~STGM_CREATE);
2685 This->base.create = create;
2687 This->base.reverted = 0;
2690 * Initialize the big block cache.
2692 This->bigBlockSize = sector_size;
2693 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2695 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2698 This->lockBytes = pLkbyt;
2699 ILockBytes_AddRef(pLkbyt);
2707 ULARGE_INTEGER size;
2708 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2711 * Initialize all header variables:
2712 * - The big block depot consists of one block and it is at block 0
2713 * - The directory table starts at block 1
2714 * - There is no small block depot
2716 memset( This->bigBlockDepotStart,
2718 sizeof(This->bigBlockDepotStart));
2720 This->bigBlockDepotCount = 1;
2721 This->bigBlockDepotStart[0] = 0;
2722 This->rootStartBlock = 1;
2723 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2724 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2725 if (sector_size == 4096)
2726 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2728 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2729 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2730 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2731 This->extBigBlockDepotCount = 0;
2733 StorageImpl_SaveFileHeader(This);
2736 * Add one block for the big block depot and one block for the directory table
2738 size.u.HighPart = 0;
2739 size.u.LowPart = This->bigBlockSize * 3;
2740 ILockBytes_SetSize(This->lockBytes, size);
2743 * Initialize the big block depot
2745 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2746 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2747 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2748 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2753 * Load the header for the file.
2755 hr = StorageImpl_LoadFileHeader(This);
2764 * There is no block depot cached yet.
2766 This->indexBlockDepotCached = 0xFFFFFFFF;
2767 This->indexExtBlockDepotCached = 0xFFFFFFFF;
2770 * Start searching for free blocks with block 0.
2772 This->prevFreeBlock = 0;
2774 This->firstFreeSmallBlock = 0;
2776 /* Read the extended big block depot locations. */
2777 if (This->extBigBlockDepotCount != 0)
2779 ULONG current_block = This->extBigBlockDepotStart;
2780 ULONG cache_size = This->extBigBlockDepotCount * 2;
2783 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
2784 if (!This->extBigBlockDepotLocations)
2790 This->extBigBlockDepotLocationsSize = cache_size;
2792 for (i=0; i<This->extBigBlockDepotCount; i++)
2794 if (current_block == BLOCK_END_OF_CHAIN)
2796 WARN("File has too few extended big block depot blocks.\n");
2797 hr = STG_E_DOCFILECORRUPT;
2800 This->extBigBlockDepotLocations[i] = current_block;
2801 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
2806 This->extBigBlockDepotLocations = NULL;
2807 This->extBigBlockDepotLocationsSize = 0;
2811 * Create the block chain abstractions.
2813 if(!(This->rootBlockChain =
2814 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2816 hr = STG_E_READFAULT;
2820 if(!(This->smallBlockDepotChain =
2821 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2824 hr = STG_E_READFAULT;
2829 * Write the root storage entry (memory only)
2835 * Initialize the directory table
2837 memset(&rootEntry, 0, sizeof(rootEntry));
2838 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2839 sizeof(rootEntry.name)/sizeof(WCHAR) );
2840 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2841 rootEntry.stgType = STGTY_ROOT;
2842 rootEntry.leftChild = DIRENTRY_NULL;
2843 rootEntry.rightChild = DIRENTRY_NULL;
2844 rootEntry.dirRootEntry = DIRENTRY_NULL;
2845 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2846 rootEntry.size.u.HighPart = 0;
2847 rootEntry.size.u.LowPart = 0;
2849 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2853 * Find the ID of the root storage.
2855 currentEntryRef = 0;
2859 hr = StorageImpl_ReadDirEntry(
2866 if ( (currentEntry.sizeOfNameString != 0 ) &&
2867 (currentEntry.stgType == STGTY_ROOT) )
2869 This->base.storageDirEntry = currentEntryRef;
2875 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2879 hr = STG_E_READFAULT;
2884 * Create the block chain abstraction for the small block root chain.
2886 if(!(This->smallBlockRootChain =
2887 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2889 hr = STG_E_READFAULT;
2895 IStorage_Release((IStorage*)This);
2900 StorageImpl_Flush((StorageBaseImpl*)This);
2907 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2909 StorageImpl *This = (StorageImpl*) iface;
2911 StorageBaseImpl_DeleteAll(&This->base);
2913 This->base.reverted = 1;
2916 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2918 StorageImpl *This = (StorageImpl*) iface;
2920 TRACE("(%p)\n", This);
2922 StorageImpl_Flush(iface);
2924 StorageImpl_Invalidate(iface);
2926 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
2928 BlockChainStream_Destroy(This->smallBlockRootChain);
2929 BlockChainStream_Destroy(This->rootBlockChain);
2930 BlockChainStream_Destroy(This->smallBlockDepotChain);
2932 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2933 BlockChainStream_Destroy(This->blockChainCache[i]);
2935 if (This->lockBytes)
2936 ILockBytes_Release(This->lockBytes);
2937 HeapFree(GetProcessHeap(), 0, This);
2940 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface)
2942 StorageImpl *This = (StorageImpl*) iface;
2945 TRACE("(%p)\n", This);
2947 hr = BlockChainStream_Flush(This->smallBlockRootChain);
2950 hr = BlockChainStream_Flush(This->rootBlockChain);
2953 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
2955 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
2956 if (This->blockChainCache[i])
2957 hr = BlockChainStream_Flush(This->blockChainCache[i]);
2960 hr = ILockBytes_Flush(This->lockBytes);
2965 /******************************************************************************
2966 * Storage32Impl_GetNextFreeBigBlock
2968 * Returns the index of the next free big block.
2969 * If the big block depot is filled, this method will enlarge it.
2972 static ULONG StorageImpl_GetNextFreeBigBlock(
2975 ULONG depotBlockIndexPos;
2976 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
2978 ULONG depotBlockOffset;
2979 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2980 ULONG nextBlockIndex = BLOCK_SPECIAL;
2982 ULONG freeBlock = BLOCK_UNUSED;
2983 ULARGE_INTEGER neededSize;
2986 depotIndex = This->prevFreeBlock / blocksPerDepot;
2987 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2990 * Scan the entire big block depot until we find a block marked free
2992 while (nextBlockIndex != BLOCK_UNUSED)
2994 if (depotIndex < COUNT_BBDEPOTINHEADER)
2996 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2999 * Grow the primary depot.
3001 if (depotBlockIndexPos == BLOCK_UNUSED)
3003 depotBlockIndexPos = depotIndex*blocksPerDepot;
3006 * Add a block depot.
3008 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3009 This->bigBlockDepotCount++;
3010 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3013 * Flag it as a block depot.
3015 StorageImpl_SetNextBlockInChain(This,
3019 /* Save new header information.
3021 StorageImpl_SaveFileHeader(This);
3026 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3028 if (depotBlockIndexPos == BLOCK_UNUSED)
3031 * Grow the extended depot.
3033 ULONG extIndex = BLOCK_UNUSED;
3034 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3035 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3037 if (extBlockOffset == 0)
3039 /* We need an extended block.
3041 extIndex = Storage32Impl_AddExtBlockDepot(This);
3042 This->extBigBlockDepotCount++;
3043 depotBlockIndexPos = extIndex + 1;
3046 depotBlockIndexPos = depotIndex * blocksPerDepot;
3049 * Add a block depot and mark it in the extended block.
3051 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3052 This->bigBlockDepotCount++;
3053 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3055 /* Flag the block depot.
3057 StorageImpl_SetNextBlockInChain(This,
3061 /* If necessary, flag the extended depot block.
3063 if (extIndex != BLOCK_UNUSED)
3064 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3066 /* Save header information.
3068 StorageImpl_SaveFileHeader(This);
3072 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3076 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3077 ( nextBlockIndex != BLOCK_UNUSED))
3079 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3081 if (nextBlockIndex == BLOCK_UNUSED)
3083 freeBlock = (depotIndex * blocksPerDepot) +
3084 (depotBlockOffset/sizeof(ULONG));
3087 depotBlockOffset += sizeof(ULONG);
3092 depotBlockOffset = 0;
3096 * make sure that the block physically exists before using it
3098 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3100 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3102 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3103 ILockBytes_SetSize(This->lockBytes, neededSize);
3105 This->prevFreeBlock = freeBlock;
3110 /******************************************************************************
3111 * Storage32Impl_AddBlockDepot
3113 * This will create a depot block, essentially it is a block initialized
3116 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3118 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3121 * Initialize blocks as free
3123 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3124 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3127 /******************************************************************************
3128 * Storage32Impl_GetExtDepotBlock
3130 * Returns the index of the block that corresponds to the specified depot
3131 * index. This method is only for depot indexes equal or greater than
3132 * COUNT_BBDEPOTINHEADER.
3134 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3136 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3137 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3138 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3139 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3140 ULONG blockIndex = BLOCK_UNUSED;
3141 ULONG extBlockIndex;
3142 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3143 int index, num_blocks;
3145 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3147 if (extBlockCount >= This->extBigBlockDepotCount)
3148 return BLOCK_UNUSED;
3150 if (This->indexExtBlockDepotCached != extBlockCount)
3152 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3154 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer);
3156 num_blocks = This->bigBlockSize / 4;
3158 for (index = 0; index < num_blocks; index++)
3160 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3161 This->extBlockDepotCached[index] = blockIndex;
3164 This->indexExtBlockDepotCached = extBlockCount;
3167 blockIndex = This->extBlockDepotCached[extBlockOffset];
3172 /******************************************************************************
3173 * Storage32Impl_SetExtDepotBlock
3175 * Associates the specified block index to the specified depot index.
3176 * This method is only for depot indexes equal or greater than
3177 * COUNT_BBDEPOTINHEADER.
3179 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3181 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3182 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3183 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3184 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3185 ULONG extBlockIndex;
3187 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3189 assert(extBlockCount < This->extBigBlockDepotCount);
3191 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3193 if (extBlockIndex != BLOCK_UNUSED)
3195 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3196 extBlockOffset * sizeof(ULONG),
3200 if (This->indexExtBlockDepotCached == extBlockCount)
3202 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3206 /******************************************************************************
3207 * Storage32Impl_AddExtBlockDepot
3209 * Creates an extended depot block.
3211 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3213 ULONG numExtBlocks = This->extBigBlockDepotCount;
3214 ULONG nextExtBlock = This->extBigBlockDepotStart;
3215 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3216 ULONG index = BLOCK_UNUSED;
3217 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3218 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3219 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3221 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3222 blocksPerDepotBlock;
3224 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3227 * The first extended block.
3229 This->extBigBlockDepotStart = index;
3234 * Find the last existing extended block.
3236 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3239 * Add the new extended block to the chain.
3241 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3246 * Initialize this block.
3248 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3249 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3251 /* Add the block to our cache. */
3252 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3254 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3255 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3257 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3258 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3260 This->extBigBlockDepotLocations = new_cache;
3261 This->extBigBlockDepotLocationsSize = new_cache_size;
3263 This->extBigBlockDepotLocations[numExtBlocks] = index;
3268 /******************************************************************************
3269 * Storage32Impl_FreeBigBlock
3271 * This method will flag the specified block as free in the big block depot.
3273 static void StorageImpl_FreeBigBlock(
3277 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3279 if (blockIndex < This->prevFreeBlock)
3280 This->prevFreeBlock = blockIndex;
3283 /************************************************************************
3284 * Storage32Impl_GetNextBlockInChain
3286 * This method will retrieve the block index of the next big block in
3289 * Params: This - Pointer to the Storage object.
3290 * blockIndex - Index of the block to retrieve the chain
3292 * nextBlockIndex - receives the return value.
3294 * Returns: This method returns the index of the next block in the chain.
3295 * It will return the constants:
3296 * BLOCK_SPECIAL - If the block given was not part of a
3298 * BLOCK_END_OF_CHAIN - If the block given was the last in
3300 * BLOCK_UNUSED - If the block given was not past of a chain
3302 * BLOCK_EXTBBDEPOT - This block is part of the extended
3305 * See Windows documentation for more details on IStorage methods.
3307 static HRESULT StorageImpl_GetNextBlockInChain(
3310 ULONG* nextBlockIndex)
3312 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3313 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3314 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3315 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3317 ULONG depotBlockIndexPos;
3318 int index, num_blocks;
3320 *nextBlockIndex = BLOCK_SPECIAL;
3322 if(depotBlockCount >= This->bigBlockDepotCount)
3324 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3325 This->bigBlockDepotCount);
3326 return STG_E_READFAULT;
3330 * Cache the currently accessed depot block.
3332 if (depotBlockCount != This->indexBlockDepotCached)
3334 This->indexBlockDepotCached = depotBlockCount;
3336 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3338 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3343 * We have to look in the extended depot.
3345 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3348 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3351 return STG_E_READFAULT;
3353 num_blocks = This->bigBlockSize / 4;
3355 for (index = 0; index < num_blocks; index++)
3357 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3358 This->blockDepotCached[index] = *nextBlockIndex;
3362 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3367 /******************************************************************************
3368 * Storage32Impl_GetNextExtendedBlock
3370 * Given an extended block this method will return the next extended block.
3373 * The last ULONG of an extended block is the block index of the next
3374 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3378 * - The index of the next extended block
3379 * - BLOCK_UNUSED: there is no next extended block.
3380 * - Any other return values denotes failure.
3382 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3384 ULONG nextBlockIndex = BLOCK_SPECIAL;
3385 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3387 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3390 return nextBlockIndex;
3393 /******************************************************************************
3394 * Storage32Impl_SetNextBlockInChain
3396 * This method will write the index of the specified block's next block
3397 * in the big block depot.
3399 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3402 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3403 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3404 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3407 static void StorageImpl_SetNextBlockInChain(
3412 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3413 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3414 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3415 ULONG depotBlockIndexPos;
3417 assert(depotBlockCount < This->bigBlockDepotCount);
3418 assert(blockIndex != nextBlock);
3420 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3422 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3427 * We have to look in the extended depot.
3429 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3432 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3435 * Update the cached block depot, if necessary.
3437 if (depotBlockCount == This->indexBlockDepotCached)
3439 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3443 /******************************************************************************
3444 * Storage32Impl_LoadFileHeader
3446 * This method will read in the file header
3448 static HRESULT StorageImpl_LoadFileHeader(
3452 BYTE headerBigBlock[HEADER_SIZE];
3454 ULARGE_INTEGER offset;
3459 * Get a pointer to the big block of data containing the header.
3461 offset.u.HighPart = 0;
3462 offset.u.LowPart = 0;
3463 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3464 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3465 hr = STG_E_FILENOTFOUND;
3468 * Extract the information from the header.
3473 * Check for the "magic number" signature and return an error if it is not
3476 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3478 return STG_E_OLDFORMAT;
3481 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3483 return STG_E_INVALIDHEADER;
3486 StorageUtl_ReadWord(
3488 OFFSET_BIGBLOCKSIZEBITS,
3489 &This->bigBlockSizeBits);
3491 StorageUtl_ReadWord(
3493 OFFSET_SMALLBLOCKSIZEBITS,
3494 &This->smallBlockSizeBits);
3496 StorageUtl_ReadDWord(
3498 OFFSET_BBDEPOTCOUNT,
3499 &This->bigBlockDepotCount);
3501 StorageUtl_ReadDWord(
3503 OFFSET_ROOTSTARTBLOCK,
3504 &This->rootStartBlock);
3506 StorageUtl_ReadDWord(
3508 OFFSET_SMALLBLOCKLIMIT,
3509 &This->smallBlockLimit);
3511 StorageUtl_ReadDWord(
3513 OFFSET_SBDEPOTSTART,
3514 &This->smallBlockDepotStart);
3516 StorageUtl_ReadDWord(
3518 OFFSET_EXTBBDEPOTSTART,
3519 &This->extBigBlockDepotStart);
3521 StorageUtl_ReadDWord(
3523 OFFSET_EXTBBDEPOTCOUNT,
3524 &This->extBigBlockDepotCount);
3526 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3528 StorageUtl_ReadDWord(
3530 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3531 &(This->bigBlockDepotStart[index]));
3535 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3537 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3538 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3541 * Right now, the code is making some assumptions about the size of the
3542 * blocks, just make sure they are what we're expecting.
3544 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3545 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3546 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3548 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3549 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3550 hr = STG_E_INVALIDHEADER;
3559 /******************************************************************************
3560 * Storage32Impl_SaveFileHeader
3562 * This method will save to the file the header
3564 static void StorageImpl_SaveFileHeader(
3567 BYTE headerBigBlock[HEADER_SIZE];
3570 ULARGE_INTEGER offset;
3571 DWORD bytes_read, bytes_written;
3572 DWORD major_version, dirsectorcount;
3575 * Get a pointer to the big block of data containing the header.
3577 offset.u.HighPart = 0;
3578 offset.u.LowPart = 0;
3579 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3580 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3581 hr = STG_E_FILENOTFOUND;
3583 if (This->bigBlockSizeBits == 0x9)
3585 else if (This->bigBlockSizeBits == 0xc)
3589 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3594 * If the block read failed, the file is probably new.
3599 * Initialize for all unknown fields.
3601 memset(headerBigBlock, 0, HEADER_SIZE);
3604 * Initialize the magic number.
3606 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3610 * Write the information to the header.
3612 StorageUtl_WriteWord(
3614 OFFSET_MINORVERSION,
3617 StorageUtl_WriteWord(
3619 OFFSET_MAJORVERSION,
3622 StorageUtl_WriteWord(
3624 OFFSET_BYTEORDERMARKER,
3627 StorageUtl_WriteWord(
3629 OFFSET_BIGBLOCKSIZEBITS,
3630 This->bigBlockSizeBits);
3632 StorageUtl_WriteWord(
3634 OFFSET_SMALLBLOCKSIZEBITS,
3635 This->smallBlockSizeBits);
3637 if (major_version >= 4)
3639 if (This->rootBlockChain)
3640 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3642 /* This file is being created, and it will start out with one block. */
3646 /* This field must be 0 in versions older than 4 */
3649 StorageUtl_WriteDWord(
3651 OFFSET_DIRSECTORCOUNT,
3654 StorageUtl_WriteDWord(
3656 OFFSET_BBDEPOTCOUNT,
3657 This->bigBlockDepotCount);
3659 StorageUtl_WriteDWord(
3661 OFFSET_ROOTSTARTBLOCK,
3662 This->rootStartBlock);
3664 StorageUtl_WriteDWord(
3666 OFFSET_SMALLBLOCKLIMIT,
3667 This->smallBlockLimit);
3669 StorageUtl_WriteDWord(
3671 OFFSET_SBDEPOTSTART,
3672 This->smallBlockDepotStart);
3674 StorageUtl_WriteDWord(
3676 OFFSET_SBDEPOTCOUNT,
3677 This->smallBlockDepotChain ?
3678 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3680 StorageUtl_WriteDWord(
3682 OFFSET_EXTBBDEPOTSTART,
3683 This->extBigBlockDepotStart);
3685 StorageUtl_WriteDWord(
3687 OFFSET_EXTBBDEPOTCOUNT,
3688 This->extBigBlockDepotCount);
3690 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3692 StorageUtl_WriteDWord(
3694 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3695 (This->bigBlockDepotStart[index]));
3699 * Write the big block back to the file.
3701 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3704 /******************************************************************************
3705 * StorageImpl_ReadRawDirEntry
3707 * This method will read the raw data from a directory entry in the file.
3709 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3711 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3713 ULARGE_INTEGER offset;
3717 offset.u.HighPart = 0;
3718 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3720 hr = BlockChainStream_ReadAt(
3721 This->rootBlockChain,
3727 if (bytesRead != RAW_DIRENTRY_SIZE)
3728 return STG_E_READFAULT;
3733 /******************************************************************************
3734 * StorageImpl_WriteRawDirEntry
3736 * This method will write the raw data from a directory entry in the file.
3738 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3740 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3742 ULARGE_INTEGER offset;
3746 offset.u.HighPart = 0;
3747 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3749 hr = BlockChainStream_WriteAt(
3750 This->rootBlockChain,
3759 /******************************************************************************
3762 * Update raw directory entry data from the fields in newData.
3764 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3766 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3768 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3771 buffer + OFFSET_PS_NAME,
3773 DIRENTRY_NAME_BUFFER_LEN );
3775 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3777 StorageUtl_WriteWord(
3779 OFFSET_PS_NAMELENGTH,
3780 newData->sizeOfNameString);
3782 StorageUtl_WriteDWord(
3784 OFFSET_PS_LEFTCHILD,
3785 newData->leftChild);
3787 StorageUtl_WriteDWord(
3789 OFFSET_PS_RIGHTCHILD,
3790 newData->rightChild);
3792 StorageUtl_WriteDWord(
3795 newData->dirRootEntry);
3797 StorageUtl_WriteGUID(
3802 StorageUtl_WriteDWord(
3805 newData->ctime.dwLowDateTime);
3807 StorageUtl_WriteDWord(
3809 OFFSET_PS_CTIMEHIGH,
3810 newData->ctime.dwHighDateTime);
3812 StorageUtl_WriteDWord(
3815 newData->mtime.dwLowDateTime);
3817 StorageUtl_WriteDWord(
3819 OFFSET_PS_MTIMEHIGH,
3820 newData->ctime.dwHighDateTime);
3822 StorageUtl_WriteDWord(
3824 OFFSET_PS_STARTBLOCK,
3825 newData->startingBlock);
3827 StorageUtl_WriteDWord(
3830 newData->size.u.LowPart);
3833 /******************************************************************************
3834 * Storage32Impl_ReadDirEntry
3836 * This method will read the specified directory entry.
3838 HRESULT StorageImpl_ReadDirEntry(
3843 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3846 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3848 if (SUCCEEDED(readRes))
3850 memset(buffer->name, 0, sizeof(buffer->name));
3853 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3854 DIRENTRY_NAME_BUFFER_LEN );
3855 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3857 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3859 StorageUtl_ReadWord(
3861 OFFSET_PS_NAMELENGTH,
3862 &buffer->sizeOfNameString);
3864 StorageUtl_ReadDWord(
3866 OFFSET_PS_LEFTCHILD,
3867 &buffer->leftChild);
3869 StorageUtl_ReadDWord(
3871 OFFSET_PS_RIGHTCHILD,
3872 &buffer->rightChild);
3874 StorageUtl_ReadDWord(
3877 &buffer->dirRootEntry);
3879 StorageUtl_ReadGUID(
3884 StorageUtl_ReadDWord(
3887 &buffer->ctime.dwLowDateTime);
3889 StorageUtl_ReadDWord(
3891 OFFSET_PS_CTIMEHIGH,
3892 &buffer->ctime.dwHighDateTime);
3894 StorageUtl_ReadDWord(
3897 &buffer->mtime.dwLowDateTime);
3899 StorageUtl_ReadDWord(
3901 OFFSET_PS_MTIMEHIGH,
3902 &buffer->mtime.dwHighDateTime);
3904 StorageUtl_ReadDWord(
3906 OFFSET_PS_STARTBLOCK,
3907 &buffer->startingBlock);
3909 StorageUtl_ReadDWord(
3912 &buffer->size.u.LowPart);
3914 buffer->size.u.HighPart = 0;
3920 /*********************************************************************
3921 * Write the specified directory entry to the file
3923 HRESULT StorageImpl_WriteDirEntry(
3926 const DirEntry* buffer)
3928 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3931 UpdateRawDirEntry(currentEntry, buffer);
3933 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3937 static BOOL StorageImpl_ReadBigBlock(
3942 ULARGE_INTEGER ulOffset;
3945 ulOffset.u.HighPart = 0;
3946 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3948 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3950 if (read && read < This->bigBlockSize)
3952 /* File ends during this block; fill the rest with 0's. */
3953 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3959 static BOOL StorageImpl_ReadDWordFromBigBlock(
3965 ULARGE_INTEGER ulOffset;
3969 ulOffset.u.HighPart = 0;
3970 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3971 ulOffset.u.LowPart += offset;
3973 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3974 *value = lendian32toh(tmp);
3975 return (read == sizeof(DWORD));
3978 static BOOL StorageImpl_WriteBigBlock(
3983 ULARGE_INTEGER ulOffset;
3986 ulOffset.u.HighPart = 0;
3987 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3989 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3990 return (wrote == This->bigBlockSize);
3993 static BOOL StorageImpl_WriteDWordToBigBlock(
3999 ULARGE_INTEGER ulOffset;
4002 ulOffset.u.HighPart = 0;
4003 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4004 ulOffset.u.LowPart += offset;
4006 value = htole32(value);
4007 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4008 return (wrote == sizeof(DWORD));
4011 /******************************************************************************
4012 * Storage32Impl_SmallBlocksToBigBlocks
4014 * This method will convert a small block chain to a big block chain.
4015 * The small block chain will be destroyed.
4017 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4019 SmallBlockChainStream** ppsbChain)
4021 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4022 ULARGE_INTEGER size, offset;
4023 ULONG cbRead, cbWritten;
4024 ULARGE_INTEGER cbTotalRead;
4025 DirRef streamEntryRef;
4026 HRESULT resWrite = S_OK;
4028 DirEntry streamEntry;
4030 BlockChainStream *bbTempChain = NULL;
4031 BlockChainStream *bigBlockChain = NULL;
4034 * Create a temporary big block chain that doesn't have
4035 * an associated directory entry. This temporary chain will be
4036 * used to copy data from small blocks to big blocks.
4038 bbTempChain = BlockChainStream_Construct(This,
4041 if(!bbTempChain) return NULL;
4043 * Grow the big block chain.
4045 size = SmallBlockChainStream_GetSize(*ppsbChain);
4046 BlockChainStream_SetSize(bbTempChain, size);
4049 * Copy the contents of the small block chain to the big block chain
4050 * by small block size increments.
4052 offset.u.LowPart = 0;
4053 offset.u.HighPart = 0;
4054 cbTotalRead.QuadPart = 0;
4056 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4059 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4061 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4064 if (FAILED(resRead))
4069 cbTotalRead.QuadPart += cbRead;
4071 resWrite = BlockChainStream_WriteAt(bbTempChain,
4077 if (FAILED(resWrite))
4080 offset.u.LowPart += cbRead;
4084 resRead = STG_E_READFAULT;
4087 } while (cbTotalRead.QuadPart < size.QuadPart);
4088 HeapFree(GetProcessHeap(),0,buffer);
4090 size.u.HighPart = 0;
4093 if (FAILED(resRead) || FAILED(resWrite))
4095 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4096 BlockChainStream_SetSize(bbTempChain, size);
4097 BlockChainStream_Destroy(bbTempChain);
4102 * Destroy the small block chain.
4104 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4105 SmallBlockChainStream_SetSize(*ppsbChain, size);
4106 SmallBlockChainStream_Destroy(*ppsbChain);
4110 * Change the directory entry. This chain is now a big block chain
4111 * and it doesn't reside in the small blocks chain anymore.
4113 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4115 streamEntry.startingBlock = bbHeadOfChain;
4117 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4120 * Destroy the temporary entryless big block chain.
4121 * Create a new big block chain associated with this entry.
4123 BlockChainStream_Destroy(bbTempChain);
4124 bigBlockChain = BlockChainStream_Construct(This,
4128 return bigBlockChain;
4131 /******************************************************************************
4132 * Storage32Impl_BigBlocksToSmallBlocks
4134 * This method will convert a big block chain to a small block chain.
4135 * The big block chain will be destroyed on success.
4137 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4139 BlockChainStream** ppbbChain,
4140 ULARGE_INTEGER newSize)
4142 ULARGE_INTEGER size, offset, cbTotalRead;
4143 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4144 DirRef streamEntryRef;
4145 HRESULT resWrite = S_OK, resRead = S_OK;
4146 DirEntry streamEntry;
4148 SmallBlockChainStream* sbTempChain;
4150 TRACE("%p %p\n", This, ppbbChain);
4152 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4158 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4159 size = BlockChainStream_GetSize(*ppbbChain);
4160 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4162 offset.u.HighPart = 0;
4163 offset.u.LowPart = 0;
4164 cbTotalRead.QuadPart = 0;
4165 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4166 while(cbTotalRead.QuadPart < size.QuadPart)
4168 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4169 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4177 cbTotalRead.QuadPart += cbRead;
4179 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4180 cbRead, buffer, &cbWritten);
4182 if(FAILED(resWrite))
4185 offset.u.LowPart += cbRead;
4189 resRead = STG_E_READFAULT;
4193 HeapFree(GetProcessHeap(), 0, buffer);
4195 size.u.HighPart = 0;
4198 if(FAILED(resRead) || FAILED(resWrite))
4200 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4201 SmallBlockChainStream_SetSize(sbTempChain, size);
4202 SmallBlockChainStream_Destroy(sbTempChain);
4206 /* destroy the original big block chain */
4207 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4208 BlockChainStream_SetSize(*ppbbChain, size);
4209 BlockChainStream_Destroy(*ppbbChain);
4212 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4213 streamEntry.startingBlock = sbHeadOfChain;
4214 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4216 SmallBlockChainStream_Destroy(sbTempChain);
4217 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4220 static HRESULT StorageBaseImpl_CopyStream(
4221 StorageBaseImpl *dst, DirRef dst_entry,
4222 StorageBaseImpl *src, DirRef src_entry)
4227 ULARGE_INTEGER bytes_copied;
4228 ULONG bytestocopy, bytesread, byteswritten;
4230 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4234 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4236 bytes_copied.QuadPart = 0;
4237 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4239 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4241 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4243 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4246 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4247 data, &byteswritten);
4250 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4251 bytes_copied.QuadPart += byteswritten;
4259 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4261 DirRef result=This->firstFreeEntry;
4263 while (result < This->entries_size && This->entries[result].inuse)
4266 if (result == This->entries_size)
4268 ULONG new_size = This->entries_size * 2;
4269 TransactedDirEntry *new_entries;
4271 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4272 if (!new_entries) return DIRENTRY_NULL;
4274 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4275 HeapFree(GetProcessHeap(), 0, This->entries);
4277 This->entries = new_entries;
4278 This->entries_size = new_size;
4281 This->entries[result].inuse = 1;
4283 This->firstFreeEntry = result+1;
4288 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4289 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4291 DirRef stubEntryRef;
4292 TransactedDirEntry *entry;
4294 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4296 if (stubEntryRef != DIRENTRY_NULL)
4298 entry = &This->entries[stubEntryRef];
4300 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4305 return stubEntryRef;
4308 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4309 TransactedSnapshotImpl *This, DirRef entry)
4314 if (!This->entries[entry].read)
4316 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4317 This->entries[entry].transactedParentEntry,
4320 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4322 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4324 if (data.leftChild == DIRENTRY_NULL)
4328 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4330 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4332 if (data.rightChild == DIRENTRY_NULL)
4336 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4338 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4340 if (data.dirRootEntry == DIRENTRY_NULL)
4346 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4347 This->entries[entry].read = 1;
4354 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4355 TransactedSnapshotImpl *This, DirRef entry)
4359 if (!This->entries[entry].stream_dirty)
4361 DirEntry new_entrydata;
4363 memset(&new_entrydata, 0, sizeof(DirEntry));
4364 new_entrydata.name[0] = 'S';
4365 new_entrydata.sizeOfNameString = 1;
4366 new_entrydata.stgType = STGTY_STREAM;
4367 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4368 new_entrydata.leftChild = DIRENTRY_NULL;
4369 new_entrydata.rightChild = DIRENTRY_NULL;
4370 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4372 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4373 &This->entries[entry].stream_entry);
4375 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4377 hr = StorageBaseImpl_CopyStream(
4378 This->scratch, This->entries[entry].stream_entry,
4379 This->transactedParent, This->entries[entry].transactedParentEntry);
4382 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4386 This->entries[entry].stream_dirty = 1;
4388 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4390 /* Since this entry is modified, and we aren't using its stream data, we
4391 * no longer care about the original entry. */
4393 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4395 if (delete_ref != DIRENTRY_NULL)
4396 This->entries[delete_ref].deleted = 1;
4398 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4405 /* Find the first entry in a depth-first traversal. */
4406 static DirRef TransactedSnapshotImpl_FindFirstChild(
4407 TransactedSnapshotImpl* This, DirRef parent)
4409 DirRef cursor, prev;
4410 TransactedDirEntry *entry;
4413 entry = &This->entries[cursor];
4416 if (entry->data.leftChild != DIRENTRY_NULL)
4419 cursor = entry->data.leftChild;
4420 entry = &This->entries[cursor];
4421 entry->parent = prev;
4423 else if (entry->data.rightChild != DIRENTRY_NULL)
4426 cursor = entry->data.rightChild;
4427 entry = &This->entries[cursor];
4428 entry->parent = prev;
4430 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4433 cursor = entry->data.dirRootEntry;
4434 entry = &This->entries[cursor];
4435 entry->parent = prev;
4444 /* Find the next entry in a depth-first traversal. */
4445 static DirRef TransactedSnapshotImpl_FindNextChild(
4446 TransactedSnapshotImpl* This, DirRef current)
4449 TransactedDirEntry *parent_entry;
4451 parent = This->entries[current].parent;
4452 parent_entry = &This->entries[parent];
4454 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4456 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4458 This->entries[parent_entry->data.rightChild].parent = parent;
4459 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4462 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4464 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4465 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4472 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4473 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4474 TransactedSnapshotImpl* This, DirRef entry)
4476 return entry != DIRENTRY_NULL &&
4477 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4480 /* Destroy the entries created by CopyTree. */
4481 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4482 TransactedSnapshotImpl* This, DirRef stop)
4485 TransactedDirEntry *entry;
4486 ULARGE_INTEGER zero;
4490 if (!This->entries[This->base.storageDirEntry].read)
4493 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4495 if (cursor == DIRENTRY_NULL)
4498 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4500 while (cursor != DIRENTRY_NULL && cursor != stop)
4502 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4504 entry = &This->entries[cursor];
4506 if (entry->stream_dirty)
4507 StorageBaseImpl_StreamSetSize(This->transactedParent,
4508 entry->newTransactedParentEntry, zero);
4510 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4511 entry->newTransactedParentEntry);
4513 entry->newTransactedParentEntry = entry->transactedParentEntry;
4516 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4520 /* Make a copy of our edited tree that we can use in the parent. */
4521 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4524 TransactedDirEntry *entry;
4527 cursor = This->base.storageDirEntry;
4528 entry = &This->entries[cursor];
4529 entry->parent = DIRENTRY_NULL;
4530 entry->newTransactedParentEntry = entry->transactedParentEntry;
4532 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4535 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4537 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4538 entry = &This->entries[cursor];
4540 while (cursor != DIRENTRY_NULL)
4542 /* Make a copy of this entry in the transacted parent. */
4544 (!entry->dirty && !entry->stream_dirty &&
4545 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4546 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4547 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4548 entry->newTransactedParentEntry = entry->transactedParentEntry;
4553 memcpy(&newData, &entry->data, sizeof(DirEntry));
4555 newData.size.QuadPart = 0;
4556 newData.startingBlock = BLOCK_END_OF_CHAIN;
4558 if (newData.leftChild != DIRENTRY_NULL)
4559 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4561 if (newData.rightChild != DIRENTRY_NULL)
4562 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4564 if (newData.dirRootEntry != DIRENTRY_NULL)
4565 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4567 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4568 &entry->newTransactedParentEntry);
4571 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4575 if (entry->stream_dirty)
4577 hr = StorageBaseImpl_CopyStream(
4578 This->transactedParent, entry->newTransactedParentEntry,
4579 This->scratch, entry->stream_entry);
4581 else if (entry->data.size.QuadPart)
4583 hr = StorageBaseImpl_StreamLink(
4584 This->transactedParent, entry->newTransactedParentEntry,
4585 entry->transactedParentEntry);
4590 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4591 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4596 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4597 entry = &This->entries[cursor];
4603 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4605 DWORD grfCommitFlags) /* [in] */
4607 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4608 TransactedDirEntry *root_entry;
4609 DirRef i, dir_root_ref;
4611 ULARGE_INTEGER zero;
4616 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4618 /* Cannot commit a read-only transacted storage */
4619 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4620 return STG_E_ACCESSDENIED;
4622 /* To prevent data loss, we create the new structure in the file before we
4623 * delete the old one, so that in case of errors the old data is intact. We
4624 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4625 * needed in the rare situation where we have just enough free disk space to
4626 * overwrite the existing data. */
4628 root_entry = &This->entries[This->base.storageDirEntry];
4630 if (!root_entry->read)
4633 hr = TransactedSnapshotImpl_CopyTree(This);
4634 if (FAILED(hr)) return hr;
4636 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4637 dir_root_ref = DIRENTRY_NULL;
4639 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4641 hr = StorageBaseImpl_Flush(This->transactedParent);
4643 /* Update the storage to use the new data in one step. */
4645 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4646 root_entry->transactedParentEntry, &data);
4650 data.dirRootEntry = dir_root_ref;
4651 data.clsid = root_entry->data.clsid;
4652 data.ctime = root_entry->data.ctime;
4653 data.mtime = root_entry->data.mtime;
4655 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4656 root_entry->transactedParentEntry, &data);
4659 /* Try to flush after updating the root storage, but if the flush fails, keep
4660 * going, on the theory that it'll either succeed later or the subsequent
4661 * writes will fail. */
4662 StorageBaseImpl_Flush(This->transactedParent);
4666 /* Destroy the old now-orphaned data. */
4667 for (i=0; i<This->entries_size; i++)
4669 TransactedDirEntry *entry = &This->entries[i];
4674 StorageBaseImpl_StreamSetSize(This->transactedParent,
4675 entry->transactedParentEntry, zero);
4676 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4677 entry->transactedParentEntry);
4678 memset(entry, 0, sizeof(TransactedDirEntry));
4679 This->firstFreeEntry = min(i, This->firstFreeEntry);
4681 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4683 if (entry->transactedParentEntry != DIRENTRY_NULL)
4684 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4685 entry->transactedParentEntry);
4686 if (entry->stream_dirty)
4688 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4689 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4690 entry->stream_dirty = 0;
4693 entry->transactedParentEntry = entry->newTransactedParentEntry;
4700 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4704 hr = StorageBaseImpl_Flush(This->transactedParent);
4709 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4712 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4713 ULARGE_INTEGER zero;
4716 TRACE("(%p)\n", iface);
4718 /* Destroy the open objects. */
4719 StorageBaseImpl_DeleteAll(&This->base);
4721 /* Clear out the scratch file. */
4723 for (i=0; i<This->entries_size; i++)
4725 if (This->entries[i].stream_dirty)
4727 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4730 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4734 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4736 This->firstFreeEntry = 0;
4737 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4742 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4744 if (!This->reverted)
4746 TRACE("Storage invalidated (stg=%p)\n", This);
4750 StorageBaseImpl_DeleteAll(This);
4754 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4756 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4758 TransactedSnapshotImpl_Revert((IStorage*)iface);
4760 IStorage_Release((IStorage*)This->transactedParent);
4762 IStorage_Release((IStorage*)This->scratch);
4764 HeapFree(GetProcessHeap(), 0, This->entries);
4766 HeapFree(GetProcessHeap(), 0, This);
4769 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4771 /* We only need to flush when committing. */
4775 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4777 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4779 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4782 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4783 const DirEntry *newData, DirRef *index)
4785 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4787 TransactedDirEntry *new_entry;
4789 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4790 if (new_ref == DIRENTRY_NULL)
4791 return E_OUTOFMEMORY;
4793 new_entry = &This->entries[new_ref];
4795 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4796 new_entry->read = 1;
4797 new_entry->dirty = 1;
4798 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4802 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4807 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4808 DirRef index, const DirEntry *data)
4810 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4813 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4815 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4816 if (FAILED(hr)) return hr;
4818 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4820 if (index != This->base.storageDirEntry)
4822 This->entries[index].dirty = 1;
4824 if (data->size.QuadPart == 0 &&
4825 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4827 /* Since this entry is modified, and we aren't using its stream data, we
4828 * no longer care about the original entry. */
4830 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4832 if (delete_ref != DIRENTRY_NULL)
4833 This->entries[delete_ref].deleted = 1;
4835 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4842 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4843 DirRef index, DirEntry *data)
4845 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4848 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4849 if (FAILED(hr)) return hr;
4851 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4853 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4858 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4861 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4863 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4864 This->entries[index].data.size.QuadPart != 0)
4866 /* If we deleted this entry while it has stream data. We must have left the
4867 * data because some other entry is using it, and we need to leave the
4868 * original entry alone. */
4869 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4870 This->firstFreeEntry = min(index, This->firstFreeEntry);
4874 This->entries[index].deleted = 1;
4880 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4881 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4883 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4885 if (This->entries[index].stream_dirty)
4887 return StorageBaseImpl_StreamReadAt(This->scratch,
4888 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4890 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4892 /* This stream doesn't live in the parent, and we haven't allocated storage
4899 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4900 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4904 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4905 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4907 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4910 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4911 if (FAILED(hr)) return hr;
4913 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4914 if (FAILED(hr)) return hr;
4916 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4917 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4919 if (SUCCEEDED(hr) && size != 0)
4920 This->entries[index].data.size.QuadPart = max(
4921 This->entries[index].data.size.QuadPart,
4922 offset.QuadPart + size);
4927 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4928 DirRef index, ULARGE_INTEGER newsize)
4930 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4933 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4934 if (FAILED(hr)) return hr;
4936 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4939 if (newsize.QuadPart == 0)
4941 /* Destroy any parent references or entries in the scratch file. */
4942 if (This->entries[index].stream_dirty)
4944 ULARGE_INTEGER zero;
4946 StorageBaseImpl_StreamSetSize(This->scratch,
4947 This->entries[index].stream_entry, zero);
4948 StorageBaseImpl_DestroyDirEntry(This->scratch,
4949 This->entries[index].stream_entry);
4950 This->entries[index].stream_dirty = 0;
4952 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4955 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4957 if (delete_ref != DIRENTRY_NULL)
4958 This->entries[delete_ref].deleted = 1;
4960 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4965 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4966 if (FAILED(hr)) return hr;
4968 hr = StorageBaseImpl_StreamSetSize(This->scratch,
4969 This->entries[index].stream_entry, newsize);
4973 This->entries[index].data.size = newsize;
4978 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
4979 DirRef dst, DirRef src)
4981 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4983 TransactedDirEntry *dst_entry, *src_entry;
4985 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
4986 if (FAILED(hr)) return hr;
4988 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
4989 if (FAILED(hr)) return hr;
4991 dst_entry = &This->entries[dst];
4992 src_entry = &This->entries[src];
4994 dst_entry->stream_dirty = src_entry->stream_dirty;
4995 dst_entry->stream_entry = src_entry->stream_entry;
4996 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
4997 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
4998 dst_entry->data.size = src_entry->data.size;
5003 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5005 StorageBaseImpl_QueryInterface,
5006 StorageBaseImpl_AddRef,
5007 StorageBaseImpl_Release,
5008 StorageBaseImpl_CreateStream,
5009 StorageBaseImpl_OpenStream,
5010 StorageBaseImpl_CreateStorage,
5011 StorageBaseImpl_OpenStorage,
5012 StorageBaseImpl_CopyTo,
5013 StorageBaseImpl_MoveElementTo,
5014 TransactedSnapshotImpl_Commit,
5015 TransactedSnapshotImpl_Revert,
5016 StorageBaseImpl_EnumElements,
5017 StorageBaseImpl_DestroyElement,
5018 StorageBaseImpl_RenameElement,
5019 StorageBaseImpl_SetElementTimes,
5020 StorageBaseImpl_SetClass,
5021 StorageBaseImpl_SetStateBits,
5022 StorageBaseImpl_Stat
5025 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5027 TransactedSnapshotImpl_Destroy,
5028 TransactedSnapshotImpl_Invalidate,
5029 TransactedSnapshotImpl_Flush,
5030 TransactedSnapshotImpl_GetFilename,
5031 TransactedSnapshotImpl_CreateDirEntry,
5032 TransactedSnapshotImpl_WriteDirEntry,
5033 TransactedSnapshotImpl_ReadDirEntry,
5034 TransactedSnapshotImpl_DestroyDirEntry,
5035 TransactedSnapshotImpl_StreamReadAt,
5036 TransactedSnapshotImpl_StreamWriteAt,
5037 TransactedSnapshotImpl_StreamSetSize,
5038 TransactedSnapshotImpl_StreamLink
5041 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5042 TransactedSnapshotImpl** result)
5046 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5049 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5051 /* This is OK because the property set storage functions use the IStorage functions. */
5052 (*result)->base.pssVtbl = parentStorage->pssVtbl;
5054 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5056 list_init(&(*result)->base.strmHead);
5058 list_init(&(*result)->base.storageHead);
5060 (*result)->base.ref = 1;
5062 (*result)->base.openFlags = parentStorage->openFlags;
5064 /* Create a new temporary storage to act as the scratch file. */
5065 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
5066 0, (IStorage**)&(*result)->scratch);
5070 ULONG num_entries = 20;
5072 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5074 (*result)->entries_size = num_entries;
5076 (*result)->firstFreeEntry = 0;
5078 if ((*result)->entries)
5080 /* parentStorage already has 1 reference, which we take over here. */
5081 (*result)->transactedParent = parentStorage;
5083 parentStorage->transactedChild = (StorageBaseImpl*)*result;
5085 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5089 IStorage_Release((IStorage*)(*result)->scratch);
5095 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
5100 return E_OUTOFMEMORY;
5103 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5104 StorageBaseImpl** result)
5108 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5110 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5113 return TransactedSnapshotImpl_Construct(parentStorage,
5114 (TransactedSnapshotImpl**)result);
5117 static HRESULT Storage_Construct(
5125 StorageBaseImpl** result)
5127 StorageImpl *newStorage;
5128 StorageBaseImpl *newTransactedStorage;
5131 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5132 if (FAILED(hr)) goto end;
5134 if (openFlags & STGM_TRANSACTED)
5136 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5138 IStorage_Release((IStorage*)newStorage);
5140 *result = newTransactedStorage;
5143 *result = &newStorage->base;
5149 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5151 StorageInternalImpl* This = (StorageInternalImpl*) base;
5153 if (!This->base.reverted)
5155 TRACE("Storage invalidated (stg=%p)\n", This);
5157 This->base.reverted = 1;
5159 This->parentStorage = NULL;
5161 StorageBaseImpl_DeleteAll(&This->base);
5163 list_remove(&This->ParentListEntry);
5167 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5169 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5171 StorageInternalImpl_Invalidate(&This->base);
5173 HeapFree(GetProcessHeap(), 0, This);
5176 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5178 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5180 return StorageBaseImpl_Flush(This->parentStorage);
5183 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5185 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5187 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5190 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5191 const DirEntry *newData, DirRef *index)
5193 StorageInternalImpl* This = (StorageInternalImpl*) base;
5195 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5199 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5200 DirRef index, const DirEntry *data)
5202 StorageInternalImpl* This = (StorageInternalImpl*) base;
5204 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5208 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5209 DirRef index, DirEntry *data)
5211 StorageInternalImpl* This = (StorageInternalImpl*) base;
5213 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5217 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5220 StorageInternalImpl* This = (StorageInternalImpl*) base;
5222 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5226 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5227 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5229 StorageInternalImpl* This = (StorageInternalImpl*) base;
5231 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5232 index, offset, size, buffer, bytesRead);
5235 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5236 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5238 StorageInternalImpl* This = (StorageInternalImpl*) base;
5240 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5241 index, offset, size, buffer, bytesWritten);
5244 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5245 DirRef index, ULARGE_INTEGER newsize)
5247 StorageInternalImpl* This = (StorageInternalImpl*) base;
5249 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5253 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5254 DirRef dst, DirRef src)
5256 StorageInternalImpl* This = (StorageInternalImpl*) base;
5258 return StorageBaseImpl_StreamLink(This->parentStorage,
5262 /******************************************************************************
5264 ** Storage32InternalImpl_Commit
5267 static HRESULT WINAPI StorageInternalImpl_Commit(
5269 DWORD grfCommitFlags) /* [in] */
5271 StorageBaseImpl* base = (StorageBaseImpl*) iface;
5272 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5273 return StorageBaseImpl_Flush(base);
5276 /******************************************************************************
5278 ** Storage32InternalImpl_Revert
5281 static HRESULT WINAPI StorageInternalImpl_Revert(
5284 FIXME("(%p): stub\n", iface);
5288 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5290 IStorage_Release((IStorage*)This->parentStorage);
5291 HeapFree(GetProcessHeap(), 0, This);
5294 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5295 IEnumSTATSTG* iface,
5299 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5302 return E_INVALIDARG;
5306 if (IsEqualGUID(&IID_IUnknown, riid) ||
5307 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5310 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
5314 return E_NOINTERFACE;
5317 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5318 IEnumSTATSTG* iface)
5320 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5321 return InterlockedIncrement(&This->ref);
5324 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5325 IEnumSTATSTG* iface)
5327 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5331 newRef = InterlockedDecrement(&This->ref);
5335 IEnumSTATSTGImpl_Destroy(This);
5341 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5342 IEnumSTATSTGImpl* This,
5345 DirRef result = DIRENTRY_NULL;
5349 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5351 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5352 This->parentStorage->storageDirEntry, &entry);
5353 searchNode = entry.dirRootEntry;
5355 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5357 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5361 LONG diff = entryNameCmp( entry.name, This->name);
5365 searchNode = entry.rightChild;
5369 result = searchNode;
5370 memcpy(result_name, entry.name, sizeof(result_name));
5371 searchNode = entry.leftChild;
5379 if (result != DIRENTRY_NULL)
5380 memcpy(This->name, result_name, sizeof(result_name));
5386 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5387 IEnumSTATSTG* iface,
5390 ULONG* pceltFetched)
5392 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5394 DirEntry currentEntry;
5395 STATSTG* currentReturnStruct = rgelt;
5396 ULONG objectFetched = 0;
5397 DirRef currentSearchNode;
5400 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5401 return E_INVALIDARG;
5403 if (This->parentStorage->reverted)
5404 return STG_E_REVERTED;
5407 * To avoid the special case, get another pointer to a ULONG value if
5408 * the caller didn't supply one.
5410 if (pceltFetched==0)
5411 pceltFetched = &objectFetched;
5414 * Start the iteration, we will iterate until we hit the end of the
5415 * linked list or until we hit the number of items to iterate through
5419 while ( *pceltFetched < celt )
5421 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
5423 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5427 * Read the entry from the storage.
5429 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5434 * Copy the information to the return buffer.
5436 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5437 currentReturnStruct,
5442 * Step to the next item in the iteration
5445 currentReturnStruct++;
5448 if (SUCCEEDED(hr) && *pceltFetched != celt)
5455 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5456 IEnumSTATSTG* iface,
5459 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5461 ULONG objectFetched = 0;
5462 DirRef currentSearchNode;
5465 if (This->parentStorage->reverted)
5466 return STG_E_REVERTED;
5468 while ( (objectFetched < celt) )
5470 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
5472 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5478 if (SUCCEEDED(hr) && objectFetched != celt)
5484 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5485 IEnumSTATSTG* iface)
5487 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5489 if (This->parentStorage->reverted)
5490 return STG_E_REVERTED;
5497 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5498 IEnumSTATSTG* iface,
5499 IEnumSTATSTG** ppenum)
5501 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5503 IEnumSTATSTGImpl* newClone;
5505 if (This->parentStorage->reverted)
5506 return STG_E_REVERTED;
5509 * Perform a sanity check on the parameters.
5512 return E_INVALIDARG;
5514 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5515 This->storageDirEntry);
5519 * The new clone enumeration must point to the same current node as
5522 memcpy(newClone->name, This->name, sizeof(newClone->name));
5524 *ppenum = &newClone->IEnumSTATSTG_iface;
5527 * Don't forget to nail down a reference to the clone before
5530 IEnumSTATSTGImpl_AddRef(*ppenum);
5536 * Virtual function table for the IEnumSTATSTGImpl class.
5538 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5540 IEnumSTATSTGImpl_QueryInterface,
5541 IEnumSTATSTGImpl_AddRef,
5542 IEnumSTATSTGImpl_Release,
5543 IEnumSTATSTGImpl_Next,
5544 IEnumSTATSTGImpl_Skip,
5545 IEnumSTATSTGImpl_Reset,
5546 IEnumSTATSTGImpl_Clone
5549 /******************************************************************************
5550 ** IEnumSTATSTGImpl implementation
5553 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5554 StorageBaseImpl* parentStorage,
5555 DirRef storageDirEntry)
5557 IEnumSTATSTGImpl* newEnumeration;
5559 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5561 if (newEnumeration!=0)
5564 * Set-up the virtual function table and reference count.
5566 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5567 newEnumeration->ref = 0;
5570 * We want to nail-down the reference to the storage in case the
5571 * enumeration out-lives the storage in the client application.
5573 newEnumeration->parentStorage = parentStorage;
5574 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
5576 newEnumeration->storageDirEntry = storageDirEntry;
5579 * Make sure the current node of the iterator is the first one.
5581 IEnumSTATSTGImpl_Reset(&newEnumeration->IEnumSTATSTG_iface);
5584 return newEnumeration;
5588 * Virtual function table for the Storage32InternalImpl class.
5590 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5592 StorageBaseImpl_QueryInterface,
5593 StorageBaseImpl_AddRef,
5594 StorageBaseImpl_Release,
5595 StorageBaseImpl_CreateStream,
5596 StorageBaseImpl_OpenStream,
5597 StorageBaseImpl_CreateStorage,
5598 StorageBaseImpl_OpenStorage,
5599 StorageBaseImpl_CopyTo,
5600 StorageBaseImpl_MoveElementTo,
5601 StorageInternalImpl_Commit,
5602 StorageInternalImpl_Revert,
5603 StorageBaseImpl_EnumElements,
5604 StorageBaseImpl_DestroyElement,
5605 StorageBaseImpl_RenameElement,
5606 StorageBaseImpl_SetElementTimes,
5607 StorageBaseImpl_SetClass,
5608 StorageBaseImpl_SetStateBits,
5609 StorageBaseImpl_Stat
5612 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5614 StorageInternalImpl_Destroy,
5615 StorageInternalImpl_Invalidate,
5616 StorageInternalImpl_Flush,
5617 StorageInternalImpl_GetFilename,
5618 StorageInternalImpl_CreateDirEntry,
5619 StorageInternalImpl_WriteDirEntry,
5620 StorageInternalImpl_ReadDirEntry,
5621 StorageInternalImpl_DestroyDirEntry,
5622 StorageInternalImpl_StreamReadAt,
5623 StorageInternalImpl_StreamWriteAt,
5624 StorageInternalImpl_StreamSetSize,
5625 StorageInternalImpl_StreamLink
5628 /******************************************************************************
5629 ** Storage32InternalImpl implementation
5632 static StorageInternalImpl* StorageInternalImpl_Construct(
5633 StorageBaseImpl* parentStorage,
5635 DirRef storageDirEntry)
5637 StorageInternalImpl* newStorage;
5639 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5643 list_init(&newStorage->base.strmHead);
5645 list_init(&newStorage->base.storageHead);
5648 * Initialize the virtual function table.
5650 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
5651 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
5652 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5653 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5655 newStorage->base.reverted = 0;
5657 newStorage->base.ref = 1;
5659 newStorage->parentStorage = parentStorage;
5662 * Keep a reference to the directory entry of this storage
5664 newStorage->base.storageDirEntry = storageDirEntry;
5666 newStorage->base.create = 0;
5674 /******************************************************************************
5675 ** StorageUtl implementation
5678 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5682 memcpy(&tmp, buffer+offset, sizeof(WORD));
5683 *value = lendian16toh(tmp);
5686 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5688 value = htole16(value);
5689 memcpy(buffer+offset, &value, sizeof(WORD));
5692 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5696 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5697 *value = lendian32toh(tmp);
5700 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5702 value = htole32(value);
5703 memcpy(buffer+offset, &value, sizeof(DWORD));
5706 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5707 ULARGE_INTEGER* value)
5709 #ifdef WORDS_BIGENDIAN
5712 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5713 value->u.LowPart = htole32(tmp.u.HighPart);
5714 value->u.HighPart = htole32(tmp.u.LowPart);
5716 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5720 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5721 const ULARGE_INTEGER *value)
5723 #ifdef WORDS_BIGENDIAN
5726 tmp.u.LowPart = htole32(value->u.HighPart);
5727 tmp.u.HighPart = htole32(value->u.LowPart);
5728 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5730 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5734 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5736 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5737 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5738 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5740 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5743 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5745 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5746 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5747 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5749 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5752 void StorageUtl_CopyDirEntryToSTATSTG(
5753 StorageBaseImpl* storage,
5754 STATSTG* destination,
5755 const DirEntry* source,
5759 * The copy of the string occurs only when the flag is not set
5761 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5763 /* Use the filename for the root storage. */
5764 destination->pwcsName = 0;
5765 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5767 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5768 (source->name[0] == 0) )
5770 destination->pwcsName = 0;
5774 destination->pwcsName =
5775 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5777 strcpyW(destination->pwcsName, source->name);
5780 switch (source->stgType)
5784 destination->type = STGTY_STORAGE;
5787 destination->type = STGTY_STREAM;
5790 destination->type = STGTY_STREAM;
5794 destination->cbSize = source->size;
5796 currentReturnStruct->mtime = {0}; TODO
5797 currentReturnStruct->ctime = {0};
5798 currentReturnStruct->atime = {0};
5800 destination->grfMode = 0;
5801 destination->grfLocksSupported = 0;
5802 destination->clsid = source->clsid;
5803 destination->grfStateBits = 0;
5804 destination->reserved = 0;
5807 /******************************************************************************
5808 ** BlockChainStream implementation
5811 /* Read and save the index of all blocks in this stream. */
5812 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5814 ULONG next_sector, next_offset;
5816 struct BlockChainRun *last_run;
5818 if (This->indexCacheLen == 0)
5822 next_sector = BlockChainStream_GetHeadOfChain(This);
5826 last_run = &This->indexCache[This->indexCacheLen-1];
5827 next_offset = last_run->lastOffset+1;
5828 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5829 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5831 if (FAILED(hr)) return hr;
5834 while (next_sector != BLOCK_END_OF_CHAIN)
5836 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5838 /* Add the current block to the cache. */
5839 if (This->indexCacheSize == 0)
5841 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5842 if (!This->indexCache) return E_OUTOFMEMORY;
5843 This->indexCacheSize = 16;
5845 else if (This->indexCacheSize == This->indexCacheLen)
5847 struct BlockChainRun *new_cache;
5850 new_size = This->indexCacheSize * 2;
5851 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5852 if (!new_cache) return E_OUTOFMEMORY;
5853 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5855 HeapFree(GetProcessHeap(), 0, This->indexCache);
5856 This->indexCache = new_cache;
5857 This->indexCacheSize = new_size;
5860 This->indexCacheLen++;
5861 last_run = &This->indexCache[This->indexCacheLen-1];
5862 last_run->firstSector = next_sector;
5863 last_run->firstOffset = next_offset;
5866 last_run->lastOffset = next_offset;
5868 /* Find the next block. */
5870 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5871 if (FAILED(hr)) return hr;
5874 if (This->indexCacheLen)
5876 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5877 This->numBlocks = last_run->lastOffset+1;
5881 This->tailIndex = BLOCK_END_OF_CHAIN;
5882 This->numBlocks = 0;
5888 /* Locate the nth block in this stream. */
5889 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5891 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5892 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5894 if (offset >= This->numBlocks)
5895 return BLOCK_END_OF_CHAIN;
5897 while (min_run < max_run)
5899 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5900 if (offset < This->indexCache[run_to_check].firstOffset)
5902 max_offset = This->indexCache[run_to_check].firstOffset-1;
5903 max_run = run_to_check-1;
5905 else if (offset > This->indexCache[run_to_check].lastOffset)
5907 min_offset = This->indexCache[run_to_check].lastOffset+1;
5908 min_run = run_to_check+1;
5911 /* Block is in this run. */
5912 min_run = max_run = run_to_check;
5915 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5918 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
5919 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
5921 BlockChainBlock *result=NULL;
5925 if (This->cachedBlocks[i].index == index)
5927 *sector = This->cachedBlocks[i].sector;
5928 *block = &This->cachedBlocks[i];
5932 *sector = BlockChainStream_GetSectorOfOffset(This, index);
5933 if (*sector == BLOCK_END_OF_CHAIN)
5934 return STG_E_DOCFILECORRUPT;
5938 if (This->cachedBlocks[0].index == 0xffffffff)
5939 result = &This->cachedBlocks[0];
5940 else if (This->cachedBlocks[1].index == 0xffffffff)
5941 result = &This->cachedBlocks[1];
5944 result = &This->cachedBlocks[This->blockToEvict++];
5945 if (This->blockToEvict == 2)
5946 This->blockToEvict = 0;
5951 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
5952 return STG_E_WRITEFAULT;
5957 result->index = index;
5958 result->sector = *sector;
5965 BlockChainStream* BlockChainStream_Construct(
5966 StorageImpl* parentStorage,
5967 ULONG* headOfStreamPlaceHolder,
5970 BlockChainStream* newStream;
5972 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5974 newStream->parentStorage = parentStorage;
5975 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5976 newStream->ownerDirEntry = dirEntry;
5977 newStream->indexCache = NULL;
5978 newStream->indexCacheLen = 0;
5979 newStream->indexCacheSize = 0;
5980 newStream->cachedBlocks[0].index = 0xffffffff;
5981 newStream->cachedBlocks[0].dirty = 0;
5982 newStream->cachedBlocks[1].index = 0xffffffff;
5983 newStream->cachedBlocks[1].dirty = 0;
5984 newStream->blockToEvict = 0;
5986 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
5988 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
5989 HeapFree(GetProcessHeap(), 0, newStream);
5996 HRESULT BlockChainStream_Flush(BlockChainStream* This)
5999 if (!This) return S_OK;
6002 if (This->cachedBlocks[i].dirty)
6004 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6005 This->cachedBlocks[i].dirty = 0;
6007 return STG_E_WRITEFAULT;
6013 void BlockChainStream_Destroy(BlockChainStream* This)
6017 BlockChainStream_Flush(This);
6018 HeapFree(GetProcessHeap(), 0, This->indexCache);
6020 HeapFree(GetProcessHeap(), 0, This);
6023 /******************************************************************************
6024 * BlockChainStream_GetHeadOfChain
6026 * Returns the head of this stream chain.
6027 * Some special chains don't have directory entries, their heads are kept in
6028 * This->headOfStreamPlaceHolder.
6031 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6033 DirEntry chainEntry;
6036 if (This->headOfStreamPlaceHolder != 0)
6037 return *(This->headOfStreamPlaceHolder);
6039 if (This->ownerDirEntry != DIRENTRY_NULL)
6041 hr = StorageImpl_ReadDirEntry(
6042 This->parentStorage,
6043 This->ownerDirEntry,
6048 return chainEntry.startingBlock;
6052 return BLOCK_END_OF_CHAIN;
6055 /******************************************************************************
6056 * BlockChainStream_GetCount
6058 * Returns the number of blocks that comprises this chain.
6059 * This is not the size of the stream as the last block may not be full!
6061 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6063 return This->numBlocks;
6066 /******************************************************************************
6067 * BlockChainStream_ReadAt
6069 * Reads a specified number of bytes from this chain at the specified offset.
6070 * bytesRead may be NULL.
6071 * Failure will be returned if the specified number of bytes has not been read.
6073 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6074 ULARGE_INTEGER offset,
6079 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6080 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6081 ULONG bytesToReadInBuffer;
6084 ULARGE_INTEGER stream_size;
6086 BlockChainBlock *cachedBlock;
6088 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6091 * Find the first block in the stream that contains part of the buffer.
6093 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6097 stream_size = BlockChainStream_GetSize(This);
6098 if (stream_size.QuadPart > offset.QuadPart)
6099 size = min(stream_size.QuadPart - offset.QuadPart, size);
6104 * Start reading the buffer.
6106 bufferWalker = buffer;
6110 ULARGE_INTEGER ulOffset;
6114 * Calculate how many bytes we can copy from this big block.
6116 bytesToReadInBuffer =
6117 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6119 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6126 /* Not in cache, and we're going to read past the end of the block. */
6127 ulOffset.u.HighPart = 0;
6128 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6131 StorageImpl_ReadAt(This->parentStorage,
6134 bytesToReadInBuffer,
6139 if (!cachedBlock->read)
6141 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6142 return STG_E_READFAULT;
6144 cachedBlock->read = 1;
6147 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6148 bytesReadAt = bytesToReadInBuffer;
6151 blockNoInSequence++;
6152 bufferWalker += bytesReadAt;
6153 size -= bytesReadAt;
6154 *bytesRead += bytesReadAt;
6155 offsetInBlock = 0; /* There is no offset on the next block */
6157 if (bytesToReadInBuffer != bytesReadAt)
6164 /******************************************************************************
6165 * BlockChainStream_WriteAt
6167 * Writes the specified number of bytes to this chain at the specified offset.
6168 * Will fail if not all specified number of bytes have been written.
6170 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6171 ULARGE_INTEGER offset,
6174 ULONG* bytesWritten)
6176 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6177 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6180 const BYTE* bufferWalker;
6182 BlockChainBlock *cachedBlock;
6185 bufferWalker = buffer;
6189 ULARGE_INTEGER ulOffset;
6190 DWORD bytesWrittenAt;
6193 * Calculate how many bytes we can copy to this big block.
6196 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6198 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6200 /* BlockChainStream_SetSize should have already been called to ensure we have
6201 * enough blocks in the chain to write into */
6204 ERR("not enough blocks in chain to write data\n");
6210 /* Not in cache, and we're going to write past the end of the block. */
6211 ulOffset.u.HighPart = 0;
6212 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6215 StorageImpl_WriteAt(This->parentStorage,
6223 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6225 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6226 return STG_E_READFAULT;
6229 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6230 bytesWrittenAt = bytesToWrite;
6231 cachedBlock->read = 1;
6232 cachedBlock->dirty = 1;
6235 blockNoInSequence++;
6236 bufferWalker += bytesWrittenAt;
6237 size -= bytesWrittenAt;
6238 *bytesWritten += bytesWrittenAt;
6239 offsetInBlock = 0; /* There is no offset on the next block */
6241 if (bytesWrittenAt != bytesToWrite)
6245 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6248 /******************************************************************************
6249 * BlockChainStream_Shrink
6251 * Shrinks this chain in the big block depot.
6253 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6254 ULARGE_INTEGER newSize)
6261 * Figure out how many blocks are needed to contain the new size
6263 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6265 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6271 * Go to the new end of chain
6273 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6275 /* Mark the new end of chain */
6276 StorageImpl_SetNextBlockInChain(
6277 This->parentStorage,
6279 BLOCK_END_OF_CHAIN);
6281 This->tailIndex = blockIndex;
6285 if (This->headOfStreamPlaceHolder != 0)
6287 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6291 DirEntry chainEntry;
6292 assert(This->ownerDirEntry != DIRENTRY_NULL);
6294 StorageImpl_ReadDirEntry(
6295 This->parentStorage,
6296 This->ownerDirEntry,
6299 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6301 StorageImpl_WriteDirEntry(
6302 This->parentStorage,
6303 This->ownerDirEntry,
6307 This->tailIndex = BLOCK_END_OF_CHAIN;
6310 This->numBlocks = numBlocks;
6313 * Mark the extra blocks as free
6315 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6317 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6318 StorageImpl_FreeBigBlock(This->parentStorage,
6319 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6320 if (last_run->lastOffset == last_run->firstOffset)
6321 This->indexCacheLen--;
6323 last_run->lastOffset--;
6327 * Reset the last accessed block cache.
6331 if (This->cachedBlocks[i].index >= numBlocks)
6333 This->cachedBlocks[i].index = 0xffffffff;
6334 This->cachedBlocks[i].dirty = 0;
6341 /******************************************************************************
6342 * BlockChainStream_Enlarge
6344 * Grows this chain in the big block depot.
6346 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6347 ULARGE_INTEGER newSize)
6349 ULONG blockIndex, currentBlock;
6351 ULONG oldNumBlocks = 0;
6353 blockIndex = BlockChainStream_GetHeadOfChain(This);
6356 * Empty chain. Create the head.
6358 if (blockIndex == BLOCK_END_OF_CHAIN)
6360 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6361 StorageImpl_SetNextBlockInChain(This->parentStorage,
6363 BLOCK_END_OF_CHAIN);
6365 if (This->headOfStreamPlaceHolder != 0)
6367 *(This->headOfStreamPlaceHolder) = blockIndex;
6371 DirEntry chainEntry;
6372 assert(This->ownerDirEntry != DIRENTRY_NULL);
6374 StorageImpl_ReadDirEntry(
6375 This->parentStorage,
6376 This->ownerDirEntry,
6379 chainEntry.startingBlock = blockIndex;
6381 StorageImpl_WriteDirEntry(
6382 This->parentStorage,
6383 This->ownerDirEntry,
6387 This->tailIndex = blockIndex;
6388 This->numBlocks = 1;
6392 * Figure out how many blocks are needed to contain this stream
6394 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6396 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6400 * Go to the current end of chain
6402 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6404 currentBlock = blockIndex;
6406 while (blockIndex != BLOCK_END_OF_CHAIN)
6409 currentBlock = blockIndex;
6411 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6416 This->tailIndex = currentBlock;
6419 currentBlock = This->tailIndex;
6420 oldNumBlocks = This->numBlocks;
6423 * Add new blocks to the chain
6425 if (oldNumBlocks < newNumBlocks)
6427 while (oldNumBlocks < newNumBlocks)
6429 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6431 StorageImpl_SetNextBlockInChain(
6432 This->parentStorage,
6436 StorageImpl_SetNextBlockInChain(
6437 This->parentStorage,
6439 BLOCK_END_OF_CHAIN);
6441 currentBlock = blockIndex;
6445 This->tailIndex = blockIndex;
6446 This->numBlocks = newNumBlocks;
6449 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6455 /******************************************************************************
6456 * BlockChainStream_SetSize
6458 * Sets the size of this stream. The big block depot will be updated.
6459 * The file will grow if we grow the chain.
6461 * TODO: Free the actual blocks in the file when we shrink the chain.
6462 * Currently, the blocks are still in the file. So the file size
6463 * doesn't shrink even if we shrink streams.
6465 BOOL BlockChainStream_SetSize(
6466 BlockChainStream* This,
6467 ULARGE_INTEGER newSize)
6469 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6471 if (newSize.u.LowPart == size.u.LowPart)
6474 if (newSize.u.LowPart < size.u.LowPart)
6476 BlockChainStream_Shrink(This, newSize);
6480 BlockChainStream_Enlarge(This, newSize);
6486 /******************************************************************************
6487 * BlockChainStream_GetSize
6489 * Returns the size of this chain.
6490 * Will return the block count if this chain doesn't have a directory entry.
6492 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6494 DirEntry chainEntry;
6496 if(This->headOfStreamPlaceHolder == NULL)
6499 * This chain has a directory entry so use the size value from there.
6501 StorageImpl_ReadDirEntry(
6502 This->parentStorage,
6503 This->ownerDirEntry,
6506 return chainEntry.size;
6511 * this chain is a chain that does not have a directory entry, figure out the
6512 * size by making the product number of used blocks times the
6515 ULARGE_INTEGER result;
6516 result.u.HighPart = 0;
6519 BlockChainStream_GetCount(This) *
6520 This->parentStorage->bigBlockSize;
6526 /******************************************************************************
6527 ** SmallBlockChainStream implementation
6530 SmallBlockChainStream* SmallBlockChainStream_Construct(
6531 StorageImpl* parentStorage,
6532 ULONG* headOfStreamPlaceHolder,
6535 SmallBlockChainStream* newStream;
6537 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6539 newStream->parentStorage = parentStorage;
6540 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6541 newStream->ownerDirEntry = dirEntry;
6546 void SmallBlockChainStream_Destroy(
6547 SmallBlockChainStream* This)
6549 HeapFree(GetProcessHeap(), 0, This);
6552 /******************************************************************************
6553 * SmallBlockChainStream_GetHeadOfChain
6555 * Returns the head of this chain of small blocks.
6557 static ULONG SmallBlockChainStream_GetHeadOfChain(
6558 SmallBlockChainStream* This)
6560 DirEntry chainEntry;
6563 if (This->headOfStreamPlaceHolder != NULL)
6564 return *(This->headOfStreamPlaceHolder);
6566 if (This->ownerDirEntry)
6568 hr = StorageImpl_ReadDirEntry(
6569 This->parentStorage,
6570 This->ownerDirEntry,
6575 return chainEntry.startingBlock;
6580 return BLOCK_END_OF_CHAIN;
6583 /******************************************************************************
6584 * SmallBlockChainStream_GetNextBlockInChain
6586 * Returns the index of the next small block in this chain.
6589 * - BLOCK_END_OF_CHAIN: end of this chain
6590 * - BLOCK_UNUSED: small block 'blockIndex' is free
6592 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6593 SmallBlockChainStream* This,
6595 ULONG* nextBlockInChain)
6597 ULARGE_INTEGER offsetOfBlockInDepot;
6602 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6604 offsetOfBlockInDepot.u.HighPart = 0;
6605 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6608 * Read those bytes in the buffer from the small block file.
6610 res = BlockChainStream_ReadAt(
6611 This->parentStorage->smallBlockDepotChain,
6612 offsetOfBlockInDepot,
6617 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6618 res = STG_E_READFAULT;
6622 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6629 /******************************************************************************
6630 * SmallBlockChainStream_SetNextBlockInChain
6632 * Writes the index of the next block of the specified block in the small
6634 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6635 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6637 static void SmallBlockChainStream_SetNextBlockInChain(
6638 SmallBlockChainStream* This,
6642 ULARGE_INTEGER offsetOfBlockInDepot;
6646 offsetOfBlockInDepot.u.HighPart = 0;
6647 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6649 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6652 * Read those bytes in the buffer from the small block file.
6654 BlockChainStream_WriteAt(
6655 This->parentStorage->smallBlockDepotChain,
6656 offsetOfBlockInDepot,
6662 /******************************************************************************
6663 * SmallBlockChainStream_FreeBlock
6665 * Flag small block 'blockIndex' as free in the small block depot.
6667 static void SmallBlockChainStream_FreeBlock(
6668 SmallBlockChainStream* This,
6671 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6674 /******************************************************************************
6675 * SmallBlockChainStream_GetNextFreeBlock
6677 * Returns the index of a free small block. The small block depot will be
6678 * enlarged if necessary. The small block chain will also be enlarged if
6681 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6682 SmallBlockChainStream* This)
6684 ULARGE_INTEGER offsetOfBlockInDepot;
6687 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6688 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6690 ULONG smallBlocksPerBigBlock;
6692 ULONG blocksRequired;
6693 ULARGE_INTEGER old_size, size_required;
6695 offsetOfBlockInDepot.u.HighPart = 0;
6698 * Scan the small block depot for a free block
6700 while (nextBlockIndex != BLOCK_UNUSED)
6702 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6704 res = BlockChainStream_ReadAt(
6705 This->parentStorage->smallBlockDepotChain,
6706 offsetOfBlockInDepot,
6712 * If we run out of space for the small block depot, enlarge it
6714 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6716 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6718 if (nextBlockIndex != BLOCK_UNUSED)
6724 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6726 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6727 ULARGE_INTEGER newSize, offset;
6730 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6731 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6734 * Initialize all the small blocks to free
6736 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6737 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6738 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6739 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6741 StorageImpl_SaveFileHeader(This->parentStorage);
6745 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6747 smallBlocksPerBigBlock =
6748 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6751 * Verify if we have to allocate big blocks to contain small blocks
6753 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6755 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6757 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6759 if (size_required.QuadPart > old_size.QuadPart)
6761 BlockChainStream_SetSize(
6762 This->parentStorage->smallBlockRootChain,
6765 StorageImpl_ReadDirEntry(
6766 This->parentStorage,
6767 This->parentStorage->base.storageDirEntry,
6770 rootEntry.size = size_required;
6772 StorageImpl_WriteDirEntry(
6773 This->parentStorage,
6774 This->parentStorage->base.storageDirEntry,
6781 /******************************************************************************
6782 * SmallBlockChainStream_ReadAt
6784 * Reads a specified number of bytes from this chain at the specified offset.
6785 * bytesRead may be NULL.
6786 * Failure will be returned if the specified number of bytes has not been read.
6788 HRESULT SmallBlockChainStream_ReadAt(
6789 SmallBlockChainStream* This,
6790 ULARGE_INTEGER offset,
6796 ULARGE_INTEGER offsetInBigBlockFile;
6797 ULONG blockNoInSequence =
6798 offset.u.LowPart / This->parentStorage->smallBlockSize;
6800 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6801 ULONG bytesToReadInBuffer;
6803 ULONG bytesReadFromBigBlockFile;
6805 ULARGE_INTEGER stream_size;
6808 * This should never happen on a small block file.
6810 assert(offset.u.HighPart==0);
6814 stream_size = SmallBlockChainStream_GetSize(This);
6815 if (stream_size.QuadPart > offset.QuadPart)
6816 size = min(stream_size.QuadPart - offset.QuadPart, size);
6821 * Find the first block in the stream that contains part of the buffer.
6823 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6825 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6827 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6830 blockNoInSequence--;
6834 * Start reading the buffer.
6836 bufferWalker = buffer;
6838 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6841 * Calculate how many bytes we can copy from this small block.
6843 bytesToReadInBuffer =
6844 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6847 * Calculate the offset of the small block in the small block file.
6849 offsetInBigBlockFile.u.HighPart = 0;
6850 offsetInBigBlockFile.u.LowPart =
6851 blockIndex * This->parentStorage->smallBlockSize;
6853 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6856 * Read those bytes in the buffer from the small block file.
6857 * The small block has already been identified so it shouldn't fail
6858 * unless the file is corrupt.
6860 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6861 offsetInBigBlockFile,
6862 bytesToReadInBuffer,
6864 &bytesReadFromBigBlockFile);
6869 if (!bytesReadFromBigBlockFile)
6870 return STG_E_DOCFILECORRUPT;
6873 * Step to the next big block.
6875 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6877 return STG_E_DOCFILECORRUPT;
6879 bufferWalker += bytesReadFromBigBlockFile;
6880 size -= bytesReadFromBigBlockFile;
6881 *bytesRead += bytesReadFromBigBlockFile;
6882 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6888 /******************************************************************************
6889 * SmallBlockChainStream_WriteAt
6891 * Writes the specified number of bytes to this chain at the specified offset.
6892 * Will fail if not all specified number of bytes have been written.
6894 HRESULT SmallBlockChainStream_WriteAt(
6895 SmallBlockChainStream* This,
6896 ULARGE_INTEGER offset,
6899 ULONG* bytesWritten)
6901 ULARGE_INTEGER offsetInBigBlockFile;
6902 ULONG blockNoInSequence =
6903 offset.u.LowPart / This->parentStorage->smallBlockSize;
6905 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6906 ULONG bytesToWriteInBuffer;
6908 ULONG bytesWrittenToBigBlockFile;
6909 const BYTE* bufferWalker;
6913 * This should never happen on a small block file.
6915 assert(offset.u.HighPart==0);
6918 * Find the first block in the stream that contains part of the buffer.
6920 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6922 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6924 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6925 return STG_E_DOCFILECORRUPT;
6926 blockNoInSequence--;
6930 * Start writing the buffer.
6933 bufferWalker = buffer;
6934 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6937 * Calculate how many bytes we can copy to this small block.
6939 bytesToWriteInBuffer =
6940 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6943 * Calculate the offset of the small block in the small block file.
6945 offsetInBigBlockFile.u.HighPart = 0;
6946 offsetInBigBlockFile.u.LowPart =
6947 blockIndex * This->parentStorage->smallBlockSize;
6949 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6952 * Write those bytes in the buffer to the small block file.
6954 res = BlockChainStream_WriteAt(
6955 This->parentStorage->smallBlockRootChain,
6956 offsetInBigBlockFile,
6957 bytesToWriteInBuffer,
6959 &bytesWrittenToBigBlockFile);
6964 * Step to the next big block.
6966 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6969 bufferWalker += bytesWrittenToBigBlockFile;
6970 size -= bytesWrittenToBigBlockFile;
6971 *bytesWritten += bytesWrittenToBigBlockFile;
6972 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6975 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6978 /******************************************************************************
6979 * SmallBlockChainStream_Shrink
6981 * Shrinks this chain in the small block depot.
6983 static BOOL SmallBlockChainStream_Shrink(
6984 SmallBlockChainStream* This,
6985 ULARGE_INTEGER newSize)
6987 ULONG blockIndex, extraBlock;
6991 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6993 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6996 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6999 * Go to the new end of chain
7001 while (count < numBlocks)
7003 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7010 * If the count is 0, we have a special case, the head of the chain was
7015 DirEntry chainEntry;
7017 StorageImpl_ReadDirEntry(This->parentStorage,
7018 This->ownerDirEntry,
7021 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7023 StorageImpl_WriteDirEntry(This->parentStorage,
7024 This->ownerDirEntry,
7028 * We start freeing the chain at the head block.
7030 extraBlock = blockIndex;
7034 /* Get the next block before marking the new end */
7035 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7039 /* Mark the new end of chain */
7040 SmallBlockChainStream_SetNextBlockInChain(
7043 BLOCK_END_OF_CHAIN);
7047 * Mark the extra blocks as free
7049 while (extraBlock != BLOCK_END_OF_CHAIN)
7051 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7054 SmallBlockChainStream_FreeBlock(This, extraBlock);
7055 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7056 extraBlock = blockIndex;
7062 /******************************************************************************
7063 * SmallBlockChainStream_Enlarge
7065 * Grows this chain in the small block depot.
7067 static BOOL SmallBlockChainStream_Enlarge(
7068 SmallBlockChainStream* This,
7069 ULARGE_INTEGER newSize)
7071 ULONG blockIndex, currentBlock;
7073 ULONG oldNumBlocks = 0;
7075 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7078 * Empty chain. Create the head.
7080 if (blockIndex == BLOCK_END_OF_CHAIN)
7082 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7083 SmallBlockChainStream_SetNextBlockInChain(
7086 BLOCK_END_OF_CHAIN);
7088 if (This->headOfStreamPlaceHolder != NULL)
7090 *(This->headOfStreamPlaceHolder) = blockIndex;
7094 DirEntry chainEntry;
7096 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7099 chainEntry.startingBlock = blockIndex;
7101 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7106 currentBlock = blockIndex;
7109 * Figure out how many blocks are needed to contain this stream
7111 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7113 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7117 * Go to the current end of chain
7119 while (blockIndex != BLOCK_END_OF_CHAIN)
7122 currentBlock = blockIndex;
7123 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7128 * Add new blocks to the chain
7130 while (oldNumBlocks < newNumBlocks)
7132 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7133 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7135 SmallBlockChainStream_SetNextBlockInChain(
7138 BLOCK_END_OF_CHAIN);
7140 currentBlock = blockIndex;
7147 /******************************************************************************
7148 * SmallBlockChainStream_SetSize
7150 * Sets the size of this stream.
7151 * The file will grow if we grow the chain.
7153 * TODO: Free the actual blocks in the file when we shrink the chain.
7154 * Currently, the blocks are still in the file. So the file size
7155 * doesn't shrink even if we shrink streams.
7157 BOOL SmallBlockChainStream_SetSize(
7158 SmallBlockChainStream* This,
7159 ULARGE_INTEGER newSize)
7161 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7163 if (newSize.u.LowPart == size.u.LowPart)
7166 if (newSize.u.LowPart < size.u.LowPart)
7168 SmallBlockChainStream_Shrink(This, newSize);
7172 SmallBlockChainStream_Enlarge(This, newSize);
7178 /******************************************************************************
7179 * SmallBlockChainStream_GetCount
7181 * Returns the number of small blocks that comprises this chain.
7182 * This is not the size of the stream as the last block may not be full!
7185 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7190 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7192 while(blockIndex != BLOCK_END_OF_CHAIN)
7196 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7197 blockIndex, &blockIndex)))
7204 /******************************************************************************
7205 * SmallBlockChainStream_GetSize
7207 * Returns the size of this chain.
7209 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7211 DirEntry chainEntry;
7213 if(This->headOfStreamPlaceHolder != NULL)
7215 ULARGE_INTEGER result;
7216 result.u.HighPart = 0;
7218 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7219 This->parentStorage->smallBlockSize;
7224 StorageImpl_ReadDirEntry(
7225 This->parentStorage,
7226 This->ownerDirEntry,
7229 return chainEntry.size;
7232 static HRESULT create_storagefile(
7236 STGOPTIONS* pStgOptions,
7240 StorageBaseImpl* newStorage = 0;
7241 HANDLE hFile = INVALID_HANDLE_VALUE;
7242 HRESULT hr = STG_E_INVALIDFLAG;
7246 DWORD fileAttributes;
7247 WCHAR tempFileName[MAX_PATH];
7250 return STG_E_INVALIDPOINTER;
7252 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7253 return STG_E_INVALIDPARAMETER;
7255 /* if no share mode given then DENY_NONE is the default */
7256 if (STGM_SHARE_MODE(grfMode) == 0)
7257 grfMode |= STGM_SHARE_DENY_NONE;
7259 if ( FAILED( validateSTGM(grfMode) ))
7262 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7263 switch(STGM_ACCESS_MODE(grfMode))
7266 case STGM_READWRITE:
7272 /* in direct mode, can only use SHARE_EXCLUSIVE */
7273 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7276 /* but in transacted mode, any share mode is valid */
7279 * Generate a unique name.
7283 WCHAR tempPath[MAX_PATH];
7284 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7286 memset(tempPath, 0, sizeof(tempPath));
7287 memset(tempFileName, 0, sizeof(tempFileName));
7289 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7292 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7293 pwcsName = tempFileName;
7296 hr = STG_E_INSUFFICIENTMEMORY;
7300 creationMode = TRUNCATE_EXISTING;
7304 creationMode = GetCreationModeFromSTGM(grfMode);
7308 * Interpret the STGM value grfMode
7310 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7311 accessMode = GetAccessModeFromSTGM(grfMode);
7313 if (grfMode & STGM_DELETEONRELEASE)
7314 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7316 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7318 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7322 FIXME("Storage share mode not implemented.\n");
7327 hFile = CreateFileW(pwcsName,
7335 if (hFile == INVALID_HANDLE_VALUE)
7337 if(GetLastError() == ERROR_FILE_EXISTS)
7338 hr = STG_E_FILEALREADYEXISTS;
7345 * Allocate and initialize the new IStorage32object.
7347 hr = Storage_Construct(
7354 pStgOptions->ulSectorSize,
7362 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
7364 IStorage_Release((IStorage*)newStorage);
7367 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7372 /******************************************************************************
7373 * StgCreateDocfile [OLE32.@]
7374 * Creates a new compound file storage object
7377 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7378 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7379 * reserved [ ?] unused?, usually 0
7380 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7383 * S_OK if the file was successfully created
7384 * some STG_E_ value if error
7386 * if pwcsName is NULL, create file with new unique name
7387 * the function can returns
7388 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7391 HRESULT WINAPI StgCreateDocfile(
7395 IStorage **ppstgOpen)
7397 STGOPTIONS stgoptions = {1, 0, 512};
7399 TRACE("(%s, %x, %d, %p)\n",
7400 debugstr_w(pwcsName), grfMode,
7401 reserved, ppstgOpen);
7404 return STG_E_INVALIDPOINTER;
7406 return STG_E_INVALIDPARAMETER;
7408 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7411 /******************************************************************************
7412 * StgCreateStorageEx [OLE32.@]
7414 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7416 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7417 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7419 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7421 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7422 return STG_E_INVALIDPARAMETER;
7425 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7427 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7428 return STG_E_INVALIDPARAMETER;
7431 if (stgfmt == STGFMT_FILE)
7433 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7434 return STG_E_INVALIDPARAMETER;
7437 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7439 STGOPTIONS defaultOptions = {1, 0, 512};
7441 if (!pStgOptions) pStgOptions = &defaultOptions;
7442 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7446 ERR("Invalid stgfmt argument\n");
7447 return STG_E_INVALIDPARAMETER;
7450 /******************************************************************************
7451 * StgCreatePropSetStg [OLE32.@]
7453 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7454 IPropertySetStorage **ppPropSetStg)
7458 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
7460 hr = STG_E_INVALIDPARAMETER;
7462 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
7463 (void**)ppPropSetStg);
7467 /******************************************************************************
7468 * StgOpenStorageEx [OLE32.@]
7470 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7472 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7473 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7475 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7477 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7478 return STG_E_INVALIDPARAMETER;
7484 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7485 return STG_E_INVALIDPARAMETER;
7487 case STGFMT_STORAGE:
7490 case STGFMT_DOCFILE:
7491 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7493 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7494 return STG_E_INVALIDPARAMETER;
7496 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7500 WARN("STGFMT_ANY assuming storage\n");
7504 return STG_E_INVALIDPARAMETER;
7507 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7511 /******************************************************************************
7512 * StgOpenStorage [OLE32.@]
7514 HRESULT WINAPI StgOpenStorage(
7515 const OLECHAR *pwcsName,
7516 IStorage *pstgPriority,
7520 IStorage **ppstgOpen)
7522 StorageBaseImpl* newStorage = 0;
7528 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7529 debugstr_w(pwcsName), pstgPriority, grfMode,
7530 snbExclude, reserved, ppstgOpen);
7534 hr = STG_E_INVALIDNAME;
7540 hr = STG_E_INVALIDPOINTER;
7546 hr = STG_E_INVALIDPARAMETER;
7550 if (grfMode & STGM_PRIORITY)
7552 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7553 return STG_E_INVALIDFLAG;
7554 if (grfMode & STGM_DELETEONRELEASE)
7555 return STG_E_INVALIDFUNCTION;
7556 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7557 return STG_E_INVALIDFLAG;
7558 grfMode &= ~0xf0; /* remove the existing sharing mode */
7559 grfMode |= STGM_SHARE_DENY_NONE;
7561 /* STGM_PRIORITY stops other IStorage objects on the same file from
7562 * committing until the STGM_PRIORITY IStorage is closed. it also
7563 * stops non-transacted mode StgOpenStorage calls with write access from
7564 * succeeding. obviously, both of these cannot be achieved through just
7565 * file share flags */
7566 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7570 * Validate the sharing mode
7572 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7573 switch(STGM_SHARE_MODE(grfMode))
7575 case STGM_SHARE_EXCLUSIVE:
7576 case STGM_SHARE_DENY_WRITE:
7579 hr = STG_E_INVALIDFLAG;
7583 if ( FAILED( validateSTGM(grfMode) ) ||
7584 (grfMode&STGM_CREATE))
7586 hr = STG_E_INVALIDFLAG;
7590 /* shared reading requires transacted mode */
7591 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7592 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7593 !(grfMode&STGM_TRANSACTED) )
7595 hr = STG_E_INVALIDFLAG;
7600 * Interpret the STGM value grfMode
7602 shareMode = GetShareModeFromSTGM(grfMode);
7603 accessMode = GetAccessModeFromSTGM(grfMode);
7607 hFile = CreateFileW( pwcsName,
7612 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7615 if (hFile==INVALID_HANDLE_VALUE)
7617 DWORD last_error = GetLastError();
7623 case ERROR_FILE_NOT_FOUND:
7624 hr = STG_E_FILENOTFOUND;
7627 case ERROR_PATH_NOT_FOUND:
7628 hr = STG_E_PATHNOTFOUND;
7631 case ERROR_ACCESS_DENIED:
7632 case ERROR_WRITE_PROTECT:
7633 hr = STG_E_ACCESSDENIED;
7636 case ERROR_SHARING_VIOLATION:
7637 hr = STG_E_SHAREVIOLATION;
7648 * Refuse to open the file if it's too small to be a structured storage file
7649 * FIXME: verify the file when reading instead of here
7651 if (GetFileSize(hFile, NULL) < 0x100)
7654 hr = STG_E_FILEALREADYEXISTS;
7659 * Allocate and initialize the new IStorage32object.
7661 hr = Storage_Construct(
7674 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7676 if(hr == STG_E_INVALIDHEADER)
7677 hr = STG_E_FILEALREADYEXISTS;
7682 * Get an "out" pointer for the caller.
7684 *ppstgOpen = (IStorage*)newStorage;
7687 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7691 /******************************************************************************
7692 * StgCreateDocfileOnILockBytes [OLE32.@]
7694 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7698 IStorage** ppstgOpen)
7700 StorageBaseImpl* newStorage = 0;
7703 if ((ppstgOpen == 0) || (plkbyt == 0))
7704 return STG_E_INVALIDPOINTER;
7707 * Allocate and initialize the new IStorage object.
7709 hr = Storage_Construct(
7725 * Get an "out" pointer for the caller.
7727 *ppstgOpen = (IStorage*)newStorage;
7732 /******************************************************************************
7733 * StgOpenStorageOnILockBytes [OLE32.@]
7735 HRESULT WINAPI StgOpenStorageOnILockBytes(
7737 IStorage *pstgPriority,
7741 IStorage **ppstgOpen)
7743 StorageBaseImpl* newStorage = 0;
7746 if ((plkbyt == 0) || (ppstgOpen == 0))
7747 return STG_E_INVALIDPOINTER;
7749 if ( FAILED( validateSTGM(grfMode) ))
7750 return STG_E_INVALIDFLAG;
7755 * Allocate and initialize the new IStorage object.
7757 hr = Storage_Construct(
7773 * Get an "out" pointer for the caller.
7775 *ppstgOpen = (IStorage*)newStorage;
7780 /******************************************************************************
7781 * StgSetTimes [ole32.@]
7782 * StgSetTimes [OLE32.@]
7786 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7787 FILETIME const *patime, FILETIME const *pmtime)
7789 IStorage *stg = NULL;
7792 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7794 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7798 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7799 IStorage_Release(stg);
7805 /******************************************************************************
7806 * StgIsStorageILockBytes [OLE32.@]
7808 * Determines if the ILockBytes contains a storage object.
7810 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7813 ULARGE_INTEGER offset;
7815 offset.u.HighPart = 0;
7816 offset.u.LowPart = 0;
7818 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
7820 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
7826 /******************************************************************************
7827 * WriteClassStg [OLE32.@]
7829 * This method will store the specified CLSID in the specified storage object
7831 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7836 return E_INVALIDARG;
7839 return STG_E_INVALIDPOINTER;
7841 hRes = IStorage_SetClass(pStg, rclsid);
7846 /***********************************************************************
7847 * ReadClassStg (OLE32.@)
7849 * This method reads the CLSID previously written to a storage object with
7850 * the WriteClassStg.
7853 * pstg [I] IStorage pointer
7854 * pclsid [O] Pointer to where the CLSID is written
7858 * Failure: HRESULT code.
7860 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7865 TRACE("(%p, %p)\n", pstg, pclsid);
7867 if(!pstg || !pclsid)
7868 return E_INVALIDARG;
7871 * read a STATSTG structure (contains the clsid) from the storage
7873 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7876 *pclsid=pstatstg.clsid;
7881 /***********************************************************************
7882 * OleLoadFromStream (OLE32.@)
7884 * This function loads an object from stream
7886 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7890 LPPERSISTSTREAM xstm;
7892 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7894 res=ReadClassStm(pStm,&clsid);
7897 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7900 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7902 IUnknown_Release((IUnknown*)*ppvObj);
7905 res=IPersistStream_Load(xstm,pStm);
7906 IPersistStream_Release(xstm);
7907 /* FIXME: all refcounts ok at this point? I think they should be:
7910 * xstm : 0 (released)
7915 /***********************************************************************
7916 * OleSaveToStream (OLE32.@)
7918 * This function saves an object with the IPersistStream interface on it
7919 * to the specified stream.
7921 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7927 TRACE("(%p,%p)\n",pPStm,pStm);
7929 res=IPersistStream_GetClassID(pPStm,&clsid);
7931 if (SUCCEEDED(res)){
7933 res=WriteClassStm(pStm,&clsid);
7937 res=IPersistStream_Save(pPStm,pStm,TRUE);
7940 TRACE("Finished Save\n");
7944 /****************************************************************************
7945 * This method validate a STGM parameter that can contain the values below
7947 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7948 * The stgm values contained in 0xffff0000 are bitmasks.
7950 * STGM_DIRECT 0x00000000
7951 * STGM_TRANSACTED 0x00010000
7952 * STGM_SIMPLE 0x08000000
7954 * STGM_READ 0x00000000
7955 * STGM_WRITE 0x00000001
7956 * STGM_READWRITE 0x00000002
7958 * STGM_SHARE_DENY_NONE 0x00000040
7959 * STGM_SHARE_DENY_READ 0x00000030
7960 * STGM_SHARE_DENY_WRITE 0x00000020
7961 * STGM_SHARE_EXCLUSIVE 0x00000010
7963 * STGM_PRIORITY 0x00040000
7964 * STGM_DELETEONRELEASE 0x04000000
7966 * STGM_CREATE 0x00001000
7967 * STGM_CONVERT 0x00020000
7968 * STGM_FAILIFTHERE 0x00000000
7970 * STGM_NOSCRATCH 0x00100000
7971 * STGM_NOSNAPSHOT 0x00200000
7973 static HRESULT validateSTGM(DWORD stgm)
7975 DWORD access = STGM_ACCESS_MODE(stgm);
7976 DWORD share = STGM_SHARE_MODE(stgm);
7977 DWORD create = STGM_CREATE_MODE(stgm);
7979 if (stgm&~STGM_KNOWN_FLAGS)
7981 ERR("unknown flags %08x\n", stgm);
7989 case STGM_READWRITE:
7997 case STGM_SHARE_DENY_NONE:
7998 case STGM_SHARE_DENY_READ:
7999 case STGM_SHARE_DENY_WRITE:
8000 case STGM_SHARE_EXCLUSIVE:
8009 case STGM_FAILIFTHERE:
8016 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8018 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8022 * STGM_CREATE | STGM_CONVERT
8023 * if both are false, STGM_FAILIFTHERE is set to TRUE
8025 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8029 * STGM_NOSCRATCH requires STGM_TRANSACTED
8031 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8035 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8036 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8038 if ( (stgm & STGM_NOSNAPSHOT) &&
8039 (!(stgm & STGM_TRANSACTED) ||
8040 share == STGM_SHARE_EXCLUSIVE ||
8041 share == STGM_SHARE_DENY_WRITE) )
8047 /****************************************************************************
8048 * GetShareModeFromSTGM
8050 * This method will return a share mode flag from a STGM value.
8051 * The STGM value is assumed valid.
8053 static DWORD GetShareModeFromSTGM(DWORD stgm)
8055 switch (STGM_SHARE_MODE(stgm))
8057 case STGM_SHARE_DENY_NONE:
8058 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8059 case STGM_SHARE_DENY_READ:
8060 return FILE_SHARE_WRITE;
8061 case STGM_SHARE_DENY_WRITE:
8062 return FILE_SHARE_READ;
8063 case STGM_SHARE_EXCLUSIVE:
8066 ERR("Invalid share mode!\n");
8071 /****************************************************************************
8072 * GetAccessModeFromSTGM
8074 * This method will return an access mode flag from a STGM value.
8075 * The STGM value is assumed valid.
8077 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8079 switch (STGM_ACCESS_MODE(stgm))
8082 return GENERIC_READ;
8084 case STGM_READWRITE:
8085 return GENERIC_READ | GENERIC_WRITE;
8087 ERR("Invalid access mode!\n");
8092 /****************************************************************************
8093 * GetCreationModeFromSTGM
8095 * This method will return a creation mode flag from a STGM value.
8096 * The STGM value is assumed valid.
8098 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8100 switch(STGM_CREATE_MODE(stgm))
8103 return CREATE_ALWAYS;
8105 FIXME("STGM_CONVERT not implemented!\n");
8107 case STGM_FAILIFTHERE:
8110 ERR("Invalid create mode!\n");
8116 /*************************************************************************
8117 * OLECONVERT_LoadOLE10 [Internal]
8119 * Loads the OLE10 STREAM to memory
8122 * pOleStream [I] The OLESTREAM
8123 * pData [I] Data Structure for the OLESTREAM Data
8127 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8128 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8131 * This function is used by OleConvertOLESTREAMToIStorage only.
8133 * Memory allocated for pData must be freed by the caller
8135 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8138 HRESULT hRes = S_OK;
8142 pData->pData = NULL;
8143 pData->pstrOleObjFileName = NULL;
8145 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8148 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8149 if(dwSize != sizeof(pData->dwOleID))
8151 hRes = CONVERT10_E_OLESTREAM_GET;
8153 else if(pData->dwOleID != OLESTREAM_ID)
8155 hRes = CONVERT10_E_OLESTREAM_FMT;
8166 /* Get the TypeID... more info needed for this field */
8167 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8168 if(dwSize != sizeof(pData->dwTypeID))
8170 hRes = CONVERT10_E_OLESTREAM_GET;
8175 if(pData->dwTypeID != 0)
8177 /* Get the length of the OleTypeName */
8178 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8179 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8181 hRes = CONVERT10_E_OLESTREAM_GET;
8186 if(pData->dwOleTypeNameLength > 0)
8188 /* Get the OleTypeName */
8189 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8190 if(dwSize != pData->dwOleTypeNameLength)
8192 hRes = CONVERT10_E_OLESTREAM_GET;
8198 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8199 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8201 hRes = CONVERT10_E_OLESTREAM_GET;
8205 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8206 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8207 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8208 if(pData->pstrOleObjFileName)
8210 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8211 if(dwSize != pData->dwOleObjFileNameLength)
8213 hRes = CONVERT10_E_OLESTREAM_GET;
8217 hRes = CONVERT10_E_OLESTREAM_GET;
8222 /* Get the Width of the Metafile */
8223 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8224 if(dwSize != sizeof(pData->dwMetaFileWidth))
8226 hRes = CONVERT10_E_OLESTREAM_GET;
8230 /* Get the Height of the Metafile */
8231 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8232 if(dwSize != sizeof(pData->dwMetaFileHeight))
8234 hRes = CONVERT10_E_OLESTREAM_GET;
8240 /* Get the Length of the Data */
8241 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8242 if(dwSize != sizeof(pData->dwDataLength))
8244 hRes = CONVERT10_E_OLESTREAM_GET;
8248 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8250 if(!bStrem1) /* if it is a second OLE stream data */
8252 pData->dwDataLength -= 8;
8253 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8254 if(dwSize != sizeof(pData->strUnknown))
8256 hRes = CONVERT10_E_OLESTREAM_GET;
8262 if(pData->dwDataLength > 0)
8264 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8266 /* Get Data (ex. IStorage, Metafile, or BMP) */
8269 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8270 if(dwSize != pData->dwDataLength)
8272 hRes = CONVERT10_E_OLESTREAM_GET;
8277 hRes = CONVERT10_E_OLESTREAM_GET;
8286 /*************************************************************************
8287 * OLECONVERT_SaveOLE10 [Internal]
8289 * Saves the OLE10 STREAM From memory
8292 * pData [I] Data Structure for the OLESTREAM Data
8293 * pOleStream [I] The OLESTREAM to save
8297 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8300 * This function is used by OleConvertIStorageToOLESTREAM only.
8303 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8306 HRESULT hRes = S_OK;
8310 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8311 if(dwSize != sizeof(pData->dwOleID))
8313 hRes = CONVERT10_E_OLESTREAM_PUT;
8318 /* Set the TypeID */
8319 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8320 if(dwSize != sizeof(pData->dwTypeID))
8322 hRes = CONVERT10_E_OLESTREAM_PUT;
8326 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8328 /* Set the Length of the OleTypeName */
8329 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8330 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8332 hRes = CONVERT10_E_OLESTREAM_PUT;
8337 if(pData->dwOleTypeNameLength > 0)
8339 /* Set the OleTypeName */
8340 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8341 if(dwSize != pData->dwOleTypeNameLength)
8343 hRes = CONVERT10_E_OLESTREAM_PUT;
8350 /* Set the width of the Metafile */
8351 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8352 if(dwSize != sizeof(pData->dwMetaFileWidth))
8354 hRes = CONVERT10_E_OLESTREAM_PUT;
8360 /* Set the height of the Metafile */
8361 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8362 if(dwSize != sizeof(pData->dwMetaFileHeight))
8364 hRes = CONVERT10_E_OLESTREAM_PUT;
8370 /* Set the length of the Data */
8371 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8372 if(dwSize != sizeof(pData->dwDataLength))
8374 hRes = CONVERT10_E_OLESTREAM_PUT;
8380 if(pData->dwDataLength > 0)
8382 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8383 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8384 if(dwSize != pData->dwDataLength)
8386 hRes = CONVERT10_E_OLESTREAM_PUT;
8394 /*************************************************************************
8395 * OLECONVERT_GetOLE20FromOLE10[Internal]
8397 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8398 * opens it, and copies the content to the dest IStorage for
8399 * OleConvertOLESTREAMToIStorage
8403 * pDestStorage [I] The IStorage to copy the data to
8404 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8405 * nBufferLength [I] The size of the buffer
8414 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8418 IStorage *pTempStorage;
8419 DWORD dwNumOfBytesWritten;
8420 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8421 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8423 /* Create a temp File */
8424 GetTempPathW(MAX_PATH, wstrTempDir);
8425 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8426 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8428 if(hFile != INVALID_HANDLE_VALUE)
8430 /* Write IStorage Data to File */
8431 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8434 /* Open and copy temp storage to the Dest Storage */
8435 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8438 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8439 IStorage_Release(pTempStorage);
8441 DeleteFileW(wstrTempFile);
8446 /*************************************************************************
8447 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8449 * Saves the OLE10 STREAM From memory
8452 * pStorage [I] The Src IStorage to copy
8453 * pData [I] The Dest Memory to write to.
8456 * The size in bytes allocated for pData
8459 * Memory allocated for pData must be freed by the caller
8461 * Used by OleConvertIStorageToOLESTREAM only.
8464 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8468 DWORD nDataLength = 0;
8469 IStorage *pTempStorage;
8470 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8471 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8475 /* Create temp Storage */
8476 GetTempPathW(MAX_PATH, wstrTempDir);
8477 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8478 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8482 /* Copy Src Storage to the Temp Storage */
8483 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8484 IStorage_Release(pTempStorage);
8486 /* Open Temp Storage as a file and copy to memory */
8487 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8488 if(hFile != INVALID_HANDLE_VALUE)
8490 nDataLength = GetFileSize(hFile, NULL);
8491 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8492 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8495 DeleteFileW(wstrTempFile);
8500 /*************************************************************************
8501 * OLECONVERT_CreateOleStream [Internal]
8503 * Creates the "\001OLE" stream in the IStorage if necessary.
8506 * pStorage [I] Dest storage to create the stream in
8512 * This function is used by OleConvertOLESTREAMToIStorage only.
8514 * This stream is still unknown, MS Word seems to have extra data
8515 * but since the data is stored in the OLESTREAM there should be
8516 * no need to recreate the stream. If the stream is manually
8517 * deleted it will create it with this default data.
8520 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8524 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8525 BYTE pOleStreamHeader [] =
8527 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8528 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8529 0x00, 0x00, 0x00, 0x00
8532 /* Create stream if not present */
8533 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8534 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8538 /* Write default Data */
8539 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8540 IStream_Release(pStream);
8544 /* write a string to a stream, preceded by its length */
8545 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8552 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8553 r = IStream_Write( stm, &len, sizeof(len), NULL);
8558 str = CoTaskMemAlloc( len );
8559 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8560 r = IStream_Write( stm, str, len, NULL);
8561 CoTaskMemFree( str );
8565 /* read a string preceded by its length from a stream */
8566 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8569 DWORD len, count = 0;
8573 r = IStream_Read( stm, &len, sizeof(len), &count );
8576 if( count != sizeof(len) )
8577 return E_OUTOFMEMORY;
8579 TRACE("%d bytes\n",len);
8581 str = CoTaskMemAlloc( len );
8583 return E_OUTOFMEMORY;
8585 r = IStream_Read( stm, str, len, &count );
8590 CoTaskMemFree( str );
8591 return E_OUTOFMEMORY;
8594 TRACE("Read string %s\n",debugstr_an(str,len));
8596 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8597 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8599 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8600 CoTaskMemFree( str );
8608 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8609 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8613 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8615 static const BYTE unknown1[12] =
8616 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8617 0xFF, 0xFF, 0xFF, 0xFF};
8618 static const BYTE unknown2[16] =
8619 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8620 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8622 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8623 debugstr_w(lpszUserType), debugstr_w(szClipName),
8624 debugstr_w(szProgIDName));
8626 /* Create a CompObj stream */
8627 r = IStorage_CreateStream(pstg, szwStreamName,
8628 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8632 /* Write CompObj Structure to stream */
8633 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8635 if( SUCCEEDED( r ) )
8636 r = WriteClassStm( pstm, clsid );
8638 if( SUCCEEDED( r ) )
8639 r = STREAM_WriteString( pstm, lpszUserType );
8640 if( SUCCEEDED( r ) )
8641 r = STREAM_WriteString( pstm, szClipName );
8642 if( SUCCEEDED( r ) )
8643 r = STREAM_WriteString( pstm, szProgIDName );
8644 if( SUCCEEDED( r ) )
8645 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8647 IStream_Release( pstm );
8652 /***********************************************************************
8653 * WriteFmtUserTypeStg (OLE32.@)
8655 HRESULT WINAPI WriteFmtUserTypeStg(
8656 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8659 WCHAR szwClipName[0x40];
8660 CLSID clsid = CLSID_NULL;
8661 LPWSTR wstrProgID = NULL;
8664 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8666 /* get the clipboard format name */
8667 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8670 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8672 /* FIXME: There's room to save a CLSID and its ProgID, but
8673 the CLSID is not looked up in the registry and in all the
8674 tests I wrote it was CLSID_NULL. Where does it come from?
8677 /* get the real program ID. This may fail, but that's fine */
8678 ProgIDFromCLSID(&clsid, &wstrProgID);
8680 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8682 r = STORAGE_WriteCompObj( pstg, &clsid,
8683 lpszUserType, szwClipName, wstrProgID );
8685 CoTaskMemFree(wstrProgID);
8691 /******************************************************************************
8692 * ReadFmtUserTypeStg [OLE32.@]
8694 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8698 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8699 unsigned char unknown1[12];
8700 unsigned char unknown2[16];
8702 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8705 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8707 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8708 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8711 WARN("Failed to open stream r = %08x\n", r);
8715 /* read the various parts of the structure */
8716 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8717 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8719 r = ReadClassStm( stm, &clsid );
8723 r = STREAM_ReadString( stm, &szCLSIDName );
8727 r = STREAM_ReadString( stm, &szOleTypeName );
8731 r = STREAM_ReadString( stm, &szProgIDName );
8735 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8736 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8739 /* ok, success... now we just need to store what we found */
8741 *pcf = RegisterClipboardFormatW( szOleTypeName );
8742 CoTaskMemFree( szOleTypeName );
8744 if( lplpszUserType )
8745 *lplpszUserType = szCLSIDName;
8746 CoTaskMemFree( szProgIDName );
8749 IStream_Release( stm );
8755 /*************************************************************************
8756 * OLECONVERT_CreateCompObjStream [Internal]
8758 * Creates a "\001CompObj" is the destination IStorage if necessary.
8761 * pStorage [I] The dest IStorage to create the CompObj Stream
8763 * strOleTypeName [I] The ProgID
8767 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8770 * This function is used by OleConvertOLESTREAMToIStorage only.
8772 * The stream data is stored in the OLESTREAM and there should be
8773 * no need to recreate the stream. If the stream is manually
8774 * deleted it will attempt to create it by querying the registry.
8778 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8781 HRESULT hStorageRes, hRes = S_OK;
8782 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8783 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8784 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8786 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8787 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8789 /* Initialize the CompObj structure */
8790 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8791 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8792 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8795 /* Create a CompObj stream if it doesn't exist */
8796 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8797 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8798 if(hStorageRes == S_OK)
8800 /* copy the OleTypeName to the compobj struct */
8801 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8802 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8804 /* copy the OleTypeName to the compobj struct */
8805 /* Note: in the test made, these were Identical */
8806 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8807 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8810 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8811 bufferW, OLESTREAM_MAX_STR_LEN );
8812 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8818 /* Get the CLSID Default Name from the Registry */
8819 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8820 if(hErr == ERROR_SUCCESS)
8822 char strTemp[OLESTREAM_MAX_STR_LEN];
8823 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8824 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8825 if(hErr == ERROR_SUCCESS)
8827 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8833 /* Write CompObj Structure to stream */
8834 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8836 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8838 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8839 if(IStorageCompObj.dwCLSIDNameLength > 0)
8841 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8843 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8844 if(IStorageCompObj.dwOleTypeNameLength > 0)
8846 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8848 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8849 if(IStorageCompObj.dwProgIDNameLength > 0)
8851 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8853 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8854 IStream_Release(pStream);
8860 /*************************************************************************
8861 * OLECONVERT_CreateOlePresStream[Internal]
8863 * Creates the "\002OlePres000" Stream with the Metafile data
8866 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8867 * dwExtentX [I] Width of the Metafile
8868 * dwExtentY [I] Height of the Metafile
8869 * pData [I] Metafile data
8870 * dwDataLength [I] Size of the Metafile data
8874 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8877 * This function is used by OleConvertOLESTREAMToIStorage only.
8880 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8884 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8885 BYTE pOlePresStreamHeader [] =
8887 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8888 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8889 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8890 0x00, 0x00, 0x00, 0x00
8893 BYTE pOlePresStreamHeaderEmpty [] =
8895 0x00, 0x00, 0x00, 0x00,
8896 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8897 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8898 0x00, 0x00, 0x00, 0x00
8901 /* Create the OlePres000 Stream */
8902 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8903 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8908 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8910 memset(&OlePres, 0, sizeof(OlePres));
8911 /* Do we have any metafile data to save */
8912 if(dwDataLength > 0)
8914 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8915 nHeaderSize = sizeof(pOlePresStreamHeader);
8919 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8920 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8922 /* Set width and height of the metafile */
8923 OlePres.dwExtentX = dwExtentX;
8924 OlePres.dwExtentY = -dwExtentY;
8926 /* Set Data and Length */
8927 if(dwDataLength > sizeof(METAFILEPICT16))
8929 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8930 OlePres.pData = &(pData[8]);
8932 /* Save OlePres000 Data to Stream */
8933 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8934 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8935 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8936 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8937 if(OlePres.dwSize > 0)
8939 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8941 IStream_Release(pStream);
8945 /*************************************************************************
8946 * OLECONVERT_CreateOle10NativeStream [Internal]
8948 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8951 * pStorage [I] Dest storage to create the stream in
8952 * pData [I] Ole10 Native Data (ex. bmp)
8953 * dwDataLength [I] Size of the Ole10 Native Data
8959 * This function is used by OleConvertOLESTREAMToIStorage only.
8961 * Might need to verify the data and return appropriate error message
8964 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8968 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8970 /* Create the Ole10Native Stream */
8971 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8972 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8976 /* Write info to stream */
8977 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8978 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8979 IStream_Release(pStream);
8984 /*************************************************************************
8985 * OLECONVERT_GetOLE10ProgID [Internal]
8987 * Finds the ProgID (or OleTypeID) from the IStorage
8990 * pStorage [I] The Src IStorage to get the ProgID
8991 * strProgID [I] the ProgID string to get
8992 * dwSize [I] the size of the string
8996 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8999 * This function is used by OleConvertIStorageToOLESTREAM only.
9003 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9007 LARGE_INTEGER iSeekPos;
9008 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9009 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9011 /* Open the CompObj Stream */
9012 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9013 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9017 /*Get the OleType from the CompObj Stream */
9018 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9019 iSeekPos.u.HighPart = 0;
9021 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9022 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9023 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9024 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9025 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9026 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9027 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9029 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9032 IStream_Read(pStream, strProgID, *dwSize, NULL);
9034 IStream_Release(pStream);
9039 LPOLESTR wstrProgID;
9041 /* Get the OleType from the registry */
9042 REFCLSID clsid = &(stat.clsid);
9043 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9044 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9047 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9054 /*************************************************************************
9055 * OLECONVERT_GetOle10PresData [Internal]
9057 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9060 * pStorage [I] Src IStroage
9061 * pOleStream [I] Dest OleStream Mem Struct
9067 * This function is used by OleConvertIStorageToOLESTREAM only.
9069 * Memory allocated for pData must be freed by the caller
9073 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9078 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9080 /* Initialize Default data for OLESTREAM */
9081 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9082 pOleStreamData[0].dwTypeID = 2;
9083 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9084 pOleStreamData[1].dwTypeID = 0;
9085 pOleStreamData[0].dwMetaFileWidth = 0;
9086 pOleStreamData[0].dwMetaFileHeight = 0;
9087 pOleStreamData[0].pData = NULL;
9088 pOleStreamData[1].pData = NULL;
9090 /* Open Ole10Native Stream */
9091 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9092 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9096 /* Read Size and Data */
9097 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9098 if(pOleStreamData->dwDataLength > 0)
9100 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9101 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9103 IStream_Release(pStream);
9109 /*************************************************************************
9110 * OLECONVERT_GetOle20PresData[Internal]
9112 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9115 * pStorage [I] Src IStroage
9116 * pOleStreamData [I] Dest OleStream Mem Struct
9122 * This function is used by OleConvertIStorageToOLESTREAM only.
9124 * Memory allocated for pData must be freed by the caller
9126 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9130 OLECONVERT_ISTORAGE_OLEPRES olePress;
9131 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9133 /* Initialize Default data for OLESTREAM */
9134 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9135 pOleStreamData[0].dwTypeID = 2;
9136 pOleStreamData[0].dwMetaFileWidth = 0;
9137 pOleStreamData[0].dwMetaFileHeight = 0;
9138 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9139 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9140 pOleStreamData[1].dwTypeID = 0;
9141 pOleStreamData[1].dwOleTypeNameLength = 0;
9142 pOleStreamData[1].strOleTypeName[0] = 0;
9143 pOleStreamData[1].dwMetaFileWidth = 0;
9144 pOleStreamData[1].dwMetaFileHeight = 0;
9145 pOleStreamData[1].pData = NULL;
9146 pOleStreamData[1].dwDataLength = 0;
9149 /* Open OlePress000 stream */
9150 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9151 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9154 LARGE_INTEGER iSeekPos;
9155 METAFILEPICT16 MetaFilePict;
9156 static const char strMetafilePictName[] = "METAFILEPICT";
9158 /* Set the TypeID for a Metafile */
9159 pOleStreamData[1].dwTypeID = 5;
9161 /* Set the OleTypeName to Metafile */
9162 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9163 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9165 iSeekPos.u.HighPart = 0;
9166 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9168 /* Get Presentation Data */
9169 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9170 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9171 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9172 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9174 /*Set width and Height */
9175 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9176 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9177 if(olePress.dwSize > 0)
9180 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9182 /* Set MetaFilePict struct */
9183 MetaFilePict.mm = 8;
9184 MetaFilePict.xExt = olePress.dwExtentX;
9185 MetaFilePict.yExt = olePress.dwExtentY;
9186 MetaFilePict.hMF = 0;
9188 /* Get Metafile Data */
9189 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9190 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9191 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9193 IStream_Release(pStream);
9197 /*************************************************************************
9198 * OleConvertOLESTREAMToIStorage [OLE32.@]
9203 * DVTARGETDEVICE parameter is not handled
9204 * Still unsure of some mem fields for OLE 10 Stream
9205 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9206 * and "\001OLE" streams
9209 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9210 LPOLESTREAM pOleStream,
9212 const DVTARGETDEVICE* ptd)
9216 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9218 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9220 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9224 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9227 if(pstg == NULL || pOleStream == NULL)
9229 hRes = E_INVALIDARG;
9234 /* Load the OLESTREAM to Memory */
9235 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9240 /* Load the OLESTREAM to Memory (part 2)*/
9241 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9247 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9249 /* Do we have the IStorage Data in the OLESTREAM */
9250 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9252 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9253 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9257 /* It must be an original OLE 1.0 source */
9258 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9263 /* It must be an original OLE 1.0 source */
9264 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9267 /* Create CompObj Stream if necessary */
9268 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9271 /*Create the Ole Stream if necessary */
9272 OLECONVERT_CreateOleStream(pstg);
9277 /* Free allocated memory */
9278 for(i=0; i < 2; i++)
9280 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9281 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9282 pOleStreamData[i].pstrOleObjFileName = NULL;
9287 /*************************************************************************
9288 * OleConvertIStorageToOLESTREAM [OLE32.@]
9295 * Still unsure of some mem fields for OLE 10 Stream
9296 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9297 * and "\001OLE" streams.
9300 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9302 LPOLESTREAM pOleStream)
9305 HRESULT hRes = S_OK;
9307 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9308 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9310 TRACE("%p %p\n", pstg, pOleStream);
9312 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9314 if(pstg == NULL || pOleStream == NULL)
9316 hRes = E_INVALIDARG;
9320 /* Get the ProgID */
9321 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9322 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9326 /* Was it originally Ole10 */
9327 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9330 IStream_Release(pStream);
9331 /* Get Presentation Data for Ole10Native */
9332 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9336 /* Get Presentation Data (OLE20) */
9337 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9340 /* Save OLESTREAM */
9341 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9344 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9349 /* Free allocated memory */
9350 for(i=0; i < 2; i++)
9352 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9358 /***********************************************************************
9359 * GetConvertStg (OLE32.@)
9361 HRESULT WINAPI GetConvertStg(IStorage *stg) {
9362 FIXME("unimplemented stub!\n");
9366 /******************************************************************************
9367 * StgIsStorageFile [OLE32.@]
9368 * Verify if the file contains a storage object
9374 * S_OK if file has magic bytes as a storage object
9375 * S_FALSE if file is not storage
9378 StgIsStorageFile(LPCOLESTR fn)
9384 TRACE("%s\n", debugstr_w(fn));
9385 hf = CreateFileW(fn, GENERIC_READ,
9386 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9387 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9389 if (hf == INVALID_HANDLE_VALUE)
9390 return STG_E_FILENOTFOUND;
9392 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9394 WARN(" unable to read file\n");
9401 if (bytes_read != 8) {
9402 TRACE(" too short\n");
9406 if (!memcmp(magic,STORAGE_magic,8)) {
9411 TRACE(" -> Invalid header.\n");
9415 /***********************************************************************
9416 * WriteClassStm (OLE32.@)
9418 * Writes a CLSID to a stream.
9421 * pStm [I] Stream to write to.
9422 * rclsid [I] CLSID to write.
9426 * Failure: HRESULT code.
9428 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9430 TRACE("(%p,%p)\n",pStm,rclsid);
9432 if (!pStm || !rclsid)
9433 return E_INVALIDARG;
9435 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9438 /***********************************************************************
9439 * ReadClassStm (OLE32.@)
9441 * Reads a CLSID from a stream.
9444 * pStm [I] Stream to read from.
9445 * rclsid [O] CLSID to read.
9449 * Failure: HRESULT code.
9451 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9456 TRACE("(%p,%p)\n",pStm,pclsid);
9458 if (!pStm || !pclsid)
9459 return E_INVALIDARG;
9461 /* clear the output args */
9462 *pclsid = CLSID_NULL;
9464 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
9469 if (nbByte != sizeof(CLSID))
9470 return STG_E_READFAULT;