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 */
261 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
265 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
266 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
268 /************************************************************************
272 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
274 return (index+1) * This->bigBlockSize;
277 /************************************************************************
278 ** Storage32BaseImpl implementation
280 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
281 ULARGE_INTEGER offset,
286 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
289 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
290 ULARGE_INTEGER offset,
295 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
298 /************************************************************************
299 * Storage32BaseImpl_QueryInterface (IUnknown)
301 * This method implements the common QueryInterface for all IStorage32
302 * implementations contained in this file.
304 * See Windows documentation for more details on IUnknown methods.
306 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
311 StorageBaseImpl *This = (StorageBaseImpl *)iface;
313 if ( (This==0) || (ppvObject==0) )
318 if (IsEqualGUID(&IID_IUnknown, riid) ||
319 IsEqualGUID(&IID_IStorage, riid))
323 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
325 *ppvObject = &This->pssVtbl;
329 return E_NOINTERFACE;
331 IStorage_AddRef(iface);
336 /************************************************************************
337 * Storage32BaseImpl_AddRef (IUnknown)
339 * This method implements the common AddRef for all IStorage32
340 * implementations contained in this file.
342 * See Windows documentation for more details on IUnknown methods.
344 static ULONG WINAPI StorageBaseImpl_AddRef(
347 StorageBaseImpl *This = (StorageBaseImpl *)iface;
348 ULONG ref = InterlockedIncrement(&This->ref);
350 TRACE("(%p) AddRef to %d\n", This, ref);
355 /************************************************************************
356 * Storage32BaseImpl_Release (IUnknown)
358 * This method implements the common Release for all IStorage32
359 * implementations contained in this file.
361 * See Windows documentation for more details on IUnknown methods.
363 static ULONG WINAPI StorageBaseImpl_Release(
366 StorageBaseImpl *This = (StorageBaseImpl *)iface;
368 ULONG ref = InterlockedDecrement(&This->ref);
370 TRACE("(%p) ReleaseRef to %d\n", This, ref);
375 * Since we are using a system of base-classes, we want to call the
376 * destructor of the appropriate derived class. To do this, we are
377 * using virtual functions to implement the destructor.
379 StorageBaseImpl_Destroy(This);
385 /************************************************************************
386 * Storage32BaseImpl_OpenStream (IStorage)
388 * This method will open the specified stream object from the current storage.
390 * See Windows documentation for more details on IStorage methods.
392 static HRESULT WINAPI StorageBaseImpl_OpenStream(
394 const OLECHAR* pwcsName, /* [string][in] */
395 void* reserved1, /* [unique][in] */
396 DWORD grfMode, /* [in] */
397 DWORD reserved2, /* [in] */
398 IStream** ppstm) /* [out] */
400 StorageBaseImpl *This = (StorageBaseImpl *)iface;
401 StgStreamImpl* newStream;
402 DirEntry currentEntry;
403 DirRef streamEntryRef;
404 HRESULT res = STG_E_UNKNOWN;
406 TRACE("(%p, %s, %p, %x, %d, %p)\n",
407 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
409 if ( (pwcsName==NULL) || (ppstm==0) )
417 if ( FAILED( validateSTGM(grfMode) ) ||
418 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
420 res = STG_E_INVALIDFLAG;
427 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
429 res = STG_E_INVALIDFUNCTION;
435 res = STG_E_REVERTED;
440 * Check that we're compatible with the parent's storage mode, but
441 * only if we are not in transacted mode
443 if(!(This->openFlags & STGM_TRANSACTED)) {
444 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
446 res = STG_E_INVALIDFLAG;
452 * Search for the element with the given name
454 streamEntryRef = findElement(
456 This->storageDirEntry,
461 * If it was found, construct the stream object and return a pointer to it.
463 if ( (streamEntryRef!=DIRENTRY_NULL) &&
464 (currentEntry.stgType==STGTY_STREAM) )
466 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
468 /* A single stream cannot be opened a second time. */
469 res = STG_E_ACCESSDENIED;
473 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
477 newStream->grfMode = grfMode;
478 *ppstm = (IStream*)newStream;
480 IStream_AddRef(*ppstm);
490 res = STG_E_FILENOTFOUND;
494 TRACE("<-- IStream %p\n", *ppstm);
495 TRACE("<-- %08x\n", res);
499 /************************************************************************
500 * Storage32BaseImpl_OpenStorage (IStorage)
502 * This method will open a new storage object from the current storage.
504 * See Windows documentation for more details on IStorage methods.
506 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
508 const OLECHAR* pwcsName, /* [string][unique][in] */
509 IStorage* pstgPriority, /* [unique][in] */
510 DWORD grfMode, /* [in] */
511 SNB snbExclude, /* [unique][in] */
512 DWORD reserved, /* [in] */
513 IStorage** ppstg) /* [out] */
515 StorageBaseImpl *This = (StorageBaseImpl *)iface;
516 StorageInternalImpl* newStorage;
517 StorageBaseImpl* newTransactedStorage;
518 DirEntry currentEntry;
519 DirRef storageEntryRef;
520 HRESULT res = STG_E_UNKNOWN;
522 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
523 iface, debugstr_w(pwcsName), pstgPriority,
524 grfMode, snbExclude, reserved, ppstg);
526 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
532 if (This->openFlags & STGM_SIMPLE)
534 res = STG_E_INVALIDFUNCTION;
539 if (snbExclude != NULL)
541 res = STG_E_INVALIDPARAMETER;
545 if ( FAILED( validateSTGM(grfMode) ))
547 res = STG_E_INVALIDFLAG;
554 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
555 (grfMode & STGM_DELETEONRELEASE) ||
556 (grfMode & STGM_PRIORITY) )
558 res = STG_E_INVALIDFUNCTION;
563 return STG_E_REVERTED;
566 * Check that we're compatible with the parent's storage mode,
567 * but only if we are not transacted
569 if(!(This->openFlags & STGM_TRANSACTED)) {
570 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
572 res = STG_E_ACCESSDENIED;
579 storageEntryRef = findElement(
581 This->storageDirEntry,
585 if ( (storageEntryRef!=DIRENTRY_NULL) &&
586 (currentEntry.stgType==STGTY_STORAGE) )
588 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
590 /* A single storage cannot be opened a second time. */
591 res = STG_E_ACCESSDENIED;
595 newStorage = StorageInternalImpl_Construct(
602 if (grfMode & STGM_TRANSACTED)
604 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
608 HeapFree(GetProcessHeap(), 0, newStorage);
612 *ppstg = (IStorage*)newTransactedStorage;
616 *ppstg = (IStorage*)newStorage;
619 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
625 res = STG_E_INSUFFICIENTMEMORY;
629 res = STG_E_FILENOTFOUND;
632 TRACE("<-- %08x\n", res);
636 /************************************************************************
637 * Storage32BaseImpl_EnumElements (IStorage)
639 * This method will create an enumerator object that can be used to
640 * retrieve information about all the elements in the storage object.
642 * See Windows documentation for more details on IStorage methods.
644 static HRESULT WINAPI StorageBaseImpl_EnumElements(
646 DWORD reserved1, /* [in] */
647 void* reserved2, /* [size_is][unique][in] */
648 DWORD reserved3, /* [in] */
649 IEnumSTATSTG** ppenum) /* [out] */
651 StorageBaseImpl *This = (StorageBaseImpl *)iface;
652 IEnumSTATSTGImpl* newEnum;
654 TRACE("(%p, %d, %p, %d, %p)\n",
655 iface, reserved1, reserved2, reserved3, ppenum);
657 if ( (This==0) || (ppenum==0))
661 return STG_E_REVERTED;
663 newEnum = IEnumSTATSTGImpl_Construct(
665 This->storageDirEntry);
669 *ppenum = (IEnumSTATSTG*)newEnum;
671 IEnumSTATSTG_AddRef(*ppenum);
676 return E_OUTOFMEMORY;
679 /************************************************************************
680 * Storage32BaseImpl_Stat (IStorage)
682 * This method will retrieve information about this storage object.
684 * See Windows documentation for more details on IStorage methods.
686 static HRESULT WINAPI StorageBaseImpl_Stat(
688 STATSTG* pstatstg, /* [out] */
689 DWORD grfStatFlag) /* [in] */
691 StorageBaseImpl *This = (StorageBaseImpl *)iface;
692 DirEntry currentEntry;
693 HRESULT res = STG_E_UNKNOWN;
695 TRACE("(%p, %p, %x)\n",
696 iface, pstatstg, grfStatFlag);
698 if ( (This==0) || (pstatstg==0))
706 res = STG_E_REVERTED;
710 res = StorageBaseImpl_ReadDirEntry(
712 This->storageDirEntry,
717 StorageUtl_CopyDirEntryToSTATSTG(
723 pstatstg->grfMode = This->openFlags;
724 pstatstg->grfStateBits = This->stateBits;
730 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);
732 TRACE("<-- %08x\n", res);
736 /************************************************************************
737 * Storage32BaseImpl_RenameElement (IStorage)
739 * This method will rename the specified element.
741 * See Windows documentation for more details on IStorage methods.
743 static HRESULT WINAPI StorageBaseImpl_RenameElement(
745 const OLECHAR* pwcsOldName, /* [in] */
746 const OLECHAR* pwcsNewName) /* [in] */
748 StorageBaseImpl *This = (StorageBaseImpl *)iface;
749 DirEntry currentEntry;
750 DirRef currentEntryRef;
752 TRACE("(%p, %s, %s)\n",
753 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
756 return STG_E_REVERTED;
758 currentEntryRef = findElement(This,
759 This->storageDirEntry,
763 if (currentEntryRef != DIRENTRY_NULL)
766 * There is already an element with the new name
768 return STG_E_FILEALREADYEXISTS;
772 * Search for the old element name
774 currentEntryRef = findElement(This,
775 This->storageDirEntry,
779 if (currentEntryRef != DIRENTRY_NULL)
781 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
782 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
784 WARN("Element is already open; cannot rename.\n");
785 return STG_E_ACCESSDENIED;
788 /* Remove the element from its current position in the tree */
789 removeFromTree(This, This->storageDirEntry,
792 /* Change the name of the element */
793 strcpyW(currentEntry.name, pwcsNewName);
795 /* Delete any sibling links */
796 currentEntry.leftChild = DIRENTRY_NULL;
797 currentEntry.rightChild = DIRENTRY_NULL;
799 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
802 /* Insert the element in a new position in the tree */
803 insertIntoTree(This, This->storageDirEntry,
809 * There is no element with the old name
811 return STG_E_FILENOTFOUND;
817 /************************************************************************
818 * Storage32BaseImpl_CreateStream (IStorage)
820 * This method will create a stream object within this storage
822 * See Windows documentation for more details on IStorage methods.
824 static HRESULT WINAPI StorageBaseImpl_CreateStream(
826 const OLECHAR* pwcsName, /* [string][in] */
827 DWORD grfMode, /* [in] */
828 DWORD reserved1, /* [in] */
829 DWORD reserved2, /* [in] */
830 IStream** ppstm) /* [out] */
832 StorageBaseImpl *This = (StorageBaseImpl *)iface;
833 StgStreamImpl* newStream;
834 DirEntry currentEntry, newStreamEntry;
835 DirRef currentEntryRef, newStreamEntryRef;
838 TRACE("(%p, %s, %x, %d, %d, %p)\n",
839 iface, debugstr_w(pwcsName), grfMode,
840 reserved1, reserved2, ppstm);
843 return STG_E_INVALIDPOINTER;
846 return STG_E_INVALIDNAME;
848 if (reserved1 || reserved2)
849 return STG_E_INVALIDPARAMETER;
851 if ( FAILED( validateSTGM(grfMode) ))
852 return STG_E_INVALIDFLAG;
854 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
855 return STG_E_INVALIDFLAG;
858 return STG_E_REVERTED;
863 if ((grfMode & STGM_DELETEONRELEASE) ||
864 (grfMode & STGM_TRANSACTED))
865 return STG_E_INVALIDFUNCTION;
868 * Don't worry about permissions in transacted mode, as we can always write
869 * changes; we just can't always commit them.
871 if(!(This->openFlags & STGM_TRANSACTED)) {
872 /* Can't create a stream on read-only storage */
873 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
874 return STG_E_ACCESSDENIED;
876 /* Can't create a stream with greater access than the parent. */
877 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
878 return STG_E_ACCESSDENIED;
881 if(This->openFlags & STGM_SIMPLE)
882 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
886 currentEntryRef = findElement(This,
887 This->storageDirEntry,
891 if (currentEntryRef != DIRENTRY_NULL)
894 * An element with this name already exists
896 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
898 IStorage_DestroyElement(iface, pwcsName);
901 return STG_E_FILEALREADYEXISTS;
905 * memset the empty entry
907 memset(&newStreamEntry, 0, sizeof(DirEntry));
909 newStreamEntry.sizeOfNameString =
910 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
912 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
913 return STG_E_INVALIDNAME;
915 strcpyW(newStreamEntry.name, pwcsName);
917 newStreamEntry.stgType = STGTY_STREAM;
918 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
919 newStreamEntry.size.u.LowPart = 0;
920 newStreamEntry.size.u.HighPart = 0;
922 newStreamEntry.leftChild = DIRENTRY_NULL;
923 newStreamEntry.rightChild = DIRENTRY_NULL;
924 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
926 /* call CoFileTime to get the current time
931 /* newStreamEntry.clsid */
934 * Create an entry with the new data
936 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
941 * Insert the new entry in the parent storage's tree.
945 This->storageDirEntry,
949 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
954 * Open the stream to return it.
956 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
960 *ppstm = (IStream*)newStream;
962 IStream_AddRef(*ppstm);
966 return STG_E_INSUFFICIENTMEMORY;
972 /************************************************************************
973 * Storage32BaseImpl_SetClass (IStorage)
975 * This method will write the specified CLSID in the directory entry of this
978 * See Windows documentation for more details on IStorage methods.
980 static HRESULT WINAPI StorageBaseImpl_SetClass(
982 REFCLSID clsid) /* [in] */
984 StorageBaseImpl *This = (StorageBaseImpl *)iface;
986 DirEntry currentEntry;
988 TRACE("(%p, %p)\n", iface, clsid);
991 return STG_E_REVERTED;
993 hRes = StorageBaseImpl_ReadDirEntry(This,
994 This->storageDirEntry,
998 currentEntry.clsid = *clsid;
1000 hRes = StorageBaseImpl_WriteDirEntry(This,
1001 This->storageDirEntry,
1008 /************************************************************************
1009 ** Storage32Impl implementation
1012 /************************************************************************
1013 * Storage32BaseImpl_CreateStorage (IStorage)
1015 * This method will create the storage object within the provided storage.
1017 * See Windows documentation for more details on IStorage methods.
1019 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1021 const OLECHAR *pwcsName, /* [string][in] */
1022 DWORD grfMode, /* [in] */
1023 DWORD reserved1, /* [in] */
1024 DWORD reserved2, /* [in] */
1025 IStorage **ppstg) /* [out] */
1027 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1029 DirEntry currentEntry;
1031 DirRef currentEntryRef;
1035 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1036 iface, debugstr_w(pwcsName), grfMode,
1037 reserved1, reserved2, ppstg);
1040 return STG_E_INVALIDPOINTER;
1042 if (This->openFlags & STGM_SIMPLE)
1044 return STG_E_INVALIDFUNCTION;
1048 return STG_E_INVALIDNAME;
1052 if ( FAILED( validateSTGM(grfMode) ) ||
1053 (grfMode & STGM_DELETEONRELEASE) )
1055 WARN("bad grfMode: 0x%x\n", grfMode);
1056 return STG_E_INVALIDFLAG;
1060 return STG_E_REVERTED;
1063 * Check that we're compatible with the parent's storage mode
1065 if ( !(This->openFlags & STGM_TRANSACTED) &&
1066 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1068 WARN("access denied\n");
1069 return STG_E_ACCESSDENIED;
1072 currentEntryRef = findElement(This,
1073 This->storageDirEntry,
1077 if (currentEntryRef != DIRENTRY_NULL)
1080 * An element with this name already exists
1082 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1083 ((This->openFlags & STGM_TRANSACTED) ||
1084 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1086 hr = IStorage_DestroyElement(iface, pwcsName);
1092 WARN("file already exists\n");
1093 return STG_E_FILEALREADYEXISTS;
1096 else if (!(This->openFlags & STGM_TRANSACTED) &&
1097 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1099 WARN("read-only storage\n");
1100 return STG_E_ACCESSDENIED;
1103 memset(&newEntry, 0, sizeof(DirEntry));
1105 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1107 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1109 FIXME("name too long\n");
1110 return STG_E_INVALIDNAME;
1113 strcpyW(newEntry.name, pwcsName);
1115 newEntry.stgType = STGTY_STORAGE;
1116 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1117 newEntry.size.u.LowPart = 0;
1118 newEntry.size.u.HighPart = 0;
1120 newEntry.leftChild = DIRENTRY_NULL;
1121 newEntry.rightChild = DIRENTRY_NULL;
1122 newEntry.dirRootEntry = DIRENTRY_NULL;
1124 /* call CoFileTime to get the current time
1129 /* newEntry.clsid */
1132 * Create a new directory entry for the storage
1134 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1139 * Insert the new directory entry into the parent storage's tree
1141 hr = insertIntoTree(
1143 This->storageDirEntry,
1147 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1152 * Open it to get a pointer to return.
1154 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1156 if( (hr != S_OK) || (*ppstg == NULL))
1166 /***************************************************************************
1170 * Reserve a directory entry in the file and initialize it.
1172 static HRESULT StorageImpl_CreateDirEntry(
1173 StorageBaseImpl *base,
1174 const DirEntry *newData,
1177 StorageImpl *storage = (StorageImpl*)base;
1178 ULONG currentEntryIndex = 0;
1179 ULONG newEntryIndex = DIRENTRY_NULL;
1181 BYTE currentData[RAW_DIRENTRY_SIZE];
1182 WORD sizeOfNameString;
1186 hr = StorageImpl_ReadRawDirEntry(storage,
1192 StorageUtl_ReadWord(
1194 OFFSET_PS_NAMELENGTH,
1197 if (sizeOfNameString == 0)
1200 * The entry exists and is available, we found it.
1202 newEntryIndex = currentEntryIndex;
1208 * We exhausted the directory entries, we will create more space below
1210 newEntryIndex = currentEntryIndex;
1212 currentEntryIndex++;
1214 } while (newEntryIndex == DIRENTRY_NULL);
1217 * grow the directory stream
1221 BYTE emptyData[RAW_DIRENTRY_SIZE];
1222 ULARGE_INTEGER newSize;
1224 ULONG lastEntry = 0;
1225 ULONG blockCount = 0;
1228 * obtain the new count of blocks in the directory stream
1230 blockCount = BlockChainStream_GetCount(
1231 storage->rootBlockChain)+1;
1234 * initialize the size used by the directory stream
1236 newSize.u.HighPart = 0;
1237 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1240 * add a block to the directory stream
1242 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1245 * memset the empty entry in order to initialize the unused newly
1248 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1253 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1256 entryIndex = newEntryIndex + 1;
1257 entryIndex < lastEntry;
1260 StorageImpl_WriteRawDirEntry(
1267 UpdateRawDirEntry(currentData, newData);
1269 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1272 *index = newEntryIndex;
1277 /***************************************************************************
1281 * Mark a directory entry in the file as free.
1283 static HRESULT StorageImpl_DestroyDirEntry(
1284 StorageBaseImpl *base,
1288 BYTE emptyData[RAW_DIRENTRY_SIZE];
1289 StorageImpl *storage = (StorageImpl*)base;
1291 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1293 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1299 /***************************************************************************
1303 * Destroy an entry, its attached data, and all entries reachable from it.
1305 static HRESULT DestroyReachableEntries(
1306 StorageBaseImpl *base,
1311 ULARGE_INTEGER zero;
1315 if (index != DIRENTRY_NULL)
1317 hr = StorageBaseImpl_ReadDirEntry(base, index, &data);
1320 hr = DestroyReachableEntries(base, data.dirRootEntry);
1323 hr = DestroyReachableEntries(base, data.leftChild);
1326 hr = DestroyReachableEntries(base, data.rightChild);
1329 hr = StorageBaseImpl_StreamSetSize(base, index, zero);
1332 hr = StorageBaseImpl_DestroyDirEntry(base, index);
1339 /****************************************************************************
1343 * Case insensitive comparison of DirEntry.name by first considering
1346 * Returns <0 when name1 < name2
1347 * >0 when name1 > name2
1348 * 0 when name1 == name2
1350 static LONG entryNameCmp(
1351 const OLECHAR *name1,
1352 const OLECHAR *name2)
1354 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1356 while (diff == 0 && *name1 != 0)
1359 * We compare the string themselves only when they are of the same length
1361 diff = toupperW(*name1++) - toupperW(*name2++);
1367 /****************************************************************************
1371 * Add a directory entry to a storage
1373 static HRESULT insertIntoTree(
1374 StorageBaseImpl *This,
1375 DirRef parentStorageIndex,
1376 DirRef newEntryIndex)
1378 DirEntry currentEntry;
1382 * Read the inserted entry
1384 StorageBaseImpl_ReadDirEntry(This,
1389 * Read the storage entry
1391 StorageBaseImpl_ReadDirEntry(This,
1395 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1398 * The root storage contains some element, therefore, start the research
1399 * for the appropriate location.
1402 DirRef current, next, previous, currentEntryId;
1405 * Keep a reference to the root of the storage's element tree
1407 currentEntryId = currentEntry.dirRootEntry;
1412 StorageBaseImpl_ReadDirEntry(This,
1413 currentEntry.dirRootEntry,
1416 previous = currentEntry.leftChild;
1417 next = currentEntry.rightChild;
1418 current = currentEntryId;
1422 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1426 if (previous != DIRENTRY_NULL)
1428 StorageBaseImpl_ReadDirEntry(This,
1435 currentEntry.leftChild = newEntryIndex;
1436 StorageBaseImpl_WriteDirEntry(This,
1444 if (next != DIRENTRY_NULL)
1446 StorageBaseImpl_ReadDirEntry(This,
1453 currentEntry.rightChild = newEntryIndex;
1454 StorageBaseImpl_WriteDirEntry(This,
1463 * Trying to insert an item with the same name in the
1464 * subtree structure.
1466 return STG_E_FILEALREADYEXISTS;
1469 previous = currentEntry.leftChild;
1470 next = currentEntry.rightChild;
1476 * The storage is empty, make the new entry the root of its element tree
1478 currentEntry.dirRootEntry = newEntryIndex;
1479 StorageBaseImpl_WriteDirEntry(This,
1487 /****************************************************************************
1491 * Find and read the element of a storage with the given name.
1493 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1494 const OLECHAR *name, DirEntry *data)
1496 DirRef currentEntry;
1498 /* Read the storage entry to find the root of the tree. */
1499 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1501 currentEntry = data->dirRootEntry;
1503 while (currentEntry != DIRENTRY_NULL)
1507 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1509 cmp = entryNameCmp(name, data->name);
1516 currentEntry = data->leftChild;
1519 currentEntry = data->rightChild;
1522 return currentEntry;
1525 /****************************************************************************
1529 * Find and read the binary tree parent of the element with the given name.
1531 * If there is no such element, find a place where it could be inserted and
1532 * return STG_E_FILENOTFOUND.
1534 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1535 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1541 /* Read the storage entry to find the root of the tree. */
1542 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1544 *parentEntry = storageEntry;
1545 *relation = DIRENTRY_RELATION_DIR;
1547 childEntry = parentData->dirRootEntry;
1549 while (childEntry != DIRENTRY_NULL)
1553 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1555 cmp = entryNameCmp(childName, childData.name);
1563 *parentData = childData;
1564 *parentEntry = childEntry;
1565 *relation = DIRENTRY_RELATION_PREVIOUS;
1567 childEntry = parentData->leftChild;
1572 *parentData = childData;
1573 *parentEntry = childEntry;
1574 *relation = DIRENTRY_RELATION_NEXT;
1576 childEntry = parentData->rightChild;
1580 if (childEntry == DIRENTRY_NULL)
1581 return STG_E_FILENOTFOUND;
1587 /*************************************************************************
1590 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1592 DWORD ciidExclude, /* [in] */
1593 const IID* rgiidExclude, /* [size_is][unique][in] */
1594 SNB snbExclude, /* [unique][in] */
1595 IStorage* pstgDest) /* [unique][in] */
1597 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1599 IEnumSTATSTG *elements = 0;
1600 STATSTG curElement, strStat;
1602 IStorage *pstgTmp, *pstgChild;
1603 IStream *pstrTmp, *pstrChild;
1606 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1609 TRACE("(%p, %d, %p, %p, %p)\n",
1610 iface, ciidExclude, rgiidExclude,
1611 snbExclude, pstgDest);
1613 if ( pstgDest == 0 )
1614 return STG_E_INVALIDPOINTER;
1617 * Enumerate the elements
1619 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1627 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1628 IStorage_SetClass( pstgDest, &curElement.clsid );
1630 for(i = 0; i < ciidExclude; ++i)
1632 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1633 skip_storage = TRUE;
1634 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1637 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1643 * Obtain the next element
1645 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1647 if ( hr == S_FALSE )
1649 hr = S_OK; /* done, every element has been copied */
1655 WCHAR **snb = snbExclude;
1657 while ( *snb != NULL && !skip )
1659 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1668 if (curElement.type == STGTY_STORAGE)
1674 * open child source storage
1676 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1677 STGM_READ|STGM_SHARE_EXCLUSIVE,
1678 NULL, 0, &pstgChild );
1684 * create a new storage in destination storage
1686 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1687 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1691 * if it already exist, don't create a new one use this one
1693 if (hr == STG_E_FILEALREADYEXISTS)
1695 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1696 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1697 NULL, 0, &pstgTmp );
1703 * do the copy recursively
1705 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1708 IStorage_Release( pstgTmp );
1711 IStorage_Release( pstgChild );
1713 else if (curElement.type == STGTY_STREAM)
1719 * create a new stream in destination storage. If the stream already
1720 * exist, it will be deleted and a new one will be created.
1722 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1723 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1730 * open child stream storage. This operation must succeed even if the
1731 * stream is already open, so we use internal functions to do it.
1733 srcEntryRef = findElement( This, This->storageDirEntry, curElement.pwcsName,
1737 ERR("source stream not found\n");
1738 hr = STG_E_DOCFILECORRUPT;
1743 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntryRef);
1745 IStream_AddRef(pstrChild);
1753 * Get the size of the source stream
1755 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1758 * Set the size of the destination stream.
1760 IStream_SetSize(pstrTmp, strStat.cbSize);
1765 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1768 IStream_Release( pstrChild );
1771 IStream_Release( pstrTmp );
1775 WARN("unknown element type: %d\n", curElement.type);
1779 CoTaskMemFree(curElement.pwcsName);
1780 } while (hr == S_OK);
1785 IEnumSTATSTG_Release(elements);
1790 /*************************************************************************
1791 * MoveElementTo (IStorage)
1793 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1795 const OLECHAR *pwcsName, /* [string][in] */
1796 IStorage *pstgDest, /* [unique][in] */
1797 const OLECHAR *pwcsNewName,/* [string][in] */
1798 DWORD grfFlags) /* [in] */
1800 FIXME("(%p %s %p %s %u): stub\n", iface,
1801 debugstr_w(pwcsName), pstgDest,
1802 debugstr_w(pwcsNewName), grfFlags);
1806 /*************************************************************************
1809 * Ensures that any changes made to a storage object open in transacted mode
1810 * are reflected in the parent storage
1813 * Wine doesn't implement transacted mode, which seems to be a basic
1814 * optimization, so we can ignore this stub for now.
1816 static HRESULT WINAPI StorageImpl_Commit(
1818 DWORD grfCommitFlags)/* [in] */
1820 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1824 /*************************************************************************
1827 * Discard all changes that have been made since the last commit operation
1829 static HRESULT WINAPI StorageImpl_Revert(
1832 TRACE("(%p)\n", iface);
1836 /*************************************************************************
1837 * DestroyElement (IStorage)
1839 * Strategy: This implementation is built this way for simplicity not for speed.
1840 * I always delete the topmost element of the enumeration and adjust
1841 * the deleted element pointer all the time. This takes longer to
1842 * do but allow to reinvoke DestroyElement whenever we encounter a
1843 * storage object. The optimisation resides in the usage of another
1844 * enumeration strategy that would give all the leaves of a storage
1845 * first. (postfix order)
1847 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1849 const OLECHAR *pwcsName)/* [string][in] */
1851 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1854 DirEntry entryToDelete;
1855 DirRef entryToDeleteRef;
1858 iface, debugstr_w(pwcsName));
1861 return STG_E_INVALIDPOINTER;
1864 return STG_E_REVERTED;
1866 if ( !(This->openFlags & STGM_TRANSACTED) &&
1867 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1868 return STG_E_ACCESSDENIED;
1870 entryToDeleteRef = findElement(
1872 This->storageDirEntry,
1876 if ( entryToDeleteRef == DIRENTRY_NULL )
1878 return STG_E_FILENOTFOUND;
1881 if ( entryToDelete.stgType == STGTY_STORAGE )
1883 hr = deleteStorageContents(
1888 else if ( entryToDelete.stgType == STGTY_STREAM )
1890 hr = deleteStreamContents(
1900 * Remove the entry from its parent storage
1902 hr = removeFromTree(
1904 This->storageDirEntry,
1908 * Invalidate the entry
1911 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1917 /******************************************************************************
1918 * Internal stream list handlers
1921 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1923 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1924 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1927 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1929 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1930 list_remove(&(strm->StrmListEntry));
1933 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1935 StgStreamImpl *strm;
1937 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1939 if (strm->dirEntry == streamEntry)
1948 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1950 StorageInternalImpl *childstg;
1952 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1954 if (childstg->base.storageDirEntry == storageEntry)
1963 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1965 struct list *cur, *cur2;
1966 StgStreamImpl *strm=NULL;
1967 StorageInternalImpl *childstg=NULL;
1969 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1970 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1971 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1972 strm->parentStorage = NULL;
1976 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1977 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1978 StorageBaseImpl_Invalidate( &childstg->base );
1981 if (stg->transactedChild)
1983 StorageBaseImpl_Invalidate(stg->transactedChild);
1985 stg->transactedChild = NULL;
1990 /*********************************************************************
1994 * Delete the contents of a storage entry.
1997 static HRESULT deleteStorageContents(
1998 StorageBaseImpl *parentStorage,
1999 DirRef indexToDelete,
2000 DirEntry entryDataToDelete)
2002 IEnumSTATSTG *elements = 0;
2003 IStorage *childStorage = 0;
2004 STATSTG currentElement;
2006 HRESULT destroyHr = S_OK;
2007 StorageInternalImpl *stg, *stg2;
2009 /* Invalidate any open storage objects. */
2010 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2012 if (stg->base.storageDirEntry == indexToDelete)
2014 StorageBaseImpl_Invalidate(&stg->base);
2019 * Open the storage and enumerate it
2021 hr = StorageBaseImpl_OpenStorage(
2022 (IStorage*)parentStorage,
2023 entryDataToDelete.name,
2025 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2036 * Enumerate the elements
2038 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2043 * Obtain the next element
2045 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
2048 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2050 CoTaskMemFree(currentElement.pwcsName);
2054 * We need to Reset the enumeration every time because we delete elements
2055 * and the enumeration could be invalid
2057 IEnumSTATSTG_Reset(elements);
2059 } while ((hr == S_OK) && (destroyHr == S_OK));
2061 IStorage_Release(childStorage);
2062 IEnumSTATSTG_Release(elements);
2067 /*********************************************************************
2071 * Perform the deletion of a stream's data
2074 static HRESULT deleteStreamContents(
2075 StorageBaseImpl *parentStorage,
2076 DirRef indexToDelete,
2077 DirEntry entryDataToDelete)
2081 ULARGE_INTEGER size;
2082 StgStreamImpl *strm, *strm2;
2084 /* Invalidate any open stream objects. */
2085 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2087 if (strm->dirEntry == indexToDelete)
2089 TRACE("Stream deleted %p\n", strm);
2090 strm->parentStorage = NULL;
2091 list_remove(&strm->StrmListEntry);
2095 size.u.HighPart = 0;
2098 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2099 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2109 hr = IStream_SetSize(pis, size);
2117 * Release the stream object.
2119 IStream_Release(pis);
2124 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2128 case DIRENTRY_RELATION_PREVIOUS:
2129 entry->leftChild = new_target;
2131 case DIRENTRY_RELATION_NEXT:
2132 entry->rightChild = new_target;
2134 case DIRENTRY_RELATION_DIR:
2135 entry->dirRootEntry = new_target;
2142 /*************************************************************************
2146 * This method removes a directory entry from its parent storage tree without
2147 * freeing any resources attached to it.
2149 static HRESULT removeFromTree(
2150 StorageBaseImpl *This,
2151 DirRef parentStorageIndex,
2152 DirRef deletedIndex)
2155 DirEntry entryToDelete;
2156 DirEntry parentEntry;
2157 DirRef parentEntryRef;
2158 ULONG typeOfRelation;
2160 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2166 * Find the element that links to the one we want to delete.
2168 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2169 &parentEntry, &parentEntryRef, &typeOfRelation);
2174 if (entryToDelete.leftChild != DIRENTRY_NULL)
2177 * Replace the deleted entry with its left child
2179 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2181 hr = StorageBaseImpl_WriteDirEntry(
2190 if (entryToDelete.rightChild != DIRENTRY_NULL)
2193 * We need to reinsert the right child somewhere. We already know it and
2194 * its children are greater than everything in the left tree, so we
2195 * insert it at the rightmost point in the left tree.
2197 DirRef newRightChildParent = entryToDelete.leftChild;
2198 DirEntry newRightChildParentEntry;
2202 hr = StorageBaseImpl_ReadDirEntry(
2204 newRightChildParent,
2205 &newRightChildParentEntry);
2211 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2212 newRightChildParent = newRightChildParentEntry.rightChild;
2213 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2215 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2217 hr = StorageBaseImpl_WriteDirEntry(
2219 newRightChildParent,
2220 &newRightChildParentEntry);
2230 * Replace the deleted entry with its right child
2232 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2234 hr = StorageBaseImpl_WriteDirEntry(
2248 /******************************************************************************
2249 * SetElementTimes (IStorage)
2251 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2253 const OLECHAR *pwcsName,/* [string][in] */
2254 const FILETIME *pctime, /* [in] */
2255 const FILETIME *patime, /* [in] */
2256 const FILETIME *pmtime) /* [in] */
2258 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2262 /******************************************************************************
2263 * SetStateBits (IStorage)
2265 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2267 DWORD grfStateBits,/* [in] */
2268 DWORD grfMask) /* [in] */
2270 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2273 return STG_E_REVERTED;
2275 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2279 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2280 DirRef index, const DirEntry *data)
2282 StorageImpl *This = (StorageImpl*)base;
2283 return StorageImpl_WriteDirEntry(This, index, data);
2286 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2287 DirRef index, DirEntry *data)
2289 StorageImpl *This = (StorageImpl*)base;
2290 return StorageImpl_ReadDirEntry(This, index, data);
2293 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2297 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2299 if (!This->blockChainCache[i])
2301 return &This->blockChainCache[i];
2305 i = This->blockChainToEvict;
2307 BlockChainStream_Destroy(This->blockChainCache[i]);
2308 This->blockChainCache[i] = NULL;
2310 This->blockChainToEvict++;
2311 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2312 This->blockChainToEvict = 0;
2314 return &This->blockChainCache[i];
2317 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2320 int i, free_index=-1;
2322 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2324 if (!This->blockChainCache[i])
2326 if (free_index == -1) free_index = i;
2328 else if (This->blockChainCache[i]->ownerDirEntry == index)
2330 return &This->blockChainCache[i];
2334 if (free_index == -1)
2336 free_index = This->blockChainToEvict;
2338 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2339 This->blockChainCache[free_index] = NULL;
2341 This->blockChainToEvict++;
2342 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2343 This->blockChainToEvict = 0;
2346 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2347 return &This->blockChainCache[free_index];
2350 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2351 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2353 StorageImpl *This = (StorageImpl*)base;
2358 hr = StorageImpl_ReadDirEntry(This, index, &data);
2359 if (FAILED(hr)) return hr;
2361 if (data.size.QuadPart == 0)
2367 if (offset.QuadPart + size > data.size.QuadPart)
2369 bytesToRead = data.size.QuadPart - offset.QuadPart;
2376 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2378 SmallBlockChainStream *stream;
2380 stream = SmallBlockChainStream_Construct(This, NULL, index);
2381 if (!stream) return E_OUTOFMEMORY;
2383 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2385 SmallBlockChainStream_Destroy(stream);
2391 BlockChainStream *stream = NULL;
2393 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2394 if (!stream) return E_OUTOFMEMORY;
2396 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2402 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2403 ULARGE_INTEGER newsize)
2405 StorageImpl *This = (StorageImpl*)base;
2408 SmallBlockChainStream *smallblock=NULL;
2409 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2411 hr = StorageImpl_ReadDirEntry(This, index, &data);
2412 if (FAILED(hr)) return hr;
2414 /* In simple mode keep the stream size above the small block limit */
2415 if (This->base.openFlags & STGM_SIMPLE)
2416 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2418 if (data.size.QuadPart == newsize.QuadPart)
2421 /* Create a block chain object of the appropriate type */
2422 if (data.size.QuadPart == 0)
2424 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2426 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2427 if (!smallblock) return E_OUTOFMEMORY;
2431 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2432 bigblock = *pbigblock;
2433 if (!bigblock) return E_OUTOFMEMORY;
2436 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2438 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2439 if (!smallblock) return E_OUTOFMEMORY;
2443 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2444 bigblock = *pbigblock;
2445 if (!bigblock) return E_OUTOFMEMORY;
2448 /* Change the block chain type if necessary. */
2449 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2451 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2454 SmallBlockChainStream_Destroy(smallblock);
2458 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2459 *pbigblock = bigblock;
2461 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2463 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2468 /* Set the size of the block chain. */
2471 SmallBlockChainStream_SetSize(smallblock, newsize);
2472 SmallBlockChainStream_Destroy(smallblock);
2476 BlockChainStream_SetSize(bigblock, newsize);
2479 /* Set the size in the directory entry. */
2480 hr = StorageImpl_ReadDirEntry(This, index, &data);
2483 data.size = newsize;
2485 hr = StorageImpl_WriteDirEntry(This, index, &data);
2490 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2491 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2493 StorageImpl *This = (StorageImpl*)base;
2496 ULARGE_INTEGER newSize;
2498 hr = StorageImpl_ReadDirEntry(This, index, &data);
2499 if (FAILED(hr)) return hr;
2501 /* Grow the stream if necessary */
2502 newSize.QuadPart = 0;
2503 newSize.QuadPart = offset.QuadPart + size;
2505 if (newSize.QuadPart > data.size.QuadPart)
2507 hr = StorageImpl_StreamSetSize(base, index, newSize);
2511 hr = StorageImpl_ReadDirEntry(This, index, &data);
2512 if (FAILED(hr)) return hr;
2515 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2517 SmallBlockChainStream *stream;
2519 stream = SmallBlockChainStream_Construct(This, NULL, index);
2520 if (!stream) return E_OUTOFMEMORY;
2522 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2524 SmallBlockChainStream_Destroy(stream);
2530 BlockChainStream *stream;
2532 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2533 if (!stream) return E_OUTOFMEMORY;
2535 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2542 * Virtual function table for the IStorage32Impl class.
2544 static const IStorageVtbl Storage32Impl_Vtbl =
2546 StorageBaseImpl_QueryInterface,
2547 StorageBaseImpl_AddRef,
2548 StorageBaseImpl_Release,
2549 StorageBaseImpl_CreateStream,
2550 StorageBaseImpl_OpenStream,
2551 StorageBaseImpl_CreateStorage,
2552 StorageBaseImpl_OpenStorage,
2553 StorageBaseImpl_CopyTo,
2554 StorageBaseImpl_MoveElementTo,
2557 StorageBaseImpl_EnumElements,
2558 StorageBaseImpl_DestroyElement,
2559 StorageBaseImpl_RenameElement,
2560 StorageBaseImpl_SetElementTimes,
2561 StorageBaseImpl_SetClass,
2562 StorageBaseImpl_SetStateBits,
2563 StorageBaseImpl_Stat
2566 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2568 StorageImpl_Destroy,
2569 StorageImpl_Invalidate,
2570 StorageImpl_CreateDirEntry,
2571 StorageImpl_BaseWriteDirEntry,
2572 StorageImpl_BaseReadDirEntry,
2573 StorageImpl_DestroyDirEntry,
2574 StorageImpl_StreamReadAt,
2575 StorageImpl_StreamWriteAt,
2576 StorageImpl_StreamSetSize
2579 static HRESULT StorageImpl_Construct(
2586 StorageImpl** result)
2590 DirEntry currentEntry;
2591 DirRef currentEntryRef;
2592 WCHAR fullpath[MAX_PATH];
2594 if ( FAILED( validateSTGM(openFlags) ))
2595 return STG_E_INVALIDFLAG;
2597 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2599 return E_OUTOFMEMORY;
2601 memset(This, 0, sizeof(StorageImpl));
2603 list_init(&This->base.strmHead);
2605 list_init(&This->base.storageHead);
2607 This->base.lpVtbl = &Storage32Impl_Vtbl;
2608 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2609 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2610 This->base.openFlags = (openFlags & ~STGM_CREATE);
2612 This->base.create = create;
2614 This->base.reverted = 0;
2616 This->hFile = hFile;
2619 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2621 lstrcpynW(fullpath, pwcsName, MAX_PATH);
2623 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2624 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2625 if (!This->pwcsName)
2627 hr = STG_E_INSUFFICIENTMEMORY;
2630 strcpyW(This->pwcsName, fullpath);
2631 This->base.filename = This->pwcsName;
2635 * Initialize the big block cache.
2637 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2638 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2639 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2644 if (This->bigBlockFile == 0)
2652 ULARGE_INTEGER size;
2653 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2656 * Initialize all header variables:
2657 * - The big block depot consists of one block and it is at block 0
2658 * - The directory table starts at block 1
2659 * - There is no small block depot
2661 memset( This->bigBlockDepotStart,
2663 sizeof(This->bigBlockDepotStart));
2665 This->bigBlockDepotCount = 1;
2666 This->bigBlockDepotStart[0] = 0;
2667 This->rootStartBlock = 1;
2668 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2669 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2670 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2671 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2672 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2673 This->extBigBlockDepotCount = 0;
2675 StorageImpl_SaveFileHeader(This);
2678 * Add one block for the big block depot and one block for the directory table
2680 size.u.HighPart = 0;
2681 size.u.LowPart = This->bigBlockSize * 3;
2682 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2685 * Initialize the big block depot
2687 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2688 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2689 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2690 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2695 * Load the header for the file.
2697 hr = StorageImpl_LoadFileHeader(This);
2706 * There is no block depot cached yet.
2708 This->indexBlockDepotCached = 0xFFFFFFFF;
2711 * Start searching for free blocks with block 0.
2713 This->prevFreeBlock = 0;
2715 This->firstFreeSmallBlock = 0;
2718 * Create the block chain abstractions.
2720 if(!(This->rootBlockChain =
2721 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2723 hr = STG_E_READFAULT;
2727 if(!(This->smallBlockDepotChain =
2728 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2731 hr = STG_E_READFAULT;
2736 * Write the root storage entry (memory only)
2742 * Initialize the directory table
2744 memset(&rootEntry, 0, sizeof(rootEntry));
2745 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2746 sizeof(rootEntry.name)/sizeof(WCHAR) );
2747 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2748 rootEntry.stgType = STGTY_ROOT;
2749 rootEntry.leftChild = DIRENTRY_NULL;
2750 rootEntry.rightChild = DIRENTRY_NULL;
2751 rootEntry.dirRootEntry = DIRENTRY_NULL;
2752 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2753 rootEntry.size.u.HighPart = 0;
2754 rootEntry.size.u.LowPart = 0;
2756 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2760 * Find the ID of the root storage.
2762 currentEntryRef = 0;
2766 hr = StorageImpl_ReadDirEntry(
2773 if ( (currentEntry.sizeOfNameString != 0 ) &&
2774 (currentEntry.stgType == STGTY_ROOT) )
2776 This->base.storageDirEntry = currentEntryRef;
2782 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2786 hr = STG_E_READFAULT;
2791 * Create the block chain abstraction for the small block root chain.
2793 if(!(This->smallBlockRootChain =
2794 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2796 hr = STG_E_READFAULT;
2802 IStorage_Release((IStorage*)This);
2811 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2813 StorageImpl *This = (StorageImpl*) iface;
2815 StorageBaseImpl_DeleteAll(&This->base);
2817 This->base.reverted = 1;
2820 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2822 StorageImpl *This = (StorageImpl*) iface;
2824 TRACE("(%p)\n", This);
2826 StorageImpl_Invalidate(iface);
2828 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2830 BlockChainStream_Destroy(This->smallBlockRootChain);
2831 BlockChainStream_Destroy(This->rootBlockChain);
2832 BlockChainStream_Destroy(This->smallBlockDepotChain);
2834 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2835 BlockChainStream_Destroy(This->blockChainCache[i]);
2837 if (This->bigBlockFile)
2838 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2839 HeapFree(GetProcessHeap(), 0, This);
2842 /******************************************************************************
2843 * Storage32Impl_GetNextFreeBigBlock
2845 * Returns the index of the next free big block.
2846 * If the big block depot is filled, this method will enlarge it.
2849 static ULONG StorageImpl_GetNextFreeBigBlock(
2852 ULONG depotBlockIndexPos;
2853 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
2855 ULONG depotBlockOffset;
2856 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2857 ULONG nextBlockIndex = BLOCK_SPECIAL;
2859 ULONG freeBlock = BLOCK_UNUSED;
2860 ULARGE_INTEGER neededSize;
2862 depotIndex = This->prevFreeBlock / blocksPerDepot;
2863 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2866 * Scan the entire big block depot until we find a block marked free
2868 while (nextBlockIndex != BLOCK_UNUSED)
2870 if (depotIndex < COUNT_BBDEPOTINHEADER)
2872 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2875 * Grow the primary depot.
2877 if (depotBlockIndexPos == BLOCK_UNUSED)
2879 depotBlockIndexPos = depotIndex*blocksPerDepot;
2882 * Add a block depot.
2884 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2885 This->bigBlockDepotCount++;
2886 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2889 * Flag it as a block depot.
2891 StorageImpl_SetNextBlockInChain(This,
2895 /* Save new header information.
2897 StorageImpl_SaveFileHeader(This);
2902 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2904 if (depotBlockIndexPos == BLOCK_UNUSED)
2907 * Grow the extended depot.
2909 ULONG extIndex = BLOCK_UNUSED;
2910 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2911 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2913 if (extBlockOffset == 0)
2915 /* We need an extended block.
2917 extIndex = Storage32Impl_AddExtBlockDepot(This);
2918 This->extBigBlockDepotCount++;
2919 depotBlockIndexPos = extIndex + 1;
2922 depotBlockIndexPos = depotIndex * blocksPerDepot;
2925 * Add a block depot and mark it in the extended block.
2927 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2928 This->bigBlockDepotCount++;
2929 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2931 /* Flag the block depot.
2933 StorageImpl_SetNextBlockInChain(This,
2937 /* If necessary, flag the extended depot block.
2939 if (extIndex != BLOCK_UNUSED)
2940 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2942 /* Save header information.
2944 StorageImpl_SaveFileHeader(This);
2948 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2952 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2953 ( nextBlockIndex != BLOCK_UNUSED))
2955 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2957 if (nextBlockIndex == BLOCK_UNUSED)
2959 freeBlock = (depotIndex * blocksPerDepot) +
2960 (depotBlockOffset/sizeof(ULONG));
2963 depotBlockOffset += sizeof(ULONG);
2968 depotBlockOffset = 0;
2972 * make sure that the block physically exists before using it
2974 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
2975 BIGBLOCKFILE_Expand(This->bigBlockFile, neededSize);
2977 This->prevFreeBlock = freeBlock;
2982 /******************************************************************************
2983 * Storage32Impl_AddBlockDepot
2985 * This will create a depot block, essentially it is a block initialized
2988 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2990 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
2993 * Initialize blocks as free
2995 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2996 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2999 /******************************************************************************
3000 * Storage32Impl_GetExtDepotBlock
3002 * Returns the index of the block that corresponds to the specified depot
3003 * index. This method is only for depot indexes equal or greater than
3004 * COUNT_BBDEPOTINHEADER.
3006 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3008 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3009 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3010 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3011 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3012 ULONG blockIndex = BLOCK_UNUSED;
3013 ULONG extBlockIndex = This->extBigBlockDepotStart;
3015 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3017 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
3018 return BLOCK_UNUSED;
3020 while (extBlockCount > 0)
3022 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3026 if (extBlockIndex != BLOCK_UNUSED)
3027 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3028 extBlockOffset * sizeof(ULONG), &blockIndex);
3033 /******************************************************************************
3034 * Storage32Impl_SetExtDepotBlock
3036 * Associates the specified block index to the specified depot index.
3037 * This method is only for depot indexes equal or greater than
3038 * COUNT_BBDEPOTINHEADER.
3040 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3042 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3043 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3044 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3045 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3046 ULONG extBlockIndex = This->extBigBlockDepotStart;
3048 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3050 while (extBlockCount > 0)
3052 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3056 if (extBlockIndex != BLOCK_UNUSED)
3058 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3059 extBlockOffset * sizeof(ULONG),
3064 /******************************************************************************
3065 * Storage32Impl_AddExtBlockDepot
3067 * Creates an extended depot block.
3069 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3071 ULONG numExtBlocks = This->extBigBlockDepotCount;
3072 ULONG nextExtBlock = This->extBigBlockDepotStart;
3073 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3074 ULONG index = BLOCK_UNUSED;
3075 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3076 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3077 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3079 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3080 blocksPerDepotBlock;
3082 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3085 * The first extended block.
3087 This->extBigBlockDepotStart = index;
3093 * Follow the chain to the last one.
3095 for (i = 0; i < (numExtBlocks - 1); i++)
3097 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3101 * Add the new extended block to the chain.
3103 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3108 * Initialize this block.
3110 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3111 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3116 /******************************************************************************
3117 * Storage32Impl_FreeBigBlock
3119 * This method will flag the specified block as free in the big block depot.
3121 static void StorageImpl_FreeBigBlock(
3125 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3127 if (blockIndex < This->prevFreeBlock)
3128 This->prevFreeBlock = blockIndex;
3131 /************************************************************************
3132 * Storage32Impl_GetNextBlockInChain
3134 * This method will retrieve the block index of the next big block in
3137 * Params: This - Pointer to the Storage object.
3138 * blockIndex - Index of the block to retrieve the chain
3140 * nextBlockIndex - receives the return value.
3142 * Returns: This method returns the index of the next block in the chain.
3143 * It will return the constants:
3144 * BLOCK_SPECIAL - If the block given was not part of a
3146 * BLOCK_END_OF_CHAIN - If the block given was the last in
3148 * BLOCK_UNUSED - If the block given was not past of a chain
3150 * BLOCK_EXTBBDEPOT - This block is part of the extended
3153 * See Windows documentation for more details on IStorage methods.
3155 static HRESULT StorageImpl_GetNextBlockInChain(
3158 ULONG* nextBlockIndex)
3160 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3161 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3162 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3163 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3165 ULONG depotBlockIndexPos;
3166 int index, num_blocks;
3168 *nextBlockIndex = BLOCK_SPECIAL;
3170 if(depotBlockCount >= This->bigBlockDepotCount)
3172 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3173 This->bigBlockDepotCount);
3174 return STG_E_READFAULT;
3178 * Cache the currently accessed depot block.
3180 if (depotBlockCount != This->indexBlockDepotCached)
3182 This->indexBlockDepotCached = depotBlockCount;
3184 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3186 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3191 * We have to look in the extended depot.
3193 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3196 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3199 return STG_E_READFAULT;
3201 num_blocks = This->bigBlockSize / 4;
3203 for (index = 0; index < num_blocks; index++)
3205 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3206 This->blockDepotCached[index] = *nextBlockIndex;
3210 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3215 /******************************************************************************
3216 * Storage32Impl_GetNextExtendedBlock
3218 * Given an extended block this method will return the next extended block.
3221 * The last ULONG of an extended block is the block index of the next
3222 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3226 * - The index of the next extended block
3227 * - BLOCK_UNUSED: there is no next extended block.
3228 * - Any other return values denotes failure.
3230 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3232 ULONG nextBlockIndex = BLOCK_SPECIAL;
3233 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3235 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3238 return nextBlockIndex;
3241 /******************************************************************************
3242 * Storage32Impl_SetNextBlockInChain
3244 * This method will write the index of the specified block's next block
3245 * in the big block depot.
3247 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3250 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3251 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3252 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3255 static void StorageImpl_SetNextBlockInChain(
3260 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3261 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3262 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3263 ULONG depotBlockIndexPos;
3265 assert(depotBlockCount < This->bigBlockDepotCount);
3266 assert(blockIndex != nextBlock);
3268 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3270 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3275 * We have to look in the extended depot.
3277 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3280 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3283 * Update the cached block depot, if necessary.
3285 if (depotBlockCount == This->indexBlockDepotCached)
3287 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3291 /******************************************************************************
3292 * Storage32Impl_LoadFileHeader
3294 * This method will read in the file header
3296 static HRESULT StorageImpl_LoadFileHeader(
3300 BYTE headerBigBlock[HEADER_SIZE];
3302 ULARGE_INTEGER offset;
3307 * Get a pointer to the big block of data containing the header.
3309 offset.u.HighPart = 0;
3310 offset.u.LowPart = 0;
3311 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3312 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3313 hr = STG_E_FILENOTFOUND;
3316 * Extract the information from the header.
3321 * Check for the "magic number" signature and return an error if it is not
3324 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3326 return STG_E_OLDFORMAT;
3329 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3331 return STG_E_INVALIDHEADER;
3334 StorageUtl_ReadWord(
3336 OFFSET_BIGBLOCKSIZEBITS,
3337 &This->bigBlockSizeBits);
3339 StorageUtl_ReadWord(
3341 OFFSET_SMALLBLOCKSIZEBITS,
3342 &This->smallBlockSizeBits);
3344 StorageUtl_ReadDWord(
3346 OFFSET_BBDEPOTCOUNT,
3347 &This->bigBlockDepotCount);
3349 StorageUtl_ReadDWord(
3351 OFFSET_ROOTSTARTBLOCK,
3352 &This->rootStartBlock);
3354 StorageUtl_ReadDWord(
3356 OFFSET_SMALLBLOCKLIMIT,
3357 &This->smallBlockLimit);
3359 StorageUtl_ReadDWord(
3361 OFFSET_SBDEPOTSTART,
3362 &This->smallBlockDepotStart);
3364 StorageUtl_ReadDWord(
3366 OFFSET_EXTBBDEPOTSTART,
3367 &This->extBigBlockDepotStart);
3369 StorageUtl_ReadDWord(
3371 OFFSET_EXTBBDEPOTCOUNT,
3372 &This->extBigBlockDepotCount);
3374 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3376 StorageUtl_ReadDWord(
3378 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3379 &(This->bigBlockDepotStart[index]));
3383 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3385 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3386 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3389 * Right now, the code is making some assumptions about the size of the
3390 * blocks, just make sure they are what we're expecting.
3392 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3393 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3394 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3396 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3397 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3398 hr = STG_E_INVALIDHEADER;
3407 /******************************************************************************
3408 * Storage32Impl_SaveFileHeader
3410 * This method will save to the file the header
3412 static void StorageImpl_SaveFileHeader(
3415 BYTE headerBigBlock[HEADER_SIZE];
3418 ULARGE_INTEGER offset;
3419 DWORD bytes_read, bytes_written;
3422 * Get a pointer to the big block of data containing the header.
3424 offset.u.HighPart = 0;
3425 offset.u.LowPart = 0;
3426 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3427 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3428 hr = STG_E_FILENOTFOUND;
3431 * If the block read failed, the file is probably new.
3436 * Initialize for all unknown fields.
3438 memset(headerBigBlock, 0, HEADER_SIZE);
3441 * Initialize the magic number.
3443 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3446 * And a bunch of things we don't know what they mean
3448 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3449 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3450 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3454 * Write the information to the header.
3456 StorageUtl_WriteWord(
3458 OFFSET_BIGBLOCKSIZEBITS,
3459 This->bigBlockSizeBits);
3461 StorageUtl_WriteWord(
3463 OFFSET_SMALLBLOCKSIZEBITS,
3464 This->smallBlockSizeBits);
3466 StorageUtl_WriteDWord(
3468 OFFSET_BBDEPOTCOUNT,
3469 This->bigBlockDepotCount);
3471 StorageUtl_WriteDWord(
3473 OFFSET_ROOTSTARTBLOCK,
3474 This->rootStartBlock);
3476 StorageUtl_WriteDWord(
3478 OFFSET_SMALLBLOCKLIMIT,
3479 This->smallBlockLimit);
3481 StorageUtl_WriteDWord(
3483 OFFSET_SBDEPOTSTART,
3484 This->smallBlockDepotStart);
3486 StorageUtl_WriteDWord(
3488 OFFSET_SBDEPOTCOUNT,
3489 This->smallBlockDepotChain ?
3490 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3492 StorageUtl_WriteDWord(
3494 OFFSET_EXTBBDEPOTSTART,
3495 This->extBigBlockDepotStart);
3497 StorageUtl_WriteDWord(
3499 OFFSET_EXTBBDEPOTCOUNT,
3500 This->extBigBlockDepotCount);
3502 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3504 StorageUtl_WriteDWord(
3506 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3507 (This->bigBlockDepotStart[index]));
3511 * Write the big block back to the file.
3513 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3516 /******************************************************************************
3517 * StorageImpl_ReadRawDirEntry
3519 * This method will read the raw data from a directory entry in the file.
3521 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3523 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3525 ULARGE_INTEGER offset;
3529 offset.u.HighPart = 0;
3530 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3532 hr = BlockChainStream_ReadAt(
3533 This->rootBlockChain,
3542 /******************************************************************************
3543 * StorageImpl_WriteRawDirEntry
3545 * This method will write the raw data from a directory entry in the file.
3547 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3549 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3551 ULARGE_INTEGER offset;
3555 offset.u.HighPart = 0;
3556 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3558 hr = BlockChainStream_WriteAt(
3559 This->rootBlockChain,
3568 /******************************************************************************
3571 * Update raw directory entry data from the fields in newData.
3573 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3575 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3577 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3580 buffer + OFFSET_PS_NAME,
3582 DIRENTRY_NAME_BUFFER_LEN );
3584 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3586 StorageUtl_WriteWord(
3588 OFFSET_PS_NAMELENGTH,
3589 newData->sizeOfNameString);
3591 StorageUtl_WriteDWord(
3593 OFFSET_PS_LEFTCHILD,
3594 newData->leftChild);
3596 StorageUtl_WriteDWord(
3598 OFFSET_PS_RIGHTCHILD,
3599 newData->rightChild);
3601 StorageUtl_WriteDWord(
3604 newData->dirRootEntry);
3606 StorageUtl_WriteGUID(
3611 StorageUtl_WriteDWord(
3614 newData->ctime.dwLowDateTime);
3616 StorageUtl_WriteDWord(
3618 OFFSET_PS_CTIMEHIGH,
3619 newData->ctime.dwHighDateTime);
3621 StorageUtl_WriteDWord(
3624 newData->mtime.dwLowDateTime);
3626 StorageUtl_WriteDWord(
3628 OFFSET_PS_MTIMEHIGH,
3629 newData->ctime.dwHighDateTime);
3631 StorageUtl_WriteDWord(
3633 OFFSET_PS_STARTBLOCK,
3634 newData->startingBlock);
3636 StorageUtl_WriteDWord(
3639 newData->size.u.LowPart);
3642 /******************************************************************************
3643 * Storage32Impl_ReadDirEntry
3645 * This method will read the specified directory entry.
3647 HRESULT StorageImpl_ReadDirEntry(
3652 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3655 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3657 if (SUCCEEDED(readRes))
3659 memset(buffer->name, 0, sizeof(buffer->name));
3662 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3663 DIRENTRY_NAME_BUFFER_LEN );
3664 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3666 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3668 StorageUtl_ReadWord(
3670 OFFSET_PS_NAMELENGTH,
3671 &buffer->sizeOfNameString);
3673 StorageUtl_ReadDWord(
3675 OFFSET_PS_LEFTCHILD,
3676 &buffer->leftChild);
3678 StorageUtl_ReadDWord(
3680 OFFSET_PS_RIGHTCHILD,
3681 &buffer->rightChild);
3683 StorageUtl_ReadDWord(
3686 &buffer->dirRootEntry);
3688 StorageUtl_ReadGUID(
3693 StorageUtl_ReadDWord(
3696 &buffer->ctime.dwLowDateTime);
3698 StorageUtl_ReadDWord(
3700 OFFSET_PS_CTIMEHIGH,
3701 &buffer->ctime.dwHighDateTime);
3703 StorageUtl_ReadDWord(
3706 &buffer->mtime.dwLowDateTime);
3708 StorageUtl_ReadDWord(
3710 OFFSET_PS_MTIMEHIGH,
3711 &buffer->mtime.dwHighDateTime);
3713 StorageUtl_ReadDWord(
3715 OFFSET_PS_STARTBLOCK,
3716 &buffer->startingBlock);
3718 StorageUtl_ReadDWord(
3721 &buffer->size.u.LowPart);
3723 buffer->size.u.HighPart = 0;
3729 /*********************************************************************
3730 * Write the specified directory entry to the file
3732 HRESULT StorageImpl_WriteDirEntry(
3735 const DirEntry* buffer)
3737 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3740 UpdateRawDirEntry(currentEntry, buffer);
3742 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3746 static BOOL StorageImpl_ReadBigBlock(
3751 ULARGE_INTEGER ulOffset;
3754 ulOffset.u.HighPart = 0;
3755 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3757 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3758 return (read == This->bigBlockSize);
3761 static BOOL StorageImpl_ReadDWordFromBigBlock(
3767 ULARGE_INTEGER ulOffset;
3771 ulOffset.u.HighPart = 0;
3772 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3773 ulOffset.u.LowPart += offset;
3775 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3776 *value = lendian32toh(tmp);
3777 return (read == sizeof(DWORD));
3780 static BOOL StorageImpl_WriteBigBlock(
3785 ULARGE_INTEGER ulOffset;
3788 ulOffset.u.HighPart = 0;
3789 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3791 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3792 return (wrote == This->bigBlockSize);
3795 static BOOL StorageImpl_WriteDWordToBigBlock(
3801 ULARGE_INTEGER ulOffset;
3804 ulOffset.u.HighPart = 0;
3805 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3806 ulOffset.u.LowPart += offset;
3808 value = htole32(value);
3809 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3810 return (wrote == sizeof(DWORD));
3813 /******************************************************************************
3814 * Storage32Impl_SmallBlocksToBigBlocks
3816 * This method will convert a small block chain to a big block chain.
3817 * The small block chain will be destroyed.
3819 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3821 SmallBlockChainStream** ppsbChain)
3823 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3824 ULARGE_INTEGER size, offset;
3825 ULONG cbRead, cbWritten;
3826 ULARGE_INTEGER cbTotalRead;
3827 DirRef streamEntryRef;
3828 HRESULT resWrite = S_OK;
3830 DirEntry streamEntry;
3832 BlockChainStream *bbTempChain = NULL;
3833 BlockChainStream *bigBlockChain = NULL;
3836 * Create a temporary big block chain that doesn't have
3837 * an associated directory entry. This temporary chain will be
3838 * used to copy data from small blocks to big blocks.
3840 bbTempChain = BlockChainStream_Construct(This,
3843 if(!bbTempChain) return NULL;
3845 * Grow the big block chain.
3847 size = SmallBlockChainStream_GetSize(*ppsbChain);
3848 BlockChainStream_SetSize(bbTempChain, size);
3851 * Copy the contents of the small block chain to the big block chain
3852 * by small block size increments.
3854 offset.u.LowPart = 0;
3855 offset.u.HighPart = 0;
3856 cbTotalRead.QuadPart = 0;
3858 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3861 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3863 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3866 if (FAILED(resRead))
3871 cbTotalRead.QuadPart += cbRead;
3873 resWrite = BlockChainStream_WriteAt(bbTempChain,
3879 if (FAILED(resWrite))
3882 offset.u.LowPart += cbRead;
3884 } while (cbTotalRead.QuadPart < size.QuadPart);
3885 HeapFree(GetProcessHeap(),0,buffer);
3887 size.u.HighPart = 0;
3890 if (FAILED(resRead) || FAILED(resWrite))
3892 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3893 BlockChainStream_SetSize(bbTempChain, size);
3894 BlockChainStream_Destroy(bbTempChain);
3899 * Destroy the small block chain.
3901 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3902 SmallBlockChainStream_SetSize(*ppsbChain, size);
3903 SmallBlockChainStream_Destroy(*ppsbChain);
3907 * Change the directory entry. This chain is now a big block chain
3908 * and it doesn't reside in the small blocks chain anymore.
3910 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3912 streamEntry.startingBlock = bbHeadOfChain;
3914 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3917 * Destroy the temporary entryless big block chain.
3918 * Create a new big block chain associated with this entry.
3920 BlockChainStream_Destroy(bbTempChain);
3921 bigBlockChain = BlockChainStream_Construct(This,
3925 return bigBlockChain;
3928 /******************************************************************************
3929 * Storage32Impl_BigBlocksToSmallBlocks
3931 * This method will convert a big block chain to a small block chain.
3932 * The big block chain will be destroyed on success.
3934 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3936 BlockChainStream** ppbbChain)
3938 ULARGE_INTEGER size, offset, cbTotalRead;
3939 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3940 DirRef streamEntryRef;
3941 HRESULT resWrite = S_OK, resRead;
3942 DirEntry streamEntry;
3944 SmallBlockChainStream* sbTempChain;
3946 TRACE("%p %p\n", This, ppbbChain);
3948 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3954 size = BlockChainStream_GetSize(*ppbbChain);
3955 SmallBlockChainStream_SetSize(sbTempChain, size);
3957 offset.u.HighPart = 0;
3958 offset.u.LowPart = 0;
3959 cbTotalRead.QuadPart = 0;
3960 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3963 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3964 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3972 cbTotalRead.QuadPart += cbRead;
3974 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3975 cbRead, buffer, &cbWritten);
3977 if(FAILED(resWrite))
3980 offset.u.LowPart += cbRead;
3982 }while(cbTotalRead.QuadPart < size.QuadPart);
3983 HeapFree(GetProcessHeap(), 0, buffer);
3985 size.u.HighPart = 0;
3988 if(FAILED(resRead) || FAILED(resWrite))
3990 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3991 SmallBlockChainStream_SetSize(sbTempChain, size);
3992 SmallBlockChainStream_Destroy(sbTempChain);
3996 /* destroy the original big block chain */
3997 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3998 BlockChainStream_SetSize(*ppbbChain, size);
3999 BlockChainStream_Destroy(*ppbbChain);
4002 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4003 streamEntry.startingBlock = sbHeadOfChain;
4004 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4006 SmallBlockChainStream_Destroy(sbTempChain);
4007 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4010 static HRESULT CreateSnapshotFile(StorageBaseImpl* original, StorageBaseImpl **snapshot)
4013 DirEntry parentData, snapshotData;
4015 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DELETEONRELEASE,
4016 0, (IStorage**)snapshot);
4020 hr = StorageBaseImpl_ReadDirEntry(original,
4021 original->storageDirEntry, &parentData);
4024 hr = StorageBaseImpl_ReadDirEntry((*snapshot),
4025 (*snapshot)->storageDirEntry, &snapshotData);
4029 memcpy(snapshotData.name, parentData.name, sizeof(snapshotData.name));
4030 snapshotData.sizeOfNameString = parentData.sizeOfNameString;
4031 snapshotData.stgType = parentData.stgType;
4032 snapshotData.clsid = parentData.clsid;
4033 snapshotData.ctime = parentData.ctime;
4034 snapshotData.mtime = parentData.mtime;
4035 hr = StorageBaseImpl_WriteDirEntry((*snapshot),
4036 (*snapshot)->storageDirEntry, &snapshotData);
4040 hr = IStorage_CopyTo((IStorage*)original, 0, NULL, NULL,
4041 (IStorage*)(*snapshot));
4043 if (FAILED(hr)) IStorage_Release((IStorage*)(*snapshot));
4049 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4051 DWORD grfCommitFlags) /* [in] */
4053 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4055 DirEntry data, tempStorageData, snapshotRootData;
4056 DirRef tempStorageEntry, oldDirRoot;
4057 StorageInternalImpl *tempStorage;
4059 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4061 /* Cannot commit a read-only transacted storage */
4062 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4063 return STG_E_ACCESSDENIED;
4065 /* To prevent data loss, we create the new structure in the file before we
4066 * delete the old one, so that in case of errors the old data is intact. We
4067 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4068 * needed in the rare situation where we have just enough free disk space to
4069 * overwrite the existing data. */
4071 /* Create an orphaned storage in the parent for the new directory structure. */
4072 memset(&data, 0, sizeof(data));
4074 data.sizeOfNameString = 1;
4075 data.stgType = STGTY_STORAGE;
4076 data.leftChild = DIRENTRY_NULL;
4077 data.rightChild = DIRENTRY_NULL;
4078 data.dirRootEntry = DIRENTRY_NULL;
4079 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &data, &tempStorageEntry);
4081 if (FAILED(hr)) return hr;
4083 tempStorage = StorageInternalImpl_Construct(This->transactedParent,
4084 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, tempStorageEntry);
4087 hr = IStorage_CopyTo((IStorage*)This->snapshot, 0, NULL, NULL,
4088 (IStorage*)tempStorage);
4090 list_init(&tempStorage->ParentListEntry);
4092 IStorage_Release((IStorage*) tempStorage);
4099 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4103 /* Update the storage to use the new data in one step. */
4104 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4105 This->transactedParent->storageDirEntry, &data);
4109 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4110 tempStorageEntry, &tempStorageData);
4115 hr = StorageBaseImpl_ReadDirEntry(This->snapshot,
4116 This->snapshot->storageDirEntry, &snapshotRootData);
4121 oldDirRoot = data.dirRootEntry;
4122 data.dirRootEntry = tempStorageData.dirRootEntry;
4123 data.clsid = snapshotRootData.clsid;
4124 data.ctime = snapshotRootData.ctime;
4125 data.mtime = snapshotRootData.mtime;
4127 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4128 This->transactedParent->storageDirEntry, &data);
4133 /* Destroy the old now-orphaned data. */
4134 DestroyReachableEntries(This->transactedParent, oldDirRoot);
4135 StorageBaseImpl_DestroyDirEntry(This->transactedParent, tempStorageEntry);
4139 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4145 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4148 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4149 StorageBaseImpl *newSnapshot;
4152 TRACE("(%p)\n", iface);
4154 /* Create a new copy of the parent data. */
4155 hr = CreateSnapshotFile(This->transactedParent, &newSnapshot);
4156 if (FAILED(hr)) return hr;
4158 /* Destroy the open objects. */
4159 StorageBaseImpl_DeleteAll(&This->base);
4161 /* Replace our current snapshot. */
4162 IStorage_Release((IStorage*)This->snapshot);
4163 This->snapshot = newSnapshot;
4168 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4170 if (!This->reverted)
4172 TRACE("Storage invalidated (stg=%p)\n", This);
4176 StorageBaseImpl_DeleteAll(This);
4180 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4182 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4184 TransactedSnapshotImpl_Invalidate(iface);
4186 IStorage_Release((IStorage*)This->transactedParent);
4188 IStorage_Release((IStorage*)This->snapshot);
4190 HeapFree(GetProcessHeap(), 0, This);
4193 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4194 const DirEntry *newData, DirRef *index)
4196 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4198 return StorageBaseImpl_CreateDirEntry(This->snapshot,
4202 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4203 DirRef index, const DirEntry *data)
4205 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4207 return StorageBaseImpl_WriteDirEntry(This->snapshot,
4211 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4212 DirRef index, DirEntry *data)
4214 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4216 return StorageBaseImpl_ReadDirEntry(This->snapshot,
4220 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4223 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4225 return StorageBaseImpl_DestroyDirEntry(This->snapshot,
4229 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4230 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4232 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4234 return StorageBaseImpl_StreamReadAt(This->snapshot,
4235 index, offset, size, buffer, bytesRead);
4238 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4239 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4241 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4243 return StorageBaseImpl_StreamWriteAt(This->snapshot,
4244 index, offset, size, buffer, bytesWritten);
4247 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4248 DirRef index, ULARGE_INTEGER newsize)
4250 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4252 return StorageBaseImpl_StreamSetSize(This->snapshot,
4256 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4258 StorageBaseImpl_QueryInterface,
4259 StorageBaseImpl_AddRef,
4260 StorageBaseImpl_Release,
4261 StorageBaseImpl_CreateStream,
4262 StorageBaseImpl_OpenStream,
4263 StorageBaseImpl_CreateStorage,
4264 StorageBaseImpl_OpenStorage,
4265 StorageBaseImpl_CopyTo,
4266 StorageBaseImpl_MoveElementTo,
4267 TransactedSnapshotImpl_Commit,
4268 TransactedSnapshotImpl_Revert,
4269 StorageBaseImpl_EnumElements,
4270 StorageBaseImpl_DestroyElement,
4271 StorageBaseImpl_RenameElement,
4272 StorageBaseImpl_SetElementTimes,
4273 StorageBaseImpl_SetClass,
4274 StorageBaseImpl_SetStateBits,
4275 StorageBaseImpl_Stat
4278 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4280 TransactedSnapshotImpl_Destroy,
4281 TransactedSnapshotImpl_Invalidate,
4282 TransactedSnapshotImpl_CreateDirEntry,
4283 TransactedSnapshotImpl_WriteDirEntry,
4284 TransactedSnapshotImpl_ReadDirEntry,
4285 TransactedSnapshotImpl_DestroyDirEntry,
4286 TransactedSnapshotImpl_StreamReadAt,
4287 TransactedSnapshotImpl_StreamWriteAt,
4288 TransactedSnapshotImpl_StreamSetSize
4291 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4292 TransactedSnapshotImpl** result)
4296 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4299 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4301 /* This is OK because the property set storage functions use the IStorage functions. */
4302 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4304 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4306 list_init(&(*result)->base.strmHead);
4308 list_init(&(*result)->base.storageHead);
4310 (*result)->base.ref = 1;
4312 (*result)->base.openFlags = parentStorage->openFlags;
4314 (*result)->base.filename = parentStorage->filename;
4316 /* Create a new temporary storage to act as the snapshot */
4317 hr = CreateSnapshotFile(parentStorage, &(*result)->snapshot);
4321 (*result)->base.storageDirEntry = (*result)->snapshot->storageDirEntry;
4323 /* parentStorage already has 1 reference, which we take over here. */
4324 (*result)->transactedParent = parentStorage;
4326 parentStorage->transactedChild = (StorageBaseImpl*)*result;
4329 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4334 return E_OUTOFMEMORY;
4337 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
4338 StorageBaseImpl** result)
4342 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
4344 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
4347 return TransactedSnapshotImpl_Construct(parentStorage,
4348 (TransactedSnapshotImpl**)result);
4351 static HRESULT Storage_Construct(
4358 StorageBaseImpl** result)
4360 StorageImpl *newStorage;
4361 StorageBaseImpl *newTransactedStorage;
4364 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, &newStorage);
4365 if (FAILED(hr)) goto end;
4367 if (openFlags & STGM_TRANSACTED)
4369 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
4371 IStorage_Release((IStorage*)newStorage);
4373 *result = newTransactedStorage;
4376 *result = &newStorage->base;
4382 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
4384 StorageInternalImpl* This = (StorageInternalImpl*) base;
4386 if (!This->base.reverted)
4388 TRACE("Storage invalidated (stg=%p)\n", This);
4390 This->base.reverted = 1;
4392 This->parentStorage = NULL;
4394 StorageBaseImpl_DeleteAll(&This->base);
4396 list_remove(&This->ParentListEntry);
4400 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
4402 StorageInternalImpl* This = (StorageInternalImpl*) iface;
4404 StorageInternalImpl_Invalidate(&This->base);
4406 HeapFree(GetProcessHeap(), 0, This);
4409 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
4410 const DirEntry *newData, DirRef *index)
4412 StorageInternalImpl* This = (StorageInternalImpl*) base;
4414 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
4418 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
4419 DirRef index, const DirEntry *data)
4421 StorageInternalImpl* This = (StorageInternalImpl*) base;
4423 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
4427 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
4428 DirRef index, DirEntry *data)
4430 StorageInternalImpl* This = (StorageInternalImpl*) base;
4432 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
4436 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
4439 StorageInternalImpl* This = (StorageInternalImpl*) base;
4441 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
4445 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
4446 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4448 StorageInternalImpl* This = (StorageInternalImpl*) base;
4450 return StorageBaseImpl_StreamReadAt(This->parentStorage,
4451 index, offset, size, buffer, bytesRead);
4454 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
4455 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4457 StorageInternalImpl* This = (StorageInternalImpl*) base;
4459 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
4460 index, offset, size, buffer, bytesWritten);
4463 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
4464 DirRef index, ULARGE_INTEGER newsize)
4466 StorageInternalImpl* This = (StorageInternalImpl*) base;
4468 return StorageBaseImpl_StreamSetSize(This->parentStorage,
4472 /******************************************************************************
4474 ** Storage32InternalImpl_Commit
4477 static HRESULT WINAPI StorageInternalImpl_Commit(
4479 DWORD grfCommitFlags) /* [in] */
4481 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
4485 /******************************************************************************
4487 ** Storage32InternalImpl_Revert
4490 static HRESULT WINAPI StorageInternalImpl_Revert(
4493 FIXME("(%p): stub\n", iface);
4497 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
4499 IStorage_Release((IStorage*)This->parentStorage);
4500 HeapFree(GetProcessHeap(), 0, This);
4503 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
4504 IEnumSTATSTG* iface,
4508 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4511 return E_INVALIDARG;
4515 if (IsEqualGUID(&IID_IUnknown, riid) ||
4516 IsEqualGUID(&IID_IEnumSTATSTG, riid))
4519 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
4523 return E_NOINTERFACE;
4526 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
4527 IEnumSTATSTG* iface)
4529 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4530 return InterlockedIncrement(&This->ref);
4533 static ULONG WINAPI IEnumSTATSTGImpl_Release(
4534 IEnumSTATSTG* iface)
4536 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4540 newRef = InterlockedDecrement(&This->ref);
4544 IEnumSTATSTGImpl_Destroy(This);
4550 static HRESULT IEnumSTATSTGImpl_GetNextRef(
4551 IEnumSTATSTGImpl* This,
4554 DirRef result = DIRENTRY_NULL;
4558 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
4560 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
4561 This->parentStorage->storageDirEntry, &entry);
4562 searchNode = entry.dirRootEntry;
4564 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
4566 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
4570 LONG diff = entryNameCmp( entry.name, This->name);
4574 searchNode = entry.rightChild;
4578 result = searchNode;
4579 memcpy(result_name, entry.name, sizeof(result_name));
4580 searchNode = entry.leftChild;
4588 if (result != DIRENTRY_NULL)
4589 memcpy(This->name, result_name, sizeof(result_name));
4595 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
4596 IEnumSTATSTG* iface,
4599 ULONG* pceltFetched)
4601 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4603 DirEntry currentEntry;
4604 STATSTG* currentReturnStruct = rgelt;
4605 ULONG objectFetched = 0;
4606 DirRef currentSearchNode;
4609 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
4610 return E_INVALIDARG;
4612 if (This->parentStorage->reverted)
4613 return STG_E_REVERTED;
4616 * To avoid the special case, get another pointer to a ULONG value if
4617 * the caller didn't supply one.
4619 if (pceltFetched==0)
4620 pceltFetched = &objectFetched;
4623 * Start the iteration, we will iterate until we hit the end of the
4624 * linked list or until we hit the number of items to iterate through
4628 while ( *pceltFetched < celt )
4630 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
4632 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
4636 * Read the entry from the storage.
4638 StorageBaseImpl_ReadDirEntry(This->parentStorage,
4643 * Copy the information to the return buffer.
4645 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
4646 currentReturnStruct,
4651 * Step to the next item in the iteration
4654 currentReturnStruct++;
4657 if (SUCCEEDED(hr) && *pceltFetched != celt)
4664 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
4665 IEnumSTATSTG* iface,
4668 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4670 ULONG objectFetched = 0;
4671 DirRef currentSearchNode;
4674 if (This->parentStorage->reverted)
4675 return STG_E_REVERTED;
4677 while ( (objectFetched < celt) )
4679 hr = IEnumSTATSTGImpl_GetNextRef(This, ¤tSearchNode);
4681 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
4687 if (SUCCEEDED(hr) && objectFetched != celt)
4693 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
4694 IEnumSTATSTG* iface)
4696 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4698 if (This->parentStorage->reverted)
4699 return STG_E_REVERTED;
4706 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
4707 IEnumSTATSTG* iface,
4708 IEnumSTATSTG** ppenum)
4710 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4712 IEnumSTATSTGImpl* newClone;
4714 if (This->parentStorage->reverted)
4715 return STG_E_REVERTED;
4718 * Perform a sanity check on the parameters.
4721 return E_INVALIDARG;
4723 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
4724 This->storageDirEntry);
4728 * The new clone enumeration must point to the same current node as
4731 memcpy(newClone->name, This->name, sizeof(newClone->name));
4733 *ppenum = (IEnumSTATSTG*)newClone;
4736 * Don't forget to nail down a reference to the clone before
4739 IEnumSTATSTGImpl_AddRef(*ppenum);
4745 * Virtual function table for the IEnumSTATSTGImpl class.
4747 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4749 IEnumSTATSTGImpl_QueryInterface,
4750 IEnumSTATSTGImpl_AddRef,
4751 IEnumSTATSTGImpl_Release,
4752 IEnumSTATSTGImpl_Next,
4753 IEnumSTATSTGImpl_Skip,
4754 IEnumSTATSTGImpl_Reset,
4755 IEnumSTATSTGImpl_Clone
4758 /******************************************************************************
4759 ** IEnumSTATSTGImpl implementation
4762 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4763 StorageBaseImpl* parentStorage,
4764 DirRef storageDirEntry)
4766 IEnumSTATSTGImpl* newEnumeration;
4768 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4770 if (newEnumeration!=0)
4773 * Set-up the virtual function table and reference count.
4775 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4776 newEnumeration->ref = 0;
4779 * We want to nail-down the reference to the storage in case the
4780 * enumeration out-lives the storage in the client application.
4782 newEnumeration->parentStorage = parentStorage;
4783 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4785 newEnumeration->storageDirEntry = storageDirEntry;
4788 * Make sure the current node of the iterator is the first one.
4790 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4793 return newEnumeration;
4797 * Virtual function table for the Storage32InternalImpl class.
4799 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4801 StorageBaseImpl_QueryInterface,
4802 StorageBaseImpl_AddRef,
4803 StorageBaseImpl_Release,
4804 StorageBaseImpl_CreateStream,
4805 StorageBaseImpl_OpenStream,
4806 StorageBaseImpl_CreateStorage,
4807 StorageBaseImpl_OpenStorage,
4808 StorageBaseImpl_CopyTo,
4809 StorageBaseImpl_MoveElementTo,
4810 StorageInternalImpl_Commit,
4811 StorageInternalImpl_Revert,
4812 StorageBaseImpl_EnumElements,
4813 StorageBaseImpl_DestroyElement,
4814 StorageBaseImpl_RenameElement,
4815 StorageBaseImpl_SetElementTimes,
4816 StorageBaseImpl_SetClass,
4817 StorageBaseImpl_SetStateBits,
4818 StorageBaseImpl_Stat
4821 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
4823 StorageInternalImpl_Destroy,
4824 StorageInternalImpl_Invalidate,
4825 StorageInternalImpl_CreateDirEntry,
4826 StorageInternalImpl_WriteDirEntry,
4827 StorageInternalImpl_ReadDirEntry,
4828 StorageInternalImpl_DestroyDirEntry,
4829 StorageInternalImpl_StreamReadAt,
4830 StorageInternalImpl_StreamWriteAt,
4831 StorageInternalImpl_StreamSetSize
4834 /******************************************************************************
4835 ** Storage32InternalImpl implementation
4838 static StorageInternalImpl* StorageInternalImpl_Construct(
4839 StorageBaseImpl* parentStorage,
4841 DirRef storageDirEntry)
4843 StorageInternalImpl* newStorage;
4845 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4849 list_init(&newStorage->base.strmHead);
4851 list_init(&newStorage->base.storageHead);
4854 * Initialize the virtual function table.
4856 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4857 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
4858 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4860 newStorage->base.reverted = 0;
4862 newStorage->base.ref = 1;
4864 newStorage->parentStorage = parentStorage;
4867 * Keep a reference to the directory entry of this storage
4869 newStorage->base.storageDirEntry = storageDirEntry;
4871 newStorage->base.create = 0;
4879 /******************************************************************************
4880 ** StorageUtl implementation
4883 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4887 memcpy(&tmp, buffer+offset, sizeof(WORD));
4888 *value = lendian16toh(tmp);
4891 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4893 value = htole16(value);
4894 memcpy(buffer+offset, &value, sizeof(WORD));
4897 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4901 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4902 *value = lendian32toh(tmp);
4905 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4907 value = htole32(value);
4908 memcpy(buffer+offset, &value, sizeof(DWORD));
4911 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4912 ULARGE_INTEGER* value)
4914 #ifdef WORDS_BIGENDIAN
4917 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4918 value->u.LowPart = htole32(tmp.u.HighPart);
4919 value->u.HighPart = htole32(tmp.u.LowPart);
4921 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4925 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4926 const ULARGE_INTEGER *value)
4928 #ifdef WORDS_BIGENDIAN
4931 tmp.u.LowPart = htole32(value->u.HighPart);
4932 tmp.u.HighPart = htole32(value->u.LowPart);
4933 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4935 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4939 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4941 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4942 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4943 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4945 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4948 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4950 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4951 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4952 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4954 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4957 void StorageUtl_CopyDirEntryToSTATSTG(
4958 StorageBaseImpl* storage,
4959 STATSTG* destination,
4960 const DirEntry* source,
4965 if (source->stgType == STGTY_ROOT)
4967 /* replace the name of root entry (often "Root Entry") by the file name */
4968 entryName = storage->filename;
4972 entryName = source->name;
4976 * The copy of the string occurs only when the flag is not set
4978 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4979 (entryName == NULL) ||
4980 (entryName[0] == 0) )
4982 destination->pwcsName = 0;
4986 destination->pwcsName =
4987 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
4989 strcpyW(destination->pwcsName, entryName);
4992 switch (source->stgType)
4996 destination->type = STGTY_STORAGE;
4999 destination->type = STGTY_STREAM;
5002 destination->type = STGTY_STREAM;
5006 destination->cbSize = source->size;
5008 currentReturnStruct->mtime = {0}; TODO
5009 currentReturnStruct->ctime = {0};
5010 currentReturnStruct->atime = {0};
5012 destination->grfMode = 0;
5013 destination->grfLocksSupported = 0;
5014 destination->clsid = source->clsid;
5015 destination->grfStateBits = 0;
5016 destination->reserved = 0;
5019 /******************************************************************************
5020 ** BlockChainStream implementation
5023 BlockChainStream* BlockChainStream_Construct(
5024 StorageImpl* parentStorage,
5025 ULONG* headOfStreamPlaceHolder,
5028 BlockChainStream* newStream;
5031 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5033 newStream->parentStorage = parentStorage;
5034 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5035 newStream->ownerDirEntry = dirEntry;
5036 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
5037 newStream->tailIndex = BLOCK_END_OF_CHAIN;
5038 newStream->numBlocks = 0;
5040 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
5042 while (blockIndex != BLOCK_END_OF_CHAIN)
5044 newStream->numBlocks++;
5045 newStream->tailIndex = blockIndex;
5047 if(FAILED(StorageImpl_GetNextBlockInChain(
5052 HeapFree(GetProcessHeap(), 0, newStream);
5060 void BlockChainStream_Destroy(BlockChainStream* This)
5062 HeapFree(GetProcessHeap(), 0, This);
5065 /******************************************************************************
5066 * BlockChainStream_GetHeadOfChain
5068 * Returns the head of this stream chain.
5069 * Some special chains don't have directory entries, their heads are kept in
5070 * This->headOfStreamPlaceHolder.
5073 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5075 DirEntry chainEntry;
5078 if (This->headOfStreamPlaceHolder != 0)
5079 return *(This->headOfStreamPlaceHolder);
5081 if (This->ownerDirEntry != DIRENTRY_NULL)
5083 hr = StorageImpl_ReadDirEntry(
5084 This->parentStorage,
5085 This->ownerDirEntry,
5090 return chainEntry.startingBlock;
5094 return BLOCK_END_OF_CHAIN;
5097 /******************************************************************************
5098 * BlockChainStream_GetCount
5100 * Returns the number of blocks that comprises this chain.
5101 * This is not the size of the stream as the last block may not be full!
5104 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5109 blockIndex = BlockChainStream_GetHeadOfChain(This);
5111 while (blockIndex != BLOCK_END_OF_CHAIN)
5115 if(FAILED(StorageImpl_GetNextBlockInChain(
5116 This->parentStorage,
5125 /******************************************************************************
5126 * BlockChainStream_ReadAt
5128 * Reads a specified number of bytes from this chain at the specified offset.
5129 * bytesRead may be NULL.
5130 * Failure will be returned if the specified number of bytes has not been read.
5132 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5133 ULARGE_INTEGER offset,
5138 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5139 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5140 ULONG bytesToReadInBuffer;
5144 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5147 * Find the first block in the stream that contains part of the buffer.
5149 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5150 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5151 (blockNoInSequence < This->lastBlockNoInSequence) )
5153 blockIndex = BlockChainStream_GetHeadOfChain(This);
5154 This->lastBlockNoInSequence = blockNoInSequence;
5158 ULONG temp = blockNoInSequence;
5160 blockIndex = This->lastBlockNoInSequenceIndex;
5161 blockNoInSequence -= This->lastBlockNoInSequence;
5162 This->lastBlockNoInSequence = temp;
5165 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5167 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5168 return STG_E_DOCFILECORRUPT;
5169 blockNoInSequence--;
5172 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
5173 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
5175 This->lastBlockNoInSequenceIndex = blockIndex;
5178 * Start reading the buffer.
5181 bufferWalker = buffer;
5183 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5185 ULARGE_INTEGER ulOffset;
5188 * Calculate how many bytes we can copy from this big block.
5190 bytesToReadInBuffer =
5191 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5193 TRACE("block %i\n",blockIndex);
5194 ulOffset.u.HighPart = 0;
5195 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
5198 StorageImpl_ReadAt(This->parentStorage,
5201 bytesToReadInBuffer,
5204 * Step to the next big block.
5206 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5207 return STG_E_DOCFILECORRUPT;
5209 bufferWalker += bytesReadAt;
5210 size -= bytesReadAt;
5211 *bytesRead += bytesReadAt;
5212 offsetInBlock = 0; /* There is no offset on the next block */
5214 if (bytesToReadInBuffer != bytesReadAt)
5218 return (size == 0) ? S_OK : STG_E_READFAULT;
5221 /******************************************************************************
5222 * BlockChainStream_WriteAt
5224 * Writes the specified number of bytes to this chain at the specified offset.
5225 * Will fail if not all specified number of bytes have been written.
5227 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5228 ULARGE_INTEGER offset,
5231 ULONG* bytesWritten)
5233 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5234 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5237 const BYTE* bufferWalker;
5240 * Find the first block in the stream that contains part of the buffer.
5242 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5243 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5244 (blockNoInSequence < This->lastBlockNoInSequence) )
5246 blockIndex = BlockChainStream_GetHeadOfChain(This);
5247 This->lastBlockNoInSequence = blockNoInSequence;
5251 ULONG temp = blockNoInSequence;
5253 blockIndex = This->lastBlockNoInSequenceIndex;
5254 blockNoInSequence -= This->lastBlockNoInSequence;
5255 This->lastBlockNoInSequence = temp;
5258 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5260 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5262 return STG_E_DOCFILECORRUPT;
5263 blockNoInSequence--;
5266 This->lastBlockNoInSequenceIndex = blockIndex;
5268 /* BlockChainStream_SetSize should have already been called to ensure we have
5269 * enough blocks in the chain to write into */
5270 if (blockIndex == BLOCK_END_OF_CHAIN)
5272 ERR("not enough blocks in chain to write data\n");
5273 return STG_E_DOCFILECORRUPT;
5277 bufferWalker = buffer;
5279 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5281 ULARGE_INTEGER ulOffset;
5282 DWORD bytesWrittenAt;
5284 * Calculate how many bytes we can copy from this big block.
5287 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5289 TRACE("block %i\n",blockIndex);
5290 ulOffset.u.HighPart = 0;
5291 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
5294 StorageImpl_WriteAt(This->parentStorage,
5301 * Step to the next big block.
5303 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5305 return STG_E_DOCFILECORRUPT;
5307 bufferWalker += bytesWrittenAt;
5308 size -= bytesWrittenAt;
5309 *bytesWritten += bytesWrittenAt;
5310 offsetInBlock = 0; /* There is no offset on the next block */
5312 if (bytesWrittenAt != bytesToWrite)
5316 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5319 /******************************************************************************
5320 * BlockChainStream_Shrink
5322 * Shrinks this chain in the big block depot.
5324 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
5325 ULARGE_INTEGER newSize)
5327 ULONG blockIndex, extraBlock;
5332 * Reset the last accessed block cache.
5334 This->lastBlockNoInSequence = 0xFFFFFFFF;
5335 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
5338 * Figure out how many blocks are needed to contain the new size
5340 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5342 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5345 blockIndex = BlockChainStream_GetHeadOfChain(This);
5348 * Go to the new end of chain
5350 while (count < numBlocks)
5352 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5358 /* Get the next block before marking the new end */
5359 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5363 /* Mark the new end of chain */
5364 StorageImpl_SetNextBlockInChain(
5365 This->parentStorage,
5367 BLOCK_END_OF_CHAIN);
5369 This->tailIndex = blockIndex;
5370 This->numBlocks = numBlocks;
5373 * Mark the extra blocks as free
5375 while (extraBlock != BLOCK_END_OF_CHAIN)
5377 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
5380 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
5381 extraBlock = blockIndex;
5387 /******************************************************************************
5388 * BlockChainStream_Enlarge
5390 * Grows this chain in the big block depot.
5392 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
5393 ULARGE_INTEGER newSize)
5395 ULONG blockIndex, currentBlock;
5397 ULONG oldNumBlocks = 0;
5399 blockIndex = BlockChainStream_GetHeadOfChain(This);
5402 * Empty chain. Create the head.
5404 if (blockIndex == BLOCK_END_OF_CHAIN)
5406 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5407 StorageImpl_SetNextBlockInChain(This->parentStorage,
5409 BLOCK_END_OF_CHAIN);
5411 if (This->headOfStreamPlaceHolder != 0)
5413 *(This->headOfStreamPlaceHolder) = blockIndex;
5417 DirEntry chainEntry;
5418 assert(This->ownerDirEntry != DIRENTRY_NULL);
5420 StorageImpl_ReadDirEntry(
5421 This->parentStorage,
5422 This->ownerDirEntry,
5425 chainEntry.startingBlock = blockIndex;
5427 StorageImpl_WriteDirEntry(
5428 This->parentStorage,
5429 This->ownerDirEntry,
5433 This->tailIndex = blockIndex;
5434 This->numBlocks = 1;
5438 * Figure out how many blocks are needed to contain this stream
5440 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5442 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5446 * Go to the current end of chain
5448 if (This->tailIndex == BLOCK_END_OF_CHAIN)
5450 currentBlock = blockIndex;
5452 while (blockIndex != BLOCK_END_OF_CHAIN)
5455 currentBlock = blockIndex;
5457 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
5462 This->tailIndex = currentBlock;
5465 currentBlock = This->tailIndex;
5466 oldNumBlocks = This->numBlocks;
5469 * Add new blocks to the chain
5471 if (oldNumBlocks < newNumBlocks)
5473 while (oldNumBlocks < newNumBlocks)
5475 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5477 StorageImpl_SetNextBlockInChain(
5478 This->parentStorage,
5482 StorageImpl_SetNextBlockInChain(
5483 This->parentStorage,
5485 BLOCK_END_OF_CHAIN);
5487 currentBlock = blockIndex;
5491 This->tailIndex = blockIndex;
5492 This->numBlocks = newNumBlocks;
5498 /******************************************************************************
5499 * BlockChainStream_SetSize
5501 * Sets the size of this stream. The big block depot will be updated.
5502 * The file will grow if we grow the chain.
5504 * TODO: Free the actual blocks in the file when we shrink the chain.
5505 * Currently, the blocks are still in the file. So the file size
5506 * doesn't shrink even if we shrink streams.
5508 BOOL BlockChainStream_SetSize(
5509 BlockChainStream* This,
5510 ULARGE_INTEGER newSize)
5512 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
5514 if (newSize.u.LowPart == size.u.LowPart)
5517 if (newSize.u.LowPart < size.u.LowPart)
5519 BlockChainStream_Shrink(This, newSize);
5523 BlockChainStream_Enlarge(This, newSize);
5529 /******************************************************************************
5530 * BlockChainStream_GetSize
5532 * Returns the size of this chain.
5533 * Will return the block count if this chain doesn't have a directory entry.
5535 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
5537 DirEntry chainEntry;
5539 if(This->headOfStreamPlaceHolder == NULL)
5542 * This chain has a directory entry so use the size value from there.
5544 StorageImpl_ReadDirEntry(
5545 This->parentStorage,
5546 This->ownerDirEntry,
5549 return chainEntry.size;
5554 * this chain is a chain that does not have a directory entry, figure out the
5555 * size by making the product number of used blocks times the
5558 ULARGE_INTEGER result;
5559 result.u.HighPart = 0;
5562 BlockChainStream_GetCount(This) *
5563 This->parentStorage->bigBlockSize;
5569 /******************************************************************************
5570 ** SmallBlockChainStream implementation
5573 SmallBlockChainStream* SmallBlockChainStream_Construct(
5574 StorageImpl* parentStorage,
5575 ULONG* headOfStreamPlaceHolder,
5578 SmallBlockChainStream* newStream;
5580 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
5582 newStream->parentStorage = parentStorage;
5583 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5584 newStream->ownerDirEntry = dirEntry;
5589 void SmallBlockChainStream_Destroy(
5590 SmallBlockChainStream* This)
5592 HeapFree(GetProcessHeap(), 0, This);
5595 /******************************************************************************
5596 * SmallBlockChainStream_GetHeadOfChain
5598 * Returns the head of this chain of small blocks.
5600 static ULONG SmallBlockChainStream_GetHeadOfChain(
5601 SmallBlockChainStream* This)
5603 DirEntry chainEntry;
5606 if (This->headOfStreamPlaceHolder != NULL)
5607 return *(This->headOfStreamPlaceHolder);
5609 if (This->ownerDirEntry)
5611 hr = StorageImpl_ReadDirEntry(
5612 This->parentStorage,
5613 This->ownerDirEntry,
5618 return chainEntry.startingBlock;
5623 return BLOCK_END_OF_CHAIN;
5626 /******************************************************************************
5627 * SmallBlockChainStream_GetNextBlockInChain
5629 * Returns the index of the next small block in this chain.
5632 * - BLOCK_END_OF_CHAIN: end of this chain
5633 * - BLOCK_UNUSED: small block 'blockIndex' is free
5635 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5636 SmallBlockChainStream* This,
5638 ULONG* nextBlockInChain)
5640 ULARGE_INTEGER offsetOfBlockInDepot;
5645 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5647 offsetOfBlockInDepot.u.HighPart = 0;
5648 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5651 * Read those bytes in the buffer from the small block file.
5653 res = BlockChainStream_ReadAt(
5654 This->parentStorage->smallBlockDepotChain,
5655 offsetOfBlockInDepot,
5662 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5669 /******************************************************************************
5670 * SmallBlockChainStream_SetNextBlockInChain
5672 * Writes the index of the next block of the specified block in the small
5674 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5675 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5677 static void SmallBlockChainStream_SetNextBlockInChain(
5678 SmallBlockChainStream* This,
5682 ULARGE_INTEGER offsetOfBlockInDepot;
5686 offsetOfBlockInDepot.u.HighPart = 0;
5687 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5689 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5692 * Read those bytes in the buffer from the small block file.
5694 BlockChainStream_WriteAt(
5695 This->parentStorage->smallBlockDepotChain,
5696 offsetOfBlockInDepot,
5702 /******************************************************************************
5703 * SmallBlockChainStream_FreeBlock
5705 * Flag small block 'blockIndex' as free in the small block depot.
5707 static void SmallBlockChainStream_FreeBlock(
5708 SmallBlockChainStream* This,
5711 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5714 /******************************************************************************
5715 * SmallBlockChainStream_GetNextFreeBlock
5717 * Returns the index of a free small block. The small block depot will be
5718 * enlarged if necessary. The small block chain will also be enlarged if
5721 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5722 SmallBlockChainStream* This)
5724 ULARGE_INTEGER offsetOfBlockInDepot;
5727 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
5728 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5730 ULONG smallBlocksPerBigBlock;
5732 offsetOfBlockInDepot.u.HighPart = 0;
5735 * Scan the small block depot for a free block
5737 while (nextBlockIndex != BLOCK_UNUSED)
5739 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5741 res = BlockChainStream_ReadAt(
5742 This->parentStorage->smallBlockDepotChain,
5743 offsetOfBlockInDepot,
5749 * If we run out of space for the small block depot, enlarge it
5753 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5755 if (nextBlockIndex != BLOCK_UNUSED)
5761 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5763 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5764 ULONG nextBlock, newsbdIndex;
5765 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
5767 nextBlock = sbdIndex;
5768 while (nextBlock != BLOCK_END_OF_CHAIN)
5770 sbdIndex = nextBlock;
5771 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5774 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5775 if (sbdIndex != BLOCK_END_OF_CHAIN)
5776 StorageImpl_SetNextBlockInChain(
5777 This->parentStorage,
5781 StorageImpl_SetNextBlockInChain(
5782 This->parentStorage,
5784 BLOCK_END_OF_CHAIN);
5787 * Initialize all the small blocks to free
5789 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5790 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5795 * We have just created the small block depot.
5801 * Save it in the header
5803 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5804 StorageImpl_SaveFileHeader(This->parentStorage);
5807 * And allocate the first big block that will contain small blocks
5810 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5812 StorageImpl_SetNextBlockInChain(
5813 This->parentStorage,
5815 BLOCK_END_OF_CHAIN);
5817 StorageImpl_ReadDirEntry(
5818 This->parentStorage,
5819 This->parentStorage->base.storageDirEntry,
5822 rootEntry.startingBlock = sbStartIndex;
5823 rootEntry.size.u.HighPart = 0;
5824 rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
5826 StorageImpl_WriteDirEntry(
5827 This->parentStorage,
5828 This->parentStorage->base.storageDirEntry,
5832 StorageImpl_SaveFileHeader(This->parentStorage);
5836 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
5838 smallBlocksPerBigBlock =
5839 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5842 * Verify if we have to allocate big blocks to contain small blocks
5844 if (blockIndex % smallBlocksPerBigBlock == 0)
5847 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5849 StorageImpl_ReadDirEntry(
5850 This->parentStorage,
5851 This->parentStorage->base.storageDirEntry,
5854 if (rootEntry.size.u.LowPart <
5855 (blocksRequired * This->parentStorage->bigBlockSize))
5857 rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5859 BlockChainStream_SetSize(
5860 This->parentStorage->smallBlockRootChain,
5863 StorageImpl_WriteDirEntry(
5864 This->parentStorage,
5865 This->parentStorage->base.storageDirEntry,
5873 /******************************************************************************
5874 * SmallBlockChainStream_ReadAt
5876 * Reads a specified number of bytes from this chain at the specified offset.
5877 * bytesRead may be NULL.
5878 * Failure will be returned if the specified number of bytes has not been read.
5880 HRESULT SmallBlockChainStream_ReadAt(
5881 SmallBlockChainStream* This,
5882 ULARGE_INTEGER offset,
5888 ULARGE_INTEGER offsetInBigBlockFile;
5889 ULONG blockNoInSequence =
5890 offset.u.LowPart / This->parentStorage->smallBlockSize;
5892 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5893 ULONG bytesToReadInBuffer;
5895 ULONG bytesReadFromBigBlockFile;
5899 * This should never happen on a small block file.
5901 assert(offset.u.HighPart==0);
5904 * Find the first block in the stream that contains part of the buffer.
5906 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5908 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5910 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5913 blockNoInSequence--;
5917 * Start reading the buffer.
5920 bufferWalker = buffer;
5922 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5925 * Calculate how many bytes we can copy from this small block.
5927 bytesToReadInBuffer =
5928 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5931 * Calculate the offset of the small block in the small block file.
5933 offsetInBigBlockFile.u.HighPart = 0;
5934 offsetInBigBlockFile.u.LowPart =
5935 blockIndex * This->parentStorage->smallBlockSize;
5937 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5940 * Read those bytes in the buffer from the small block file.
5941 * The small block has already been identified so it shouldn't fail
5942 * unless the file is corrupt.
5944 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5945 offsetInBigBlockFile,
5946 bytesToReadInBuffer,
5948 &bytesReadFromBigBlockFile);
5954 * Step to the next big block.
5956 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5958 return STG_E_DOCFILECORRUPT;
5960 bufferWalker += bytesReadFromBigBlockFile;
5961 size -= bytesReadFromBigBlockFile;
5962 *bytesRead += bytesReadFromBigBlockFile;
5963 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5966 return (size == 0) ? S_OK : STG_E_READFAULT;
5969 /******************************************************************************
5970 * SmallBlockChainStream_WriteAt
5972 * Writes the specified number of bytes to this chain at the specified offset.
5973 * Will fail if not all specified number of bytes have been written.
5975 HRESULT SmallBlockChainStream_WriteAt(
5976 SmallBlockChainStream* This,
5977 ULARGE_INTEGER offset,
5980 ULONG* bytesWritten)
5982 ULARGE_INTEGER offsetInBigBlockFile;
5983 ULONG blockNoInSequence =
5984 offset.u.LowPart / This->parentStorage->smallBlockSize;
5986 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5987 ULONG bytesToWriteInBuffer;
5989 ULONG bytesWrittenToBigBlockFile;
5990 const BYTE* bufferWalker;
5994 * This should never happen on a small block file.
5996 assert(offset.u.HighPart==0);
5999 * Find the first block in the stream that contains part of the buffer.
6001 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6003 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6005 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6006 return STG_E_DOCFILECORRUPT;
6007 blockNoInSequence--;
6011 * Start writing the buffer.
6014 bufferWalker = buffer;
6015 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6018 * Calculate how many bytes we can copy to this small block.
6020 bytesToWriteInBuffer =
6021 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6024 * Calculate the offset of the small block in the small block file.
6026 offsetInBigBlockFile.u.HighPart = 0;
6027 offsetInBigBlockFile.u.LowPart =
6028 blockIndex * This->parentStorage->smallBlockSize;
6030 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6033 * Write those bytes in the buffer to the small block file.
6035 res = BlockChainStream_WriteAt(
6036 This->parentStorage->smallBlockRootChain,
6037 offsetInBigBlockFile,
6038 bytesToWriteInBuffer,
6040 &bytesWrittenToBigBlockFile);
6045 * Step to the next big block.
6047 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6050 bufferWalker += bytesWrittenToBigBlockFile;
6051 size -= bytesWrittenToBigBlockFile;
6052 *bytesWritten += bytesWrittenToBigBlockFile;
6053 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6056 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6059 /******************************************************************************
6060 * SmallBlockChainStream_Shrink
6062 * Shrinks this chain in the small block depot.
6064 static BOOL SmallBlockChainStream_Shrink(
6065 SmallBlockChainStream* This,
6066 ULARGE_INTEGER newSize)
6068 ULONG blockIndex, extraBlock;
6072 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6074 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6077 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6080 * Go to the new end of chain
6082 while (count < numBlocks)
6084 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6091 * If the count is 0, we have a special case, the head of the chain was
6096 DirEntry chainEntry;
6098 StorageImpl_ReadDirEntry(This->parentStorage,
6099 This->ownerDirEntry,
6102 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6104 StorageImpl_WriteDirEntry(This->parentStorage,
6105 This->ownerDirEntry,
6109 * We start freeing the chain at the head block.
6111 extraBlock = blockIndex;
6115 /* Get the next block before marking the new end */
6116 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6120 /* Mark the new end of chain */
6121 SmallBlockChainStream_SetNextBlockInChain(
6124 BLOCK_END_OF_CHAIN);
6128 * Mark the extra blocks as free
6130 while (extraBlock != BLOCK_END_OF_CHAIN)
6132 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6135 SmallBlockChainStream_FreeBlock(This, extraBlock);
6136 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
6137 extraBlock = blockIndex;
6143 /******************************************************************************
6144 * SmallBlockChainStream_Enlarge
6146 * Grows this chain in the small block depot.
6148 static BOOL SmallBlockChainStream_Enlarge(
6149 SmallBlockChainStream* This,
6150 ULARGE_INTEGER newSize)
6152 ULONG blockIndex, currentBlock;
6154 ULONG oldNumBlocks = 0;
6156 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6159 * Empty chain. Create the head.
6161 if (blockIndex == BLOCK_END_OF_CHAIN)
6163 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6164 SmallBlockChainStream_SetNextBlockInChain(
6167 BLOCK_END_OF_CHAIN);
6169 if (This->headOfStreamPlaceHolder != NULL)
6171 *(This->headOfStreamPlaceHolder) = blockIndex;
6175 DirEntry chainEntry;
6177 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6180 chainEntry.startingBlock = blockIndex;
6182 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6187 currentBlock = blockIndex;
6190 * Figure out how many blocks are needed to contain this stream
6192 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6194 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6198 * Go to the current end of chain
6200 while (blockIndex != BLOCK_END_OF_CHAIN)
6203 currentBlock = blockIndex;
6204 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6209 * Add new blocks to the chain
6211 while (oldNumBlocks < newNumBlocks)
6213 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6214 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6216 SmallBlockChainStream_SetNextBlockInChain(
6219 BLOCK_END_OF_CHAIN);
6221 currentBlock = blockIndex;
6228 /******************************************************************************
6229 * SmallBlockChainStream_SetSize
6231 * Sets the size of this stream.
6232 * The file will grow if we grow the chain.
6234 * TODO: Free the actual blocks in the file when we shrink the chain.
6235 * Currently, the blocks are still in the file. So the file size
6236 * doesn't shrink even if we shrink streams.
6238 BOOL SmallBlockChainStream_SetSize(
6239 SmallBlockChainStream* This,
6240 ULARGE_INTEGER newSize)
6242 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6244 if (newSize.u.LowPart == size.u.LowPart)
6247 if (newSize.u.LowPart < size.u.LowPart)
6249 SmallBlockChainStream_Shrink(This, newSize);
6253 SmallBlockChainStream_Enlarge(This, newSize);
6259 /******************************************************************************
6260 * SmallBlockChainStream_GetCount
6262 * Returns the number of small blocks that comprises this chain.
6263 * This is not the size of the stream as the last block may not be full!
6266 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6271 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6273 while(blockIndex != BLOCK_END_OF_CHAIN)
6277 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6278 blockIndex, &blockIndex)))
6285 /******************************************************************************
6286 * SmallBlockChainStream_GetSize
6288 * Returns the size of this chain.
6290 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6292 DirEntry chainEntry;
6294 if(This->headOfStreamPlaceHolder != NULL)
6296 ULARGE_INTEGER result;
6297 result.u.HighPart = 0;
6299 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
6300 This->parentStorage->smallBlockSize;
6305 StorageImpl_ReadDirEntry(
6306 This->parentStorage,
6307 This->ownerDirEntry,
6310 return chainEntry.size;
6313 /******************************************************************************
6314 * StgCreateDocfile [OLE32.@]
6315 * Creates a new compound file storage object
6318 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6319 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6320 * reserved [ ?] unused?, usually 0
6321 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6324 * S_OK if the file was successfully created
6325 * some STG_E_ value if error
6327 * if pwcsName is NULL, create file with new unique name
6328 * the function can returns
6329 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6332 HRESULT WINAPI StgCreateDocfile(
6336 IStorage **ppstgOpen)
6338 StorageBaseImpl* newStorage = 0;
6339 HANDLE hFile = INVALID_HANDLE_VALUE;
6340 HRESULT hr = STG_E_INVALIDFLAG;
6344 DWORD fileAttributes;
6345 WCHAR tempFileName[MAX_PATH];
6347 TRACE("(%s, %x, %d, %p)\n",
6348 debugstr_w(pwcsName), grfMode,
6349 reserved, ppstgOpen);
6352 return STG_E_INVALIDPOINTER;
6354 return STG_E_INVALIDPARAMETER;
6356 /* if no share mode given then DENY_NONE is the default */
6357 if (STGM_SHARE_MODE(grfMode) == 0)
6358 grfMode |= STGM_SHARE_DENY_NONE;
6360 if ( FAILED( validateSTGM(grfMode) ))
6363 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6364 switch(STGM_ACCESS_MODE(grfMode))
6367 case STGM_READWRITE:
6373 /* in direct mode, can only use SHARE_EXCLUSIVE */
6374 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
6377 /* but in transacted mode, any share mode is valid */
6380 * Generate a unique name.
6384 WCHAR tempPath[MAX_PATH];
6385 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
6387 memset(tempPath, 0, sizeof(tempPath));
6388 memset(tempFileName, 0, sizeof(tempFileName));
6390 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
6393 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
6394 pwcsName = tempFileName;
6397 hr = STG_E_INSUFFICIENTMEMORY;
6401 creationMode = TRUNCATE_EXISTING;
6405 creationMode = GetCreationModeFromSTGM(grfMode);
6409 * Interpret the STGM value grfMode
6411 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6412 accessMode = GetAccessModeFromSTGM(grfMode);
6414 if (grfMode & STGM_DELETEONRELEASE)
6415 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
6417 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
6419 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
6423 FIXME("Storage share mode not implemented.\n");
6428 hFile = CreateFileW(pwcsName,
6436 if (hFile == INVALID_HANDLE_VALUE)
6438 if(GetLastError() == ERROR_FILE_EXISTS)
6439 hr = STG_E_FILEALREADYEXISTS;
6446 * Allocate and initialize the new IStorage32object.
6448 hr = Storage_Construct(
6463 * Get an "out" pointer for the caller.
6465 *ppstgOpen = (IStorage*)newStorage;
6468 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
6473 /******************************************************************************
6474 * StgCreateStorageEx [OLE32.@]
6476 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6478 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6479 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6481 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
6483 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6484 return STG_E_INVALIDPARAMETER;
6487 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
6489 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6490 return STG_E_INVALIDPARAMETER;
6493 if (stgfmt == STGFMT_FILE)
6495 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6496 return STG_E_INVALIDPARAMETER;
6499 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
6501 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6502 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
6505 ERR("Invalid stgfmt argument\n");
6506 return STG_E_INVALIDPARAMETER;
6509 /******************************************************************************
6510 * StgCreatePropSetStg [OLE32.@]
6512 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
6513 IPropertySetStorage **ppPropSetStg)
6517 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
6519 hr = STG_E_INVALIDPARAMETER;
6521 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
6522 (void**)ppPropSetStg);
6526 /******************************************************************************
6527 * StgOpenStorageEx [OLE32.@]
6529 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6531 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6532 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6534 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
6536 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6537 return STG_E_INVALIDPARAMETER;
6543 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6544 return STG_E_INVALIDPARAMETER;
6546 case STGFMT_STORAGE:
6549 case STGFMT_DOCFILE:
6550 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
6552 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6553 return STG_E_INVALIDPARAMETER;
6555 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6559 WARN("STGFMT_ANY assuming storage\n");
6563 return STG_E_INVALIDPARAMETER;
6566 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
6570 /******************************************************************************
6571 * StgOpenStorage [OLE32.@]
6573 HRESULT WINAPI StgOpenStorage(
6574 const OLECHAR *pwcsName,
6575 IStorage *pstgPriority,
6579 IStorage **ppstgOpen)
6581 StorageBaseImpl* newStorage = 0;
6587 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6588 debugstr_w(pwcsName), pstgPriority, grfMode,
6589 snbExclude, reserved, ppstgOpen);
6593 hr = STG_E_INVALIDNAME;
6599 hr = STG_E_INVALIDPOINTER;
6605 hr = STG_E_INVALIDPARAMETER;
6609 if (grfMode & STGM_PRIORITY)
6611 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
6612 return STG_E_INVALIDFLAG;
6613 if (grfMode & STGM_DELETEONRELEASE)
6614 return STG_E_INVALIDFUNCTION;
6615 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
6616 return STG_E_INVALIDFLAG;
6617 grfMode &= ~0xf0; /* remove the existing sharing mode */
6618 grfMode |= STGM_SHARE_DENY_NONE;
6620 /* STGM_PRIORITY stops other IStorage objects on the same file from
6621 * committing until the STGM_PRIORITY IStorage is closed. it also
6622 * stops non-transacted mode StgOpenStorage calls with write access from
6623 * succeeding. obviously, both of these cannot be achieved through just
6624 * file share flags */
6625 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6629 * Validate the sharing mode
6631 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
6632 switch(STGM_SHARE_MODE(grfMode))
6634 case STGM_SHARE_EXCLUSIVE:
6635 case STGM_SHARE_DENY_WRITE:
6638 hr = STG_E_INVALIDFLAG;
6642 if ( FAILED( validateSTGM(grfMode) ) ||
6643 (grfMode&STGM_CREATE))
6645 hr = STG_E_INVALIDFLAG;
6649 /* shared reading requires transacted mode */
6650 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6651 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6652 !(grfMode&STGM_TRANSACTED) )
6654 hr = STG_E_INVALIDFLAG;
6659 * Interpret the STGM value grfMode
6661 shareMode = GetShareModeFromSTGM(grfMode);
6662 accessMode = GetAccessModeFromSTGM(grfMode);
6666 hFile = CreateFileW( pwcsName,
6671 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6674 if (hFile==INVALID_HANDLE_VALUE)
6676 DWORD last_error = GetLastError();
6682 case ERROR_FILE_NOT_FOUND:
6683 hr = STG_E_FILENOTFOUND;
6686 case ERROR_PATH_NOT_FOUND:
6687 hr = STG_E_PATHNOTFOUND;
6690 case ERROR_ACCESS_DENIED:
6691 case ERROR_WRITE_PROTECT:
6692 hr = STG_E_ACCESSDENIED;
6695 case ERROR_SHARING_VIOLATION:
6696 hr = STG_E_SHAREVIOLATION;
6707 * Refuse to open the file if it's too small to be a structured storage file
6708 * FIXME: verify the file when reading instead of here
6710 if (GetFileSize(hFile, NULL) < 0x100)
6713 hr = STG_E_FILEALREADYEXISTS;
6718 * Allocate and initialize the new IStorage32object.
6720 hr = Storage_Construct(
6732 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6734 if(hr == STG_E_INVALIDHEADER)
6735 hr = STG_E_FILEALREADYEXISTS;
6740 * Get an "out" pointer for the caller.
6742 *ppstgOpen = (IStorage*)newStorage;
6745 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6749 /******************************************************************************
6750 * StgCreateDocfileOnILockBytes [OLE32.@]
6752 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6756 IStorage** ppstgOpen)
6758 StorageBaseImpl* newStorage = 0;
6761 if ((ppstgOpen == 0) || (plkbyt == 0))
6762 return STG_E_INVALIDPOINTER;
6765 * Allocate and initialize the new IStorage object.
6767 hr = Storage_Construct(
6782 * Get an "out" pointer for the caller.
6784 *ppstgOpen = (IStorage*)newStorage;
6789 /******************************************************************************
6790 * StgOpenStorageOnILockBytes [OLE32.@]
6792 HRESULT WINAPI StgOpenStorageOnILockBytes(
6794 IStorage *pstgPriority,
6798 IStorage **ppstgOpen)
6800 StorageBaseImpl* newStorage = 0;
6803 if ((plkbyt == 0) || (ppstgOpen == 0))
6804 return STG_E_INVALIDPOINTER;
6806 if ( FAILED( validateSTGM(grfMode) ))
6807 return STG_E_INVALIDFLAG;
6812 * Allocate and initialize the new IStorage object.
6814 hr = Storage_Construct(
6829 * Get an "out" pointer for the caller.
6831 *ppstgOpen = (IStorage*)newStorage;
6836 /******************************************************************************
6837 * StgSetTimes [ole32.@]
6838 * StgSetTimes [OLE32.@]
6842 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6843 FILETIME const *patime, FILETIME const *pmtime)
6845 IStorage *stg = NULL;
6848 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6850 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6854 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6855 IStorage_Release(stg);
6861 /******************************************************************************
6862 * StgIsStorageILockBytes [OLE32.@]
6864 * Determines if the ILockBytes contains a storage object.
6866 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6869 ULARGE_INTEGER offset;
6871 offset.u.HighPart = 0;
6872 offset.u.LowPart = 0;
6874 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6876 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6882 /******************************************************************************
6883 * WriteClassStg [OLE32.@]
6885 * This method will store the specified CLSID in the specified storage object
6887 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6892 return E_INVALIDARG;
6895 return STG_E_INVALIDPOINTER;
6897 hRes = IStorage_SetClass(pStg, rclsid);
6902 /***********************************************************************
6903 * ReadClassStg (OLE32.@)
6905 * This method reads the CLSID previously written to a storage object with
6906 * the WriteClassStg.
6909 * pstg [I] IStorage pointer
6910 * pclsid [O] Pointer to where the CLSID is written
6914 * Failure: HRESULT code.
6916 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6921 TRACE("(%p, %p)\n", pstg, pclsid);
6923 if(!pstg || !pclsid)
6924 return E_INVALIDARG;
6927 * read a STATSTG structure (contains the clsid) from the storage
6929 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6932 *pclsid=pstatstg.clsid;
6937 /***********************************************************************
6938 * OleLoadFromStream (OLE32.@)
6940 * This function loads an object from stream
6942 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6946 LPPERSISTSTREAM xstm;
6948 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6950 res=ReadClassStm(pStm,&clsid);
6953 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6956 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6958 IUnknown_Release((IUnknown*)*ppvObj);
6961 res=IPersistStream_Load(xstm,pStm);
6962 IPersistStream_Release(xstm);
6963 /* FIXME: all refcounts ok at this point? I think they should be:
6966 * xstm : 0 (released)
6971 /***********************************************************************
6972 * OleSaveToStream (OLE32.@)
6974 * This function saves an object with the IPersistStream interface on it
6975 * to the specified stream.
6977 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6983 TRACE("(%p,%p)\n",pPStm,pStm);
6985 res=IPersistStream_GetClassID(pPStm,&clsid);
6987 if (SUCCEEDED(res)){
6989 res=WriteClassStm(pStm,&clsid);
6993 res=IPersistStream_Save(pPStm,pStm,TRUE);
6996 TRACE("Finished Save\n");
7000 /****************************************************************************
7001 * This method validate a STGM parameter that can contain the values below
7003 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7004 * The stgm values contained in 0xffff0000 are bitmasks.
7006 * STGM_DIRECT 0x00000000
7007 * STGM_TRANSACTED 0x00010000
7008 * STGM_SIMPLE 0x08000000
7010 * STGM_READ 0x00000000
7011 * STGM_WRITE 0x00000001
7012 * STGM_READWRITE 0x00000002
7014 * STGM_SHARE_DENY_NONE 0x00000040
7015 * STGM_SHARE_DENY_READ 0x00000030
7016 * STGM_SHARE_DENY_WRITE 0x00000020
7017 * STGM_SHARE_EXCLUSIVE 0x00000010
7019 * STGM_PRIORITY 0x00040000
7020 * STGM_DELETEONRELEASE 0x04000000
7022 * STGM_CREATE 0x00001000
7023 * STGM_CONVERT 0x00020000
7024 * STGM_FAILIFTHERE 0x00000000
7026 * STGM_NOSCRATCH 0x00100000
7027 * STGM_NOSNAPSHOT 0x00200000
7029 static HRESULT validateSTGM(DWORD stgm)
7031 DWORD access = STGM_ACCESS_MODE(stgm);
7032 DWORD share = STGM_SHARE_MODE(stgm);
7033 DWORD create = STGM_CREATE_MODE(stgm);
7035 if (stgm&~STGM_KNOWN_FLAGS)
7037 ERR("unknown flags %08x\n", stgm);
7045 case STGM_READWRITE:
7053 case STGM_SHARE_DENY_NONE:
7054 case STGM_SHARE_DENY_READ:
7055 case STGM_SHARE_DENY_WRITE:
7056 case STGM_SHARE_EXCLUSIVE:
7065 case STGM_FAILIFTHERE:
7072 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7074 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7078 * STGM_CREATE | STGM_CONVERT
7079 * if both are false, STGM_FAILIFTHERE is set to TRUE
7081 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7085 * STGM_NOSCRATCH requires STGM_TRANSACTED
7087 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7091 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7092 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7094 if ( (stgm & STGM_NOSNAPSHOT) &&
7095 (!(stgm & STGM_TRANSACTED) ||
7096 share == STGM_SHARE_EXCLUSIVE ||
7097 share == STGM_SHARE_DENY_WRITE) )
7103 /****************************************************************************
7104 * GetShareModeFromSTGM
7106 * This method will return a share mode flag from a STGM value.
7107 * The STGM value is assumed valid.
7109 static DWORD GetShareModeFromSTGM(DWORD stgm)
7111 switch (STGM_SHARE_MODE(stgm))
7113 case STGM_SHARE_DENY_NONE:
7114 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7115 case STGM_SHARE_DENY_READ:
7116 return FILE_SHARE_WRITE;
7117 case STGM_SHARE_DENY_WRITE:
7118 return FILE_SHARE_READ;
7119 case STGM_SHARE_EXCLUSIVE:
7122 ERR("Invalid share mode!\n");
7127 /****************************************************************************
7128 * GetAccessModeFromSTGM
7130 * This method will return an access mode flag from a STGM value.
7131 * The STGM value is assumed valid.
7133 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7135 switch (STGM_ACCESS_MODE(stgm))
7138 return GENERIC_READ;
7140 case STGM_READWRITE:
7141 return GENERIC_READ | GENERIC_WRITE;
7143 ERR("Invalid access mode!\n");
7148 /****************************************************************************
7149 * GetCreationModeFromSTGM
7151 * This method will return a creation mode flag from a STGM value.
7152 * The STGM value is assumed valid.
7154 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7156 switch(STGM_CREATE_MODE(stgm))
7159 return CREATE_ALWAYS;
7161 FIXME("STGM_CONVERT not implemented!\n");
7163 case STGM_FAILIFTHERE:
7166 ERR("Invalid create mode!\n");
7172 /*************************************************************************
7173 * OLECONVERT_LoadOLE10 [Internal]
7175 * Loads the OLE10 STREAM to memory
7178 * pOleStream [I] The OLESTREAM
7179 * pData [I] Data Structure for the OLESTREAM Data
7183 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7184 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7187 * This function is used by OleConvertOLESTREAMToIStorage only.
7189 * Memory allocated for pData must be freed by the caller
7191 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7194 HRESULT hRes = S_OK;
7198 pData->pData = NULL;
7199 pData->pstrOleObjFileName = NULL;
7201 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7204 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7205 if(dwSize != sizeof(pData->dwOleID))
7207 hRes = CONVERT10_E_OLESTREAM_GET;
7209 else if(pData->dwOleID != OLESTREAM_ID)
7211 hRes = CONVERT10_E_OLESTREAM_FMT;
7222 /* Get the TypeID... more info needed for this field */
7223 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7224 if(dwSize != sizeof(pData->dwTypeID))
7226 hRes = CONVERT10_E_OLESTREAM_GET;
7231 if(pData->dwTypeID != 0)
7233 /* Get the length of the OleTypeName */
7234 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7235 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7237 hRes = CONVERT10_E_OLESTREAM_GET;
7242 if(pData->dwOleTypeNameLength > 0)
7244 /* Get the OleTypeName */
7245 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7246 if(dwSize != pData->dwOleTypeNameLength)
7248 hRes = CONVERT10_E_OLESTREAM_GET;
7254 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7255 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7257 hRes = CONVERT10_E_OLESTREAM_GET;
7261 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7262 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7263 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7264 if(pData->pstrOleObjFileName)
7266 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7267 if(dwSize != pData->dwOleObjFileNameLength)
7269 hRes = CONVERT10_E_OLESTREAM_GET;
7273 hRes = CONVERT10_E_OLESTREAM_GET;
7278 /* Get the Width of the Metafile */
7279 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7280 if(dwSize != sizeof(pData->dwMetaFileWidth))
7282 hRes = CONVERT10_E_OLESTREAM_GET;
7286 /* Get the Height of the Metafile */
7287 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7288 if(dwSize != sizeof(pData->dwMetaFileHeight))
7290 hRes = CONVERT10_E_OLESTREAM_GET;
7296 /* Get the Length of the Data */
7297 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7298 if(dwSize != sizeof(pData->dwDataLength))
7300 hRes = CONVERT10_E_OLESTREAM_GET;
7304 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7306 if(!bStrem1) /* if it is a second OLE stream data */
7308 pData->dwDataLength -= 8;
7309 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7310 if(dwSize != sizeof(pData->strUnknown))
7312 hRes = CONVERT10_E_OLESTREAM_GET;
7318 if(pData->dwDataLength > 0)
7320 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
7322 /* Get Data (ex. IStorage, Metafile, or BMP) */
7325 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
7326 if(dwSize != pData->dwDataLength)
7328 hRes = CONVERT10_E_OLESTREAM_GET;
7333 hRes = CONVERT10_E_OLESTREAM_GET;
7342 /*************************************************************************
7343 * OLECONVERT_SaveOLE10 [Internal]
7345 * Saves the OLE10 STREAM From memory
7348 * pData [I] Data Structure for the OLESTREAM Data
7349 * pOleStream [I] The OLESTREAM to save
7353 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7356 * This function is used by OleConvertIStorageToOLESTREAM only.
7359 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
7362 HRESULT hRes = S_OK;
7366 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7367 if(dwSize != sizeof(pData->dwOleID))
7369 hRes = CONVERT10_E_OLESTREAM_PUT;
7374 /* Set the TypeID */
7375 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7376 if(dwSize != sizeof(pData->dwTypeID))
7378 hRes = CONVERT10_E_OLESTREAM_PUT;
7382 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
7384 /* Set the Length of the OleTypeName */
7385 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7386 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7388 hRes = CONVERT10_E_OLESTREAM_PUT;
7393 if(pData->dwOleTypeNameLength > 0)
7395 /* Set the OleTypeName */
7396 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7397 if(dwSize != pData->dwOleTypeNameLength)
7399 hRes = CONVERT10_E_OLESTREAM_PUT;
7406 /* Set the width of the Metafile */
7407 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7408 if(dwSize != sizeof(pData->dwMetaFileWidth))
7410 hRes = CONVERT10_E_OLESTREAM_PUT;
7416 /* Set the height of the Metafile */
7417 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7418 if(dwSize != sizeof(pData->dwMetaFileHeight))
7420 hRes = CONVERT10_E_OLESTREAM_PUT;
7426 /* Set the length of the Data */
7427 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7428 if(dwSize != sizeof(pData->dwDataLength))
7430 hRes = CONVERT10_E_OLESTREAM_PUT;
7436 if(pData->dwDataLength > 0)
7438 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7439 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
7440 if(dwSize != pData->dwDataLength)
7442 hRes = CONVERT10_E_OLESTREAM_PUT;
7450 /*************************************************************************
7451 * OLECONVERT_GetOLE20FromOLE10[Internal]
7453 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7454 * opens it, and copies the content to the dest IStorage for
7455 * OleConvertOLESTREAMToIStorage
7459 * pDestStorage [I] The IStorage to copy the data to
7460 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7461 * nBufferLength [I] The size of the buffer
7470 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
7474 IStorage *pTempStorage;
7475 DWORD dwNumOfBytesWritten;
7476 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7477 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7479 /* Create a temp File */
7480 GetTempPathW(MAX_PATH, wstrTempDir);
7481 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7482 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
7484 if(hFile != INVALID_HANDLE_VALUE)
7486 /* Write IStorage Data to File */
7487 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
7490 /* Open and copy temp storage to the Dest Storage */
7491 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
7494 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
7495 IStorage_Release(pTempStorage);
7497 DeleteFileW(wstrTempFile);
7502 /*************************************************************************
7503 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7505 * Saves the OLE10 STREAM From memory
7508 * pStorage [I] The Src IStorage to copy
7509 * pData [I] The Dest Memory to write to.
7512 * The size in bytes allocated for pData
7515 * Memory allocated for pData must be freed by the caller
7517 * Used by OleConvertIStorageToOLESTREAM only.
7520 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
7524 DWORD nDataLength = 0;
7525 IStorage *pTempStorage;
7526 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7527 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7531 /* Create temp Storage */
7532 GetTempPathW(MAX_PATH, wstrTempDir);
7533 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7534 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
7538 /* Copy Src Storage to the Temp Storage */
7539 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
7540 IStorage_Release(pTempStorage);
7542 /* Open Temp Storage as a file and copy to memory */
7543 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7544 if(hFile != INVALID_HANDLE_VALUE)
7546 nDataLength = GetFileSize(hFile, NULL);
7547 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
7548 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
7551 DeleteFileW(wstrTempFile);
7556 /*************************************************************************
7557 * OLECONVERT_CreateOleStream [Internal]
7559 * Creates the "\001OLE" stream in the IStorage if necessary.
7562 * pStorage [I] Dest storage to create the stream in
7568 * This function is used by OleConvertOLESTREAMToIStorage only.
7570 * This stream is still unknown, MS Word seems to have extra data
7571 * but since the data is stored in the OLESTREAM there should be
7572 * no need to recreate the stream. If the stream is manually
7573 * deleted it will create it with this default data.
7576 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7580 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7581 BYTE pOleStreamHeader [] =
7583 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7584 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7585 0x00, 0x00, 0x00, 0x00
7588 /* Create stream if not present */
7589 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7590 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7594 /* Write default Data */
7595 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7596 IStream_Release(pStream);
7600 /* write a string to a stream, preceded by its length */
7601 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7608 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7609 r = IStream_Write( stm, &len, sizeof(len), NULL);
7614 str = CoTaskMemAlloc( len );
7615 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7616 r = IStream_Write( stm, str, len, NULL);
7617 CoTaskMemFree( str );
7621 /* read a string preceded by its length from a stream */
7622 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7625 DWORD len, count = 0;
7629 r = IStream_Read( stm, &len, sizeof(len), &count );
7632 if( count != sizeof(len) )
7633 return E_OUTOFMEMORY;
7635 TRACE("%d bytes\n",len);
7637 str = CoTaskMemAlloc( len );
7639 return E_OUTOFMEMORY;
7641 r = IStream_Read( stm, str, len, &count );
7646 CoTaskMemFree( str );
7647 return E_OUTOFMEMORY;
7650 TRACE("Read string %s\n",debugstr_an(str,len));
7652 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7653 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7655 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7656 CoTaskMemFree( str );
7664 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7665 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7669 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7671 static const BYTE unknown1[12] =
7672 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7673 0xFF, 0xFF, 0xFF, 0xFF};
7674 static const BYTE unknown2[16] =
7675 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7676 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7678 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7679 debugstr_w(lpszUserType), debugstr_w(szClipName),
7680 debugstr_w(szProgIDName));
7682 /* Create a CompObj stream */
7683 r = IStorage_CreateStream(pstg, szwStreamName,
7684 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7688 /* Write CompObj Structure to stream */
7689 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7691 if( SUCCEEDED( r ) )
7692 r = WriteClassStm( pstm, clsid );
7694 if( SUCCEEDED( r ) )
7695 r = STREAM_WriteString( pstm, lpszUserType );
7696 if( SUCCEEDED( r ) )
7697 r = STREAM_WriteString( pstm, szClipName );
7698 if( SUCCEEDED( r ) )
7699 r = STREAM_WriteString( pstm, szProgIDName );
7700 if( SUCCEEDED( r ) )
7701 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7703 IStream_Release( pstm );
7708 /***********************************************************************
7709 * WriteFmtUserTypeStg (OLE32.@)
7711 HRESULT WINAPI WriteFmtUserTypeStg(
7712 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7715 WCHAR szwClipName[0x40];
7716 CLSID clsid = CLSID_NULL;
7717 LPWSTR wstrProgID = NULL;
7720 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7722 /* get the clipboard format name */
7723 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7726 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7728 /* FIXME: There's room to save a CLSID and its ProgID, but
7729 the CLSID is not looked up in the registry and in all the
7730 tests I wrote it was CLSID_NULL. Where does it come from?
7733 /* get the real program ID. This may fail, but that's fine */
7734 ProgIDFromCLSID(&clsid, &wstrProgID);
7736 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7738 r = STORAGE_WriteCompObj( pstg, &clsid,
7739 lpszUserType, szwClipName, wstrProgID );
7741 CoTaskMemFree(wstrProgID);
7747 /******************************************************************************
7748 * ReadFmtUserTypeStg [OLE32.@]
7750 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7754 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7755 unsigned char unknown1[12];
7756 unsigned char unknown2[16];
7758 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7761 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7763 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7764 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7767 WARN("Failed to open stream r = %08x\n", r);
7771 /* read the various parts of the structure */
7772 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7773 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7775 r = ReadClassStm( stm, &clsid );
7779 r = STREAM_ReadString( stm, &szCLSIDName );
7783 r = STREAM_ReadString( stm, &szOleTypeName );
7787 r = STREAM_ReadString( stm, &szProgIDName );
7791 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7792 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7795 /* ok, success... now we just need to store what we found */
7797 *pcf = RegisterClipboardFormatW( szOleTypeName );
7798 CoTaskMemFree( szOleTypeName );
7800 if( lplpszUserType )
7801 *lplpszUserType = szCLSIDName;
7802 CoTaskMemFree( szProgIDName );
7805 IStream_Release( stm );
7811 /*************************************************************************
7812 * OLECONVERT_CreateCompObjStream [Internal]
7814 * Creates a "\001CompObj" is the destination IStorage if necessary.
7817 * pStorage [I] The dest IStorage to create the CompObj Stream
7819 * strOleTypeName [I] The ProgID
7823 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7826 * This function is used by OleConvertOLESTREAMToIStorage only.
7828 * The stream data is stored in the OLESTREAM and there should be
7829 * no need to recreate the stream. If the stream is manually
7830 * deleted it will attempt to create it by querying the registry.
7834 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7837 HRESULT hStorageRes, hRes = S_OK;
7838 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7839 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7840 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7842 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7843 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7845 /* Initialize the CompObj structure */
7846 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7847 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7848 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7851 /* Create a CompObj stream if it doesn't exist */
7852 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7853 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7854 if(hStorageRes == S_OK)
7856 /* copy the OleTypeName to the compobj struct */
7857 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7858 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7860 /* copy the OleTypeName to the compobj struct */
7861 /* Note: in the test made, these were Identical */
7862 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7863 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7866 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7867 bufferW, OLESTREAM_MAX_STR_LEN );
7868 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7874 /* Get the CLSID Default Name from the Registry */
7875 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7876 if(hErr == ERROR_SUCCESS)
7878 char strTemp[OLESTREAM_MAX_STR_LEN];
7879 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7880 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7881 if(hErr == ERROR_SUCCESS)
7883 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7889 /* Write CompObj Structure to stream */
7890 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7892 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7894 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7895 if(IStorageCompObj.dwCLSIDNameLength > 0)
7897 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7899 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7900 if(IStorageCompObj.dwOleTypeNameLength > 0)
7902 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7904 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7905 if(IStorageCompObj.dwProgIDNameLength > 0)
7907 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7909 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7910 IStream_Release(pStream);
7916 /*************************************************************************
7917 * OLECONVERT_CreateOlePresStream[Internal]
7919 * Creates the "\002OlePres000" Stream with the Metafile data
7922 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7923 * dwExtentX [I] Width of the Metafile
7924 * dwExtentY [I] Height of the Metafile
7925 * pData [I] Metafile data
7926 * dwDataLength [I] Size of the Metafile data
7930 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7933 * This function is used by OleConvertOLESTREAMToIStorage only.
7936 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7940 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7941 BYTE pOlePresStreamHeader [] =
7943 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7944 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7945 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7946 0x00, 0x00, 0x00, 0x00
7949 BYTE pOlePresStreamHeaderEmpty [] =
7951 0x00, 0x00, 0x00, 0x00,
7952 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7953 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7954 0x00, 0x00, 0x00, 0x00
7957 /* Create the OlePres000 Stream */
7958 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7959 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7964 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7966 memset(&OlePres, 0, sizeof(OlePres));
7967 /* Do we have any metafile data to save */
7968 if(dwDataLength > 0)
7970 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7971 nHeaderSize = sizeof(pOlePresStreamHeader);
7975 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7976 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7978 /* Set width and height of the metafile */
7979 OlePres.dwExtentX = dwExtentX;
7980 OlePres.dwExtentY = -dwExtentY;
7982 /* Set Data and Length */
7983 if(dwDataLength > sizeof(METAFILEPICT16))
7985 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7986 OlePres.pData = &(pData[8]);
7988 /* Save OlePres000 Data to Stream */
7989 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7990 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7991 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7992 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7993 if(OlePres.dwSize > 0)
7995 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7997 IStream_Release(pStream);
8001 /*************************************************************************
8002 * OLECONVERT_CreateOle10NativeStream [Internal]
8004 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8007 * pStorage [I] Dest storage to create the stream in
8008 * pData [I] Ole10 Native Data (ex. bmp)
8009 * dwDataLength [I] Size of the Ole10 Native Data
8015 * This function is used by OleConvertOLESTREAMToIStorage only.
8017 * Might need to verify the data and return appropriate error message
8020 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8024 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8026 /* Create the Ole10Native Stream */
8027 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8028 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8032 /* Write info to stream */
8033 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8034 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8035 IStream_Release(pStream);
8040 /*************************************************************************
8041 * OLECONVERT_GetOLE10ProgID [Internal]
8043 * Finds the ProgID (or OleTypeID) from the IStorage
8046 * pStorage [I] The Src IStorage to get the ProgID
8047 * strProgID [I] the ProgID string to get
8048 * dwSize [I] the size of the string
8052 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8055 * This function is used by OleConvertIStorageToOLESTREAM only.
8059 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8063 LARGE_INTEGER iSeekPos;
8064 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8065 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8067 /* Open the CompObj Stream */
8068 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8069 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8073 /*Get the OleType from the CompObj Stream */
8074 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8075 iSeekPos.u.HighPart = 0;
8077 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8078 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8079 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8080 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8081 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8082 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8083 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8085 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8088 IStream_Read(pStream, strProgID, *dwSize, NULL);
8090 IStream_Release(pStream);
8095 LPOLESTR wstrProgID;
8097 /* Get the OleType from the registry */
8098 REFCLSID clsid = &(stat.clsid);
8099 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8100 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8103 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8110 /*************************************************************************
8111 * OLECONVERT_GetOle10PresData [Internal]
8113 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8116 * pStorage [I] Src IStroage
8117 * pOleStream [I] Dest OleStream Mem Struct
8123 * This function is used by OleConvertIStorageToOLESTREAM only.
8125 * Memory allocated for pData must be freed by the caller
8129 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8134 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8136 /* Initialize Default data for OLESTREAM */
8137 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8138 pOleStreamData[0].dwTypeID = 2;
8139 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8140 pOleStreamData[1].dwTypeID = 0;
8141 pOleStreamData[0].dwMetaFileWidth = 0;
8142 pOleStreamData[0].dwMetaFileHeight = 0;
8143 pOleStreamData[0].pData = NULL;
8144 pOleStreamData[1].pData = NULL;
8146 /* Open Ole10Native Stream */
8147 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8148 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8152 /* Read Size and Data */
8153 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8154 if(pOleStreamData->dwDataLength > 0)
8156 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8157 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8159 IStream_Release(pStream);
8165 /*************************************************************************
8166 * OLECONVERT_GetOle20PresData[Internal]
8168 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8171 * pStorage [I] Src IStroage
8172 * pOleStreamData [I] Dest OleStream Mem Struct
8178 * This function is used by OleConvertIStorageToOLESTREAM only.
8180 * Memory allocated for pData must be freed by the caller
8182 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8186 OLECONVERT_ISTORAGE_OLEPRES olePress;
8187 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8189 /* Initialize Default data for OLESTREAM */
8190 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8191 pOleStreamData[0].dwTypeID = 2;
8192 pOleStreamData[0].dwMetaFileWidth = 0;
8193 pOleStreamData[0].dwMetaFileHeight = 0;
8194 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8195 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8196 pOleStreamData[1].dwTypeID = 0;
8197 pOleStreamData[1].dwOleTypeNameLength = 0;
8198 pOleStreamData[1].strOleTypeName[0] = 0;
8199 pOleStreamData[1].dwMetaFileWidth = 0;
8200 pOleStreamData[1].dwMetaFileHeight = 0;
8201 pOleStreamData[1].pData = NULL;
8202 pOleStreamData[1].dwDataLength = 0;
8205 /* Open OlePress000 stream */
8206 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8207 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8210 LARGE_INTEGER iSeekPos;
8211 METAFILEPICT16 MetaFilePict;
8212 static const char strMetafilePictName[] = "METAFILEPICT";
8214 /* Set the TypeID for a Metafile */
8215 pOleStreamData[1].dwTypeID = 5;
8217 /* Set the OleTypeName to Metafile */
8218 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8219 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8221 iSeekPos.u.HighPart = 0;
8222 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8224 /* Get Presentation Data */
8225 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8226 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8227 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8228 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8230 /*Set width and Height */
8231 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8232 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8233 if(olePress.dwSize > 0)
8236 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8238 /* Set MetaFilePict struct */
8239 MetaFilePict.mm = 8;
8240 MetaFilePict.xExt = olePress.dwExtentX;
8241 MetaFilePict.yExt = olePress.dwExtentY;
8242 MetaFilePict.hMF = 0;
8244 /* Get Metafile Data */
8245 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8246 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8247 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8249 IStream_Release(pStream);
8253 /*************************************************************************
8254 * OleConvertOLESTREAMToIStorage [OLE32.@]
8259 * DVTARGETDEVICE parameter is not handled
8260 * Still unsure of some mem fields for OLE 10 Stream
8261 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8262 * and "\001OLE" streams
8265 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8266 LPOLESTREAM pOleStream,
8268 const DVTARGETDEVICE* ptd)
8272 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8274 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8276 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8280 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8283 if(pstg == NULL || pOleStream == NULL)
8285 hRes = E_INVALIDARG;
8290 /* Load the OLESTREAM to Memory */
8291 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8296 /* Load the OLESTREAM to Memory (part 2)*/
8297 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8303 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8305 /* Do we have the IStorage Data in the OLESTREAM */
8306 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8308 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8309 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8313 /* It must be an original OLE 1.0 source */
8314 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8319 /* It must be an original OLE 1.0 source */
8320 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8323 /* Create CompObj Stream if necessary */
8324 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
8327 /*Create the Ole Stream if necessary */
8328 OLECONVERT_CreateOleStream(pstg);
8333 /* Free allocated memory */
8334 for(i=0; i < 2; i++)
8336 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8337 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
8338 pOleStreamData[i].pstrOleObjFileName = NULL;
8343 /*************************************************************************
8344 * OleConvertIStorageToOLESTREAM [OLE32.@]
8351 * Still unsure of some mem fields for OLE 10 Stream
8352 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8353 * and "\001OLE" streams.
8356 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
8358 LPOLESTREAM pOleStream)
8361 HRESULT hRes = S_OK;
8363 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8364 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8366 TRACE("%p %p\n", pstg, pOleStream);
8368 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8370 if(pstg == NULL || pOleStream == NULL)
8372 hRes = E_INVALIDARG;
8376 /* Get the ProgID */
8377 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
8378 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
8382 /* Was it originally Ole10 */
8383 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
8386 IStream_Release(pStream);
8387 /* Get Presentation Data for Ole10Native */
8388 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
8392 /* Get Presentation Data (OLE20) */
8393 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
8396 /* Save OLESTREAM */
8397 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
8400 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
8405 /* Free allocated memory */
8406 for(i=0; i < 2; i++)
8408 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8414 /***********************************************************************
8415 * GetConvertStg (OLE32.@)
8417 HRESULT WINAPI GetConvertStg(IStorage *stg) {
8418 FIXME("unimplemented stub!\n");
8422 /******************************************************************************
8423 * StgIsStorageFile [OLE32.@]
8424 * Verify if the file contains a storage object
8430 * S_OK if file has magic bytes as a storage object
8431 * S_FALSE if file is not storage
8434 StgIsStorageFile(LPCOLESTR fn)
8440 TRACE("%s\n", debugstr_w(fn));
8441 hf = CreateFileW(fn, GENERIC_READ,
8442 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
8443 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8445 if (hf == INVALID_HANDLE_VALUE)
8446 return STG_E_FILENOTFOUND;
8448 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
8450 WARN(" unable to read file\n");
8457 if (bytes_read != 8) {
8458 TRACE(" too short\n");
8462 if (!memcmp(magic,STORAGE_magic,8)) {
8467 TRACE(" -> Invalid header.\n");
8471 /***********************************************************************
8472 * WriteClassStm (OLE32.@)
8474 * Writes a CLSID to a stream.
8477 * pStm [I] Stream to write to.
8478 * rclsid [I] CLSID to write.
8482 * Failure: HRESULT code.
8484 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
8486 TRACE("(%p,%p)\n",pStm,rclsid);
8488 if (!pStm || !rclsid)
8489 return E_INVALIDARG;
8491 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
8494 /***********************************************************************
8495 * ReadClassStm (OLE32.@)
8497 * Reads a CLSID from a stream.
8500 * pStm [I] Stream to read from.
8501 * rclsid [O] CLSID to read.
8505 * Failure: HRESULT code.
8507 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
8512 TRACE("(%p,%p)\n",pStm,pclsid);
8514 if (!pStm || !pclsid)
8515 return E_INVALIDARG;
8517 /* clear the output args */
8518 *pclsid = CLSID_NULL;
8520 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
8525 if (nbByte != sizeof(CLSID))
8526 return STG_E_READFAULT;