2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootEntryName[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base;
82 * Entry in the parent's stream tracking list
84 struct list ParentListEntry;
86 StorageBaseImpl *parentStorage;
88 typedef struct StorageInternalImpl StorageInternalImpl;
90 /* Method definitions for the Storage32InternalImpl class. */
91 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
92 DWORD openFlags, DirRef storageDirEntry);
93 static void StorageImpl_Destroy(StorageBaseImpl* iface);
94 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
95 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
96 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
97 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
98 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
99 static void StorageImpl_SaveFileHeader(StorageImpl* This);
101 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
102 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
103 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
104 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
105 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
107 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
108 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
109 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
111 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
112 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
113 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
114 ULONG blockIndex, ULONG offset, DWORD value);
115 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
116 ULONG blockIndex, ULONG offset, DWORD* value);
118 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
119 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
121 /****************************************************************************
122 * Transacted storage object that reads/writes a snapshot file.
124 typedef struct TransactedSnapshotImpl
126 struct StorageBaseImpl base;
129 * Changes are temporarily saved to the snapshot.
131 StorageBaseImpl *snapshot;
134 * Changes are committed to the transacted parent.
136 StorageBaseImpl *transactedParent;
137 } TransactedSnapshotImpl;
139 /* Generic function to create a transacted wrapper for a direct storage object. */
140 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
142 /* OLESTREAM memory structure to use for Get and Put Routines */
143 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
148 DWORD dwOleTypeNameLength;
149 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
150 CHAR *pstrOleObjFileName;
151 DWORD dwOleObjFileNameLength;
152 DWORD dwMetaFileWidth;
153 DWORD dwMetaFileHeight;
154 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
157 }OLECONVERT_OLESTREAM_DATA;
159 /* CompObj Stream structure */
160 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
165 DWORD dwCLSIDNameLength;
166 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
167 DWORD dwOleTypeNameLength;
168 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
169 DWORD dwProgIDNameLength;
170 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
172 }OLECONVERT_ISTORAGE_COMPOBJ;
175 /* Ole Presentation Stream structure */
176 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
184 }OLECONVERT_ISTORAGE_OLEPRES;
188 /***********************************************************************
189 * Forward declaration of internal functions used by the method DestroyElement
191 static HRESULT deleteStorageContents(
192 StorageBaseImpl *parentStorage,
193 DirRef indexToDelete,
194 DirEntry entryDataToDelete);
196 static HRESULT deleteStreamContents(
197 StorageBaseImpl *parentStorage,
198 DirRef indexToDelete,
199 DirEntry entryDataToDelete);
201 static HRESULT removeFromTree(
202 StorageBaseImpl *This,
203 DirRef parentStorageIndex,
204 DirRef deletedIndex);
206 /***********************************************************************
207 * Declaration of the functions used to manipulate DirEntry
210 static HRESULT insertIntoTree(
211 StorageBaseImpl *This,
212 DirRef parentStorageIndex,
213 DirRef newEntryIndex);
215 static LONG entryNameCmp(
216 const OLECHAR *name1,
217 const OLECHAR *name2);
219 static DirRef findElement(
220 StorageBaseImpl *storage,
225 static HRESULT findTreeParent(
226 StorageBaseImpl *storage,
228 const OLECHAR *childName,
229 DirEntry *parentData,
233 /***********************************************************************
234 * Declaration of miscellaneous functions...
236 static HRESULT validateSTGM(DWORD stgmValue);
238 static DWORD GetShareModeFromSTGM(DWORD stgm);
239 static DWORD GetAccessModeFromSTGM(DWORD stgm);
240 static DWORD GetCreationModeFromSTGM(DWORD stgm);
242 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
245 /****************************************************************************
246 * IEnumSTATSTGImpl definitions.
248 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
249 * This class allows iterating through the content of a storage and to find
250 * specific items inside it.
252 struct IEnumSTATSTGImpl
254 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
255 * since we want to cast this in an IEnumSTATSTG pointer */
257 LONG ref; /* Reference count */
258 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
259 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
262 * The current implementation of the IEnumSTATSTGImpl class uses a stack
263 * to walk the directory entries to get the content of a storage. This stack
264 * is implemented by the following 3 data members
268 DirRef* stackToVisit;
270 #define ENUMSTATSGT_SIZE_INCREMENT 10
274 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
275 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
276 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, DirRef nodeToPush);
277 static DirRef IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
279 /************************************************************************
283 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
285 if (index == 0xffffffff)
290 return index * BIG_BLOCK_SIZE;
293 /************************************************************************
294 ** Storage32BaseImpl implementation
296 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
297 ULARGE_INTEGER offset,
302 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
305 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
306 ULARGE_INTEGER offset,
311 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
314 /************************************************************************
315 * Storage32BaseImpl_QueryInterface (IUnknown)
317 * This method implements the common QueryInterface for all IStorage32
318 * implementations contained in this file.
320 * See Windows documentation for more details on IUnknown methods.
322 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
327 StorageBaseImpl *This = (StorageBaseImpl *)iface;
329 if ( (This==0) || (ppvObject==0) )
334 if (IsEqualGUID(&IID_IUnknown, riid) ||
335 IsEqualGUID(&IID_IStorage, riid))
339 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
341 *ppvObject = &This->pssVtbl;
345 return E_NOINTERFACE;
347 IStorage_AddRef(iface);
352 /************************************************************************
353 * Storage32BaseImpl_AddRef (IUnknown)
355 * This method implements the common AddRef for all IStorage32
356 * implementations contained in this file.
358 * See Windows documentation for more details on IUnknown methods.
360 static ULONG WINAPI StorageBaseImpl_AddRef(
363 StorageBaseImpl *This = (StorageBaseImpl *)iface;
364 ULONG ref = InterlockedIncrement(&This->ref);
366 TRACE("(%p) AddRef to %d\n", This, ref);
371 /************************************************************************
372 * Storage32BaseImpl_Release (IUnknown)
374 * This method implements the common Release for all IStorage32
375 * implementations contained in this file.
377 * See Windows documentation for more details on IUnknown methods.
379 static ULONG WINAPI StorageBaseImpl_Release(
382 StorageBaseImpl *This = (StorageBaseImpl *)iface;
384 ULONG ref = InterlockedDecrement(&This->ref);
386 TRACE("(%p) ReleaseRef to %d\n", This, ref);
391 * Since we are using a system of base-classes, we want to call the
392 * destructor of the appropriate derived class. To do this, we are
393 * using virtual functions to implement the destructor.
395 StorageBaseImpl_Destroy(This);
401 /************************************************************************
402 * Storage32BaseImpl_OpenStream (IStorage)
404 * This method will open the specified stream object from the current storage.
406 * See Windows documentation for more details on IStorage methods.
408 static HRESULT WINAPI StorageBaseImpl_OpenStream(
410 const OLECHAR* pwcsName, /* [string][in] */
411 void* reserved1, /* [unique][in] */
412 DWORD grfMode, /* [in] */
413 DWORD reserved2, /* [in] */
414 IStream** ppstm) /* [out] */
416 StorageBaseImpl *This = (StorageBaseImpl *)iface;
417 StgStreamImpl* newStream;
418 DirEntry currentEntry;
419 DirRef streamEntryRef;
420 HRESULT res = STG_E_UNKNOWN;
422 TRACE("(%p, %s, %p, %x, %d, %p)\n",
423 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
425 if ( (pwcsName==NULL) || (ppstm==0) )
433 if ( FAILED( validateSTGM(grfMode) ) ||
434 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
436 res = STG_E_INVALIDFLAG;
443 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
445 res = STG_E_INVALIDFUNCTION;
451 res = STG_E_REVERTED;
456 * Check that we're compatible with the parent's storage mode, but
457 * only if we are not in transacted mode
459 if(!(This->openFlags & STGM_TRANSACTED)) {
460 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
462 res = STG_E_ACCESSDENIED;
468 * Search for the element with the given name
470 streamEntryRef = findElement(
472 This->storageDirEntry,
477 * If it was found, construct the stream object and return a pointer to it.
479 if ( (streamEntryRef!=DIRENTRY_NULL) &&
480 (currentEntry.stgType==STGTY_STREAM) )
482 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
484 /* A single stream cannot be opened a second time. */
485 res = STG_E_ACCESSDENIED;
489 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
493 newStream->grfMode = grfMode;
494 *ppstm = (IStream*)newStream;
496 IStream_AddRef(*ppstm);
506 res = STG_E_FILENOTFOUND;
510 TRACE("<-- IStream %p\n", *ppstm);
511 TRACE("<-- %08x\n", res);
515 /************************************************************************
516 * Storage32BaseImpl_OpenStorage (IStorage)
518 * This method will open a new storage object from the current storage.
520 * See Windows documentation for more details on IStorage methods.
522 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
524 const OLECHAR* pwcsName, /* [string][unique][in] */
525 IStorage* pstgPriority, /* [unique][in] */
526 DWORD grfMode, /* [in] */
527 SNB snbExclude, /* [unique][in] */
528 DWORD reserved, /* [in] */
529 IStorage** ppstg) /* [out] */
531 StorageBaseImpl *This = (StorageBaseImpl *)iface;
532 StorageInternalImpl* newStorage;
533 StorageBaseImpl* newTransactedStorage;
534 DirEntry currentEntry;
535 DirRef storageEntryRef;
536 HRESULT res = STG_E_UNKNOWN;
538 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
539 iface, debugstr_w(pwcsName), pstgPriority,
540 grfMode, snbExclude, reserved, ppstg);
542 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
548 if (This->openFlags & STGM_SIMPLE)
550 res = STG_E_INVALIDFUNCTION;
555 if (snbExclude != NULL)
557 res = STG_E_INVALIDPARAMETER;
561 if ( FAILED( validateSTGM(grfMode) ))
563 res = STG_E_INVALIDFLAG;
570 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
571 (grfMode & STGM_DELETEONRELEASE) ||
572 (grfMode & STGM_PRIORITY) )
574 res = STG_E_INVALIDFUNCTION;
579 return STG_E_REVERTED;
582 * Check that we're compatible with the parent's storage mode,
583 * but only if we are not transacted
585 if(!(This->openFlags & STGM_TRANSACTED)) {
586 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
588 res = STG_E_ACCESSDENIED;
595 storageEntryRef = findElement(
597 This->storageDirEntry,
601 if ( (storageEntryRef!=DIRENTRY_NULL) &&
602 (currentEntry.stgType==STGTY_STORAGE) )
604 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
606 /* A single storage cannot be opened a second time. */
607 res = STG_E_ACCESSDENIED;
611 newStorage = StorageInternalImpl_Construct(
618 if (grfMode & STGM_TRANSACTED)
620 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
624 HeapFree(GetProcessHeap(), 0, newStorage);
628 *ppstg = (IStorage*)newTransactedStorage;
632 *ppstg = (IStorage*)newStorage;
635 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
641 res = STG_E_INSUFFICIENTMEMORY;
645 res = STG_E_FILENOTFOUND;
648 TRACE("<-- %08x\n", res);
652 /************************************************************************
653 * Storage32BaseImpl_EnumElements (IStorage)
655 * This method will create an enumerator object that can be used to
656 * retrieve information about all the elements in the storage object.
658 * See Windows documentation for more details on IStorage methods.
660 static HRESULT WINAPI StorageBaseImpl_EnumElements(
662 DWORD reserved1, /* [in] */
663 void* reserved2, /* [size_is][unique][in] */
664 DWORD reserved3, /* [in] */
665 IEnumSTATSTG** ppenum) /* [out] */
667 StorageBaseImpl *This = (StorageBaseImpl *)iface;
668 IEnumSTATSTGImpl* newEnum;
670 TRACE("(%p, %d, %p, %d, %p)\n",
671 iface, reserved1, reserved2, reserved3, ppenum);
673 if ( (This==0) || (ppenum==0))
677 return STG_E_REVERTED;
679 newEnum = IEnumSTATSTGImpl_Construct(
681 This->storageDirEntry);
685 *ppenum = (IEnumSTATSTG*)newEnum;
687 IEnumSTATSTG_AddRef(*ppenum);
692 return E_OUTOFMEMORY;
695 /************************************************************************
696 * Storage32BaseImpl_Stat (IStorage)
698 * This method will retrieve information about this storage object.
700 * See Windows documentation for more details on IStorage methods.
702 static HRESULT WINAPI StorageBaseImpl_Stat(
704 STATSTG* pstatstg, /* [out] */
705 DWORD grfStatFlag) /* [in] */
707 StorageBaseImpl *This = (StorageBaseImpl *)iface;
708 DirEntry currentEntry;
709 HRESULT res = STG_E_UNKNOWN;
711 TRACE("(%p, %p, %x)\n",
712 iface, pstatstg, grfStatFlag);
714 if ( (This==0) || (pstatstg==0))
722 res = STG_E_REVERTED;
726 res = StorageBaseImpl_ReadDirEntry(
728 This->storageDirEntry,
733 StorageUtl_CopyDirEntryToSTATSTG(
739 pstatstg->grfMode = This->openFlags;
740 pstatstg->grfStateBits = This->stateBits;
746 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);
748 TRACE("<-- %08x\n", res);
752 /************************************************************************
753 * Storage32BaseImpl_RenameElement (IStorage)
755 * This method will rename the specified element.
757 * See Windows documentation for more details on IStorage methods.
759 static HRESULT WINAPI StorageBaseImpl_RenameElement(
761 const OLECHAR* pwcsOldName, /* [in] */
762 const OLECHAR* pwcsNewName) /* [in] */
764 StorageBaseImpl *This = (StorageBaseImpl *)iface;
765 DirEntry currentEntry;
766 DirRef currentEntryRef;
768 TRACE("(%p, %s, %s)\n",
769 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
772 return STG_E_REVERTED;
774 currentEntryRef = findElement(This,
775 This->storageDirEntry,
779 if (currentEntryRef != DIRENTRY_NULL)
782 * There is already an element with the new name
784 return STG_E_FILEALREADYEXISTS;
788 * Search for the old element name
790 currentEntryRef = findElement(This,
791 This->storageDirEntry,
795 if (currentEntryRef != DIRENTRY_NULL)
797 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
798 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
800 WARN("Element is already open; cannot rename.\n");
801 return STG_E_ACCESSDENIED;
804 /* Remove the element from its current position in the tree */
805 removeFromTree(This, This->storageDirEntry,
808 /* Change the name of the element */
809 strcpyW(currentEntry.name, pwcsNewName);
811 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
814 /* Insert the element in a new position in the tree */
815 insertIntoTree(This, This->storageDirEntry,
821 * There is no element with the old name
823 return STG_E_FILENOTFOUND;
829 /************************************************************************
830 * Storage32BaseImpl_CreateStream (IStorage)
832 * This method will create a stream object within this storage
834 * See Windows documentation for more details on IStorage methods.
836 static HRESULT WINAPI StorageBaseImpl_CreateStream(
838 const OLECHAR* pwcsName, /* [string][in] */
839 DWORD grfMode, /* [in] */
840 DWORD reserved1, /* [in] */
841 DWORD reserved2, /* [in] */
842 IStream** ppstm) /* [out] */
844 StorageBaseImpl *This = (StorageBaseImpl *)iface;
845 StgStreamImpl* newStream;
846 DirEntry currentEntry, newStreamEntry;
847 DirRef currentEntryRef, newStreamEntryRef;
849 TRACE("(%p, %s, %x, %d, %d, %p)\n",
850 iface, debugstr_w(pwcsName), grfMode,
851 reserved1, reserved2, ppstm);
854 return STG_E_INVALIDPOINTER;
857 return STG_E_INVALIDNAME;
859 if (reserved1 || reserved2)
860 return STG_E_INVALIDPARAMETER;
862 if ( FAILED( validateSTGM(grfMode) ))
863 return STG_E_INVALIDFLAG;
865 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
866 return STG_E_INVALIDFLAG;
869 return STG_E_REVERTED;
874 if ((grfMode & STGM_DELETEONRELEASE) ||
875 (grfMode & STGM_TRANSACTED))
876 return STG_E_INVALIDFUNCTION;
878 /* Can't create a stream on read-only storage */
879 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
880 return STG_E_ACCESSDENIED;
883 * Check that we're compatible with the parent's storage mode
884 * if not in transacted mode
886 if(!(This->openFlags & STGM_TRANSACTED)) {
887 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
888 return STG_E_ACCESSDENIED;
891 if(This->openFlags & STGM_SIMPLE)
892 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
896 currentEntryRef = findElement(This,
897 This->storageDirEntry,
901 if (currentEntryRef != DIRENTRY_NULL)
904 * An element with this name already exists
906 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
908 IStorage_DestroyElement(iface, pwcsName);
911 return STG_E_FILEALREADYEXISTS;
913 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
915 WARN("read-only storage\n");
916 return STG_E_ACCESSDENIED;
920 * memset the empty entry
922 memset(&newStreamEntry, 0, sizeof(DirEntry));
924 newStreamEntry.sizeOfNameString =
925 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
927 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
928 return STG_E_INVALIDNAME;
930 strcpyW(newStreamEntry.name, pwcsName);
932 newStreamEntry.stgType = STGTY_STREAM;
933 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
934 newStreamEntry.size.u.LowPart = 0;
935 newStreamEntry.size.u.HighPart = 0;
937 newStreamEntry.leftChild = DIRENTRY_NULL;
938 newStreamEntry.rightChild = DIRENTRY_NULL;
939 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
941 /* call CoFileTime to get the current time
946 /* newStreamEntry.clsid */
949 * Create an entry with the new data
951 StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
953 * Insert the new entry in the parent storage's tree.
957 This->storageDirEntry,
961 * Open the stream to return it.
963 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
967 *ppstm = (IStream*)newStream;
969 IStream_AddRef(*ppstm);
973 return STG_E_INSUFFICIENTMEMORY;
979 /************************************************************************
980 * Storage32BaseImpl_SetClass (IStorage)
982 * This method will write the specified CLSID in the directory entry of this
985 * See Windows documentation for more details on IStorage methods.
987 static HRESULT WINAPI StorageBaseImpl_SetClass(
989 REFCLSID clsid) /* [in] */
991 StorageBaseImpl *This = (StorageBaseImpl *)iface;
993 DirEntry currentEntry;
995 TRACE("(%p, %p)\n", iface, clsid);
998 return STG_E_REVERTED;
1000 hRes = StorageBaseImpl_ReadDirEntry(This,
1001 This->storageDirEntry,
1003 if (SUCCEEDED(hRes))
1005 currentEntry.clsid = *clsid;
1007 hRes = StorageBaseImpl_WriteDirEntry(This,
1008 This->storageDirEntry,
1015 /************************************************************************
1016 ** Storage32Impl implementation
1019 /************************************************************************
1020 * Storage32BaseImpl_CreateStorage (IStorage)
1022 * This method will create the storage object within the provided storage.
1024 * See Windows documentation for more details on IStorage methods.
1026 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1028 const OLECHAR *pwcsName, /* [string][in] */
1029 DWORD grfMode, /* [in] */
1030 DWORD reserved1, /* [in] */
1031 DWORD reserved2, /* [in] */
1032 IStorage **ppstg) /* [out] */
1034 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1036 DirEntry currentEntry;
1038 DirRef currentEntryRef;
1042 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1043 iface, debugstr_w(pwcsName), grfMode,
1044 reserved1, reserved2, ppstg);
1047 return STG_E_INVALIDPOINTER;
1049 if (This->openFlags & STGM_SIMPLE)
1051 return STG_E_INVALIDFUNCTION;
1055 return STG_E_INVALIDNAME;
1059 if ( FAILED( validateSTGM(grfMode) ) ||
1060 (grfMode & STGM_DELETEONRELEASE) )
1062 WARN("bad grfMode: 0x%x\n", grfMode);
1063 return STG_E_INVALIDFLAG;
1067 return STG_E_REVERTED;
1070 * Check that we're compatible with the parent's storage mode
1072 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1074 WARN("access denied\n");
1075 return STG_E_ACCESSDENIED;
1078 currentEntryRef = findElement(This,
1079 This->storageDirEntry,
1083 if (currentEntryRef != DIRENTRY_NULL)
1086 * An element with this name already exists
1088 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1089 STGM_ACCESS_MODE(This->openFlags) != STGM_READ)
1091 hr = IStorage_DestroyElement(iface, pwcsName);
1097 WARN("file already exists\n");
1098 return STG_E_FILEALREADYEXISTS;
1101 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1103 WARN("read-only storage\n");
1104 return STG_E_ACCESSDENIED;
1107 memset(&newEntry, 0, sizeof(DirEntry));
1109 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1111 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1113 FIXME("name too long\n");
1114 return STG_E_INVALIDNAME;
1117 strcpyW(newEntry.name, pwcsName);
1119 newEntry.stgType = STGTY_STORAGE;
1120 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1121 newEntry.size.u.LowPart = 0;
1122 newEntry.size.u.HighPart = 0;
1124 newEntry.leftChild = DIRENTRY_NULL;
1125 newEntry.rightChild = DIRENTRY_NULL;
1126 newEntry.dirRootEntry = DIRENTRY_NULL;
1128 /* call CoFileTime to get the current time
1133 /* newEntry.clsid */
1136 * Create a new directory entry for the storage
1138 StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1141 * Insert the new directory entry into the parent storage's tree
1145 This->storageDirEntry,
1149 * Open it to get a pointer to return.
1151 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1153 if( (hr != S_OK) || (*ppstg == NULL))
1163 /***************************************************************************
1167 * Reserve a directory entry in the file and initialize it.
1169 static HRESULT StorageImpl_CreateDirEntry(
1170 StorageBaseImpl *base,
1171 const DirEntry *newData,
1174 StorageImpl *storage = (StorageImpl*)base;
1175 ULONG currentEntryIndex = 0;
1176 ULONG newEntryIndex = DIRENTRY_NULL;
1178 BYTE currentData[RAW_DIRENTRY_SIZE];
1179 WORD sizeOfNameString;
1183 hr = StorageImpl_ReadRawDirEntry(storage,
1189 StorageUtl_ReadWord(
1191 OFFSET_PS_NAMELENGTH,
1194 if (sizeOfNameString == 0)
1197 * The entry exists and is available, we found it.
1199 newEntryIndex = currentEntryIndex;
1205 * We exhausted the directory entries, we will create more space below
1207 newEntryIndex = currentEntryIndex;
1209 currentEntryIndex++;
1211 } while (newEntryIndex == DIRENTRY_NULL);
1214 * grow the directory stream
1218 BYTE emptyData[RAW_DIRENTRY_SIZE];
1219 ULARGE_INTEGER newSize;
1221 ULONG lastEntry = 0;
1222 ULONG blockCount = 0;
1225 * obtain the new count of blocks in the directory stream
1227 blockCount = BlockChainStream_GetCount(
1228 storage->rootBlockChain)+1;
1231 * initialize the size used by the directory stream
1233 newSize.u.HighPart = 0;
1234 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1237 * add a block to the directory stream
1239 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1242 * memset the empty entry in order to initialize the unused newly
1245 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1250 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1253 entryIndex = newEntryIndex + 1;
1254 entryIndex < lastEntry;
1257 StorageImpl_WriteRawDirEntry(
1264 UpdateRawDirEntry(currentData, newData);
1266 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1269 *index = newEntryIndex;
1274 /***************************************************************************
1278 * Mark a directory entry in the file as free.
1280 static HRESULT StorageImpl_DestroyDirEntry(
1281 StorageBaseImpl *base,
1285 BYTE emptyData[RAW_DIRENTRY_SIZE];
1286 StorageImpl *storage = (StorageImpl*)base;
1288 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1290 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1296 /***************************************************************************
1300 * Destroy an entry, its attached data, and all entries reachable from it.
1302 static HRESULT DestroyReachableEntries(
1303 StorageBaseImpl *base,
1308 ULARGE_INTEGER zero;
1312 if (index != DIRENTRY_NULL)
1314 hr = StorageBaseImpl_ReadDirEntry(base, index, &data);
1317 hr = DestroyReachableEntries(base, data.dirRootEntry);
1320 hr = DestroyReachableEntries(base, data.leftChild);
1323 hr = DestroyReachableEntries(base, data.rightChild);
1326 hr = StorageBaseImpl_StreamSetSize(base, index, zero);
1329 hr = StorageBaseImpl_DestroyDirEntry(base, index);
1336 /****************************************************************************
1340 * Case insensitive comparison of DirEntry.name by first considering
1343 * Returns <0 when name1 < name2
1344 * >0 when name1 > name2
1345 * 0 when name1 == name2
1347 static LONG entryNameCmp(
1348 const OLECHAR *name1,
1349 const OLECHAR *name2)
1351 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1353 while (diff == 0 && *name1 != 0)
1356 * We compare the string themselves only when they are of the same length
1358 diff = toupperW(*name1++) - toupperW(*name2++);
1364 /****************************************************************************
1368 * Add a directory entry to a storage
1370 static HRESULT insertIntoTree(
1371 StorageBaseImpl *This,
1372 DirRef parentStorageIndex,
1373 DirRef newEntryIndex)
1375 DirEntry currentEntry;
1379 * Read the inserted entry
1381 StorageBaseImpl_ReadDirEntry(This,
1386 * Read the storage entry
1388 StorageBaseImpl_ReadDirEntry(This,
1392 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1395 * The root storage contains some element, therefore, start the research
1396 * for the appropriate location.
1399 DirRef current, next, previous, currentEntryId;
1402 * Keep a reference to the root of the storage's element tree
1404 currentEntryId = currentEntry.dirRootEntry;
1409 StorageBaseImpl_ReadDirEntry(This,
1410 currentEntry.dirRootEntry,
1413 previous = currentEntry.leftChild;
1414 next = currentEntry.rightChild;
1415 current = currentEntryId;
1419 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1423 if (previous != DIRENTRY_NULL)
1425 StorageBaseImpl_ReadDirEntry(This,
1432 currentEntry.leftChild = newEntryIndex;
1433 StorageBaseImpl_WriteDirEntry(This,
1441 if (next != DIRENTRY_NULL)
1443 StorageBaseImpl_ReadDirEntry(This,
1450 currentEntry.rightChild = newEntryIndex;
1451 StorageBaseImpl_WriteDirEntry(This,
1460 * Trying to insert an item with the same name in the
1461 * subtree structure.
1463 return STG_E_FILEALREADYEXISTS;
1466 previous = currentEntry.leftChild;
1467 next = currentEntry.rightChild;
1473 * The storage is empty, make the new entry the root of its element tree
1475 currentEntry.dirRootEntry = newEntryIndex;
1476 StorageBaseImpl_WriteDirEntry(This,
1484 /****************************************************************************
1488 * Find and read the element of a storage with the given name.
1490 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1491 const OLECHAR *name, DirEntry *data)
1493 DirRef currentEntry;
1495 /* Read the storage entry to find the root of the tree. */
1496 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1498 currentEntry = data->dirRootEntry;
1500 while (currentEntry != DIRENTRY_NULL)
1504 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1506 cmp = entryNameCmp(name, data->name);
1513 currentEntry = data->leftChild;
1516 currentEntry = data->rightChild;
1519 return currentEntry;
1522 /****************************************************************************
1526 * Find and read the binary tree parent of the element with the given name.
1528 * If there is no such element, find a place where it could be inserted and
1529 * return STG_E_FILENOTFOUND.
1531 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1532 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1538 /* Read the storage entry to find the root of the tree. */
1539 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1541 *parentEntry = storageEntry;
1542 *relation = DIRENTRY_RELATION_DIR;
1544 childEntry = parentData->dirRootEntry;
1546 while (childEntry != DIRENTRY_NULL)
1550 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1552 cmp = entryNameCmp(childName, childData.name);
1560 *parentData = childData;
1561 *parentEntry = childEntry;
1562 *relation = DIRENTRY_RELATION_PREVIOUS;
1564 childEntry = parentData->leftChild;
1569 *parentData = childData;
1570 *parentEntry = childEntry;
1571 *relation = DIRENTRY_RELATION_NEXT;
1573 childEntry = parentData->rightChild;
1577 if (childEntry == DIRENTRY_NULL)
1578 return STG_E_FILENOTFOUND;
1584 /*************************************************************************
1587 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1589 DWORD ciidExclude, /* [in] */
1590 const IID* rgiidExclude, /* [size_is][unique][in] */
1591 SNB snbExclude, /* [unique][in] */
1592 IStorage* pstgDest) /* [unique][in] */
1594 IEnumSTATSTG *elements = 0;
1595 STATSTG curElement, strStat;
1597 IStorage *pstgTmp, *pstgChild;
1598 IStream *pstrTmp, *pstrChild;
1599 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1602 TRACE("(%p, %d, %p, %p, %p)\n",
1603 iface, ciidExclude, rgiidExclude,
1604 snbExclude, pstgDest);
1606 if ( pstgDest == 0 )
1607 return STG_E_INVALIDPOINTER;
1610 * Enumerate the elements
1612 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1620 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1621 IStorage_SetClass( pstgDest, &curElement.clsid );
1623 for(i = 0; i < ciidExclude; ++i)
1625 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1626 skip_storage = TRUE;
1627 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1630 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1636 * Obtain the next element
1638 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1640 if ( hr == S_FALSE )
1642 hr = S_OK; /* done, every element has been copied */
1648 WCHAR **snb = snbExclude;
1650 while ( *snb != NULL && !skip )
1652 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1661 if (curElement.type == STGTY_STORAGE)
1667 * open child source storage
1669 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1670 STGM_READ|STGM_SHARE_EXCLUSIVE,
1671 NULL, 0, &pstgChild );
1677 * create a new storage in destination storage
1679 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1680 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1684 * if it already exist, don't create a new one use this one
1686 if (hr == STG_E_FILEALREADYEXISTS)
1688 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1689 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1690 NULL, 0, &pstgTmp );
1696 * do the copy recursively
1698 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1701 IStorage_Release( pstgTmp );
1704 IStorage_Release( pstgChild );
1706 else if (curElement.type == STGTY_STREAM)
1712 * create a new stream in destination storage. If the stream already
1713 * exist, it will be deleted and a new one will be created.
1715 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1716 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1723 * open child stream storage
1725 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1726 STGM_READ|STGM_SHARE_EXCLUSIVE,
1732 * Get the size of the source stream
1734 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1737 * Set the size of the destination stream.
1739 IStream_SetSize(pstrTmp, strStat.cbSize);
1744 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1747 IStream_Release( pstrChild );
1750 IStream_Release( pstrTmp );
1754 WARN("unknown element type: %d\n", curElement.type);
1758 CoTaskMemFree(curElement.pwcsName);
1759 } while (hr == S_OK);
1764 IEnumSTATSTG_Release(elements);
1769 /*************************************************************************
1770 * MoveElementTo (IStorage)
1772 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1774 const OLECHAR *pwcsName, /* [string][in] */
1775 IStorage *pstgDest, /* [unique][in] */
1776 const OLECHAR *pwcsNewName,/* [string][in] */
1777 DWORD grfFlags) /* [in] */
1779 FIXME("(%p %s %p %s %u): stub\n", iface,
1780 debugstr_w(pwcsName), pstgDest,
1781 debugstr_w(pwcsNewName), grfFlags);
1785 /*************************************************************************
1788 * Ensures that any changes made to a storage object open in transacted mode
1789 * are reflected in the parent storage
1792 * Wine doesn't implement transacted mode, which seems to be a basic
1793 * optimization, so we can ignore this stub for now.
1795 static HRESULT WINAPI StorageImpl_Commit(
1797 DWORD grfCommitFlags)/* [in] */
1799 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1803 /*************************************************************************
1806 * Discard all changes that have been made since the last commit operation
1808 static HRESULT WINAPI StorageImpl_Revert(
1811 FIXME("(%p): stub\n", iface);
1815 /*************************************************************************
1816 * DestroyElement (IStorage)
1818 * Strategy: This implementation is built this way for simplicity not for speed.
1819 * I always delete the topmost element of the enumeration and adjust
1820 * the deleted element pointer all the time. This takes longer to
1821 * do but allow to reinvoke DestroyElement whenever we encounter a
1822 * storage object. The optimisation resides in the usage of another
1823 * enumeration strategy that would give all the leaves of a storage
1824 * first. (postfix order)
1826 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1828 const OLECHAR *pwcsName)/* [string][in] */
1830 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1833 DirEntry entryToDelete;
1834 DirRef entryToDeleteRef;
1837 iface, debugstr_w(pwcsName));
1840 return STG_E_INVALIDPOINTER;
1843 return STG_E_REVERTED;
1845 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1846 return STG_E_ACCESSDENIED;
1848 entryToDeleteRef = findElement(
1850 This->storageDirEntry,
1854 if ( entryToDeleteRef == DIRENTRY_NULL )
1856 return STG_E_FILENOTFOUND;
1859 if ( entryToDelete.stgType == STGTY_STORAGE )
1861 hr = deleteStorageContents(
1866 else if ( entryToDelete.stgType == STGTY_STREAM )
1868 hr = deleteStreamContents(
1878 * Remove the entry from its parent storage
1880 hr = removeFromTree(
1882 This->storageDirEntry,
1886 * Invalidate the entry
1889 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1895 /******************************************************************************
1896 * Internal stream list handlers
1899 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1901 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1902 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1905 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1907 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1908 list_remove(&(strm->StrmListEntry));
1911 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1913 StgStreamImpl *strm;
1915 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1917 if (strm->dirEntry == streamEntry)
1926 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1928 StorageInternalImpl *childstg;
1930 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1932 if (childstg->base.storageDirEntry == storageEntry)
1941 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1943 struct list *cur, *cur2;
1944 StgStreamImpl *strm=NULL;
1945 StorageInternalImpl *childstg=NULL;
1947 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1948 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1949 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1950 strm->parentStorage = NULL;
1954 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1955 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1956 StorageBaseImpl_Invalidate( &childstg->base );
1959 if (stg->transactedChild)
1961 StorageBaseImpl_Invalidate(stg->transactedChild);
1963 stg->transactedChild = NULL;
1968 /*********************************************************************
1972 * Delete the contents of a storage entry.
1975 static HRESULT deleteStorageContents(
1976 StorageBaseImpl *parentStorage,
1977 DirRef indexToDelete,
1978 DirEntry entryDataToDelete)
1980 IEnumSTATSTG *elements = 0;
1981 IStorage *childStorage = 0;
1982 STATSTG currentElement;
1984 HRESULT destroyHr = S_OK;
1985 StorageInternalImpl *stg, *stg2;
1987 /* Invalidate any open storage objects. */
1988 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
1990 if (stg->base.storageDirEntry == indexToDelete)
1992 StorageBaseImpl_Invalidate(&stg->base);
1997 * Open the storage and enumerate it
1999 hr = StorageBaseImpl_OpenStorage(
2000 (IStorage*)parentStorage,
2001 entryDataToDelete.name,
2003 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2014 * Enumerate the elements
2016 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2021 * Obtain the next element
2023 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
2026 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2028 CoTaskMemFree(currentElement.pwcsName);
2032 * We need to Reset the enumeration every time because we delete elements
2033 * and the enumeration could be invalid
2035 IEnumSTATSTG_Reset(elements);
2037 } while ((hr == S_OK) && (destroyHr == S_OK));
2039 IStorage_Release(childStorage);
2040 IEnumSTATSTG_Release(elements);
2045 /*********************************************************************
2049 * Perform the deletion of a stream's data
2052 static HRESULT deleteStreamContents(
2053 StorageBaseImpl *parentStorage,
2054 DirRef indexToDelete,
2055 DirEntry entryDataToDelete)
2059 ULARGE_INTEGER size;
2060 StgStreamImpl *strm, *strm2;
2062 /* Invalidate any open stream objects. */
2063 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2065 if (strm->dirEntry == indexToDelete)
2067 TRACE("Stream deleted %p\n", strm);
2068 strm->parentStorage = NULL;
2069 list_remove(&strm->StrmListEntry);
2073 size.u.HighPart = 0;
2076 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2077 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2087 hr = IStream_SetSize(pis, size);
2095 * Release the stream object.
2097 IStream_Release(pis);
2102 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2106 case DIRENTRY_RELATION_PREVIOUS:
2107 entry->leftChild = new_target;
2109 case DIRENTRY_RELATION_NEXT:
2110 entry->rightChild = new_target;
2112 case DIRENTRY_RELATION_DIR:
2113 entry->dirRootEntry = new_target;
2120 /*************************************************************************
2124 * This method removes a directory entry from its parent storage tree without
2125 * freeing any resources attached to it.
2127 static HRESULT removeFromTree(
2128 StorageBaseImpl *This,
2129 DirRef parentStorageIndex,
2130 DirRef deletedIndex)
2133 DirEntry entryToDelete;
2134 DirEntry parentEntry;
2135 DirRef parentEntryRef;
2136 ULONG typeOfRelation;
2138 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2144 * Find the element that links to the one we want to delete.
2146 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2147 &parentEntry, &parentEntryRef, &typeOfRelation);
2152 if (entryToDelete.leftChild != DIRENTRY_NULL)
2155 * Replace the deleted entry with its left child
2157 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2159 hr = StorageBaseImpl_WriteDirEntry(
2168 if (entryToDelete.rightChild != DIRENTRY_NULL)
2171 * We need to reinsert the right child somewhere. We already know it and
2172 * its children are greater than everything in the left tree, so we
2173 * insert it at the rightmost point in the left tree.
2175 DirRef newRightChildParent = entryToDelete.leftChild;
2176 DirEntry newRightChildParentEntry;
2180 hr = StorageBaseImpl_ReadDirEntry(
2182 newRightChildParent,
2183 &newRightChildParentEntry);
2189 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2190 newRightChildParent = newRightChildParentEntry.rightChild;
2191 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2193 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2195 hr = StorageBaseImpl_WriteDirEntry(
2197 newRightChildParent,
2198 &newRightChildParentEntry);
2208 * Replace the deleted entry with its right child
2210 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2212 hr = StorageBaseImpl_WriteDirEntry(
2226 /******************************************************************************
2227 * SetElementTimes (IStorage)
2229 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2231 const OLECHAR *pwcsName,/* [string][in] */
2232 const FILETIME *pctime, /* [in] */
2233 const FILETIME *patime, /* [in] */
2234 const FILETIME *pmtime) /* [in] */
2236 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2240 /******************************************************************************
2241 * SetStateBits (IStorage)
2243 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2245 DWORD grfStateBits,/* [in] */
2246 DWORD grfMask) /* [in] */
2248 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2251 return STG_E_REVERTED;
2253 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2257 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2258 DirRef index, const DirEntry *data)
2260 StorageImpl *This = (StorageImpl*)base;
2261 return StorageImpl_WriteDirEntry(This, index, data);
2264 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2265 DirRef index, DirEntry *data)
2267 StorageImpl *This = (StorageImpl*)base;
2268 return StorageImpl_ReadDirEntry(This, index, data);
2271 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2275 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2277 if (!This->blockChainCache[i])
2279 return &This->blockChainCache[i];
2283 i = This->blockChainToEvict;
2285 BlockChainStream_Destroy(This->blockChainCache[i]);
2286 This->blockChainCache[i] = NULL;
2288 This->blockChainToEvict++;
2289 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2290 This->blockChainToEvict = 0;
2292 return &This->blockChainCache[i];
2295 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2298 int i, free_index=-1;
2300 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2302 if (!This->blockChainCache[i])
2304 if (free_index == -1) free_index = i;
2306 else if (This->blockChainCache[i]->ownerDirEntry == index)
2308 return &This->blockChainCache[i];
2312 if (free_index == -1)
2314 free_index = This->blockChainToEvict;
2316 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2317 This->blockChainCache[free_index] = NULL;
2319 This->blockChainToEvict++;
2320 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2321 This->blockChainToEvict = 0;
2324 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2325 return &This->blockChainCache[free_index];
2328 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2329 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2331 StorageImpl *This = (StorageImpl*)base;
2336 hr = StorageImpl_ReadDirEntry(This, index, &data);
2337 if (FAILED(hr)) return hr;
2339 if (data.size.QuadPart == 0)
2345 if (offset.QuadPart + size > data.size.QuadPart)
2347 bytesToRead = data.size.QuadPart - offset.QuadPart;
2354 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2356 SmallBlockChainStream *stream;
2358 stream = SmallBlockChainStream_Construct(This, NULL, index);
2359 if (!stream) return E_OUTOFMEMORY;
2361 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2363 SmallBlockChainStream_Destroy(stream);
2369 BlockChainStream *stream = NULL;
2371 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2372 if (!stream) return E_OUTOFMEMORY;
2374 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2380 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2381 ULARGE_INTEGER newsize)
2383 StorageImpl *This = (StorageImpl*)base;
2386 SmallBlockChainStream *smallblock=NULL;
2387 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2389 hr = StorageImpl_ReadDirEntry(This, index, &data);
2390 if (FAILED(hr)) return hr;
2392 /* In simple mode keep the stream size above the small block limit */
2393 if (This->base.openFlags & STGM_SIMPLE)
2394 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2396 if (data.size.QuadPart == newsize.QuadPart)
2399 /* Create a block chain object of the appropriate type */
2400 if (data.size.QuadPart == 0)
2402 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2404 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2405 if (!smallblock) return E_OUTOFMEMORY;
2409 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2410 bigblock = *pbigblock;
2411 if (!bigblock) return E_OUTOFMEMORY;
2414 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2416 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2417 if (!smallblock) return E_OUTOFMEMORY;
2421 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2422 bigblock = *pbigblock;
2423 if (!bigblock) return E_OUTOFMEMORY;
2426 /* Change the block chain type if necessary. */
2427 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2429 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2432 SmallBlockChainStream_Destroy(smallblock);
2436 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2437 *pbigblock = bigblock;
2439 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2441 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2446 /* Set the size of the block chain. */
2449 SmallBlockChainStream_SetSize(smallblock, newsize);
2450 SmallBlockChainStream_Destroy(smallblock);
2454 BlockChainStream_SetSize(bigblock, newsize);
2457 /* Set the size in the directory entry. */
2458 hr = StorageImpl_ReadDirEntry(This, index, &data);
2461 data.size = newsize;
2463 hr = StorageImpl_WriteDirEntry(This, index, &data);
2468 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2469 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2471 StorageImpl *This = (StorageImpl*)base;
2474 ULARGE_INTEGER newSize;
2476 hr = StorageImpl_ReadDirEntry(This, index, &data);
2477 if (FAILED(hr)) return hr;
2479 /* Grow the stream if necessary */
2480 newSize.QuadPart = 0;
2481 newSize.QuadPart = offset.QuadPart + size;
2483 if (newSize.QuadPart > data.size.QuadPart)
2485 hr = StorageImpl_StreamSetSize(base, index, newSize);
2489 data.size = newSize;
2492 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2494 SmallBlockChainStream *stream;
2496 stream = SmallBlockChainStream_Construct(This, NULL, index);
2497 if (!stream) return E_OUTOFMEMORY;
2499 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2501 SmallBlockChainStream_Destroy(stream);
2507 BlockChainStream *stream;
2509 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2510 if (!stream) return E_OUTOFMEMORY;
2512 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2519 * Virtual function table for the IStorage32Impl class.
2521 static const IStorageVtbl Storage32Impl_Vtbl =
2523 StorageBaseImpl_QueryInterface,
2524 StorageBaseImpl_AddRef,
2525 StorageBaseImpl_Release,
2526 StorageBaseImpl_CreateStream,
2527 StorageBaseImpl_OpenStream,
2528 StorageBaseImpl_CreateStorage,
2529 StorageBaseImpl_OpenStorage,
2530 StorageBaseImpl_CopyTo,
2531 StorageBaseImpl_MoveElementTo,
2534 StorageBaseImpl_EnumElements,
2535 StorageBaseImpl_DestroyElement,
2536 StorageBaseImpl_RenameElement,
2537 StorageBaseImpl_SetElementTimes,
2538 StorageBaseImpl_SetClass,
2539 StorageBaseImpl_SetStateBits,
2540 StorageBaseImpl_Stat
2543 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2545 StorageImpl_Destroy,
2546 StorageImpl_Invalidate,
2547 StorageImpl_CreateDirEntry,
2548 StorageImpl_BaseWriteDirEntry,
2549 StorageImpl_BaseReadDirEntry,
2550 StorageImpl_DestroyDirEntry,
2551 StorageImpl_StreamReadAt,
2552 StorageImpl_StreamWriteAt,
2553 StorageImpl_StreamSetSize
2556 static HRESULT StorageImpl_Construct(
2563 StorageImpl** result)
2567 DirEntry currentEntry;
2568 DirRef currentEntryRef;
2569 WCHAR fullpath[MAX_PATH];
2571 if ( FAILED( validateSTGM(openFlags) ))
2572 return STG_E_INVALIDFLAG;
2574 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2576 return E_OUTOFMEMORY;
2578 memset(This, 0, sizeof(StorageImpl));
2580 list_init(&This->base.strmHead);
2582 list_init(&This->base.storageHead);
2584 This->base.lpVtbl = &Storage32Impl_Vtbl;
2585 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2586 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2587 This->base.openFlags = (openFlags & ~STGM_CREATE);
2589 This->base.create = create;
2591 This->base.reverted = 0;
2593 This->hFile = hFile;
2596 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2598 lstrcpynW(fullpath, pwcsName, MAX_PATH);
2600 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2601 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2602 if (!This->pwcsName)
2604 hr = STG_E_INSUFFICIENTMEMORY;
2607 strcpyW(This->pwcsName, fullpath);
2608 This->base.filename = This->pwcsName;
2612 * Initialize the big block cache.
2614 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2615 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2616 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2622 if (This->bigBlockFile == 0)
2630 ULARGE_INTEGER size;
2631 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2634 * Initialize all header variables:
2635 * - The big block depot consists of one block and it is at block 0
2636 * - The directory table starts at block 1
2637 * - There is no small block depot
2639 memset( This->bigBlockDepotStart,
2641 sizeof(This->bigBlockDepotStart));
2643 This->bigBlockDepotCount = 1;
2644 This->bigBlockDepotStart[0] = 0;
2645 This->rootStartBlock = 1;
2646 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2647 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2648 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2649 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2650 This->extBigBlockDepotCount = 0;
2652 StorageImpl_SaveFileHeader(This);
2655 * Add one block for the big block depot and one block for the directory table
2657 size.u.HighPart = 0;
2658 size.u.LowPart = This->bigBlockSize * 3;
2659 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2662 * Initialize the big block depot
2664 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2665 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2666 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2667 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2672 * Load the header for the file.
2674 hr = StorageImpl_LoadFileHeader(This);
2683 * There is no block depot cached yet.
2685 This->indexBlockDepotCached = 0xFFFFFFFF;
2688 * Start searching for free blocks with block 0.
2690 This->prevFreeBlock = 0;
2693 * Create the block chain abstractions.
2695 if(!(This->rootBlockChain =
2696 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2698 hr = STG_E_READFAULT;
2702 if(!(This->smallBlockDepotChain =
2703 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2706 hr = STG_E_READFAULT;
2711 * Write the root storage entry (memory only)
2717 * Initialize the directory table
2719 memset(&rootEntry, 0, sizeof(rootEntry));
2720 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2721 sizeof(rootEntry.name)/sizeof(WCHAR) );
2722 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2723 rootEntry.stgType = STGTY_ROOT;
2724 rootEntry.leftChild = DIRENTRY_NULL;
2725 rootEntry.rightChild = DIRENTRY_NULL;
2726 rootEntry.dirRootEntry = DIRENTRY_NULL;
2727 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2728 rootEntry.size.u.HighPart = 0;
2729 rootEntry.size.u.LowPart = 0;
2731 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2735 * Find the ID of the root storage.
2737 currentEntryRef = 0;
2741 hr = StorageImpl_ReadDirEntry(
2748 if ( (currentEntry.sizeOfNameString != 0 ) &&
2749 (currentEntry.stgType == STGTY_ROOT) )
2751 This->base.storageDirEntry = currentEntryRef;
2757 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2761 hr = STG_E_READFAULT;
2766 * Create the block chain abstraction for the small block root chain.
2768 if(!(This->smallBlockRootChain =
2769 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2771 hr = STG_E_READFAULT;
2777 IStorage_Release((IStorage*)This);
2786 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2788 StorageImpl *This = (StorageImpl*) iface;
2790 StorageBaseImpl_DeleteAll(&This->base);
2792 This->base.reverted = 1;
2795 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2797 StorageImpl *This = (StorageImpl*) iface;
2799 TRACE("(%p)\n", This);
2801 StorageImpl_Invalidate(iface);
2803 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2805 BlockChainStream_Destroy(This->smallBlockRootChain);
2806 BlockChainStream_Destroy(This->rootBlockChain);
2807 BlockChainStream_Destroy(This->smallBlockDepotChain);
2809 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2810 BlockChainStream_Destroy(This->blockChainCache[i]);
2812 if (This->bigBlockFile)
2813 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2814 HeapFree(GetProcessHeap(), 0, This);
2817 /******************************************************************************
2818 * Storage32Impl_GetNextFreeBigBlock
2820 * Returns the index of the next free big block.
2821 * If the big block depot is filled, this method will enlarge it.
2824 static ULONG StorageImpl_GetNextFreeBigBlock(
2827 ULONG depotBlockIndexPos;
2828 BYTE depotBuffer[BIG_BLOCK_SIZE];
2830 ULONG depotBlockOffset;
2831 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2832 ULONG nextBlockIndex = BLOCK_SPECIAL;
2834 ULONG freeBlock = BLOCK_UNUSED;
2836 depotIndex = This->prevFreeBlock / blocksPerDepot;
2837 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2840 * Scan the entire big block depot until we find a block marked free
2842 while (nextBlockIndex != BLOCK_UNUSED)
2844 if (depotIndex < COUNT_BBDEPOTINHEADER)
2846 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2849 * Grow the primary depot.
2851 if (depotBlockIndexPos == BLOCK_UNUSED)
2853 depotBlockIndexPos = depotIndex*blocksPerDepot;
2856 * Add a block depot.
2858 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2859 This->bigBlockDepotCount++;
2860 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2863 * Flag it as a block depot.
2865 StorageImpl_SetNextBlockInChain(This,
2869 /* Save new header information.
2871 StorageImpl_SaveFileHeader(This);
2876 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2878 if (depotBlockIndexPos == BLOCK_UNUSED)
2881 * Grow the extended depot.
2883 ULONG extIndex = BLOCK_UNUSED;
2884 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2885 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2887 if (extBlockOffset == 0)
2889 /* We need an extended block.
2891 extIndex = Storage32Impl_AddExtBlockDepot(This);
2892 This->extBigBlockDepotCount++;
2893 depotBlockIndexPos = extIndex + 1;
2896 depotBlockIndexPos = depotIndex * blocksPerDepot;
2899 * Add a block depot and mark it in the extended block.
2901 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2902 This->bigBlockDepotCount++;
2903 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2905 /* Flag the block depot.
2907 StorageImpl_SetNextBlockInChain(This,
2911 /* If necessary, flag the extended depot block.
2913 if (extIndex != BLOCK_UNUSED)
2914 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2916 /* Save header information.
2918 StorageImpl_SaveFileHeader(This);
2922 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2926 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2927 ( nextBlockIndex != BLOCK_UNUSED))
2929 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2931 if (nextBlockIndex == BLOCK_UNUSED)
2933 freeBlock = (depotIndex * blocksPerDepot) +
2934 (depotBlockOffset/sizeof(ULONG));
2937 depotBlockOffset += sizeof(ULONG);
2942 depotBlockOffset = 0;
2946 * make sure that the block physically exists before using it
2948 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2950 This->prevFreeBlock = freeBlock;
2955 /******************************************************************************
2956 * Storage32Impl_AddBlockDepot
2958 * This will create a depot block, essentially it is a block initialized
2961 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2963 BYTE blockBuffer[BIG_BLOCK_SIZE];
2966 * Initialize blocks as free
2968 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2969 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2972 /******************************************************************************
2973 * Storage32Impl_GetExtDepotBlock
2975 * Returns the index of the block that corresponds to the specified depot
2976 * index. This method is only for depot indexes equal or greater than
2977 * COUNT_BBDEPOTINHEADER.
2979 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2981 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2982 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2983 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2984 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2985 ULONG blockIndex = BLOCK_UNUSED;
2986 ULONG extBlockIndex = This->extBigBlockDepotStart;
2988 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2990 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2991 return BLOCK_UNUSED;
2993 while (extBlockCount > 0)
2995 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2999 if (extBlockIndex != BLOCK_UNUSED)
3000 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3001 extBlockOffset * sizeof(ULONG), &blockIndex);
3006 /******************************************************************************
3007 * Storage32Impl_SetExtDepotBlock
3009 * Associates the specified block index to the specified depot index.
3010 * This method is only for depot indexes equal or greater than
3011 * COUNT_BBDEPOTINHEADER.
3013 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3015 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3016 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3017 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3018 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3019 ULONG extBlockIndex = This->extBigBlockDepotStart;
3021 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3023 while (extBlockCount > 0)
3025 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3029 if (extBlockIndex != BLOCK_UNUSED)
3031 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3032 extBlockOffset * sizeof(ULONG),
3037 /******************************************************************************
3038 * Storage32Impl_AddExtBlockDepot
3040 * Creates an extended depot block.
3042 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3044 ULONG numExtBlocks = This->extBigBlockDepotCount;
3045 ULONG nextExtBlock = This->extBigBlockDepotStart;
3046 BYTE depotBuffer[BIG_BLOCK_SIZE];
3047 ULONG index = BLOCK_UNUSED;
3048 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3049 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3050 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3052 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3053 blocksPerDepotBlock;
3055 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3058 * The first extended block.
3060 This->extBigBlockDepotStart = index;
3066 * Follow the chain to the last one.
3068 for (i = 0; i < (numExtBlocks - 1); i++)
3070 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3074 * Add the new extended block to the chain.
3076 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3081 * Initialize this block.
3083 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3084 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3089 /******************************************************************************
3090 * Storage32Impl_FreeBigBlock
3092 * This method will flag the specified block as free in the big block depot.
3094 static void StorageImpl_FreeBigBlock(
3098 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3100 if (blockIndex < This->prevFreeBlock)
3101 This->prevFreeBlock = blockIndex;
3104 /************************************************************************
3105 * Storage32Impl_GetNextBlockInChain
3107 * This method will retrieve the block index of the next big block in
3110 * Params: This - Pointer to the Storage object.
3111 * blockIndex - Index of the block to retrieve the chain
3113 * nextBlockIndex - receives the return value.
3115 * Returns: This method returns the index of the next block in the chain.
3116 * It will return the constants:
3117 * BLOCK_SPECIAL - If the block given was not part of a
3119 * BLOCK_END_OF_CHAIN - If the block given was the last in
3121 * BLOCK_UNUSED - If the block given was not past of a chain
3123 * BLOCK_EXTBBDEPOT - This block is part of the extended
3126 * See Windows documentation for more details on IStorage methods.
3128 static HRESULT StorageImpl_GetNextBlockInChain(
3131 ULONG* nextBlockIndex)
3133 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3134 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3135 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3136 BYTE depotBuffer[BIG_BLOCK_SIZE];
3138 ULONG depotBlockIndexPos;
3141 *nextBlockIndex = BLOCK_SPECIAL;
3143 if(depotBlockCount >= This->bigBlockDepotCount)
3145 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3146 This->bigBlockDepotCount);
3147 return STG_E_READFAULT;
3151 * Cache the currently accessed depot block.
3153 if (depotBlockCount != This->indexBlockDepotCached)
3155 This->indexBlockDepotCached = depotBlockCount;
3157 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3159 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3164 * We have to look in the extended depot.
3166 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3169 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3172 return STG_E_READFAULT;
3174 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
3176 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3177 This->blockDepotCached[index] = *nextBlockIndex;
3181 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3186 /******************************************************************************
3187 * Storage32Impl_GetNextExtendedBlock
3189 * Given an extended block this method will return the next extended block.
3192 * The last ULONG of an extended block is the block index of the next
3193 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3197 * - The index of the next extended block
3198 * - BLOCK_UNUSED: there is no next extended block.
3199 * - Any other return values denotes failure.
3201 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3203 ULONG nextBlockIndex = BLOCK_SPECIAL;
3204 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3206 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3209 return nextBlockIndex;
3212 /******************************************************************************
3213 * Storage32Impl_SetNextBlockInChain
3215 * This method will write the index of the specified block's next block
3216 * in the big block depot.
3218 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3221 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3222 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3223 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3226 static void StorageImpl_SetNextBlockInChain(
3231 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3232 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3233 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3234 ULONG depotBlockIndexPos;
3236 assert(depotBlockCount < This->bigBlockDepotCount);
3237 assert(blockIndex != nextBlock);
3239 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3241 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3246 * We have to look in the extended depot.
3248 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3251 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3254 * Update the cached block depot, if necessary.
3256 if (depotBlockCount == This->indexBlockDepotCached)
3258 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3262 /******************************************************************************
3263 * Storage32Impl_LoadFileHeader
3265 * This method will read in the file header, i.e. big block index -1.
3267 static HRESULT StorageImpl_LoadFileHeader(
3270 HRESULT hr = STG_E_FILENOTFOUND;
3271 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3277 * Get a pointer to the big block of data containing the header.
3279 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3282 * Extract the information from the header.
3287 * Check for the "magic number" signature and return an error if it is not
3290 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3292 return STG_E_OLDFORMAT;
3295 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3297 return STG_E_INVALIDHEADER;
3300 StorageUtl_ReadWord(
3302 OFFSET_BIGBLOCKSIZEBITS,
3303 &This->bigBlockSizeBits);
3305 StorageUtl_ReadWord(
3307 OFFSET_SMALLBLOCKSIZEBITS,
3308 &This->smallBlockSizeBits);
3310 StorageUtl_ReadDWord(
3312 OFFSET_BBDEPOTCOUNT,
3313 &This->bigBlockDepotCount);
3315 StorageUtl_ReadDWord(
3317 OFFSET_ROOTSTARTBLOCK,
3318 &This->rootStartBlock);
3320 StorageUtl_ReadDWord(
3322 OFFSET_SBDEPOTSTART,
3323 &This->smallBlockDepotStart);
3325 StorageUtl_ReadDWord(
3327 OFFSET_EXTBBDEPOTSTART,
3328 &This->extBigBlockDepotStart);
3330 StorageUtl_ReadDWord(
3332 OFFSET_EXTBBDEPOTCOUNT,
3333 &This->extBigBlockDepotCount);
3335 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3337 StorageUtl_ReadDWord(
3339 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3340 &(This->bigBlockDepotStart[index]));
3344 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3346 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3347 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3350 * Right now, the code is making some assumptions about the size of the
3351 * blocks, just make sure they are what we're expecting.
3353 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3354 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3356 WARN("Broken OLE storage file\n");
3357 hr = STG_E_INVALIDHEADER;
3366 /******************************************************************************
3367 * Storage32Impl_SaveFileHeader
3369 * This method will save to the file the header, i.e. big block -1.
3371 static void StorageImpl_SaveFileHeader(
3374 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3379 * Get a pointer to the big block of data containing the header.
3381 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3384 * If the block read failed, the file is probably new.
3389 * Initialize for all unknown fields.
3391 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3394 * Initialize the magic number.
3396 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3399 * And a bunch of things we don't know what they mean
3401 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3402 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3403 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3404 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3408 * Write the information to the header.
3410 StorageUtl_WriteWord(
3412 OFFSET_BIGBLOCKSIZEBITS,
3413 This->bigBlockSizeBits);
3415 StorageUtl_WriteWord(
3417 OFFSET_SMALLBLOCKSIZEBITS,
3418 This->smallBlockSizeBits);
3420 StorageUtl_WriteDWord(
3422 OFFSET_BBDEPOTCOUNT,
3423 This->bigBlockDepotCount);
3425 StorageUtl_WriteDWord(
3427 OFFSET_ROOTSTARTBLOCK,
3428 This->rootStartBlock);
3430 StorageUtl_WriteDWord(
3432 OFFSET_SBDEPOTSTART,
3433 This->smallBlockDepotStart);
3435 StorageUtl_WriteDWord(
3437 OFFSET_SBDEPOTCOUNT,
3438 This->smallBlockDepotChain ?
3439 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3441 StorageUtl_WriteDWord(
3443 OFFSET_EXTBBDEPOTSTART,
3444 This->extBigBlockDepotStart);
3446 StorageUtl_WriteDWord(
3448 OFFSET_EXTBBDEPOTCOUNT,
3449 This->extBigBlockDepotCount);
3451 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3453 StorageUtl_WriteDWord(
3455 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3456 (This->bigBlockDepotStart[index]));
3460 * Write the big block back to the file.
3462 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3465 /******************************************************************************
3466 * StorageImpl_ReadRawDirEntry
3468 * This method will read the raw data from a directory entry in the file.
3470 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3472 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3474 ULARGE_INTEGER offset;
3478 offset.u.HighPart = 0;
3479 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3481 hr = BlockChainStream_ReadAt(
3482 This->rootBlockChain,
3491 /******************************************************************************
3492 * StorageImpl_WriteRawDirEntry
3494 * This method will write the raw data from a directory entry in the file.
3496 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3498 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3500 ULARGE_INTEGER offset;
3504 offset.u.HighPart = 0;
3505 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3507 hr = BlockChainStream_WriteAt(
3508 This->rootBlockChain,
3517 /******************************************************************************
3520 * Update raw directory entry data from the fields in newData.
3522 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3524 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3526 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3529 buffer + OFFSET_PS_NAME,
3531 DIRENTRY_NAME_BUFFER_LEN );
3533 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3535 StorageUtl_WriteWord(
3537 OFFSET_PS_NAMELENGTH,
3538 newData->sizeOfNameString);
3540 StorageUtl_WriteDWord(
3542 OFFSET_PS_LEFTCHILD,
3543 newData->leftChild);
3545 StorageUtl_WriteDWord(
3547 OFFSET_PS_RIGHTCHILD,
3548 newData->rightChild);
3550 StorageUtl_WriteDWord(
3553 newData->dirRootEntry);
3555 StorageUtl_WriteGUID(
3560 StorageUtl_WriteDWord(
3563 newData->ctime.dwLowDateTime);
3565 StorageUtl_WriteDWord(
3567 OFFSET_PS_CTIMEHIGH,
3568 newData->ctime.dwHighDateTime);
3570 StorageUtl_WriteDWord(
3573 newData->mtime.dwLowDateTime);
3575 StorageUtl_WriteDWord(
3577 OFFSET_PS_MTIMEHIGH,
3578 newData->ctime.dwHighDateTime);
3580 StorageUtl_WriteDWord(
3582 OFFSET_PS_STARTBLOCK,
3583 newData->startingBlock);
3585 StorageUtl_WriteDWord(
3588 newData->size.u.LowPart);
3591 /******************************************************************************
3592 * Storage32Impl_ReadDirEntry
3594 * This method will read the specified directory entry.
3596 HRESULT StorageImpl_ReadDirEntry(
3601 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3604 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3606 if (SUCCEEDED(readRes))
3608 memset(buffer->name, 0, sizeof(buffer->name));
3611 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3612 DIRENTRY_NAME_BUFFER_LEN );
3613 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3615 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3617 StorageUtl_ReadWord(
3619 OFFSET_PS_NAMELENGTH,
3620 &buffer->sizeOfNameString);
3622 StorageUtl_ReadDWord(
3624 OFFSET_PS_LEFTCHILD,
3625 &buffer->leftChild);
3627 StorageUtl_ReadDWord(
3629 OFFSET_PS_RIGHTCHILD,
3630 &buffer->rightChild);
3632 StorageUtl_ReadDWord(
3635 &buffer->dirRootEntry);
3637 StorageUtl_ReadGUID(
3642 StorageUtl_ReadDWord(
3645 &buffer->ctime.dwLowDateTime);
3647 StorageUtl_ReadDWord(
3649 OFFSET_PS_CTIMEHIGH,
3650 &buffer->ctime.dwHighDateTime);
3652 StorageUtl_ReadDWord(
3655 &buffer->mtime.dwLowDateTime);
3657 StorageUtl_ReadDWord(
3659 OFFSET_PS_MTIMEHIGH,
3660 &buffer->mtime.dwHighDateTime);
3662 StorageUtl_ReadDWord(
3664 OFFSET_PS_STARTBLOCK,
3665 &buffer->startingBlock);
3667 StorageUtl_ReadDWord(
3670 &buffer->size.u.LowPart);
3672 buffer->size.u.HighPart = 0;
3678 /*********************************************************************
3679 * Write the specified directory entry to the file
3681 HRESULT StorageImpl_WriteDirEntry(
3684 const DirEntry* buffer)
3686 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3689 UpdateRawDirEntry(currentEntry, buffer);
3691 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3695 static BOOL StorageImpl_ReadBigBlock(
3700 ULARGE_INTEGER ulOffset;
3703 ulOffset.u.HighPart = 0;
3704 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3706 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3707 return (read == This->bigBlockSize);
3710 static BOOL StorageImpl_ReadDWordFromBigBlock(
3716 ULARGE_INTEGER ulOffset;
3720 ulOffset.u.HighPart = 0;
3721 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3722 ulOffset.u.LowPart += offset;
3724 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3725 *value = lendian32toh(tmp);
3726 return (read == sizeof(DWORD));
3729 static BOOL StorageImpl_WriteBigBlock(
3734 ULARGE_INTEGER ulOffset;
3737 ulOffset.u.HighPart = 0;
3738 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3740 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3741 return (wrote == This->bigBlockSize);
3744 static BOOL StorageImpl_WriteDWordToBigBlock(
3750 ULARGE_INTEGER ulOffset;
3753 ulOffset.u.HighPart = 0;
3754 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3755 ulOffset.u.LowPart += offset;
3757 value = htole32(value);
3758 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3759 return (wrote == sizeof(DWORD));
3762 /******************************************************************************
3763 * Storage32Impl_SmallBlocksToBigBlocks
3765 * This method will convert a small block chain to a big block chain.
3766 * The small block chain will be destroyed.
3768 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3770 SmallBlockChainStream** ppsbChain)
3772 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3773 ULARGE_INTEGER size, offset;
3774 ULONG cbRead, cbWritten;
3775 ULARGE_INTEGER cbTotalRead;
3776 DirRef streamEntryRef;
3777 HRESULT resWrite = S_OK;
3779 DirEntry streamEntry;
3781 BlockChainStream *bbTempChain = NULL;
3782 BlockChainStream *bigBlockChain = NULL;
3785 * Create a temporary big block chain that doesn't have
3786 * an associated directory entry. This temporary chain will be
3787 * used to copy data from small blocks to big blocks.
3789 bbTempChain = BlockChainStream_Construct(This,
3792 if(!bbTempChain) return NULL;
3794 * Grow the big block chain.
3796 size = SmallBlockChainStream_GetSize(*ppsbChain);
3797 BlockChainStream_SetSize(bbTempChain, size);
3800 * Copy the contents of the small block chain to the big block chain
3801 * by small block size increments.
3803 offset.u.LowPart = 0;
3804 offset.u.HighPart = 0;
3805 cbTotalRead.QuadPart = 0;
3807 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3810 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3812 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3815 if (FAILED(resRead))
3820 cbTotalRead.QuadPart += cbRead;
3822 resWrite = BlockChainStream_WriteAt(bbTempChain,
3828 if (FAILED(resWrite))
3831 offset.u.LowPart += cbRead;
3833 } while (cbTotalRead.QuadPart < size.QuadPart);
3834 HeapFree(GetProcessHeap(),0,buffer);
3836 size.u.HighPart = 0;
3839 if (FAILED(resRead) || FAILED(resWrite))
3841 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3842 BlockChainStream_SetSize(bbTempChain, size);
3843 BlockChainStream_Destroy(bbTempChain);
3848 * Destroy the small block chain.
3850 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3851 SmallBlockChainStream_SetSize(*ppsbChain, size);
3852 SmallBlockChainStream_Destroy(*ppsbChain);
3856 * Change the directory entry. This chain is now a big block chain
3857 * and it doesn't reside in the small blocks chain anymore.
3859 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3861 streamEntry.startingBlock = bbHeadOfChain;
3863 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3866 * Destroy the temporary entryless big block chain.
3867 * Create a new big block chain associated with this entry.
3869 BlockChainStream_Destroy(bbTempChain);
3870 bigBlockChain = BlockChainStream_Construct(This,
3874 return bigBlockChain;
3877 /******************************************************************************
3878 * Storage32Impl_BigBlocksToSmallBlocks
3880 * This method will convert a big block chain to a small block chain.
3881 * The big block chain will be destroyed on success.
3883 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3885 BlockChainStream** ppbbChain)
3887 ULARGE_INTEGER size, offset, cbTotalRead;
3888 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3889 DirRef streamEntryRef;
3890 HRESULT resWrite = S_OK, resRead;
3891 DirEntry streamEntry;
3893 SmallBlockChainStream* sbTempChain;
3895 TRACE("%p %p\n", This, ppbbChain);
3897 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3903 size = BlockChainStream_GetSize(*ppbbChain);
3904 SmallBlockChainStream_SetSize(sbTempChain, size);
3906 offset.u.HighPart = 0;
3907 offset.u.LowPart = 0;
3908 cbTotalRead.QuadPart = 0;
3909 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3912 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3913 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3921 cbTotalRead.QuadPart += cbRead;
3923 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3924 cbRead, buffer, &cbWritten);
3926 if(FAILED(resWrite))
3929 offset.u.LowPart += cbRead;
3931 }while(cbTotalRead.QuadPart < size.QuadPart);
3932 HeapFree(GetProcessHeap(), 0, buffer);
3934 size.u.HighPart = 0;
3937 if(FAILED(resRead) || FAILED(resWrite))
3939 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3940 SmallBlockChainStream_SetSize(sbTempChain, size);
3941 SmallBlockChainStream_Destroy(sbTempChain);
3945 /* destroy the original big block chain */
3946 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3947 BlockChainStream_SetSize(*ppbbChain, size);
3948 BlockChainStream_Destroy(*ppbbChain);
3951 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3952 streamEntry.startingBlock = sbHeadOfChain;
3953 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3955 SmallBlockChainStream_Destroy(sbTempChain);
3956 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3959 static HRESULT CreateSnapshotFile(StorageBaseImpl* original, StorageBaseImpl **snapshot)
3962 DirEntry parentData, snapshotData;
3964 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DELETEONRELEASE,
3965 0, (IStorage**)snapshot);
3969 hr = StorageBaseImpl_ReadDirEntry(original,
3970 original->storageDirEntry, &parentData);
3973 hr = StorageBaseImpl_ReadDirEntry((*snapshot),
3974 (*snapshot)->storageDirEntry, &snapshotData);
3978 memcpy(snapshotData.name, parentData.name, sizeof(snapshotData.name));
3979 snapshotData.sizeOfNameString = parentData.sizeOfNameString;
3980 snapshotData.stgType = parentData.stgType;
3981 snapshotData.clsid = parentData.clsid;
3982 snapshotData.ctime = parentData.ctime;
3983 snapshotData.mtime = parentData.mtime;
3984 hr = StorageBaseImpl_WriteDirEntry((*snapshot),
3985 (*snapshot)->storageDirEntry, &snapshotData);
3989 hr = IStorage_CopyTo((IStorage*)original, 0, NULL, NULL,
3990 (IStorage*)(*snapshot));
3992 if (FAILED(hr)) IStorage_Release((IStorage*)(*snapshot));
3998 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4000 DWORD grfCommitFlags) /* [in] */
4002 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4004 DirEntry data, tempStorageData, snapshotRootData;
4005 DirRef tempStorageEntry, oldDirRoot;
4006 StorageInternalImpl *tempStorage;
4008 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4010 /* Cannot commit a read-only transacted storage */
4011 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4012 return STG_E_ACCESSDENIED;
4014 /* To prevent data loss, we create the new structure in the file before we
4015 * delete the old one, so that in case of errors the old data is intact. We
4016 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4017 * needed in the rare situation where we have just enough free disk space to
4018 * overwrite the existing data. */
4020 /* Create an orphaned storage in the parent for the new directory structure. */
4021 memset(&data, 0, sizeof(data));
4023 data.sizeOfNameString = 1;
4024 data.stgType = STGTY_STORAGE;
4025 data.leftChild = DIRENTRY_NULL;
4026 data.rightChild = DIRENTRY_NULL;
4027 data.dirRootEntry = DIRENTRY_NULL;
4028 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &data, &tempStorageEntry);
4030 if (FAILED(hr)) return hr;
4032 tempStorage = StorageInternalImpl_Construct(This->transactedParent,
4033 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, tempStorageEntry);
4036 hr = IStorage_CopyTo((IStorage*)This->snapshot, 0, NULL, NULL,
4037 (IStorage*)tempStorage);
4039 list_init(&tempStorage->ParentListEntry);
4041 IStorage_Release((IStorage*) tempStorage);
4048 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4052 /* Update the storage to use the new data in one step. */
4053 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4054 This->transactedParent->storageDirEntry, &data);
4058 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4059 tempStorageEntry, &tempStorageData);
4064 hr = StorageBaseImpl_ReadDirEntry(This->snapshot,
4065 This->snapshot->storageDirEntry, &snapshotRootData);
4070 oldDirRoot = data.dirRootEntry;
4071 data.dirRootEntry = tempStorageData.dirRootEntry;
4072 data.clsid = snapshotRootData.clsid;
4073 data.ctime = snapshotRootData.ctime;
4074 data.mtime = snapshotRootData.mtime;
4076 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4077 This->transactedParent->storageDirEntry, &data);
4082 /* Destroy the old now-orphaned data. */
4083 DestroyReachableEntries(This->transactedParent, oldDirRoot);
4084 StorageBaseImpl_DestroyDirEntry(This->transactedParent, tempStorageEntry);
4088 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4094 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4097 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4098 StorageBaseImpl *newSnapshot;
4101 TRACE("(%p)\n", iface);
4103 /* Create a new copy of the parent data. */
4104 hr = CreateSnapshotFile(This->transactedParent, &newSnapshot);
4105 if (FAILED(hr)) return hr;
4107 /* Destroy the open objects. */
4108 StorageBaseImpl_DeleteAll(&This->base);
4110 /* Replace our current snapshot. */
4111 IStorage_Release((IStorage*)This->snapshot);
4112 This->snapshot = newSnapshot;
4117 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4119 if (!This->reverted)
4121 TRACE("Storage invalidated (stg=%p)\n", This);
4125 StorageBaseImpl_DeleteAll(This);
4129 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4131 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4133 TransactedSnapshotImpl_Invalidate(iface);
4135 IStorage_Release((IStorage*)This->transactedParent);
4137 IStorage_Release((IStorage*)This->snapshot);
4139 HeapFree(GetProcessHeap(), 0, This);
4142 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4143 const DirEntry *newData, DirRef *index)
4145 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4147 return StorageBaseImpl_CreateDirEntry(This->snapshot,
4151 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4152 DirRef index, const DirEntry *data)
4154 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4156 return StorageBaseImpl_WriteDirEntry(This->snapshot,
4160 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4161 DirRef index, DirEntry *data)
4163 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4165 return StorageBaseImpl_ReadDirEntry(This->snapshot,
4169 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4172 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4174 return StorageBaseImpl_DestroyDirEntry(This->snapshot,
4178 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4179 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4181 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4183 return StorageBaseImpl_StreamReadAt(This->snapshot,
4184 index, offset, size, buffer, bytesRead);
4187 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4188 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4190 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4192 return StorageBaseImpl_StreamWriteAt(This->snapshot,
4193 index, offset, size, buffer, bytesWritten);
4196 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4197 DirRef index, ULARGE_INTEGER newsize)
4199 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4201 return StorageBaseImpl_StreamSetSize(This->snapshot,
4205 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4207 StorageBaseImpl_QueryInterface,
4208 StorageBaseImpl_AddRef,
4209 StorageBaseImpl_Release,
4210 StorageBaseImpl_CreateStream,
4211 StorageBaseImpl_OpenStream,
4212 StorageBaseImpl_CreateStorage,
4213 StorageBaseImpl_OpenStorage,
4214 StorageBaseImpl_CopyTo,
4215 StorageBaseImpl_MoveElementTo,
4216 TransactedSnapshotImpl_Commit,
4217 TransactedSnapshotImpl_Revert,
4218 StorageBaseImpl_EnumElements,
4219 StorageBaseImpl_DestroyElement,
4220 StorageBaseImpl_RenameElement,
4221 StorageBaseImpl_SetElementTimes,
4222 StorageBaseImpl_SetClass,
4223 StorageBaseImpl_SetStateBits,
4224 StorageBaseImpl_Stat
4227 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4229 TransactedSnapshotImpl_Destroy,
4230 TransactedSnapshotImpl_Invalidate,
4231 TransactedSnapshotImpl_CreateDirEntry,
4232 TransactedSnapshotImpl_WriteDirEntry,
4233 TransactedSnapshotImpl_ReadDirEntry,
4234 TransactedSnapshotImpl_DestroyDirEntry,
4235 TransactedSnapshotImpl_StreamReadAt,
4236 TransactedSnapshotImpl_StreamWriteAt,
4237 TransactedSnapshotImpl_StreamSetSize
4240 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4241 TransactedSnapshotImpl** result)
4245 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4248 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4250 /* This is OK because the property set storage functions use the IStorage functions. */
4251 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4253 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4255 list_init(&(*result)->base.strmHead);
4257 list_init(&(*result)->base.storageHead);
4259 (*result)->base.ref = 1;
4261 (*result)->base.openFlags = parentStorage->openFlags;
4263 (*result)->base.filename = parentStorage->filename;
4265 /* Create a new temporary storage to act as the snapshot */
4266 hr = CreateSnapshotFile(parentStorage, &(*result)->snapshot);
4270 (*result)->base.storageDirEntry = (*result)->snapshot->storageDirEntry;
4272 /* parentStorage already has 1 reference, which we take over here. */
4273 (*result)->transactedParent = parentStorage;
4275 parentStorage->transactedChild = (StorageBaseImpl*)*result;
4278 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4283 return E_OUTOFMEMORY;
4286 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
4287 StorageBaseImpl** result)
4291 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
4293 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
4296 return TransactedSnapshotImpl_Construct(parentStorage,
4297 (TransactedSnapshotImpl**)result);
4300 static HRESULT Storage_Construct(
4307 StorageBaseImpl** result)
4309 StorageImpl *newStorage;
4310 StorageBaseImpl *newTransactedStorage;
4313 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, &newStorage);
4314 if (FAILED(hr)) goto end;
4316 if (openFlags & STGM_TRANSACTED)
4318 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
4320 IStorage_Release((IStorage*)newStorage);
4322 *result = newTransactedStorage;
4325 *result = &newStorage->base;
4331 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
4333 StorageInternalImpl* This = (StorageInternalImpl*) base;
4335 if (!This->base.reverted)
4337 TRACE("Storage invalidated (stg=%p)\n", This);
4339 This->base.reverted = 1;
4341 This->parentStorage = NULL;
4343 StorageBaseImpl_DeleteAll(&This->base);
4345 list_remove(&This->ParentListEntry);
4349 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
4351 StorageInternalImpl* This = (StorageInternalImpl*) iface;
4353 StorageInternalImpl_Invalidate(&This->base);
4355 HeapFree(GetProcessHeap(), 0, This);
4358 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
4359 const DirEntry *newData, DirRef *index)
4361 StorageInternalImpl* This = (StorageInternalImpl*) base;
4363 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
4367 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
4368 DirRef index, const DirEntry *data)
4370 StorageInternalImpl* This = (StorageInternalImpl*) base;
4372 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
4376 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
4377 DirRef index, DirEntry *data)
4379 StorageInternalImpl* This = (StorageInternalImpl*) base;
4381 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
4385 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
4388 StorageInternalImpl* This = (StorageInternalImpl*) base;
4390 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
4394 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
4395 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4397 StorageInternalImpl* This = (StorageInternalImpl*) base;
4399 return StorageBaseImpl_StreamReadAt(This->parentStorage,
4400 index, offset, size, buffer, bytesRead);
4403 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
4404 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4406 StorageInternalImpl* This = (StorageInternalImpl*) base;
4408 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
4409 index, offset, size, buffer, bytesWritten);
4412 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
4413 DirRef index, ULARGE_INTEGER newsize)
4415 StorageInternalImpl* This = (StorageInternalImpl*) base;
4417 return StorageBaseImpl_StreamSetSize(This->parentStorage,
4421 /******************************************************************************
4423 ** Storage32InternalImpl_Commit
4426 static HRESULT WINAPI StorageInternalImpl_Commit(
4428 DWORD grfCommitFlags) /* [in] */
4430 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
4434 /******************************************************************************
4436 ** Storage32InternalImpl_Revert
4439 static HRESULT WINAPI StorageInternalImpl_Revert(
4442 FIXME("(%p): stub\n", iface);
4446 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
4448 IStorage_Release((IStorage*)This->parentStorage);
4449 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
4450 HeapFree(GetProcessHeap(), 0, This);
4453 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
4454 IEnumSTATSTG* iface,
4458 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4461 return E_INVALIDARG;
4465 if (IsEqualGUID(&IID_IUnknown, riid) ||
4466 IsEqualGUID(&IID_IEnumSTATSTG, riid))
4469 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
4473 return E_NOINTERFACE;
4476 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
4477 IEnumSTATSTG* iface)
4479 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4480 return InterlockedIncrement(&This->ref);
4483 static ULONG WINAPI IEnumSTATSTGImpl_Release(
4484 IEnumSTATSTG* iface)
4486 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4490 newRef = InterlockedDecrement(&This->ref);
4494 IEnumSTATSTGImpl_Destroy(This);
4500 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
4501 IEnumSTATSTG* iface,
4504 ULONG* pceltFetched)
4506 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4508 DirEntry currentEntry;
4509 STATSTG* currentReturnStruct = rgelt;
4510 ULONG objectFetched = 0;
4511 DirRef currentSearchNode;
4513 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
4514 return E_INVALIDARG;
4517 * To avoid the special case, get another pointer to a ULONG value if
4518 * the caller didn't supply one.
4520 if (pceltFetched==0)
4521 pceltFetched = &objectFetched;
4524 * Start the iteration, we will iterate until we hit the end of the
4525 * linked list or until we hit the number of items to iterate through
4530 * Start with the node at the top of the stack.
4532 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4534 while ( ( *pceltFetched < celt) &&
4535 ( currentSearchNode!=DIRENTRY_NULL) )
4538 * Remove the top node from the stack
4540 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4543 * Read the entry from the storage.
4545 StorageBaseImpl_ReadDirEntry(This->parentStorage,
4550 * Copy the information to the return buffer.
4552 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
4553 currentReturnStruct,
4558 * Step to the next item in the iteration
4561 currentReturnStruct++;
4564 * Push the next search node in the search stack.
4566 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
4569 * continue the iteration.
4571 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4574 if (*pceltFetched == celt)
4581 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
4582 IEnumSTATSTG* iface,
4585 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4587 DirEntry currentEntry;
4588 ULONG objectFetched = 0;
4589 DirRef currentSearchNode;
4592 * Start with the node at the top of the stack.
4594 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4596 while ( (objectFetched < celt) &&
4597 (currentSearchNode!=DIRENTRY_NULL) )
4600 * Remove the top node from the stack
4602 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4605 * Read the entry from the storage.
4607 StorageBaseImpl_ReadDirEntry(This->parentStorage,
4612 * Step to the next item in the iteration
4617 * Push the next search node in the search stack.
4619 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
4622 * continue the iteration.
4624 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4627 if (objectFetched == celt)
4633 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
4634 IEnumSTATSTG* iface)
4636 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4638 DirEntry storageEntry;
4642 * Re-initialize the search stack to an empty stack
4644 This->stackSize = 0;
4647 * Read the storage entry from the top-level storage.
4649 hr = StorageBaseImpl_ReadDirEntry(
4650 This->parentStorage,
4651 This->storageDirEntry,
4656 assert(storageEntry.sizeOfNameString!=0);
4659 * Push the search node in the search stack.
4661 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.dirRootEntry);
4667 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
4668 IEnumSTATSTG* iface,
4669 IEnumSTATSTG** ppenum)
4671 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4673 IEnumSTATSTGImpl* newClone;
4676 * Perform a sanity check on the parameters.
4679 return E_INVALIDARG;
4681 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
4682 This->storageDirEntry);
4686 * The new clone enumeration must point to the same current node as
4689 newClone->stackSize = This->stackSize ;
4690 newClone->stackMaxSize = This->stackMaxSize ;
4691 newClone->stackToVisit =
4692 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
4695 newClone->stackToVisit,
4697 sizeof(DirRef) * newClone->stackSize);
4699 *ppenum = (IEnumSTATSTG*)newClone;
4702 * Don't forget to nail down a reference to the clone before
4705 IEnumSTATSTGImpl_AddRef(*ppenum);
4710 static void IEnumSTATSTGImpl_PushSearchNode(
4711 IEnumSTATSTGImpl* This,
4714 DirEntry storageEntry;
4718 * First, make sure we're not trying to push an unexisting node.
4720 if (nodeToPush==DIRENTRY_NULL)
4724 * First push the node to the stack
4726 if (This->stackSize == This->stackMaxSize)
4728 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4730 This->stackToVisit = HeapReAlloc(
4734 sizeof(DirRef) * This->stackMaxSize);
4737 This->stackToVisit[This->stackSize] = nodeToPush;
4741 * Read the storage entry from the top-level storage.
4743 hr = StorageBaseImpl_ReadDirEntry(
4744 This->parentStorage,
4750 assert(storageEntry.sizeOfNameString!=0);
4753 * Push the previous search node in the search stack.
4755 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.leftChild);
4759 static DirRef IEnumSTATSTGImpl_PopSearchNode(
4760 IEnumSTATSTGImpl* This,
4765 if (This->stackSize == 0)
4766 return DIRENTRY_NULL;
4768 topNode = This->stackToVisit[This->stackSize-1];
4777 * Virtual function table for the IEnumSTATSTGImpl class.
4779 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4781 IEnumSTATSTGImpl_QueryInterface,
4782 IEnumSTATSTGImpl_AddRef,
4783 IEnumSTATSTGImpl_Release,
4784 IEnumSTATSTGImpl_Next,
4785 IEnumSTATSTGImpl_Skip,
4786 IEnumSTATSTGImpl_Reset,
4787 IEnumSTATSTGImpl_Clone
4790 /******************************************************************************
4791 ** IEnumSTATSTGImpl implementation
4794 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4795 StorageBaseImpl* parentStorage,
4796 DirRef storageDirEntry)
4798 IEnumSTATSTGImpl* newEnumeration;
4800 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4802 if (newEnumeration!=0)
4805 * Set-up the virtual function table and reference count.
4807 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4808 newEnumeration->ref = 0;
4811 * We want to nail-down the reference to the storage in case the
4812 * enumeration out-lives the storage in the client application.
4814 newEnumeration->parentStorage = parentStorage;
4815 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4817 newEnumeration->storageDirEntry = storageDirEntry;
4820 * Initialize the search stack
4822 newEnumeration->stackSize = 0;
4823 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4824 newEnumeration->stackToVisit =
4825 HeapAlloc(GetProcessHeap(), 0, sizeof(DirRef)*ENUMSTATSGT_SIZE_INCREMENT);
4828 * Make sure the current node of the iterator is the first one.
4830 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4833 return newEnumeration;
4837 * Virtual function table for the Storage32InternalImpl class.
4839 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4841 StorageBaseImpl_QueryInterface,
4842 StorageBaseImpl_AddRef,
4843 StorageBaseImpl_Release,
4844 StorageBaseImpl_CreateStream,
4845 StorageBaseImpl_OpenStream,
4846 StorageBaseImpl_CreateStorage,
4847 StorageBaseImpl_OpenStorage,
4848 StorageBaseImpl_CopyTo,
4849 StorageBaseImpl_MoveElementTo,
4850 StorageInternalImpl_Commit,
4851 StorageInternalImpl_Revert,
4852 StorageBaseImpl_EnumElements,
4853 StorageBaseImpl_DestroyElement,
4854 StorageBaseImpl_RenameElement,
4855 StorageBaseImpl_SetElementTimes,
4856 StorageBaseImpl_SetClass,
4857 StorageBaseImpl_SetStateBits,
4858 StorageBaseImpl_Stat
4861 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
4863 StorageInternalImpl_Destroy,
4864 StorageInternalImpl_Invalidate,
4865 StorageInternalImpl_CreateDirEntry,
4866 StorageInternalImpl_WriteDirEntry,
4867 StorageInternalImpl_ReadDirEntry,
4868 StorageInternalImpl_DestroyDirEntry,
4869 StorageInternalImpl_StreamReadAt,
4870 StorageInternalImpl_StreamWriteAt,
4871 StorageInternalImpl_StreamSetSize
4874 /******************************************************************************
4875 ** Storage32InternalImpl implementation
4878 static StorageInternalImpl* StorageInternalImpl_Construct(
4879 StorageBaseImpl* parentStorage,
4881 DirRef storageDirEntry)
4883 StorageInternalImpl* newStorage;
4885 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4889 list_init(&newStorage->base.strmHead);
4891 list_init(&newStorage->base.storageHead);
4894 * Initialize the virtual function table.
4896 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4897 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
4898 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4900 newStorage->base.reverted = 0;
4902 newStorage->base.ref = 1;
4904 newStorage->parentStorage = parentStorage;
4907 * Keep a reference to the directory entry of this storage
4909 newStorage->base.storageDirEntry = storageDirEntry;
4911 newStorage->base.create = 0;
4919 /******************************************************************************
4920 ** StorageUtl implementation
4923 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4927 memcpy(&tmp, buffer+offset, sizeof(WORD));
4928 *value = lendian16toh(tmp);
4931 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4933 value = htole16(value);
4934 memcpy(buffer+offset, &value, sizeof(WORD));
4937 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4941 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4942 *value = lendian32toh(tmp);
4945 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4947 value = htole32(value);
4948 memcpy(buffer+offset, &value, sizeof(DWORD));
4951 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4952 ULARGE_INTEGER* value)
4954 #ifdef WORDS_BIGENDIAN
4957 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4958 value->u.LowPart = htole32(tmp.u.HighPart);
4959 value->u.HighPart = htole32(tmp.u.LowPart);
4961 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4965 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4966 const ULARGE_INTEGER *value)
4968 #ifdef WORDS_BIGENDIAN
4971 tmp.u.LowPart = htole32(value->u.HighPart);
4972 tmp.u.HighPart = htole32(value->u.LowPart);
4973 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4975 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4979 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4981 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4982 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4983 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4985 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4988 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4990 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4991 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4992 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4994 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4997 void StorageUtl_CopyDirEntryToSTATSTG(
4998 StorageBaseImpl* storage,
4999 STATSTG* destination,
5000 const DirEntry* source,
5005 if (source->stgType == STGTY_ROOT)
5007 /* replace the name of root entry (often "Root Entry") by the file name */
5008 entryName = storage->filename;
5012 entryName = source->name;
5016 * The copy of the string occurs only when the flag is not set
5018 if( ((statFlags & STATFLAG_NONAME) != 0) ||
5019 (entryName == NULL) ||
5020 (entryName[0] == 0) )
5022 destination->pwcsName = 0;
5026 destination->pwcsName =
5027 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
5029 strcpyW(destination->pwcsName, entryName);
5032 switch (source->stgType)
5036 destination->type = STGTY_STORAGE;
5039 destination->type = STGTY_STREAM;
5042 destination->type = STGTY_STREAM;
5046 destination->cbSize = source->size;
5048 currentReturnStruct->mtime = {0}; TODO
5049 currentReturnStruct->ctime = {0};
5050 currentReturnStruct->atime = {0};
5052 destination->grfMode = 0;
5053 destination->grfLocksSupported = 0;
5054 destination->clsid = source->clsid;
5055 destination->grfStateBits = 0;
5056 destination->reserved = 0;
5059 /******************************************************************************
5060 ** BlockChainStream implementation
5063 BlockChainStream* BlockChainStream_Construct(
5064 StorageImpl* parentStorage,
5065 ULONG* headOfStreamPlaceHolder,
5068 BlockChainStream* newStream;
5071 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5073 newStream->parentStorage = parentStorage;
5074 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5075 newStream->ownerDirEntry = dirEntry;
5076 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
5077 newStream->tailIndex = BLOCK_END_OF_CHAIN;
5078 newStream->numBlocks = 0;
5080 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
5082 while (blockIndex != BLOCK_END_OF_CHAIN)
5084 newStream->numBlocks++;
5085 newStream->tailIndex = blockIndex;
5087 if(FAILED(StorageImpl_GetNextBlockInChain(
5092 HeapFree(GetProcessHeap(), 0, newStream);
5100 void BlockChainStream_Destroy(BlockChainStream* This)
5102 HeapFree(GetProcessHeap(), 0, This);
5105 /******************************************************************************
5106 * BlockChainStream_GetHeadOfChain
5108 * Returns the head of this stream chain.
5109 * Some special chains don't have directory entries, their heads are kept in
5110 * This->headOfStreamPlaceHolder.
5113 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5115 DirEntry chainEntry;
5118 if (This->headOfStreamPlaceHolder != 0)
5119 return *(This->headOfStreamPlaceHolder);
5121 if (This->ownerDirEntry != DIRENTRY_NULL)
5123 hr = StorageImpl_ReadDirEntry(
5124 This->parentStorage,
5125 This->ownerDirEntry,
5130 return chainEntry.startingBlock;
5134 return BLOCK_END_OF_CHAIN;
5137 /******************************************************************************
5138 * BlockChainStream_GetCount
5140 * Returns the number of blocks that comprises this chain.
5141 * This is not the size of the stream as the last block may not be full!
5144 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5149 blockIndex = BlockChainStream_GetHeadOfChain(This);
5151 while (blockIndex != BLOCK_END_OF_CHAIN)
5155 if(FAILED(StorageImpl_GetNextBlockInChain(
5156 This->parentStorage,
5165 /******************************************************************************
5166 * BlockChainStream_ReadAt
5168 * Reads a specified number of bytes from this chain at the specified offset.
5169 * bytesRead may be NULL.
5170 * Failure will be returned if the specified number of bytes has not been read.
5172 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5173 ULARGE_INTEGER offset,
5178 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5179 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5180 ULONG bytesToReadInBuffer;
5184 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5187 * Find the first block in the stream that contains part of the buffer.
5189 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5190 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5191 (blockNoInSequence < This->lastBlockNoInSequence) )
5193 blockIndex = BlockChainStream_GetHeadOfChain(This);
5194 This->lastBlockNoInSequence = blockNoInSequence;
5198 ULONG temp = blockNoInSequence;
5200 blockIndex = This->lastBlockNoInSequenceIndex;
5201 blockNoInSequence -= This->lastBlockNoInSequence;
5202 This->lastBlockNoInSequence = temp;
5205 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5207 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5208 return STG_E_DOCFILECORRUPT;
5209 blockNoInSequence--;
5212 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
5213 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
5215 This->lastBlockNoInSequenceIndex = blockIndex;
5218 * Start reading the buffer.
5221 bufferWalker = buffer;
5223 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5225 ULARGE_INTEGER ulOffset;
5228 * Calculate how many bytes we can copy from this big block.
5230 bytesToReadInBuffer =
5231 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5233 TRACE("block %i\n",blockIndex);
5234 ulOffset.u.HighPart = 0;
5235 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5238 StorageImpl_ReadAt(This->parentStorage,
5241 bytesToReadInBuffer,
5244 * Step to the next big block.
5246 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5247 return STG_E_DOCFILECORRUPT;
5249 bufferWalker += bytesReadAt;
5250 size -= bytesReadAt;
5251 *bytesRead += bytesReadAt;
5252 offsetInBlock = 0; /* There is no offset on the next block */
5254 if (bytesToReadInBuffer != bytesReadAt)
5258 return (size == 0) ? S_OK : STG_E_READFAULT;
5261 /******************************************************************************
5262 * BlockChainStream_WriteAt
5264 * Writes the specified number of bytes to this chain at the specified offset.
5265 * Will fail if not all specified number of bytes have been written.
5267 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5268 ULARGE_INTEGER offset,
5271 ULONG* bytesWritten)
5273 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5274 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5277 const BYTE* bufferWalker;
5280 * Find the first block in the stream that contains part of the buffer.
5282 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5283 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5284 (blockNoInSequence < This->lastBlockNoInSequence) )
5286 blockIndex = BlockChainStream_GetHeadOfChain(This);
5287 This->lastBlockNoInSequence = blockNoInSequence;
5291 ULONG temp = blockNoInSequence;
5293 blockIndex = This->lastBlockNoInSequenceIndex;
5294 blockNoInSequence -= This->lastBlockNoInSequence;
5295 This->lastBlockNoInSequence = temp;
5298 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5300 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5302 return STG_E_DOCFILECORRUPT;
5303 blockNoInSequence--;
5306 This->lastBlockNoInSequenceIndex = blockIndex;
5308 /* BlockChainStream_SetSize should have already been called to ensure we have
5309 * enough blocks in the chain to write into */
5310 if (blockIndex == BLOCK_END_OF_CHAIN)
5312 ERR("not enough blocks in chain to write data\n");
5313 return STG_E_DOCFILECORRUPT;
5317 bufferWalker = buffer;
5319 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5321 ULARGE_INTEGER ulOffset;
5322 DWORD bytesWrittenAt;
5324 * Calculate how many bytes we can copy from this big block.
5327 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5329 TRACE("block %i\n",blockIndex);
5330 ulOffset.u.HighPart = 0;
5331 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5334 StorageImpl_WriteAt(This->parentStorage,
5341 * Step to the next big block.
5343 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5345 return STG_E_DOCFILECORRUPT;
5347 bufferWalker += bytesWrittenAt;
5348 size -= bytesWrittenAt;
5349 *bytesWritten += bytesWrittenAt;
5350 offsetInBlock = 0; /* There is no offset on the next block */
5352 if (bytesWrittenAt != bytesToWrite)
5356 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5359 /******************************************************************************
5360 * BlockChainStream_Shrink
5362 * Shrinks this chain in the big block depot.
5364 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
5365 ULARGE_INTEGER newSize)
5367 ULONG blockIndex, extraBlock;
5372 * Reset the last accessed block cache.
5374 This->lastBlockNoInSequence = 0xFFFFFFFF;
5375 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
5378 * Figure out how many blocks are needed to contain the new size
5380 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5382 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5385 blockIndex = BlockChainStream_GetHeadOfChain(This);
5388 * Go to the new end of chain
5390 while (count < numBlocks)
5392 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5398 /* Get the next block before marking the new end */
5399 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5403 /* Mark the new end of chain */
5404 StorageImpl_SetNextBlockInChain(
5405 This->parentStorage,
5407 BLOCK_END_OF_CHAIN);
5409 This->tailIndex = blockIndex;
5410 This->numBlocks = numBlocks;
5413 * Mark the extra blocks as free
5415 while (extraBlock != BLOCK_END_OF_CHAIN)
5417 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
5420 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
5421 extraBlock = blockIndex;
5427 /******************************************************************************
5428 * BlockChainStream_Enlarge
5430 * Grows this chain in the big block depot.
5432 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
5433 ULARGE_INTEGER newSize)
5435 ULONG blockIndex, currentBlock;
5437 ULONG oldNumBlocks = 0;
5439 blockIndex = BlockChainStream_GetHeadOfChain(This);
5442 * Empty chain. Create the head.
5444 if (blockIndex == BLOCK_END_OF_CHAIN)
5446 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5447 StorageImpl_SetNextBlockInChain(This->parentStorage,
5449 BLOCK_END_OF_CHAIN);
5451 if (This->headOfStreamPlaceHolder != 0)
5453 *(This->headOfStreamPlaceHolder) = blockIndex;
5457 DirEntry chainEntry;
5458 assert(This->ownerDirEntry != DIRENTRY_NULL);
5460 StorageImpl_ReadDirEntry(
5461 This->parentStorage,
5462 This->ownerDirEntry,
5465 chainEntry.startingBlock = blockIndex;
5467 StorageImpl_WriteDirEntry(
5468 This->parentStorage,
5469 This->ownerDirEntry,
5473 This->tailIndex = blockIndex;
5474 This->numBlocks = 1;
5478 * Figure out how many blocks are needed to contain this stream
5480 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5482 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5486 * Go to the current end of chain
5488 if (This->tailIndex == BLOCK_END_OF_CHAIN)
5490 currentBlock = blockIndex;
5492 while (blockIndex != BLOCK_END_OF_CHAIN)
5495 currentBlock = blockIndex;
5497 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
5502 This->tailIndex = currentBlock;
5505 currentBlock = This->tailIndex;
5506 oldNumBlocks = This->numBlocks;
5509 * Add new blocks to the chain
5511 if (oldNumBlocks < newNumBlocks)
5513 while (oldNumBlocks < newNumBlocks)
5515 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5517 StorageImpl_SetNextBlockInChain(
5518 This->parentStorage,
5522 StorageImpl_SetNextBlockInChain(
5523 This->parentStorage,
5525 BLOCK_END_OF_CHAIN);
5527 currentBlock = blockIndex;
5531 This->tailIndex = blockIndex;
5532 This->numBlocks = newNumBlocks;
5538 /******************************************************************************
5539 * BlockChainStream_SetSize
5541 * Sets the size of this stream. The big block depot will be updated.
5542 * The file will grow if we grow the chain.
5544 * TODO: Free the actual blocks in the file when we shrink the chain.
5545 * Currently, the blocks are still in the file. So the file size
5546 * doesn't shrink even if we shrink streams.
5548 BOOL BlockChainStream_SetSize(
5549 BlockChainStream* This,
5550 ULARGE_INTEGER newSize)
5552 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
5554 if (newSize.u.LowPart == size.u.LowPart)
5557 if (newSize.u.LowPart < size.u.LowPart)
5559 BlockChainStream_Shrink(This, newSize);
5563 BlockChainStream_Enlarge(This, newSize);
5569 /******************************************************************************
5570 * BlockChainStream_GetSize
5572 * Returns the size of this chain.
5573 * Will return the block count if this chain doesn't have a directory entry.
5575 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
5577 DirEntry chainEntry;
5579 if(This->headOfStreamPlaceHolder == NULL)
5582 * This chain has a directory entry so use the size value from there.
5584 StorageImpl_ReadDirEntry(
5585 This->parentStorage,
5586 This->ownerDirEntry,
5589 return chainEntry.size;
5594 * this chain is a chain that does not have a directory entry, figure out the
5595 * size by making the product number of used blocks times the
5598 ULARGE_INTEGER result;
5599 result.u.HighPart = 0;
5602 BlockChainStream_GetCount(This) *
5603 This->parentStorage->bigBlockSize;
5609 /******************************************************************************
5610 ** SmallBlockChainStream implementation
5613 SmallBlockChainStream* SmallBlockChainStream_Construct(
5614 StorageImpl* parentStorage,
5615 ULONG* headOfStreamPlaceHolder,
5618 SmallBlockChainStream* newStream;
5620 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
5622 newStream->parentStorage = parentStorage;
5623 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5624 newStream->ownerDirEntry = dirEntry;
5629 void SmallBlockChainStream_Destroy(
5630 SmallBlockChainStream* This)
5632 HeapFree(GetProcessHeap(), 0, This);
5635 /******************************************************************************
5636 * SmallBlockChainStream_GetHeadOfChain
5638 * Returns the head of this chain of small blocks.
5640 static ULONG SmallBlockChainStream_GetHeadOfChain(
5641 SmallBlockChainStream* This)
5643 DirEntry chainEntry;
5646 if (This->headOfStreamPlaceHolder != NULL)
5647 return *(This->headOfStreamPlaceHolder);
5649 if (This->ownerDirEntry)
5651 hr = StorageImpl_ReadDirEntry(
5652 This->parentStorage,
5653 This->ownerDirEntry,
5658 return chainEntry.startingBlock;
5663 return BLOCK_END_OF_CHAIN;
5666 /******************************************************************************
5667 * SmallBlockChainStream_GetNextBlockInChain
5669 * Returns the index of the next small block in this chain.
5672 * - BLOCK_END_OF_CHAIN: end of this chain
5673 * - BLOCK_UNUSED: small block 'blockIndex' is free
5675 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5676 SmallBlockChainStream* This,
5678 ULONG* nextBlockInChain)
5680 ULARGE_INTEGER offsetOfBlockInDepot;
5685 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5687 offsetOfBlockInDepot.u.HighPart = 0;
5688 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5691 * Read those bytes in the buffer from the small block file.
5693 res = BlockChainStream_ReadAt(
5694 This->parentStorage->smallBlockDepotChain,
5695 offsetOfBlockInDepot,
5702 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5709 /******************************************************************************
5710 * SmallBlockChainStream_SetNextBlockInChain
5712 * Writes the index of the next block of the specified block in the small
5714 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5715 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5717 static void SmallBlockChainStream_SetNextBlockInChain(
5718 SmallBlockChainStream* This,
5722 ULARGE_INTEGER offsetOfBlockInDepot;
5726 offsetOfBlockInDepot.u.HighPart = 0;
5727 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5729 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5732 * Read those bytes in the buffer from the small block file.
5734 BlockChainStream_WriteAt(
5735 This->parentStorage->smallBlockDepotChain,
5736 offsetOfBlockInDepot,
5742 /******************************************************************************
5743 * SmallBlockChainStream_FreeBlock
5745 * Flag small block 'blockIndex' as free in the small block depot.
5747 static void SmallBlockChainStream_FreeBlock(
5748 SmallBlockChainStream* This,
5751 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5754 /******************************************************************************
5755 * SmallBlockChainStream_GetNextFreeBlock
5757 * Returns the index of a free small block. The small block depot will be
5758 * enlarged if necessary. The small block chain will also be enlarged if
5761 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5762 SmallBlockChainStream* This)
5764 ULARGE_INTEGER offsetOfBlockInDepot;
5767 ULONG blockIndex = 0;
5768 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5770 ULONG smallBlocksPerBigBlock;
5772 offsetOfBlockInDepot.u.HighPart = 0;
5775 * Scan the small block depot for a free block
5777 while (nextBlockIndex != BLOCK_UNUSED)
5779 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5781 res = BlockChainStream_ReadAt(
5782 This->parentStorage->smallBlockDepotChain,
5783 offsetOfBlockInDepot,
5789 * If we run out of space for the small block depot, enlarge it
5793 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5795 if (nextBlockIndex != BLOCK_UNUSED)
5801 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5803 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5804 ULONG nextBlock, newsbdIndex;
5805 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5807 nextBlock = sbdIndex;
5808 while (nextBlock != BLOCK_END_OF_CHAIN)
5810 sbdIndex = nextBlock;
5811 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5814 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5815 if (sbdIndex != BLOCK_END_OF_CHAIN)
5816 StorageImpl_SetNextBlockInChain(
5817 This->parentStorage,
5821 StorageImpl_SetNextBlockInChain(
5822 This->parentStorage,
5824 BLOCK_END_OF_CHAIN);
5827 * Initialize all the small blocks to free
5829 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5830 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5835 * We have just created the small block depot.
5841 * Save it in the header
5843 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5844 StorageImpl_SaveFileHeader(This->parentStorage);
5847 * And allocate the first big block that will contain small blocks
5850 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5852 StorageImpl_SetNextBlockInChain(
5853 This->parentStorage,
5855 BLOCK_END_OF_CHAIN);
5857 StorageImpl_ReadDirEntry(
5858 This->parentStorage,
5859 This->parentStorage->base.storageDirEntry,
5862 rootEntry.startingBlock = sbStartIndex;
5863 rootEntry.size.u.HighPart = 0;
5864 rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
5866 StorageImpl_WriteDirEntry(
5867 This->parentStorage,
5868 This->parentStorage->base.storageDirEntry,
5872 StorageImpl_SaveFileHeader(This->parentStorage);
5876 smallBlocksPerBigBlock =
5877 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5880 * Verify if we have to allocate big blocks to contain small blocks
5882 if (blockIndex % smallBlocksPerBigBlock == 0)
5885 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5887 StorageImpl_ReadDirEntry(
5888 This->parentStorage,
5889 This->parentStorage->base.storageDirEntry,
5892 if (rootEntry.size.u.LowPart <
5893 (blocksRequired * This->parentStorage->bigBlockSize))
5895 rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5897 BlockChainStream_SetSize(
5898 This->parentStorage->smallBlockRootChain,
5901 StorageImpl_WriteDirEntry(
5902 This->parentStorage,
5903 This->parentStorage->base.storageDirEntry,
5911 /******************************************************************************
5912 * SmallBlockChainStream_ReadAt
5914 * Reads a specified number of bytes from this chain at the specified offset.
5915 * bytesRead may be NULL.
5916 * Failure will be returned if the specified number of bytes has not been read.
5918 HRESULT SmallBlockChainStream_ReadAt(
5919 SmallBlockChainStream* This,
5920 ULARGE_INTEGER offset,
5926 ULARGE_INTEGER offsetInBigBlockFile;
5927 ULONG blockNoInSequence =
5928 offset.u.LowPart / This->parentStorage->smallBlockSize;
5930 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5931 ULONG bytesToReadInBuffer;
5933 ULONG bytesReadFromBigBlockFile;
5937 * This should never happen on a small block file.
5939 assert(offset.u.HighPart==0);
5942 * Find the first block in the stream that contains part of the buffer.
5944 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5946 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5948 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5951 blockNoInSequence--;
5955 * Start reading the buffer.
5958 bufferWalker = buffer;
5960 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5963 * Calculate how many bytes we can copy from this small block.
5965 bytesToReadInBuffer =
5966 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5969 * Calculate the offset of the small block in the small block file.
5971 offsetInBigBlockFile.u.HighPart = 0;
5972 offsetInBigBlockFile.u.LowPart =
5973 blockIndex * This->parentStorage->smallBlockSize;
5975 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5978 * Read those bytes in the buffer from the small block file.
5979 * The small block has already been identified so it shouldn't fail
5980 * unless the file is corrupt.
5982 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5983 offsetInBigBlockFile,
5984 bytesToReadInBuffer,
5986 &bytesReadFromBigBlockFile);
5992 * Step to the next big block.
5994 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5996 return STG_E_DOCFILECORRUPT;
5998 bufferWalker += bytesReadFromBigBlockFile;
5999 size -= bytesReadFromBigBlockFile;
6000 *bytesRead += bytesReadFromBigBlockFile;
6001 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6004 return (size == 0) ? S_OK : STG_E_READFAULT;
6007 /******************************************************************************
6008 * SmallBlockChainStream_WriteAt
6010 * Writes the specified number of bytes to this chain at the specified offset.
6011 * Will fail if not all specified number of bytes have been written.
6013 HRESULT SmallBlockChainStream_WriteAt(
6014 SmallBlockChainStream* This,
6015 ULARGE_INTEGER offset,
6018 ULONG* bytesWritten)
6020 ULARGE_INTEGER offsetInBigBlockFile;
6021 ULONG blockNoInSequence =
6022 offset.u.LowPart / This->parentStorage->smallBlockSize;
6024 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6025 ULONG bytesToWriteInBuffer;
6027 ULONG bytesWrittenToBigBlockFile;
6028 const BYTE* bufferWalker;
6032 * This should never happen on a small block file.
6034 assert(offset.u.HighPart==0);
6037 * Find the first block in the stream that contains part of the buffer.
6039 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6041 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6043 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6044 return STG_E_DOCFILECORRUPT;
6045 blockNoInSequence--;
6049 * Start writing the buffer.
6052 bufferWalker = buffer;
6053 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6056 * Calculate how many bytes we can copy to this small block.
6058 bytesToWriteInBuffer =
6059 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6062 * Calculate the offset of the small block in the small block file.
6064 offsetInBigBlockFile.u.HighPart = 0;
6065 offsetInBigBlockFile.u.LowPart =
6066 blockIndex * This->parentStorage->smallBlockSize;
6068 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6071 * Write those bytes in the buffer to the small block file.
6073 res = BlockChainStream_WriteAt(
6074 This->parentStorage->smallBlockRootChain,
6075 offsetInBigBlockFile,
6076 bytesToWriteInBuffer,
6078 &bytesWrittenToBigBlockFile);
6083 * Step to the next big block.
6085 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6088 bufferWalker += bytesWrittenToBigBlockFile;
6089 size -= bytesWrittenToBigBlockFile;
6090 *bytesWritten += bytesWrittenToBigBlockFile;
6091 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6094 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6097 /******************************************************************************
6098 * SmallBlockChainStream_Shrink
6100 * Shrinks this chain in the small block depot.
6102 static BOOL SmallBlockChainStream_Shrink(
6103 SmallBlockChainStream* This,
6104 ULARGE_INTEGER newSize)
6106 ULONG blockIndex, extraBlock;
6110 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6112 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6115 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6118 * Go to the new end of chain
6120 while (count < numBlocks)
6122 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6129 * If the count is 0, we have a special case, the head of the chain was
6134 DirEntry chainEntry;
6136 StorageImpl_ReadDirEntry(This->parentStorage,
6137 This->ownerDirEntry,
6140 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6142 StorageImpl_WriteDirEntry(This->parentStorage,
6143 This->ownerDirEntry,
6147 * We start freeing the chain at the head block.
6149 extraBlock = blockIndex;
6153 /* Get the next block before marking the new end */
6154 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6158 /* Mark the new end of chain */
6159 SmallBlockChainStream_SetNextBlockInChain(
6162 BLOCK_END_OF_CHAIN);
6166 * Mark the extra blocks as free
6168 while (extraBlock != BLOCK_END_OF_CHAIN)
6170 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6173 SmallBlockChainStream_FreeBlock(This, extraBlock);
6174 extraBlock = blockIndex;
6180 /******************************************************************************
6181 * SmallBlockChainStream_Enlarge
6183 * Grows this chain in the small block depot.
6185 static BOOL SmallBlockChainStream_Enlarge(
6186 SmallBlockChainStream* This,
6187 ULARGE_INTEGER newSize)
6189 ULONG blockIndex, currentBlock;
6191 ULONG oldNumBlocks = 0;
6193 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6196 * Empty chain. Create the head.
6198 if (blockIndex == BLOCK_END_OF_CHAIN)
6200 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6201 SmallBlockChainStream_SetNextBlockInChain(
6204 BLOCK_END_OF_CHAIN);
6206 if (This->headOfStreamPlaceHolder != NULL)
6208 *(This->headOfStreamPlaceHolder) = blockIndex;
6212 DirEntry chainEntry;
6214 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6217 chainEntry.startingBlock = blockIndex;
6219 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6224 currentBlock = blockIndex;
6227 * Figure out how many blocks are needed to contain this stream
6229 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6231 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6235 * Go to the current end of chain
6237 while (blockIndex != BLOCK_END_OF_CHAIN)
6240 currentBlock = blockIndex;
6241 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6246 * Add new blocks to the chain
6248 while (oldNumBlocks < newNumBlocks)
6250 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6251 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6253 SmallBlockChainStream_SetNextBlockInChain(
6256 BLOCK_END_OF_CHAIN);
6258 currentBlock = blockIndex;
6265 /******************************************************************************
6266 * SmallBlockChainStream_SetSize
6268 * Sets the size of this stream.
6269 * The file will grow if we grow the chain.
6271 * TODO: Free the actual blocks in the file when we shrink the chain.
6272 * Currently, the blocks are still in the file. So the file size
6273 * doesn't shrink even if we shrink streams.
6275 BOOL SmallBlockChainStream_SetSize(
6276 SmallBlockChainStream* This,
6277 ULARGE_INTEGER newSize)
6279 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6281 if (newSize.u.LowPart == size.u.LowPart)
6284 if (newSize.u.LowPart < size.u.LowPart)
6286 SmallBlockChainStream_Shrink(This, newSize);
6290 SmallBlockChainStream_Enlarge(This, newSize);
6296 /******************************************************************************
6297 * SmallBlockChainStream_GetCount
6299 * Returns the number of small blocks that comprises this chain.
6300 * This is not the size of the stream as the last block may not be full!
6303 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6308 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6310 while(blockIndex != BLOCK_END_OF_CHAIN)
6314 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6315 blockIndex, &blockIndex)))
6322 /******************************************************************************
6323 * SmallBlockChainStream_GetSize
6325 * Returns the size of this chain.
6327 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6329 DirEntry chainEntry;
6331 if(This->headOfStreamPlaceHolder != NULL)
6333 ULARGE_INTEGER result;
6334 result.u.HighPart = 0;
6336 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
6337 This->parentStorage->smallBlockSize;
6342 StorageImpl_ReadDirEntry(
6343 This->parentStorage,
6344 This->ownerDirEntry,
6347 return chainEntry.size;
6350 /******************************************************************************
6351 * StgCreateDocfile [OLE32.@]
6352 * Creates a new compound file storage object
6355 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6356 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6357 * reserved [ ?] unused?, usually 0
6358 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6361 * S_OK if the file was successfully created
6362 * some STG_E_ value if error
6364 * if pwcsName is NULL, create file with new unique name
6365 * the function can returns
6366 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6369 HRESULT WINAPI StgCreateDocfile(
6373 IStorage **ppstgOpen)
6375 StorageBaseImpl* newStorage = 0;
6376 HANDLE hFile = INVALID_HANDLE_VALUE;
6377 HRESULT hr = STG_E_INVALIDFLAG;
6381 DWORD fileAttributes;
6382 WCHAR tempFileName[MAX_PATH];
6384 TRACE("(%s, %x, %d, %p)\n",
6385 debugstr_w(pwcsName), grfMode,
6386 reserved, ppstgOpen);
6389 return STG_E_INVALIDPOINTER;
6391 return STG_E_INVALIDPARAMETER;
6393 /* if no share mode given then DENY_NONE is the default */
6394 if (STGM_SHARE_MODE(grfMode) == 0)
6395 grfMode |= STGM_SHARE_DENY_NONE;
6397 if ( FAILED( validateSTGM(grfMode) ))
6400 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6401 switch(STGM_ACCESS_MODE(grfMode))
6404 case STGM_READWRITE:
6410 /* in direct mode, can only use SHARE_EXCLUSIVE */
6411 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
6414 /* but in transacted mode, any share mode is valid */
6417 * Generate a unique name.
6421 WCHAR tempPath[MAX_PATH];
6422 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
6424 memset(tempPath, 0, sizeof(tempPath));
6425 memset(tempFileName, 0, sizeof(tempFileName));
6427 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
6430 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
6431 pwcsName = tempFileName;
6434 hr = STG_E_INSUFFICIENTMEMORY;
6438 creationMode = TRUNCATE_EXISTING;
6442 creationMode = GetCreationModeFromSTGM(grfMode);
6446 * Interpret the STGM value grfMode
6448 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6449 accessMode = GetAccessModeFromSTGM(grfMode);
6451 if (grfMode & STGM_DELETEONRELEASE)
6452 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
6454 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
6456 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
6457 FIXME("Storage share mode not implemented.\n");
6459 if (grfMode & STGM_TRANSACTED)
6460 FIXME("Transacted mode not implemented.\n");
6464 hFile = CreateFileW(pwcsName,
6472 if (hFile == INVALID_HANDLE_VALUE)
6474 if(GetLastError() == ERROR_FILE_EXISTS)
6475 hr = STG_E_FILEALREADYEXISTS;
6482 * Allocate and initialize the new IStorage32object.
6484 hr = Storage_Construct(
6499 * Get an "out" pointer for the caller.
6501 *ppstgOpen = (IStorage*)newStorage;
6504 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
6509 /******************************************************************************
6510 * StgCreateStorageEx [OLE32.@]
6512 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6514 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6515 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6517 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
6519 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6520 return STG_E_INVALIDPARAMETER;
6523 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
6525 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6526 return STG_E_INVALIDPARAMETER;
6529 if (stgfmt == STGFMT_FILE)
6531 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6532 return STG_E_INVALIDPARAMETER;
6535 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
6537 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6538 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
6541 ERR("Invalid stgfmt argument\n");
6542 return STG_E_INVALIDPARAMETER;
6545 /******************************************************************************
6546 * StgCreatePropSetStg [OLE32.@]
6548 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
6549 IPropertySetStorage **ppPropSetStg)
6553 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
6555 hr = STG_E_INVALIDPARAMETER;
6557 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
6558 (void**)ppPropSetStg);
6562 /******************************************************************************
6563 * StgOpenStorageEx [OLE32.@]
6565 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6567 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6568 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6570 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
6572 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6573 return STG_E_INVALIDPARAMETER;
6579 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6580 return STG_E_INVALIDPARAMETER;
6582 case STGFMT_STORAGE:
6585 case STGFMT_DOCFILE:
6586 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
6588 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6589 return STG_E_INVALIDPARAMETER;
6591 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6595 WARN("STGFMT_ANY assuming storage\n");
6599 return STG_E_INVALIDPARAMETER;
6602 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
6606 /******************************************************************************
6607 * StgOpenStorage [OLE32.@]
6609 HRESULT WINAPI StgOpenStorage(
6610 const OLECHAR *pwcsName,
6611 IStorage *pstgPriority,
6615 IStorage **ppstgOpen)
6617 StorageBaseImpl* newStorage = 0;
6623 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6624 debugstr_w(pwcsName), pstgPriority, grfMode,
6625 snbExclude, reserved, ppstgOpen);
6629 hr = STG_E_INVALIDNAME;
6635 hr = STG_E_INVALIDPOINTER;
6641 hr = STG_E_INVALIDPARAMETER;
6645 if (grfMode & STGM_PRIORITY)
6647 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
6648 return STG_E_INVALIDFLAG;
6649 if (grfMode & STGM_DELETEONRELEASE)
6650 return STG_E_INVALIDFUNCTION;
6651 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
6652 return STG_E_INVALIDFLAG;
6653 grfMode &= ~0xf0; /* remove the existing sharing mode */
6654 grfMode |= STGM_SHARE_DENY_NONE;
6656 /* STGM_PRIORITY stops other IStorage objects on the same file from
6657 * committing until the STGM_PRIORITY IStorage is closed. it also
6658 * stops non-transacted mode StgOpenStorage calls with write access from
6659 * succeeding. obviously, both of these cannot be achieved through just
6660 * file share flags */
6661 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6665 * Validate the sharing mode
6667 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
6668 switch(STGM_SHARE_MODE(grfMode))
6670 case STGM_SHARE_EXCLUSIVE:
6671 case STGM_SHARE_DENY_WRITE:
6674 hr = STG_E_INVALIDFLAG;
6678 if ( FAILED( validateSTGM(grfMode) ) ||
6679 (grfMode&STGM_CREATE))
6681 hr = STG_E_INVALIDFLAG;
6685 /* shared reading requires transacted mode */
6686 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6687 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6688 !(grfMode&STGM_TRANSACTED) )
6690 hr = STG_E_INVALIDFLAG;
6695 * Interpret the STGM value grfMode
6697 shareMode = GetShareModeFromSTGM(grfMode);
6698 accessMode = GetAccessModeFromSTGM(grfMode);
6702 hFile = CreateFileW( pwcsName,
6707 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6710 if (hFile==INVALID_HANDLE_VALUE)
6712 DWORD last_error = GetLastError();
6718 case ERROR_FILE_NOT_FOUND:
6719 hr = STG_E_FILENOTFOUND;
6722 case ERROR_PATH_NOT_FOUND:
6723 hr = STG_E_PATHNOTFOUND;
6726 case ERROR_ACCESS_DENIED:
6727 case ERROR_WRITE_PROTECT:
6728 hr = STG_E_ACCESSDENIED;
6731 case ERROR_SHARING_VIOLATION:
6732 hr = STG_E_SHAREVIOLATION;
6743 * Refuse to open the file if it's too small to be a structured storage file
6744 * FIXME: verify the file when reading instead of here
6746 if (GetFileSize(hFile, NULL) < 0x100)
6749 hr = STG_E_FILEALREADYEXISTS;
6754 * Allocate and initialize the new IStorage32object.
6756 hr = Storage_Construct(
6768 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6770 if(hr == STG_E_INVALIDHEADER)
6771 hr = STG_E_FILEALREADYEXISTS;
6776 * Get an "out" pointer for the caller.
6778 *ppstgOpen = (IStorage*)newStorage;
6781 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6785 /******************************************************************************
6786 * StgCreateDocfileOnILockBytes [OLE32.@]
6788 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6792 IStorage** ppstgOpen)
6794 StorageBaseImpl* newStorage = 0;
6797 if ((ppstgOpen == 0) || (plkbyt == 0))
6798 return STG_E_INVALIDPOINTER;
6801 * Allocate and initialize the new IStorage object.
6803 hr = Storage_Construct(
6818 * Get an "out" pointer for the caller.
6820 *ppstgOpen = (IStorage*)newStorage;
6825 /******************************************************************************
6826 * StgOpenStorageOnILockBytes [OLE32.@]
6828 HRESULT WINAPI StgOpenStorageOnILockBytes(
6830 IStorage *pstgPriority,
6834 IStorage **ppstgOpen)
6836 StorageBaseImpl* newStorage = 0;
6839 if ((plkbyt == 0) || (ppstgOpen == 0))
6840 return STG_E_INVALIDPOINTER;
6842 if ( FAILED( validateSTGM(grfMode) ))
6843 return STG_E_INVALIDFLAG;
6848 * Allocate and initialize the new IStorage object.
6850 hr = Storage_Construct(
6865 * Get an "out" pointer for the caller.
6867 *ppstgOpen = (IStorage*)newStorage;
6872 /******************************************************************************
6873 * StgSetTimes [ole32.@]
6874 * StgSetTimes [OLE32.@]
6878 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6879 FILETIME const *patime, FILETIME const *pmtime)
6881 IStorage *stg = NULL;
6884 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6886 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6890 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6891 IStorage_Release(stg);
6897 /******************************************************************************
6898 * StgIsStorageILockBytes [OLE32.@]
6900 * Determines if the ILockBytes contains a storage object.
6902 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6905 ULARGE_INTEGER offset;
6907 offset.u.HighPart = 0;
6908 offset.u.LowPart = 0;
6910 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6912 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6918 /******************************************************************************
6919 * WriteClassStg [OLE32.@]
6921 * This method will store the specified CLSID in the specified storage object
6923 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6928 return E_INVALIDARG;
6931 return STG_E_INVALIDPOINTER;
6933 hRes = IStorage_SetClass(pStg, rclsid);
6938 /***********************************************************************
6939 * ReadClassStg (OLE32.@)
6941 * This method reads the CLSID previously written to a storage object with
6942 * the WriteClassStg.
6945 * pstg [I] IStorage pointer
6946 * pclsid [O] Pointer to where the CLSID is written
6950 * Failure: HRESULT code.
6952 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6957 TRACE("(%p, %p)\n", pstg, pclsid);
6959 if(!pstg || !pclsid)
6960 return E_INVALIDARG;
6963 * read a STATSTG structure (contains the clsid) from the storage
6965 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6968 *pclsid=pstatstg.clsid;
6973 /***********************************************************************
6974 * OleLoadFromStream (OLE32.@)
6976 * This function loads an object from stream
6978 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6982 LPPERSISTSTREAM xstm;
6984 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6986 res=ReadClassStm(pStm,&clsid);
6989 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6992 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6994 IUnknown_Release((IUnknown*)*ppvObj);
6997 res=IPersistStream_Load(xstm,pStm);
6998 IPersistStream_Release(xstm);
6999 /* FIXME: all refcounts ok at this point? I think they should be:
7002 * xstm : 0 (released)
7007 /***********************************************************************
7008 * OleSaveToStream (OLE32.@)
7010 * This function saves an object with the IPersistStream interface on it
7011 * to the specified stream.
7013 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7019 TRACE("(%p,%p)\n",pPStm,pStm);
7021 res=IPersistStream_GetClassID(pPStm,&clsid);
7023 if (SUCCEEDED(res)){
7025 res=WriteClassStm(pStm,&clsid);
7029 res=IPersistStream_Save(pPStm,pStm,TRUE);
7032 TRACE("Finished Save\n");
7036 /****************************************************************************
7037 * This method validate a STGM parameter that can contain the values below
7039 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7040 * The stgm values contained in 0xffff0000 are bitmasks.
7042 * STGM_DIRECT 0x00000000
7043 * STGM_TRANSACTED 0x00010000
7044 * STGM_SIMPLE 0x08000000
7046 * STGM_READ 0x00000000
7047 * STGM_WRITE 0x00000001
7048 * STGM_READWRITE 0x00000002
7050 * STGM_SHARE_DENY_NONE 0x00000040
7051 * STGM_SHARE_DENY_READ 0x00000030
7052 * STGM_SHARE_DENY_WRITE 0x00000020
7053 * STGM_SHARE_EXCLUSIVE 0x00000010
7055 * STGM_PRIORITY 0x00040000
7056 * STGM_DELETEONRELEASE 0x04000000
7058 * STGM_CREATE 0x00001000
7059 * STGM_CONVERT 0x00020000
7060 * STGM_FAILIFTHERE 0x00000000
7062 * STGM_NOSCRATCH 0x00100000
7063 * STGM_NOSNAPSHOT 0x00200000
7065 static HRESULT validateSTGM(DWORD stgm)
7067 DWORD access = STGM_ACCESS_MODE(stgm);
7068 DWORD share = STGM_SHARE_MODE(stgm);
7069 DWORD create = STGM_CREATE_MODE(stgm);
7071 if (stgm&~STGM_KNOWN_FLAGS)
7073 ERR("unknown flags %08x\n", stgm);
7081 case STGM_READWRITE:
7089 case STGM_SHARE_DENY_NONE:
7090 case STGM_SHARE_DENY_READ:
7091 case STGM_SHARE_DENY_WRITE:
7092 case STGM_SHARE_EXCLUSIVE:
7101 case STGM_FAILIFTHERE:
7108 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7110 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7114 * STGM_CREATE | STGM_CONVERT
7115 * if both are false, STGM_FAILIFTHERE is set to TRUE
7117 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7121 * STGM_NOSCRATCH requires STGM_TRANSACTED
7123 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7127 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7128 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7130 if ( (stgm & STGM_NOSNAPSHOT) &&
7131 (!(stgm & STGM_TRANSACTED) ||
7132 share == STGM_SHARE_EXCLUSIVE ||
7133 share == STGM_SHARE_DENY_WRITE) )
7139 /****************************************************************************
7140 * GetShareModeFromSTGM
7142 * This method will return a share mode flag from a STGM value.
7143 * The STGM value is assumed valid.
7145 static DWORD GetShareModeFromSTGM(DWORD stgm)
7147 switch (STGM_SHARE_MODE(stgm))
7149 case STGM_SHARE_DENY_NONE:
7150 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7151 case STGM_SHARE_DENY_READ:
7152 return FILE_SHARE_WRITE;
7153 case STGM_SHARE_DENY_WRITE:
7154 return FILE_SHARE_READ;
7155 case STGM_SHARE_EXCLUSIVE:
7158 ERR("Invalid share mode!\n");
7163 /****************************************************************************
7164 * GetAccessModeFromSTGM
7166 * This method will return an access mode flag from a STGM value.
7167 * The STGM value is assumed valid.
7169 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7171 switch (STGM_ACCESS_MODE(stgm))
7174 return GENERIC_READ;
7176 case STGM_READWRITE:
7177 return GENERIC_READ | GENERIC_WRITE;
7179 ERR("Invalid access mode!\n");
7184 /****************************************************************************
7185 * GetCreationModeFromSTGM
7187 * This method will return a creation mode flag from a STGM value.
7188 * The STGM value is assumed valid.
7190 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7192 switch(STGM_CREATE_MODE(stgm))
7195 return CREATE_ALWAYS;
7197 FIXME("STGM_CONVERT not implemented!\n");
7199 case STGM_FAILIFTHERE:
7202 ERR("Invalid create mode!\n");
7208 /*************************************************************************
7209 * OLECONVERT_LoadOLE10 [Internal]
7211 * Loads the OLE10 STREAM to memory
7214 * pOleStream [I] The OLESTREAM
7215 * pData [I] Data Structure for the OLESTREAM Data
7219 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7220 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7223 * This function is used by OleConvertOLESTREAMToIStorage only.
7225 * Memory allocated for pData must be freed by the caller
7227 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7230 HRESULT hRes = S_OK;
7234 pData->pData = NULL;
7235 pData->pstrOleObjFileName = NULL;
7237 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7240 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7241 if(dwSize != sizeof(pData->dwOleID))
7243 hRes = CONVERT10_E_OLESTREAM_GET;
7245 else if(pData->dwOleID != OLESTREAM_ID)
7247 hRes = CONVERT10_E_OLESTREAM_FMT;
7258 /* Get the TypeID... more info needed for this field */
7259 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7260 if(dwSize != sizeof(pData->dwTypeID))
7262 hRes = CONVERT10_E_OLESTREAM_GET;
7267 if(pData->dwTypeID != 0)
7269 /* Get the length of the OleTypeName */
7270 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7271 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7273 hRes = CONVERT10_E_OLESTREAM_GET;
7278 if(pData->dwOleTypeNameLength > 0)
7280 /* Get the OleTypeName */
7281 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7282 if(dwSize != pData->dwOleTypeNameLength)
7284 hRes = CONVERT10_E_OLESTREAM_GET;
7290 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7291 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7293 hRes = CONVERT10_E_OLESTREAM_GET;
7297 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7298 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7299 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7300 if(pData->pstrOleObjFileName)
7302 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7303 if(dwSize != pData->dwOleObjFileNameLength)
7305 hRes = CONVERT10_E_OLESTREAM_GET;
7309 hRes = CONVERT10_E_OLESTREAM_GET;
7314 /* Get the Width of the Metafile */
7315 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7316 if(dwSize != sizeof(pData->dwMetaFileWidth))
7318 hRes = CONVERT10_E_OLESTREAM_GET;
7322 /* Get the Height of the Metafile */
7323 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7324 if(dwSize != sizeof(pData->dwMetaFileHeight))
7326 hRes = CONVERT10_E_OLESTREAM_GET;
7332 /* Get the Length of the Data */
7333 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7334 if(dwSize != sizeof(pData->dwDataLength))
7336 hRes = CONVERT10_E_OLESTREAM_GET;
7340 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7342 if(!bStrem1) /* if it is a second OLE stream data */
7344 pData->dwDataLength -= 8;
7345 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7346 if(dwSize != sizeof(pData->strUnknown))
7348 hRes = CONVERT10_E_OLESTREAM_GET;
7354 if(pData->dwDataLength > 0)
7356 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
7358 /* Get Data (ex. IStorage, Metafile, or BMP) */
7361 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
7362 if(dwSize != pData->dwDataLength)
7364 hRes = CONVERT10_E_OLESTREAM_GET;
7369 hRes = CONVERT10_E_OLESTREAM_GET;
7378 /*************************************************************************
7379 * OLECONVERT_SaveOLE10 [Internal]
7381 * Saves the OLE10 STREAM From memory
7384 * pData [I] Data Structure for the OLESTREAM Data
7385 * pOleStream [I] The OLESTREAM to save
7389 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7392 * This function is used by OleConvertIStorageToOLESTREAM only.
7395 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
7398 HRESULT hRes = S_OK;
7402 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7403 if(dwSize != sizeof(pData->dwOleID))
7405 hRes = CONVERT10_E_OLESTREAM_PUT;
7410 /* Set the TypeID */
7411 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7412 if(dwSize != sizeof(pData->dwTypeID))
7414 hRes = CONVERT10_E_OLESTREAM_PUT;
7418 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
7420 /* Set the Length of the OleTypeName */
7421 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7422 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7424 hRes = CONVERT10_E_OLESTREAM_PUT;
7429 if(pData->dwOleTypeNameLength > 0)
7431 /* Set the OleTypeName */
7432 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7433 if(dwSize != pData->dwOleTypeNameLength)
7435 hRes = CONVERT10_E_OLESTREAM_PUT;
7442 /* Set the width of the Metafile */
7443 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7444 if(dwSize != sizeof(pData->dwMetaFileWidth))
7446 hRes = CONVERT10_E_OLESTREAM_PUT;
7452 /* Set the height of the Metafile */
7453 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7454 if(dwSize != sizeof(pData->dwMetaFileHeight))
7456 hRes = CONVERT10_E_OLESTREAM_PUT;
7462 /* Set the length of the Data */
7463 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7464 if(dwSize != sizeof(pData->dwDataLength))
7466 hRes = CONVERT10_E_OLESTREAM_PUT;
7472 if(pData->dwDataLength > 0)
7474 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7475 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
7476 if(dwSize != pData->dwDataLength)
7478 hRes = CONVERT10_E_OLESTREAM_PUT;
7486 /*************************************************************************
7487 * OLECONVERT_GetOLE20FromOLE10[Internal]
7489 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7490 * opens it, and copies the content to the dest IStorage for
7491 * OleConvertOLESTREAMToIStorage
7495 * pDestStorage [I] The IStorage to copy the data to
7496 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7497 * nBufferLength [I] The size of the buffer
7506 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
7510 IStorage *pTempStorage;
7511 DWORD dwNumOfBytesWritten;
7512 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7513 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7515 /* Create a temp File */
7516 GetTempPathW(MAX_PATH, wstrTempDir);
7517 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7518 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
7520 if(hFile != INVALID_HANDLE_VALUE)
7522 /* Write IStorage Data to File */
7523 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
7526 /* Open and copy temp storage to the Dest Storage */
7527 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
7530 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
7531 IStorage_Release(pTempStorage);
7533 DeleteFileW(wstrTempFile);
7538 /*************************************************************************
7539 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7541 * Saves the OLE10 STREAM From memory
7544 * pStorage [I] The Src IStorage to copy
7545 * pData [I] The Dest Memory to write to.
7548 * The size in bytes allocated for pData
7551 * Memory allocated for pData must be freed by the caller
7553 * Used by OleConvertIStorageToOLESTREAM only.
7556 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
7560 DWORD nDataLength = 0;
7561 IStorage *pTempStorage;
7562 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7563 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7567 /* Create temp Storage */
7568 GetTempPathW(MAX_PATH, wstrTempDir);
7569 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7570 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
7574 /* Copy Src Storage to the Temp Storage */
7575 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
7576 IStorage_Release(pTempStorage);
7578 /* Open Temp Storage as a file and copy to memory */
7579 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7580 if(hFile != INVALID_HANDLE_VALUE)
7582 nDataLength = GetFileSize(hFile, NULL);
7583 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
7584 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
7587 DeleteFileW(wstrTempFile);
7592 /*************************************************************************
7593 * OLECONVERT_CreateOleStream [Internal]
7595 * Creates the "\001OLE" stream in the IStorage if necessary.
7598 * pStorage [I] Dest storage to create the stream in
7604 * This function is used by OleConvertOLESTREAMToIStorage only.
7606 * This stream is still unknown, MS Word seems to have extra data
7607 * but since the data is stored in the OLESTREAM there should be
7608 * no need to recreate the stream. If the stream is manually
7609 * deleted it will create it with this default data.
7612 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7616 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7617 BYTE pOleStreamHeader [] =
7619 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7620 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7621 0x00, 0x00, 0x00, 0x00
7624 /* Create stream if not present */
7625 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7626 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7630 /* Write default Data */
7631 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7632 IStream_Release(pStream);
7636 /* write a string to a stream, preceded by its length */
7637 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7644 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7645 r = IStream_Write( stm, &len, sizeof(len), NULL);
7650 str = CoTaskMemAlloc( len );
7651 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7652 r = IStream_Write( stm, str, len, NULL);
7653 CoTaskMemFree( str );
7657 /* read a string preceded by its length from a stream */
7658 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7661 DWORD len, count = 0;
7665 r = IStream_Read( stm, &len, sizeof(len), &count );
7668 if( count != sizeof(len) )
7669 return E_OUTOFMEMORY;
7671 TRACE("%d bytes\n",len);
7673 str = CoTaskMemAlloc( len );
7675 return E_OUTOFMEMORY;
7677 r = IStream_Read( stm, str, len, &count );
7682 CoTaskMemFree( str );
7683 return E_OUTOFMEMORY;
7686 TRACE("Read string %s\n",debugstr_an(str,len));
7688 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7689 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7691 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7692 CoTaskMemFree( str );
7700 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7701 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7705 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7707 static const BYTE unknown1[12] =
7708 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7709 0xFF, 0xFF, 0xFF, 0xFF};
7710 static const BYTE unknown2[16] =
7711 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7712 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7714 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7715 debugstr_w(lpszUserType), debugstr_w(szClipName),
7716 debugstr_w(szProgIDName));
7718 /* Create a CompObj stream */
7719 r = IStorage_CreateStream(pstg, szwStreamName,
7720 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7724 /* Write CompObj Structure to stream */
7725 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7727 if( SUCCEEDED( r ) )
7728 r = WriteClassStm( pstm, clsid );
7730 if( SUCCEEDED( r ) )
7731 r = STREAM_WriteString( pstm, lpszUserType );
7732 if( SUCCEEDED( r ) )
7733 r = STREAM_WriteString( pstm, szClipName );
7734 if( SUCCEEDED( r ) )
7735 r = STREAM_WriteString( pstm, szProgIDName );
7736 if( SUCCEEDED( r ) )
7737 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7739 IStream_Release( pstm );
7744 /***********************************************************************
7745 * WriteFmtUserTypeStg (OLE32.@)
7747 HRESULT WINAPI WriteFmtUserTypeStg(
7748 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7751 WCHAR szwClipName[0x40];
7752 CLSID clsid = CLSID_NULL;
7753 LPWSTR wstrProgID = NULL;
7756 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7758 /* get the clipboard format name */
7759 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7762 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7764 /* FIXME: There's room to save a CLSID and its ProgID, but
7765 the CLSID is not looked up in the registry and in all the
7766 tests I wrote it was CLSID_NULL. Where does it come from?
7769 /* get the real program ID. This may fail, but that's fine */
7770 ProgIDFromCLSID(&clsid, &wstrProgID);
7772 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7774 r = STORAGE_WriteCompObj( pstg, &clsid,
7775 lpszUserType, szwClipName, wstrProgID );
7777 CoTaskMemFree(wstrProgID);
7783 /******************************************************************************
7784 * ReadFmtUserTypeStg [OLE32.@]
7786 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7790 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7791 unsigned char unknown1[12];
7792 unsigned char unknown2[16];
7794 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7797 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7799 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7800 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7803 WARN("Failed to open stream r = %08x\n", r);
7807 /* read the various parts of the structure */
7808 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7809 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7811 r = ReadClassStm( stm, &clsid );
7815 r = STREAM_ReadString( stm, &szCLSIDName );
7819 r = STREAM_ReadString( stm, &szOleTypeName );
7823 r = STREAM_ReadString( stm, &szProgIDName );
7827 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7828 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7831 /* ok, success... now we just need to store what we found */
7833 *pcf = RegisterClipboardFormatW( szOleTypeName );
7834 CoTaskMemFree( szOleTypeName );
7836 if( lplpszUserType )
7837 *lplpszUserType = szCLSIDName;
7838 CoTaskMemFree( szProgIDName );
7841 IStream_Release( stm );
7847 /*************************************************************************
7848 * OLECONVERT_CreateCompObjStream [Internal]
7850 * Creates a "\001CompObj" is the destination IStorage if necessary.
7853 * pStorage [I] The dest IStorage to create the CompObj Stream
7855 * strOleTypeName [I] The ProgID
7859 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7862 * This function is used by OleConvertOLESTREAMToIStorage only.
7864 * The stream data is stored in the OLESTREAM and there should be
7865 * no need to recreate the stream. If the stream is manually
7866 * deleted it will attempt to create it by querying the registry.
7870 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7873 HRESULT hStorageRes, hRes = S_OK;
7874 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7875 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7876 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7878 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7879 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7881 /* Initialize the CompObj structure */
7882 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7883 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7884 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7887 /* Create a CompObj stream if it doesn't exist */
7888 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7889 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7890 if(hStorageRes == S_OK)
7892 /* copy the OleTypeName to the compobj struct */
7893 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7894 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7896 /* copy the OleTypeName to the compobj struct */
7897 /* Note: in the test made, these were Identical */
7898 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7899 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7902 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7903 bufferW, OLESTREAM_MAX_STR_LEN );
7904 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7910 /* Get the CLSID Default Name from the Registry */
7911 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7912 if(hErr == ERROR_SUCCESS)
7914 char strTemp[OLESTREAM_MAX_STR_LEN];
7915 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7916 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7917 if(hErr == ERROR_SUCCESS)
7919 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7925 /* Write CompObj Structure to stream */
7926 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7928 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7930 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7931 if(IStorageCompObj.dwCLSIDNameLength > 0)
7933 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7935 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7936 if(IStorageCompObj.dwOleTypeNameLength > 0)
7938 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7940 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7941 if(IStorageCompObj.dwProgIDNameLength > 0)
7943 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7945 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7946 IStream_Release(pStream);
7952 /*************************************************************************
7953 * OLECONVERT_CreateOlePresStream[Internal]
7955 * Creates the "\002OlePres000" Stream with the Metafile data
7958 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7959 * dwExtentX [I] Width of the Metafile
7960 * dwExtentY [I] Height of the Metafile
7961 * pData [I] Metafile data
7962 * dwDataLength [I] Size of the Metafile data
7966 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7969 * This function is used by OleConvertOLESTREAMToIStorage only.
7972 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7976 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7977 BYTE pOlePresStreamHeader [] =
7979 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7980 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7981 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7982 0x00, 0x00, 0x00, 0x00
7985 BYTE pOlePresStreamHeaderEmpty [] =
7987 0x00, 0x00, 0x00, 0x00,
7988 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7989 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7990 0x00, 0x00, 0x00, 0x00
7993 /* Create the OlePres000 Stream */
7994 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7995 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8000 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8002 memset(&OlePres, 0, sizeof(OlePres));
8003 /* Do we have any metafile data to save */
8004 if(dwDataLength > 0)
8006 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8007 nHeaderSize = sizeof(pOlePresStreamHeader);
8011 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8012 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8014 /* Set width and height of the metafile */
8015 OlePres.dwExtentX = dwExtentX;
8016 OlePres.dwExtentY = -dwExtentY;
8018 /* Set Data and Length */
8019 if(dwDataLength > sizeof(METAFILEPICT16))
8021 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8022 OlePres.pData = &(pData[8]);
8024 /* Save OlePres000 Data to Stream */
8025 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8026 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8027 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8028 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8029 if(OlePres.dwSize > 0)
8031 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8033 IStream_Release(pStream);
8037 /*************************************************************************
8038 * OLECONVERT_CreateOle10NativeStream [Internal]
8040 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8043 * pStorage [I] Dest storage to create the stream in
8044 * pData [I] Ole10 Native Data (ex. bmp)
8045 * dwDataLength [I] Size of the Ole10 Native Data
8051 * This function is used by OleConvertOLESTREAMToIStorage only.
8053 * Might need to verify the data and return appropriate error message
8056 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8060 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8062 /* Create the Ole10Native Stream */
8063 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8064 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8068 /* Write info to stream */
8069 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8070 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8071 IStream_Release(pStream);
8076 /*************************************************************************
8077 * OLECONVERT_GetOLE10ProgID [Internal]
8079 * Finds the ProgID (or OleTypeID) from the IStorage
8082 * pStorage [I] The Src IStorage to get the ProgID
8083 * strProgID [I] the ProgID string to get
8084 * dwSize [I] the size of the string
8088 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8091 * This function is used by OleConvertIStorageToOLESTREAM only.
8095 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8099 LARGE_INTEGER iSeekPos;
8100 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8101 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8103 /* Open the CompObj Stream */
8104 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8105 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8109 /*Get the OleType from the CompObj Stream */
8110 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8111 iSeekPos.u.HighPart = 0;
8113 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8114 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8115 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8116 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8117 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8118 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8119 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8121 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8124 IStream_Read(pStream, strProgID, *dwSize, NULL);
8126 IStream_Release(pStream);
8131 LPOLESTR wstrProgID;
8133 /* Get the OleType from the registry */
8134 REFCLSID clsid = &(stat.clsid);
8135 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8136 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8139 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8146 /*************************************************************************
8147 * OLECONVERT_GetOle10PresData [Internal]
8149 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8152 * pStorage [I] Src IStroage
8153 * pOleStream [I] Dest OleStream Mem Struct
8159 * This function is used by OleConvertIStorageToOLESTREAM only.
8161 * Memory allocated for pData must be freed by the caller
8165 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8170 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8172 /* Initialize Default data for OLESTREAM */
8173 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8174 pOleStreamData[0].dwTypeID = 2;
8175 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8176 pOleStreamData[1].dwTypeID = 0;
8177 pOleStreamData[0].dwMetaFileWidth = 0;
8178 pOleStreamData[0].dwMetaFileHeight = 0;
8179 pOleStreamData[0].pData = NULL;
8180 pOleStreamData[1].pData = NULL;
8182 /* Open Ole10Native Stream */
8183 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8184 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8188 /* Read Size and Data */
8189 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8190 if(pOleStreamData->dwDataLength > 0)
8192 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8193 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8195 IStream_Release(pStream);
8201 /*************************************************************************
8202 * OLECONVERT_GetOle20PresData[Internal]
8204 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8207 * pStorage [I] Src IStroage
8208 * pOleStreamData [I] Dest OleStream Mem Struct
8214 * This function is used by OleConvertIStorageToOLESTREAM only.
8216 * Memory allocated for pData must be freed by the caller
8218 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8222 OLECONVERT_ISTORAGE_OLEPRES olePress;
8223 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8225 /* Initialize Default data for OLESTREAM */
8226 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8227 pOleStreamData[0].dwTypeID = 2;
8228 pOleStreamData[0].dwMetaFileWidth = 0;
8229 pOleStreamData[0].dwMetaFileHeight = 0;
8230 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8231 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8232 pOleStreamData[1].dwTypeID = 0;
8233 pOleStreamData[1].dwOleTypeNameLength = 0;
8234 pOleStreamData[1].strOleTypeName[0] = 0;
8235 pOleStreamData[1].dwMetaFileWidth = 0;
8236 pOleStreamData[1].dwMetaFileHeight = 0;
8237 pOleStreamData[1].pData = NULL;
8238 pOleStreamData[1].dwDataLength = 0;
8241 /* Open OlePress000 stream */
8242 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8243 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8246 LARGE_INTEGER iSeekPos;
8247 METAFILEPICT16 MetaFilePict;
8248 static const char strMetafilePictName[] = "METAFILEPICT";
8250 /* Set the TypeID for a Metafile */
8251 pOleStreamData[1].dwTypeID = 5;
8253 /* Set the OleTypeName to Metafile */
8254 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8255 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8257 iSeekPos.u.HighPart = 0;
8258 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8260 /* Get Presentation Data */
8261 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8262 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8263 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8264 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8266 /*Set width and Height */
8267 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8268 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8269 if(olePress.dwSize > 0)
8272 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8274 /* Set MetaFilePict struct */
8275 MetaFilePict.mm = 8;
8276 MetaFilePict.xExt = olePress.dwExtentX;
8277 MetaFilePict.yExt = olePress.dwExtentY;
8278 MetaFilePict.hMF = 0;
8280 /* Get Metafile Data */
8281 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8282 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8283 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8285 IStream_Release(pStream);
8289 /*************************************************************************
8290 * OleConvertOLESTREAMToIStorage [OLE32.@]
8295 * DVTARGETDEVICE parameter is not handled
8296 * Still unsure of some mem fields for OLE 10 Stream
8297 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8298 * and "\001OLE" streams
8301 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8302 LPOLESTREAM pOleStream,
8304 const DVTARGETDEVICE* ptd)
8308 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8310 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8312 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8316 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8319 if(pstg == NULL || pOleStream == NULL)
8321 hRes = E_INVALIDARG;
8326 /* Load the OLESTREAM to Memory */
8327 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8332 /* Load the OLESTREAM to Memory (part 2)*/
8333 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8339 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8341 /* Do we have the IStorage Data in the OLESTREAM */
8342 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8344 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8345 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8349 /* It must be an original OLE 1.0 source */
8350 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8355 /* It must be an original OLE 1.0 source */
8356 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8359 /* Create CompObj Stream if necessary */
8360 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
8363 /*Create the Ole Stream if necessary */
8364 OLECONVERT_CreateOleStream(pstg);
8369 /* Free allocated memory */
8370 for(i=0; i < 2; i++)
8372 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8373 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
8374 pOleStreamData[i].pstrOleObjFileName = NULL;
8379 /*************************************************************************
8380 * OleConvertIStorageToOLESTREAM [OLE32.@]
8387 * Still unsure of some mem fields for OLE 10 Stream
8388 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8389 * and "\001OLE" streams.
8392 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
8394 LPOLESTREAM pOleStream)
8397 HRESULT hRes = S_OK;
8399 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8400 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8402 TRACE("%p %p\n", pstg, pOleStream);
8404 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8406 if(pstg == NULL || pOleStream == NULL)
8408 hRes = E_INVALIDARG;
8412 /* Get the ProgID */
8413 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
8414 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
8418 /* Was it originally Ole10 */
8419 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
8422 IStream_Release(pStream);
8423 /* Get Presentation Data for Ole10Native */
8424 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
8428 /* Get Presentation Data (OLE20) */
8429 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
8432 /* Save OLESTREAM */
8433 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
8436 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
8441 /* Free allocated memory */
8442 for(i=0; i < 2; i++)
8444 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8450 /***********************************************************************
8451 * GetConvertStg (OLE32.@)
8453 HRESULT WINAPI GetConvertStg(IStorage *stg) {
8454 FIXME("unimplemented stub!\n");
8458 /******************************************************************************
8459 * StgIsStorageFile [OLE32.@]
8460 * Verify if the file contains a storage object
8466 * S_OK if file has magic bytes as a storage object
8467 * S_FALSE if file is not storage
8470 StgIsStorageFile(LPCOLESTR fn)
8476 TRACE("%s\n", debugstr_w(fn));
8477 hf = CreateFileW(fn, GENERIC_READ,
8478 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
8479 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8481 if (hf == INVALID_HANDLE_VALUE)
8482 return STG_E_FILENOTFOUND;
8484 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
8486 WARN(" unable to read file\n");
8493 if (bytes_read != 8) {
8494 WARN(" too short\n");
8498 if (!memcmp(magic,STORAGE_magic,8)) {
8503 WARN(" -> Invalid header.\n");
8507 /***********************************************************************
8508 * WriteClassStm (OLE32.@)
8510 * Writes a CLSID to a stream.
8513 * pStm [I] Stream to write to.
8514 * rclsid [I] CLSID to write.
8518 * Failure: HRESULT code.
8520 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
8522 TRACE("(%p,%p)\n",pStm,rclsid);
8524 if (!pStm || !rclsid)
8525 return E_INVALIDARG;
8527 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
8530 /***********************************************************************
8531 * ReadClassStm (OLE32.@)
8533 * Reads a CLSID from a stream.
8536 * pStm [I] Stream to read from.
8537 * rclsid [O] CLSID to read.
8541 * Failure: HRESULT code.
8543 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
8548 TRACE("(%p,%p)\n",pStm,pclsid);
8550 if (!pStm || !pclsid)
8551 return E_INVALIDARG;
8553 /* clear the output args */
8554 *pclsid = CLSID_NULL;
8556 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
8561 if (nbByte != sizeof(CLSID))
8562 return STG_E_READFAULT;