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;
81 * There is no specific data for this class.
84 typedef struct StorageInternalImpl StorageInternalImpl;
86 /* Method definitions for the Storage32InternalImpl class. */
87 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
88 DWORD openFlags, ULONG rootTropertyIndex);
89 static void StorageImpl_Destroy(StorageBaseImpl* iface);
90 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
91 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
92 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
93 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
94 static void StorageImpl_SaveFileHeader(StorageImpl* This);
96 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
97 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
98 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
99 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
100 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
102 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
103 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
104 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
106 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
107 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
108 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
109 ULONG blockIndex, ULONG offset, DWORD value);
110 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
111 ULONG blockIndex, ULONG offset, DWORD* value);
113 /* OLESTREAM memory structure to use for Get and Put Routines */
114 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
119 DWORD dwOleTypeNameLength;
120 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
121 CHAR *pstrOleObjFileName;
122 DWORD dwOleObjFileNameLength;
123 DWORD dwMetaFileWidth;
124 DWORD dwMetaFileHeight;
125 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
128 }OLECONVERT_OLESTREAM_DATA;
130 /* CompObj Stream structure */
131 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
136 DWORD dwCLSIDNameLength;
137 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
138 DWORD dwOleTypeNameLength;
139 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
140 DWORD dwProgIDNameLength;
141 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
143 }OLECONVERT_ISTORAGE_COMPOBJ;
146 /* Ole Presentation Stream structure */
147 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
155 }OLECONVERT_ISTORAGE_OLEPRES;
159 /***********************************************************************
160 * Forward declaration of internal functions used by the method DestroyElement
162 static HRESULT deleteStorageContents(
163 StorageBaseImpl *parentStorage,
165 DirEntry entryDataToDelete);
167 static HRESULT deleteStreamContents(
168 StorageBaseImpl *parentStorage,
170 DirEntry entryDataToDelete);
172 static HRESULT removeFromTree(
174 ULONG parentStorageIndex,
177 /***********************************************************************
178 * Declaration of the functions used to manipulate DirEntry
181 static HRESULT createDirEntry(
182 StorageImpl *storage,
183 const DirEntry *newData,
186 static HRESULT destroyDirEntry(
187 StorageImpl *storage,
190 static HRESULT insertIntoTree(
192 ULONG parentStorageIndex,
193 ULONG newEntryIndex);
195 static LONG entryNameCmp(
196 const OLECHAR *name1,
197 const OLECHAR *name2);
199 static ULONG findElement(
200 StorageImpl *storage,
205 static HRESULT findTreeParent(
206 StorageImpl *storage,
208 const OLECHAR *childName,
209 DirEntry *parentData,
213 /***********************************************************************
214 * Declaration of miscellaneous functions...
216 static HRESULT validateSTGM(DWORD stgmValue);
218 static DWORD GetShareModeFromSTGM(DWORD stgm);
219 static DWORD GetAccessModeFromSTGM(DWORD stgm);
220 static DWORD GetCreationModeFromSTGM(DWORD stgm);
222 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
225 /****************************************************************************
226 * IEnumSTATSTGImpl definitions.
228 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
229 * This class allows iterating through the content of a storage and to find
230 * specific items inside it.
232 struct IEnumSTATSTGImpl
234 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
235 * since we want to cast this in an IEnumSTATSTG pointer */
237 LONG ref; /* Reference count */
238 StorageImpl* parentStorage; /* Reference to the parent storage */
239 ULONG storageDirEntry; /* Directory entry of the storage to enumerate */
242 * The current implementation of the IEnumSTATSTGImpl class uses a stack
243 * to walk the directory entries to get the content of a storage. This stack
244 * is implemented by the following 3 data members
250 #define ENUMSTATSGT_SIZE_INCREMENT 10
254 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG storageDirEntry);
255 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
256 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
257 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
259 /************************************************************************
263 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
265 if (index == 0xffffffff)
270 return index * BIG_BLOCK_SIZE;
273 /************************************************************************
274 ** Storage32BaseImpl implementation
276 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
277 ULARGE_INTEGER offset,
282 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
285 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
286 ULARGE_INTEGER offset,
291 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
294 /************************************************************************
295 * Storage32BaseImpl_QueryInterface (IUnknown)
297 * This method implements the common QueryInterface for all IStorage32
298 * implementations contained in this file.
300 * See Windows documentation for more details on IUnknown methods.
302 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
307 StorageBaseImpl *This = (StorageBaseImpl *)iface;
309 if ( (This==0) || (ppvObject==0) )
314 if (IsEqualGUID(&IID_IUnknown, riid) ||
315 IsEqualGUID(&IID_IStorage, riid))
319 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
321 *ppvObject = &This->pssVtbl;
325 return E_NOINTERFACE;
327 IStorage_AddRef(iface);
332 /************************************************************************
333 * Storage32BaseImpl_AddRef (IUnknown)
335 * This method implements the common AddRef for all IStorage32
336 * implementations contained in this file.
338 * See Windows documentation for more details on IUnknown methods.
340 static ULONG WINAPI StorageBaseImpl_AddRef(
343 StorageBaseImpl *This = (StorageBaseImpl *)iface;
344 ULONG ref = InterlockedIncrement(&This->ref);
346 TRACE("(%p) AddRef to %d\n", This, ref);
351 /************************************************************************
352 * Storage32BaseImpl_Release (IUnknown)
354 * This method implements the common Release for all IStorage32
355 * implementations contained in this file.
357 * See Windows documentation for more details on IUnknown methods.
359 static ULONG WINAPI StorageBaseImpl_Release(
362 StorageBaseImpl *This = (StorageBaseImpl *)iface;
364 ULONG ref = InterlockedDecrement(&This->ref);
366 TRACE("(%p) ReleaseRef to %d\n", This, ref);
371 * Since we are using a system of base-classes, we want to call the
372 * destructor of the appropriate derived class. To do this, we are
373 * using virtual functions to implement the destructor.
375 This->v_destructor(This);
381 /************************************************************************
382 * Storage32BaseImpl_OpenStream (IStorage)
384 * This method will open the specified stream object from the current storage.
386 * See Windows documentation for more details on IStorage methods.
388 static HRESULT WINAPI StorageBaseImpl_OpenStream(
390 const OLECHAR* pwcsName, /* [string][in] */
391 void* reserved1, /* [unique][in] */
392 DWORD grfMode, /* [in] */
393 DWORD reserved2, /* [in] */
394 IStream** ppstm) /* [out] */
396 StorageBaseImpl *This = (StorageBaseImpl *)iface;
397 StgStreamImpl* newStream;
398 DirEntry currentEntry;
399 ULONG streamEntryRef;
400 HRESULT res = STG_E_UNKNOWN;
402 TRACE("(%p, %s, %p, %x, %d, %p)\n",
403 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
405 if ( (pwcsName==NULL) || (ppstm==0) )
413 if ( FAILED( validateSTGM(grfMode) ) ||
414 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
416 res = STG_E_INVALIDFLAG;
423 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
425 res = STG_E_INVALIDFUNCTION;
430 * Check that we're compatible with the parent's storage mode, but
431 * only if we are not in transacted mode
433 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
434 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
436 res = STG_E_ACCESSDENIED;
442 * Search for the element with the given name
444 streamEntryRef = findElement(
445 This->ancestorStorage,
446 This->storageDirEntry,
451 * If it was found, construct the stream object and return a pointer to it.
453 if ( (streamEntryRef!=DIRENTRY_NULL) &&
454 (currentEntry.stgType==STGTY_STREAM) )
456 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
460 newStream->grfMode = grfMode;
461 *ppstm = (IStream*)newStream;
463 IStream_AddRef(*ppstm);
473 res = STG_E_FILENOTFOUND;
477 TRACE("<-- IStream %p\n", *ppstm);
478 TRACE("<-- %08x\n", res);
482 /************************************************************************
483 * Storage32BaseImpl_OpenStorage (IStorage)
485 * This method will open a new storage object from the current storage.
487 * See Windows documentation for more details on IStorage methods.
489 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
491 const OLECHAR* pwcsName, /* [string][unique][in] */
492 IStorage* pstgPriority, /* [unique][in] */
493 DWORD grfMode, /* [in] */
494 SNB snbExclude, /* [unique][in] */
495 DWORD reserved, /* [in] */
496 IStorage** ppstg) /* [out] */
498 StorageBaseImpl *This = (StorageBaseImpl *)iface;
499 StorageInternalImpl* newStorage;
500 DirEntry currentEntry;
501 ULONG storageEntryRef;
502 HRESULT res = STG_E_UNKNOWN;
504 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
505 iface, debugstr_w(pwcsName), pstgPriority,
506 grfMode, snbExclude, reserved, ppstg);
508 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
515 if (snbExclude != NULL)
517 res = STG_E_INVALIDPARAMETER;
521 if ( FAILED( validateSTGM(grfMode) ))
523 res = STG_E_INVALIDFLAG;
530 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
531 (grfMode & STGM_DELETEONRELEASE) ||
532 (grfMode & STGM_PRIORITY) )
534 res = STG_E_INVALIDFUNCTION;
539 * Check that we're compatible with the parent's storage mode,
540 * but only if we are not transacted
542 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
543 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
545 res = STG_E_ACCESSDENIED;
552 storageEntryRef = findElement(
553 This->ancestorStorage,
554 This->storageDirEntry,
558 if ( (storageEntryRef!=DIRENTRY_NULL) &&
559 (currentEntry.stgType==STGTY_STORAGE) )
561 newStorage = StorageInternalImpl_Construct(
562 This->ancestorStorage,
568 *ppstg = (IStorage*)newStorage;
570 StorageBaseImpl_AddRef(*ppstg);
576 res = STG_E_INSUFFICIENTMEMORY;
580 res = STG_E_FILENOTFOUND;
583 TRACE("<-- %08x\n", res);
587 /************************************************************************
588 * Storage32BaseImpl_EnumElements (IStorage)
590 * This method will create an enumerator object that can be used to
591 * retrieve information about all the elements in the storage object.
593 * See Windows documentation for more details on IStorage methods.
595 static HRESULT WINAPI StorageBaseImpl_EnumElements(
597 DWORD reserved1, /* [in] */
598 void* reserved2, /* [size_is][unique][in] */
599 DWORD reserved3, /* [in] */
600 IEnumSTATSTG** ppenum) /* [out] */
602 StorageBaseImpl *This = (StorageBaseImpl *)iface;
603 IEnumSTATSTGImpl* newEnum;
605 TRACE("(%p, %d, %p, %d, %p)\n",
606 iface, reserved1, reserved2, reserved3, ppenum);
608 if ( (This==0) || (ppenum==0))
611 newEnum = IEnumSTATSTGImpl_Construct(
612 This->ancestorStorage,
613 This->storageDirEntry);
617 *ppenum = (IEnumSTATSTG*)newEnum;
619 IEnumSTATSTG_AddRef(*ppenum);
624 return E_OUTOFMEMORY;
627 /************************************************************************
628 * Storage32BaseImpl_Stat (IStorage)
630 * This method will retrieve information about this storage object.
632 * See Windows documentation for more details on IStorage methods.
634 static HRESULT WINAPI StorageBaseImpl_Stat(
636 STATSTG* pstatstg, /* [out] */
637 DWORD grfStatFlag) /* [in] */
639 StorageBaseImpl *This = (StorageBaseImpl *)iface;
640 DirEntry currentEntry;
642 HRESULT res = STG_E_UNKNOWN;
644 TRACE("(%p, %p, %x)\n",
645 iface, pstatstg, grfStatFlag);
647 if ( (This==0) || (pstatstg==0))
653 readSuccessful = StorageImpl_ReadDirEntry(
654 This->ancestorStorage,
655 This->storageDirEntry,
660 StorageUtl_CopyDirEntryToSTATSTG(
665 pstatstg->grfMode = This->openFlags;
666 pstatstg->grfStateBits = This->stateBits;
677 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);
679 TRACE("<-- %08x\n", res);
683 /************************************************************************
684 * Storage32BaseImpl_RenameElement (IStorage)
686 * This method will rename the specified element.
688 * See Windows documentation for more details on IStorage methods.
690 static HRESULT WINAPI StorageBaseImpl_RenameElement(
692 const OLECHAR* pwcsOldName, /* [in] */
693 const OLECHAR* pwcsNewName) /* [in] */
695 StorageBaseImpl *This = (StorageBaseImpl *)iface;
696 DirEntry currentEntry;
697 ULONG currentEntryRef;
699 TRACE("(%p, %s, %s)\n",
700 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
702 currentEntryRef = findElement(This->ancestorStorage,
703 This->storageDirEntry,
707 if (currentEntryRef != DIRENTRY_NULL)
710 * There is already an element with the new name
712 return STG_E_FILEALREADYEXISTS;
716 * Search for the old element name
718 currentEntryRef = findElement(This->ancestorStorage,
719 This->storageDirEntry,
723 if (currentEntryRef != DIRENTRY_NULL)
725 /* Remove the element from its current position in the tree */
726 removeFromTree(This->ancestorStorage, This->storageDirEntry,
729 /* Change the name of the element */
730 strcpyW(currentEntry.name, pwcsNewName);
732 StorageImpl_WriteDirEntry(This->ancestorStorage, currentEntryRef,
735 /* Insert the element in a new position in the tree */
736 insertIntoTree(This->ancestorStorage, This->storageDirEntry,
742 * There is no element with the old name
744 return STG_E_FILENOTFOUND;
750 /************************************************************************
751 * Storage32BaseImpl_CreateStream (IStorage)
753 * This method will create a stream object within this storage
755 * See Windows documentation for more details on IStorage methods.
757 static HRESULT WINAPI StorageBaseImpl_CreateStream(
759 const OLECHAR* pwcsName, /* [string][in] */
760 DWORD grfMode, /* [in] */
761 DWORD reserved1, /* [in] */
762 DWORD reserved2, /* [in] */
763 IStream** ppstm) /* [out] */
765 StorageBaseImpl *This = (StorageBaseImpl *)iface;
766 StgStreamImpl* newStream;
767 DirEntry currentEntry, newStreamEntry;
768 ULONG currentEntryRef, newStreamEntryRef;
770 TRACE("(%p, %s, %x, %d, %d, %p)\n",
771 iface, debugstr_w(pwcsName), grfMode,
772 reserved1, reserved2, ppstm);
775 return STG_E_INVALIDPOINTER;
778 return STG_E_INVALIDNAME;
780 if (reserved1 || reserved2)
781 return STG_E_INVALIDPARAMETER;
783 if ( FAILED( validateSTGM(grfMode) ))
784 return STG_E_INVALIDFLAG;
786 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
787 return STG_E_INVALIDFLAG;
792 if ((grfMode & STGM_DELETEONRELEASE) ||
793 (grfMode & STGM_TRANSACTED))
794 return STG_E_INVALIDFUNCTION;
796 /* Can't create a stream on read-only storage */
797 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
798 return STG_E_ACCESSDENIED;
801 * Check that we're compatible with the parent's storage mode
802 * if not in transacted mode
804 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
805 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
806 return STG_E_ACCESSDENIED;
809 if(This->ancestorStorage->base.openFlags & STGM_SIMPLE)
810 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
814 currentEntryRef = findElement(This->ancestorStorage,
815 This->storageDirEntry,
819 if (currentEntryRef != DIRENTRY_NULL)
822 * An element with this name already exists
824 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
828 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
830 if (strm->dirEntry == currentEntryRef)
832 TRACE("Stream deleted %p\n", strm);
833 strm->parentStorage = NULL;
834 list_remove(&strm->StrmListEntry);
837 IStorage_DestroyElement(iface, pwcsName);
840 return STG_E_FILEALREADYEXISTS;
842 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
844 WARN("read-only storage\n");
845 return STG_E_ACCESSDENIED;
849 * memset the empty entry
851 memset(&newStreamEntry, 0, sizeof(DirEntry));
853 newStreamEntry.sizeOfNameString =
854 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
856 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
857 return STG_E_INVALIDNAME;
859 strcpyW(newStreamEntry.name, pwcsName);
861 newStreamEntry.stgType = STGTY_STREAM;
862 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
863 newStreamEntry.size.u.LowPart = 0;
864 newStreamEntry.size.u.HighPart = 0;
866 newStreamEntry.leftChild = DIRENTRY_NULL;
867 newStreamEntry.rightChild = DIRENTRY_NULL;
868 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
870 /* call CoFileTime to get the current time
875 /* newStreamEntry.clsid */
878 * Create an entry with the new data
880 createDirEntry(This->ancestorStorage, &newStreamEntry, &newStreamEntryRef);
883 * Insert the new entry in the parent storage's tree.
886 This->ancestorStorage,
887 This->storageDirEntry,
891 * Open the stream to return it.
893 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
897 *ppstm = (IStream*)newStream;
899 IStream_AddRef(*ppstm);
903 return STG_E_INSUFFICIENTMEMORY;
909 /************************************************************************
910 * Storage32BaseImpl_SetClass (IStorage)
912 * This method will write the specified CLSID in the directory entry of this
915 * See Windows documentation for more details on IStorage methods.
917 static HRESULT WINAPI StorageBaseImpl_SetClass(
919 REFCLSID clsid) /* [in] */
921 StorageBaseImpl *This = (StorageBaseImpl *)iface;
922 HRESULT hRes = E_FAIL;
923 DirEntry currentEntry;
926 TRACE("(%p, %p)\n", iface, clsid);
928 success = StorageImpl_ReadDirEntry(This->ancestorStorage,
929 This->storageDirEntry,
933 currentEntry.clsid = *clsid;
935 success = StorageImpl_WriteDirEntry(This->ancestorStorage,
936 This->storageDirEntry,
945 /************************************************************************
946 ** Storage32Impl implementation
949 /************************************************************************
950 * Storage32BaseImpl_CreateStorage (IStorage)
952 * This method will create the storage object within the provided storage.
954 * See Windows documentation for more details on IStorage methods.
956 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
958 const OLECHAR *pwcsName, /* [string][in] */
959 DWORD grfMode, /* [in] */
960 DWORD reserved1, /* [in] */
961 DWORD reserved2, /* [in] */
962 IStorage **ppstg) /* [out] */
964 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
966 DirEntry currentEntry;
968 ULONG currentEntryRef;
972 TRACE("(%p, %s, %x, %d, %d, %p)\n",
973 iface, debugstr_w(pwcsName), grfMode,
974 reserved1, reserved2, ppstg);
977 return STG_E_INVALIDPOINTER;
980 return STG_E_INVALIDNAME;
984 if ( FAILED( validateSTGM(grfMode) ) ||
985 (grfMode & STGM_DELETEONRELEASE) )
987 WARN("bad grfMode: 0x%x\n", grfMode);
988 return STG_E_INVALIDFLAG;
992 * Check that we're compatible with the parent's storage mode
994 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
996 WARN("access denied\n");
997 return STG_E_ACCESSDENIED;
1000 currentEntryRef = findElement(This->ancestorStorage,
1001 This->storageDirEntry,
1005 if (currentEntryRef != DIRENTRY_NULL)
1008 * An element with this name already exists
1010 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1011 STGM_ACCESS_MODE(This->openFlags) != STGM_READ)
1013 hr = IStorage_DestroyElement(iface, pwcsName);
1019 WARN("file already exists\n");
1020 return STG_E_FILEALREADYEXISTS;
1023 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1025 WARN("read-only storage\n");
1026 return STG_E_ACCESSDENIED;
1029 memset(&newEntry, 0, sizeof(DirEntry));
1031 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1033 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1035 FIXME("name too long\n");
1036 return STG_E_INVALIDNAME;
1039 strcpyW(newEntry.name, pwcsName);
1041 newEntry.stgType = STGTY_STORAGE;
1042 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1043 newEntry.size.u.LowPart = 0;
1044 newEntry.size.u.HighPart = 0;
1046 newEntry.leftChild = DIRENTRY_NULL;
1047 newEntry.rightChild = DIRENTRY_NULL;
1048 newEntry.dirRootEntry = DIRENTRY_NULL;
1050 /* call CoFileTime to get the current time
1055 /* newEntry.clsid */
1058 * Create a new directory entry for the storage
1060 createDirEntry(This->ancestorStorage, &newEntry, &newEntryRef);
1063 * Insert the new directory entry into the parent storage's tree
1066 This->ancestorStorage,
1067 This->storageDirEntry,
1071 * Open it to get a pointer to return.
1073 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1075 if( (hr != S_OK) || (*ppstg == NULL))
1085 /***************************************************************************
1089 * Reserve a directory entry in the file and initialize it.
1091 static HRESULT createDirEntry(
1092 StorageImpl *storage,
1093 const DirEntry *newData,
1096 ULONG currentEntryIndex = 0;
1097 ULONG newEntryIndex = DIRENTRY_NULL;
1099 BYTE currentData[RAW_DIRENTRY_SIZE];
1100 WORD sizeOfNameString;
1104 hr = StorageImpl_ReadRawDirEntry(storage,
1110 StorageUtl_ReadWord(
1112 OFFSET_PS_NAMELENGTH,
1115 if (sizeOfNameString == 0)
1118 * The entry exists and is available, we found it.
1120 newEntryIndex = currentEntryIndex;
1126 * We exhausted the directory entries, we will create more space below
1128 newEntryIndex = currentEntryIndex;
1130 currentEntryIndex++;
1132 } while (newEntryIndex == DIRENTRY_NULL);
1135 * grow the directory stream
1139 BYTE emptyData[RAW_DIRENTRY_SIZE];
1140 ULARGE_INTEGER newSize;
1142 ULONG lastEntry = 0;
1143 ULONG blockCount = 0;
1146 * obtain the new count of blocks in the directory stream
1148 blockCount = BlockChainStream_GetCount(
1149 storage->rootBlockChain)+1;
1152 * initialize the size used by the directory stream
1154 newSize.u.HighPart = 0;
1155 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1158 * add a block to the directory stream
1160 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1163 * memset the empty entry in order to initialize the unused newly
1166 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1171 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1174 entryIndex = newEntryIndex + 1;
1175 entryIndex < lastEntry;
1178 StorageImpl_WriteRawDirEntry(
1185 UpdateRawDirEntry(currentData, newData);
1187 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1190 *index = newEntryIndex;
1195 /***************************************************************************
1199 * Mark a directory entry in the file as free.
1201 static HRESULT destroyDirEntry(
1202 StorageImpl *storage,
1206 BYTE emptyData[RAW_DIRENTRY_SIZE];
1208 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1210 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1216 /****************************************************************************
1220 * Case insensitive comparison of DirEntry.name by first considering
1223 * Returns <0 when name1 < name2
1224 * >0 when name1 > name2
1225 * 0 when name1 == name2
1227 static LONG entryNameCmp(
1228 const OLECHAR *name1,
1229 const OLECHAR *name2)
1231 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1236 * We compare the string themselves only when they are of the same length
1238 diff = lstrcmpiW( name1, name2);
1244 /****************************************************************************
1248 * Add a directory entry to a storage
1250 static HRESULT insertIntoTree(
1252 ULONG parentStorageIndex,
1253 ULONG newEntryIndex)
1255 DirEntry currentEntry;
1259 * Read the inserted entry
1261 StorageImpl_ReadDirEntry(This,
1266 * Read the storage entry
1268 StorageImpl_ReadDirEntry(This,
1272 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1275 * The root storage contains some element, therefore, start the research
1276 * for the appropriate location.
1279 ULONG current, next, previous, currentEntryId;
1282 * Keep a reference to the root of the storage's element tree
1284 currentEntryId = currentEntry.dirRootEntry;
1289 StorageImpl_ReadDirEntry(This,
1290 currentEntry.dirRootEntry,
1293 previous = currentEntry.leftChild;
1294 next = currentEntry.rightChild;
1295 current = currentEntryId;
1299 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1303 if (previous != DIRENTRY_NULL)
1305 StorageImpl_ReadDirEntry(This,
1312 currentEntry.leftChild = newEntryIndex;
1313 StorageImpl_WriteDirEntry(This,
1321 if (next != DIRENTRY_NULL)
1323 StorageImpl_ReadDirEntry(This,
1330 currentEntry.rightChild = newEntryIndex;
1331 StorageImpl_WriteDirEntry(This,
1340 * Trying to insert an item with the same name in the
1341 * subtree structure.
1343 return STG_E_FILEALREADYEXISTS;
1346 previous = currentEntry.leftChild;
1347 next = currentEntry.rightChild;
1353 * The storage is empty, make the new entry the root of its element tree
1355 currentEntry.dirRootEntry = newEntryIndex;
1356 StorageImpl_WriteDirEntry(This,
1364 /****************************************************************************
1368 * Find and read the element of a storage with the given name.
1370 static ULONG findElement(StorageImpl *storage, ULONG storageEntry,
1371 const OLECHAR *name, DirEntry *data)
1375 /* Read the storage entry to find the root of the tree. */
1376 StorageImpl_ReadDirEntry(storage, storageEntry, data);
1378 currentEntry = data->dirRootEntry;
1380 while (currentEntry != DIRENTRY_NULL)
1384 StorageImpl_ReadDirEntry(storage, currentEntry, data);
1386 cmp = entryNameCmp(name, data->name);
1393 currentEntry = data->leftChild;
1396 currentEntry = data->rightChild;
1399 return currentEntry;
1402 /****************************************************************************
1406 * Find and read the binary tree parent of the element with the given name.
1408 * If there is no such element, find a place where it could be inserted and
1409 * return STG_E_FILENOTFOUND.
1411 static HRESULT findTreeParent(StorageImpl *storage, ULONG storageEntry,
1412 const OLECHAR *childName, DirEntry *parentData, ULONG *parentEntry,
1418 /* Read the storage entry to find the root of the tree. */
1419 StorageImpl_ReadDirEntry(storage, storageEntry, parentData);
1421 *parentEntry = storageEntry;
1422 *relation = DIRENTRY_RELATION_DIR;
1424 childEntry = parentData->dirRootEntry;
1426 while (childEntry != DIRENTRY_NULL)
1430 StorageImpl_ReadDirEntry(storage, childEntry, &childData);
1432 cmp = entryNameCmp(childName, childData.name);
1440 *parentData = childData;
1441 *parentEntry = childEntry;
1442 *relation = DIRENTRY_RELATION_PREVIOUS;
1444 childEntry = parentData->leftChild;
1449 *parentData = childData;
1450 *parentEntry = childEntry;
1451 *relation = DIRENTRY_RELATION_NEXT;
1453 childEntry = parentData->rightChild;
1457 if (childEntry == DIRENTRY_NULL)
1458 return STG_E_FILENOTFOUND;
1464 /*************************************************************************
1467 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1469 DWORD ciidExclude, /* [in] */
1470 const IID* rgiidExclude, /* [size_is][unique][in] */
1471 SNB snbExclude, /* [unique][in] */
1472 IStorage* pstgDest) /* [unique][in] */
1474 IEnumSTATSTG *elements = 0;
1475 STATSTG curElement, strStat;
1477 IStorage *pstgTmp, *pstgChild;
1478 IStream *pstrTmp, *pstrChild;
1479 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1482 TRACE("(%p, %d, %p, %p, %p)\n",
1483 iface, ciidExclude, rgiidExclude,
1484 snbExclude, pstgDest);
1486 if ( pstgDest == 0 )
1487 return STG_E_INVALIDPOINTER;
1490 * Enumerate the elements
1492 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1500 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1501 IStorage_SetClass( pstgDest, &curElement.clsid );
1503 for(i = 0; i < ciidExclude; ++i)
1505 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1506 skip_storage = TRUE;
1507 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1510 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1516 * Obtain the next element
1518 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1520 if ( hr == S_FALSE )
1522 hr = S_OK; /* done, every element has been copied */
1528 WCHAR **snb = snbExclude;
1530 while ( *snb != NULL && !skip )
1532 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1541 if (curElement.type == STGTY_STORAGE)
1547 * open child source storage
1549 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1550 STGM_READ|STGM_SHARE_EXCLUSIVE,
1551 NULL, 0, &pstgChild );
1557 * Check if destination storage is not a child of the source
1558 * storage, which will cause an infinite loop
1560 if (pstgChild == pstgDest)
1562 IEnumSTATSTG_Release(elements);
1564 return STG_E_ACCESSDENIED;
1568 * create a new storage in destination storage
1570 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1571 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1575 * if it already exist, don't create a new one use this one
1577 if (hr == STG_E_FILEALREADYEXISTS)
1579 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1580 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1581 NULL, 0, &pstgTmp );
1589 * do the copy recursively
1591 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1594 IStorage_Release( pstgTmp );
1595 IStorage_Release( pstgChild );
1597 else if (curElement.type == STGTY_STREAM)
1603 * create a new stream in destination storage. If the stream already
1604 * exist, it will be deleted and a new one will be created.
1606 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1607 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1614 * open child stream storage
1616 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1617 STGM_READ|STGM_SHARE_EXCLUSIVE,
1624 * Get the size of the source stream
1626 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1629 * Set the size of the destination stream.
1631 IStream_SetSize(pstrTmp, strStat.cbSize);
1636 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1639 IStream_Release( pstrTmp );
1640 IStream_Release( pstrChild );
1644 WARN("unknown element type: %d\n", curElement.type);
1647 } while (hr == S_OK);
1652 IEnumSTATSTG_Release(elements);
1657 /*************************************************************************
1658 * MoveElementTo (IStorage)
1660 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1662 const OLECHAR *pwcsName, /* [string][in] */
1663 IStorage *pstgDest, /* [unique][in] */
1664 const OLECHAR *pwcsNewName,/* [string][in] */
1665 DWORD grfFlags) /* [in] */
1667 FIXME("(%p %s %p %s %u): stub\n", iface,
1668 debugstr_w(pwcsName), pstgDest,
1669 debugstr_w(pwcsNewName), grfFlags);
1673 /*************************************************************************
1676 * Ensures that any changes made to a storage object open in transacted mode
1677 * are reflected in the parent storage
1680 * Wine doesn't implement transacted mode, which seems to be a basic
1681 * optimization, so we can ignore this stub for now.
1683 static HRESULT WINAPI StorageImpl_Commit(
1685 DWORD grfCommitFlags)/* [in] */
1687 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1691 /*************************************************************************
1694 * Discard all changes that have been made since the last commit operation
1696 static HRESULT WINAPI StorageImpl_Revert(
1699 FIXME("(%p): stub\n", iface);
1703 /*************************************************************************
1704 * DestroyElement (IStorage)
1706 * Strategy: This implementation is built this way for simplicity not for speed.
1707 * I always delete the topmost element of the enumeration and adjust
1708 * the deleted element pointer all the time. This takes longer to
1709 * do but allow to reinvoke DestroyElement whenever we encounter a
1710 * storage object. The optimisation resides in the usage of another
1711 * enumeration strategy that would give all the leaves of a storage
1712 * first. (postfix order)
1714 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1716 const OLECHAR *pwcsName)/* [string][in] */
1718 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1721 DirEntry entryToDelete;
1722 ULONG entryToDeleteRef;
1725 iface, debugstr_w(pwcsName));
1728 return STG_E_INVALIDPOINTER;
1730 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1731 return STG_E_ACCESSDENIED;
1733 entryToDeleteRef = findElement(
1734 This->ancestorStorage,
1735 This->storageDirEntry,
1739 if ( entryToDeleteRef == DIRENTRY_NULL )
1741 return STG_E_FILENOTFOUND;
1744 if ( entryToDelete.stgType == STGTY_STORAGE )
1746 hr = deleteStorageContents(
1751 else if ( entryToDelete.stgType == STGTY_STREAM )
1753 hr = deleteStreamContents(
1763 * Remove the entry from its parent storage
1765 hr = removeFromTree(
1766 This->ancestorStorage,
1767 This->storageDirEntry,
1771 * Invalidate the entry
1774 destroyDirEntry(This->ancestorStorage,
1781 /************************************************************************
1782 * StorageImpl_Stat (IStorage)
1784 * This method will retrieve information about this storage object.
1786 * See Windows documentation for more details on IStorage methods.
1788 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1789 STATSTG* pstatstg, /* [out] */
1790 DWORD grfStatFlag) /* [in] */
1792 StorageImpl* const This = (StorageImpl*)iface;
1793 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1795 if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1797 CoTaskMemFree(pstatstg->pwcsName);
1798 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1799 strcpyW(pstatstg->pwcsName, This->pwcsName);
1805 /******************************************************************************
1806 * Internal stream list handlers
1809 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1811 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1812 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1815 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1817 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1818 list_remove(&(strm->StrmListEntry));
1821 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1823 struct list *cur, *cur2;
1824 StgStreamImpl *strm=NULL;
1826 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1827 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1828 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1829 strm->parentStorage = NULL;
1835 /*********************************************************************
1839 * Delete the contents of a storage entry.
1842 static HRESULT deleteStorageContents(
1843 StorageBaseImpl *parentStorage,
1844 ULONG indexToDelete,
1845 DirEntry entryDataToDelete)
1847 IEnumSTATSTG *elements = 0;
1848 IStorage *childStorage = 0;
1849 STATSTG currentElement;
1851 HRESULT destroyHr = S_OK;
1854 * Open the storage and enumerate it
1856 hr = StorageBaseImpl_OpenStorage(
1857 (IStorage*)parentStorage,
1858 entryDataToDelete.name,
1860 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1871 * Enumerate the elements
1873 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1878 * Obtain the next element
1880 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1883 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
1885 CoTaskMemFree(currentElement.pwcsName);
1889 * We need to Reset the enumeration every time because we delete elements
1890 * and the enumeration could be invalid
1892 IEnumSTATSTG_Reset(elements);
1894 } while ((hr == S_OK) && (destroyHr == S_OK));
1896 IStorage_Release(childStorage);
1897 IEnumSTATSTG_Release(elements);
1902 /*********************************************************************
1906 * Perform the deletion of a stream's data
1909 static HRESULT deleteStreamContents(
1910 StorageBaseImpl *parentStorage,
1911 ULONG indexToDelete,
1912 DirEntry entryDataToDelete)
1916 ULARGE_INTEGER size;
1918 size.u.HighPart = 0;
1921 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
1922 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
1932 hr = IStream_SetSize(pis, size);
1940 * Release the stream object.
1942 IStream_Release(pis);
1947 static void setEntryLink(DirEntry *entry, ULONG relation, ULONG new_target)
1951 case DIRENTRY_RELATION_PREVIOUS:
1952 entry->leftChild = new_target;
1954 case DIRENTRY_RELATION_NEXT:
1955 entry->rightChild = new_target;
1957 case DIRENTRY_RELATION_DIR:
1958 entry->dirRootEntry = new_target;
1965 /*************************************************************************
1969 * This method removes a directory entry from its parent storage tree without
1970 * freeing any resources attached to it.
1972 static HRESULT removeFromTree(
1974 ULONG parentStorageIndex,
1979 DirEntry entryToDelete;
1980 DirEntry parentEntry;
1981 ULONG parentEntryRef;
1982 ULONG typeOfRelation;
1984 res = StorageImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
1987 * Find the element that links to the one we want to delete.
1989 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
1990 &parentEntry, &parentEntryRef, &typeOfRelation);
1995 if (entryToDelete.leftChild != DIRENTRY_NULL)
1998 * Replace the deleted entry with its left child
2000 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2002 res = StorageImpl_WriteDirEntry(
2011 if (entryToDelete.rightChild != DIRENTRY_NULL)
2014 * We need to reinsert the right child somewhere. We already know it and
2015 * its children are greater than everything in the left tree, so we
2016 * insert it at the rightmost point in the left tree.
2018 ULONG newRightChildParent = entryToDelete.leftChild;
2019 DirEntry newRightChildParentEntry;
2023 res = StorageImpl_ReadDirEntry(
2025 newRightChildParent,
2026 &newRightChildParentEntry);
2032 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2033 newRightChildParent = newRightChildParentEntry.rightChild;
2034 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2036 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2038 res = StorageImpl_WriteDirEntry(
2040 newRightChildParent,
2041 &newRightChildParentEntry);
2051 * Replace the deleted entry with its right child
2053 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2055 res = StorageImpl_WriteDirEntry(
2069 /******************************************************************************
2070 * SetElementTimes (IStorage)
2072 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2074 const OLECHAR *pwcsName,/* [string][in] */
2075 const FILETIME *pctime, /* [in] */
2076 const FILETIME *patime, /* [in] */
2077 const FILETIME *pmtime) /* [in] */
2079 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2083 /******************************************************************************
2084 * SetStateBits (IStorage)
2086 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2088 DWORD grfStateBits,/* [in] */
2089 DWORD grfMask) /* [in] */
2091 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2092 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2097 * Virtual function table for the IStorage32Impl class.
2099 static const IStorageVtbl Storage32Impl_Vtbl =
2101 StorageBaseImpl_QueryInterface,
2102 StorageBaseImpl_AddRef,
2103 StorageBaseImpl_Release,
2104 StorageBaseImpl_CreateStream,
2105 StorageBaseImpl_OpenStream,
2106 StorageBaseImpl_CreateStorage,
2107 StorageBaseImpl_OpenStorage,
2108 StorageBaseImpl_CopyTo,
2109 StorageBaseImpl_MoveElementTo,
2112 StorageBaseImpl_EnumElements,
2113 StorageBaseImpl_DestroyElement,
2114 StorageBaseImpl_RenameElement,
2115 StorageBaseImpl_SetElementTimes,
2116 StorageBaseImpl_SetClass,
2117 StorageBaseImpl_SetStateBits,
2121 static HRESULT StorageImpl_Construct(
2131 DirEntry currentEntry;
2132 BOOL readSuccessful;
2133 ULONG currentEntryRef;
2135 if ( FAILED( validateSTGM(openFlags) ))
2136 return STG_E_INVALIDFLAG;
2138 memset(This, 0, sizeof(StorageImpl));
2140 list_init(&This->base.strmHead);
2142 This->base.lpVtbl = &Storage32Impl_Vtbl;
2143 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2144 This->base.v_destructor = StorageImpl_Destroy;
2145 This->base.openFlags = (openFlags & ~STGM_CREATE);
2146 This->create = create;
2149 * This is the top-level storage so initialize the ancestor pointer
2152 This->base.ancestorStorage = This;
2154 This->hFile = hFile;
2157 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2158 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2159 if (!This->pwcsName)
2160 return STG_E_INSUFFICIENTMEMORY;
2161 strcpyW(This->pwcsName, pwcsName);
2165 * Initialize the big block cache.
2167 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2168 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2169 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2175 if (This->bigBlockFile == 0)
2180 ULARGE_INTEGER size;
2181 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2184 * Initialize all header variables:
2185 * - The big block depot consists of one block and it is at block 0
2186 * - The directory table starts at block 1
2187 * - There is no small block depot
2189 memset( This->bigBlockDepotStart,
2191 sizeof(This->bigBlockDepotStart));
2193 This->bigBlockDepotCount = 1;
2194 This->bigBlockDepotStart[0] = 0;
2195 This->rootStartBlock = 1;
2196 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2197 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2198 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2199 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2200 This->extBigBlockDepotCount = 0;
2202 StorageImpl_SaveFileHeader(This);
2205 * Add one block for the big block depot and one block for the directory table
2207 size.u.HighPart = 0;
2208 size.u.LowPart = This->bigBlockSize * 3;
2209 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2212 * Initialize the big block depot
2214 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2215 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2216 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2217 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2222 * Load the header for the file.
2224 hr = StorageImpl_LoadFileHeader(This);
2228 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2235 * There is no block depot cached yet.
2237 This->indexBlockDepotCached = 0xFFFFFFFF;
2240 * Start searching for free blocks with block 0.
2242 This->prevFreeBlock = 0;
2245 * Create the block chain abstractions.
2247 if(!(This->rootBlockChain =
2248 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2249 return STG_E_READFAULT;
2251 if(!(This->smallBlockDepotChain =
2252 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2254 return STG_E_READFAULT;
2257 * Write the root storage entry (memory only)
2263 * Initialize the directory table
2265 memset(&rootEntry, 0, sizeof(rootEntry));
2266 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2267 sizeof(rootEntry.name)/sizeof(WCHAR) );
2268 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2269 rootEntry.stgType = STGTY_ROOT;
2270 rootEntry.leftChild = DIRENTRY_NULL;
2271 rootEntry.rightChild = DIRENTRY_NULL;
2272 rootEntry.dirRootEntry = DIRENTRY_NULL;
2273 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2274 rootEntry.size.u.HighPart = 0;
2275 rootEntry.size.u.LowPart = 0;
2277 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2281 * Find the ID of the root storage.
2283 currentEntryRef = 0;
2287 readSuccessful = StorageImpl_ReadDirEntry(
2294 if ( (currentEntry.sizeOfNameString != 0 ) &&
2295 (currentEntry.stgType == STGTY_ROOT) )
2297 This->base.storageDirEntry = currentEntryRef;
2303 } while (readSuccessful && (This->base.storageDirEntry == DIRENTRY_NULL) );
2305 if (!readSuccessful)
2308 return STG_E_READFAULT;
2312 * Create the block chain abstraction for the small block root chain.
2314 if(!(This->smallBlockRootChain =
2315 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2316 return STG_E_READFAULT;
2321 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2323 StorageImpl *This = (StorageImpl*) iface;
2324 TRACE("(%p)\n", This);
2326 StorageBaseImpl_DeleteAll(&This->base);
2328 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2330 BlockChainStream_Destroy(This->smallBlockRootChain);
2331 BlockChainStream_Destroy(This->rootBlockChain);
2332 BlockChainStream_Destroy(This->smallBlockDepotChain);
2334 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2335 HeapFree(GetProcessHeap(), 0, This);
2338 /******************************************************************************
2339 * Storage32Impl_GetNextFreeBigBlock
2341 * Returns the index of the next free big block.
2342 * If the big block depot is filled, this method will enlarge it.
2345 static ULONG StorageImpl_GetNextFreeBigBlock(
2348 ULONG depotBlockIndexPos;
2349 BYTE depotBuffer[BIG_BLOCK_SIZE];
2351 ULONG depotBlockOffset;
2352 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2353 ULONG nextBlockIndex = BLOCK_SPECIAL;
2355 ULONG freeBlock = BLOCK_UNUSED;
2357 depotIndex = This->prevFreeBlock / blocksPerDepot;
2358 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2361 * Scan the entire big block depot until we find a block marked free
2363 while (nextBlockIndex != BLOCK_UNUSED)
2365 if (depotIndex < COUNT_BBDEPOTINHEADER)
2367 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2370 * Grow the primary depot.
2372 if (depotBlockIndexPos == BLOCK_UNUSED)
2374 depotBlockIndexPos = depotIndex*blocksPerDepot;
2377 * Add a block depot.
2379 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2380 This->bigBlockDepotCount++;
2381 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2384 * Flag it as a block depot.
2386 StorageImpl_SetNextBlockInChain(This,
2390 /* Save new header information.
2392 StorageImpl_SaveFileHeader(This);
2397 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2399 if (depotBlockIndexPos == BLOCK_UNUSED)
2402 * Grow the extended depot.
2404 ULONG extIndex = BLOCK_UNUSED;
2405 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2406 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2408 if (extBlockOffset == 0)
2410 /* We need an extended block.
2412 extIndex = Storage32Impl_AddExtBlockDepot(This);
2413 This->extBigBlockDepotCount++;
2414 depotBlockIndexPos = extIndex + 1;
2417 depotBlockIndexPos = depotIndex * blocksPerDepot;
2420 * Add a block depot and mark it in the extended block.
2422 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2423 This->bigBlockDepotCount++;
2424 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2426 /* Flag the block depot.
2428 StorageImpl_SetNextBlockInChain(This,
2432 /* If necessary, flag the extended depot block.
2434 if (extIndex != BLOCK_UNUSED)
2435 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2437 /* Save header information.
2439 StorageImpl_SaveFileHeader(This);
2443 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2447 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2448 ( nextBlockIndex != BLOCK_UNUSED))
2450 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2452 if (nextBlockIndex == BLOCK_UNUSED)
2454 freeBlock = (depotIndex * blocksPerDepot) +
2455 (depotBlockOffset/sizeof(ULONG));
2458 depotBlockOffset += sizeof(ULONG);
2463 depotBlockOffset = 0;
2467 * make sure that the block physically exists before using it
2469 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2471 This->prevFreeBlock = freeBlock;
2476 /******************************************************************************
2477 * Storage32Impl_AddBlockDepot
2479 * This will create a depot block, essentially it is a block initialized
2482 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2484 BYTE blockBuffer[BIG_BLOCK_SIZE];
2487 * Initialize blocks as free
2489 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2490 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2493 /******************************************************************************
2494 * Storage32Impl_GetExtDepotBlock
2496 * Returns the index of the block that corresponds to the specified depot
2497 * index. This method is only for depot indexes equal or greater than
2498 * COUNT_BBDEPOTINHEADER.
2500 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2502 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2503 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2504 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2505 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2506 ULONG blockIndex = BLOCK_UNUSED;
2507 ULONG extBlockIndex = This->extBigBlockDepotStart;
2509 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2511 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2512 return BLOCK_UNUSED;
2514 while (extBlockCount > 0)
2516 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2520 if (extBlockIndex != BLOCK_UNUSED)
2521 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2522 extBlockOffset * sizeof(ULONG), &blockIndex);
2527 /******************************************************************************
2528 * Storage32Impl_SetExtDepotBlock
2530 * Associates the specified block index to the specified depot index.
2531 * This method is only for depot indexes equal or greater than
2532 * COUNT_BBDEPOTINHEADER.
2534 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2536 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2537 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2538 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2539 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2540 ULONG extBlockIndex = This->extBigBlockDepotStart;
2542 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2544 while (extBlockCount > 0)
2546 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2550 if (extBlockIndex != BLOCK_UNUSED)
2552 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2553 extBlockOffset * sizeof(ULONG),
2558 /******************************************************************************
2559 * Storage32Impl_AddExtBlockDepot
2561 * Creates an extended depot block.
2563 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2565 ULONG numExtBlocks = This->extBigBlockDepotCount;
2566 ULONG nextExtBlock = This->extBigBlockDepotStart;
2567 BYTE depotBuffer[BIG_BLOCK_SIZE];
2568 ULONG index = BLOCK_UNUSED;
2569 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2570 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2571 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2573 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2574 blocksPerDepotBlock;
2576 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2579 * The first extended block.
2581 This->extBigBlockDepotStart = index;
2587 * Follow the chain to the last one.
2589 for (i = 0; i < (numExtBlocks - 1); i++)
2591 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2595 * Add the new extended block to the chain.
2597 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2602 * Initialize this block.
2604 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2605 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2610 /******************************************************************************
2611 * Storage32Impl_FreeBigBlock
2613 * This method will flag the specified block as free in the big block depot.
2615 static void StorageImpl_FreeBigBlock(
2619 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2621 if (blockIndex < This->prevFreeBlock)
2622 This->prevFreeBlock = blockIndex;
2625 /************************************************************************
2626 * Storage32Impl_GetNextBlockInChain
2628 * This method will retrieve the block index of the next big block in
2631 * Params: This - Pointer to the Storage object.
2632 * blockIndex - Index of the block to retrieve the chain
2634 * nextBlockIndex - receives the return value.
2636 * Returns: This method returns the index of the next block in the chain.
2637 * It will return the constants:
2638 * BLOCK_SPECIAL - If the block given was not part of a
2640 * BLOCK_END_OF_CHAIN - If the block given was the last in
2642 * BLOCK_UNUSED - If the block given was not past of a chain
2644 * BLOCK_EXTBBDEPOT - This block is part of the extended
2647 * See Windows documentation for more details on IStorage methods.
2649 static HRESULT StorageImpl_GetNextBlockInChain(
2652 ULONG* nextBlockIndex)
2654 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2655 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2656 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2657 BYTE depotBuffer[BIG_BLOCK_SIZE];
2659 ULONG depotBlockIndexPos;
2662 *nextBlockIndex = BLOCK_SPECIAL;
2664 if(depotBlockCount >= This->bigBlockDepotCount)
2666 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2667 This->bigBlockDepotCount);
2668 return STG_E_READFAULT;
2672 * Cache the currently accessed depot block.
2674 if (depotBlockCount != This->indexBlockDepotCached)
2676 This->indexBlockDepotCached = depotBlockCount;
2678 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2680 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2685 * We have to look in the extended depot.
2687 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2690 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2693 return STG_E_READFAULT;
2695 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2697 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2698 This->blockDepotCached[index] = *nextBlockIndex;
2702 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2707 /******************************************************************************
2708 * Storage32Impl_GetNextExtendedBlock
2710 * Given an extended block this method will return the next extended block.
2713 * The last ULONG of an extended block is the block index of the next
2714 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2718 * - The index of the next extended block
2719 * - BLOCK_UNUSED: there is no next extended block.
2720 * - Any other return values denotes failure.
2722 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2724 ULONG nextBlockIndex = BLOCK_SPECIAL;
2725 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2727 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
2730 return nextBlockIndex;
2733 /******************************************************************************
2734 * Storage32Impl_SetNextBlockInChain
2736 * This method will write the index of the specified block's next block
2737 * in the big block depot.
2739 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2742 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2743 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2744 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2747 static void StorageImpl_SetNextBlockInChain(
2752 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2753 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2754 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2755 ULONG depotBlockIndexPos;
2757 assert(depotBlockCount < This->bigBlockDepotCount);
2758 assert(blockIndex != nextBlock);
2760 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2762 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2767 * We have to look in the extended depot.
2769 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2772 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
2775 * Update the cached block depot, if necessary.
2777 if (depotBlockCount == This->indexBlockDepotCached)
2779 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2783 /******************************************************************************
2784 * Storage32Impl_LoadFileHeader
2786 * This method will read in the file header, i.e. big block index -1.
2788 static HRESULT StorageImpl_LoadFileHeader(
2791 HRESULT hr = STG_E_FILENOTFOUND;
2792 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2798 * Get a pointer to the big block of data containing the header.
2800 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2803 * Extract the information from the header.
2808 * Check for the "magic number" signature and return an error if it is not
2811 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2813 return STG_E_OLDFORMAT;
2816 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2818 return STG_E_INVALIDHEADER;
2821 StorageUtl_ReadWord(
2823 OFFSET_BIGBLOCKSIZEBITS,
2824 &This->bigBlockSizeBits);
2826 StorageUtl_ReadWord(
2828 OFFSET_SMALLBLOCKSIZEBITS,
2829 &This->smallBlockSizeBits);
2831 StorageUtl_ReadDWord(
2833 OFFSET_BBDEPOTCOUNT,
2834 &This->bigBlockDepotCount);
2836 StorageUtl_ReadDWord(
2838 OFFSET_ROOTSTARTBLOCK,
2839 &This->rootStartBlock);
2841 StorageUtl_ReadDWord(
2843 OFFSET_SBDEPOTSTART,
2844 &This->smallBlockDepotStart);
2846 StorageUtl_ReadDWord(
2848 OFFSET_EXTBBDEPOTSTART,
2849 &This->extBigBlockDepotStart);
2851 StorageUtl_ReadDWord(
2853 OFFSET_EXTBBDEPOTCOUNT,
2854 &This->extBigBlockDepotCount);
2856 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2858 StorageUtl_ReadDWord(
2860 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2861 &(This->bigBlockDepotStart[index]));
2865 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2867 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2868 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2871 * Right now, the code is making some assumptions about the size of the
2872 * blocks, just make sure they are what we're expecting.
2874 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2875 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2877 WARN("Broken OLE storage file\n");
2878 hr = STG_E_INVALIDHEADER;
2887 /******************************************************************************
2888 * Storage32Impl_SaveFileHeader
2890 * This method will save to the file the header, i.e. big block -1.
2892 static void StorageImpl_SaveFileHeader(
2895 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2900 * Get a pointer to the big block of data containing the header.
2902 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2905 * If the block read failed, the file is probably new.
2910 * Initialize for all unknown fields.
2912 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2915 * Initialize the magic number.
2917 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2920 * And a bunch of things we don't know what they mean
2922 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2923 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2924 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2925 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2929 * Write the information to the header.
2931 StorageUtl_WriteWord(
2933 OFFSET_BIGBLOCKSIZEBITS,
2934 This->bigBlockSizeBits);
2936 StorageUtl_WriteWord(
2938 OFFSET_SMALLBLOCKSIZEBITS,
2939 This->smallBlockSizeBits);
2941 StorageUtl_WriteDWord(
2943 OFFSET_BBDEPOTCOUNT,
2944 This->bigBlockDepotCount);
2946 StorageUtl_WriteDWord(
2948 OFFSET_ROOTSTARTBLOCK,
2949 This->rootStartBlock);
2951 StorageUtl_WriteDWord(
2953 OFFSET_SBDEPOTSTART,
2954 This->smallBlockDepotStart);
2956 StorageUtl_WriteDWord(
2958 OFFSET_SBDEPOTCOUNT,
2959 This->smallBlockDepotChain ?
2960 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
2962 StorageUtl_WriteDWord(
2964 OFFSET_EXTBBDEPOTSTART,
2965 This->extBigBlockDepotStart);
2967 StorageUtl_WriteDWord(
2969 OFFSET_EXTBBDEPOTCOUNT,
2970 This->extBigBlockDepotCount);
2972 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2974 StorageUtl_WriteDWord(
2976 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2977 (This->bigBlockDepotStart[index]));
2981 * Write the big block back to the file.
2983 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2986 /******************************************************************************
2987 * StorageImpl_ReadRawDirEntry
2989 * This method will read the raw data from a directory entry in the file.
2991 * buffer must be RAW_DIRENTRY_SIZE bytes long.
2993 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
2995 ULARGE_INTEGER offset;
2999 offset.u.HighPart = 0;
3000 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3002 hr = BlockChainStream_ReadAt(
3003 This->rootBlockChain,
3012 /******************************************************************************
3013 * StorageImpl_WriteRawDirEntry
3015 * This method will write the raw data from a directory entry in the file.
3017 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3019 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3021 ULARGE_INTEGER offset;
3025 offset.u.HighPart = 0;
3026 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3028 hr = BlockChainStream_WriteAt(
3029 This->rootBlockChain,
3038 /******************************************************************************
3041 * Update raw directory entry data from the fields in newData.
3043 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3045 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3047 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3050 buffer + OFFSET_PS_NAME,
3052 DIRENTRY_NAME_BUFFER_LEN );
3054 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3056 StorageUtl_WriteWord(
3058 OFFSET_PS_NAMELENGTH,
3059 newData->sizeOfNameString);
3061 StorageUtl_WriteDWord(
3063 OFFSET_PS_LEFTCHILD,
3064 newData->leftChild);
3066 StorageUtl_WriteDWord(
3068 OFFSET_PS_RIGHTCHILD,
3069 newData->rightChild);
3071 StorageUtl_WriteDWord(
3074 newData->dirRootEntry);
3076 StorageUtl_WriteGUID(
3081 StorageUtl_WriteDWord(
3084 newData->ctime.dwLowDateTime);
3086 StorageUtl_WriteDWord(
3088 OFFSET_PS_CTIMEHIGH,
3089 newData->ctime.dwHighDateTime);
3091 StorageUtl_WriteDWord(
3094 newData->mtime.dwLowDateTime);
3096 StorageUtl_WriteDWord(
3098 OFFSET_PS_MTIMEHIGH,
3099 newData->ctime.dwHighDateTime);
3101 StorageUtl_WriteDWord(
3103 OFFSET_PS_STARTBLOCK,
3104 newData->startingBlock);
3106 StorageUtl_WriteDWord(
3109 newData->size.u.LowPart);
3112 /******************************************************************************
3113 * Storage32Impl_ReadDirEntry
3115 * This method will read the specified directory entry.
3117 BOOL StorageImpl_ReadDirEntry(
3122 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3125 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3127 if (SUCCEEDED(readRes))
3129 /* replace the name of root entry (often "Root Entry") by the file name */
3130 WCHAR *entryName = (index == This->base.storageDirEntry) ?
3131 This->filename : (WCHAR *)currentEntry+OFFSET_PS_NAME;
3133 memset(buffer->name, 0, sizeof(buffer->name));
3137 DIRENTRY_NAME_BUFFER_LEN );
3138 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3140 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3142 StorageUtl_ReadWord(
3144 OFFSET_PS_NAMELENGTH,
3145 &buffer->sizeOfNameString);
3147 StorageUtl_ReadDWord(
3149 OFFSET_PS_LEFTCHILD,
3150 &buffer->leftChild);
3152 StorageUtl_ReadDWord(
3154 OFFSET_PS_RIGHTCHILD,
3155 &buffer->rightChild);
3157 StorageUtl_ReadDWord(
3160 &buffer->dirRootEntry);
3162 StorageUtl_ReadGUID(
3167 StorageUtl_ReadDWord(
3170 &buffer->ctime.dwLowDateTime);
3172 StorageUtl_ReadDWord(
3174 OFFSET_PS_CTIMEHIGH,
3175 &buffer->ctime.dwHighDateTime);
3177 StorageUtl_ReadDWord(
3180 &buffer->mtime.dwLowDateTime);
3182 StorageUtl_ReadDWord(
3184 OFFSET_PS_MTIMEHIGH,
3185 &buffer->mtime.dwHighDateTime);
3187 StorageUtl_ReadDWord(
3189 OFFSET_PS_STARTBLOCK,
3190 &buffer->startingBlock);
3192 StorageUtl_ReadDWord(
3195 &buffer->size.u.LowPart);
3197 buffer->size.u.HighPart = 0;
3200 return SUCCEEDED(readRes) ? TRUE : FALSE;
3203 /*********************************************************************
3204 * Write the specified directory entry to the file
3206 BOOL StorageImpl_WriteDirEntry(
3209 const DirEntry* buffer)
3211 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3214 UpdateRawDirEntry(currentEntry, buffer);
3216 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3217 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3220 static BOOL StorageImpl_ReadBigBlock(
3225 ULARGE_INTEGER ulOffset;
3228 ulOffset.u.HighPart = 0;
3229 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3231 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3232 return (read == This->bigBlockSize);
3235 static BOOL StorageImpl_ReadDWordFromBigBlock(
3241 ULARGE_INTEGER ulOffset;
3245 ulOffset.u.HighPart = 0;
3246 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3247 ulOffset.u.LowPart += offset;
3249 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3250 *value = lendian32toh(tmp);
3251 return (read == sizeof(DWORD));
3254 static BOOL StorageImpl_WriteBigBlock(
3259 ULARGE_INTEGER ulOffset;
3262 ulOffset.u.HighPart = 0;
3263 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3265 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3266 return (wrote == This->bigBlockSize);
3269 static BOOL StorageImpl_WriteDWordToBigBlock(
3275 ULARGE_INTEGER ulOffset;
3278 ulOffset.u.HighPart = 0;
3279 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3280 ulOffset.u.LowPart += offset;
3282 value = htole32(value);
3283 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3284 return (wrote == sizeof(DWORD));
3287 /******************************************************************************
3288 * Storage32Impl_SmallBlocksToBigBlocks
3290 * This method will convert a small block chain to a big block chain.
3291 * The small block chain will be destroyed.
3293 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3295 SmallBlockChainStream** ppsbChain)
3297 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3298 ULARGE_INTEGER size, offset;
3299 ULONG cbRead, cbWritten;
3300 ULARGE_INTEGER cbTotalRead;
3301 ULONG streamEntryRef;
3302 HRESULT resWrite = S_OK;
3304 DirEntry streamEntry;
3306 BlockChainStream *bbTempChain = NULL;
3307 BlockChainStream *bigBlockChain = NULL;
3310 * Create a temporary big block chain that doesn't have
3311 * an associated directory entry. This temporary chain will be
3312 * used to copy data from small blocks to big blocks.
3314 bbTempChain = BlockChainStream_Construct(This,
3317 if(!bbTempChain) return NULL;
3319 * Grow the big block chain.
3321 size = SmallBlockChainStream_GetSize(*ppsbChain);
3322 BlockChainStream_SetSize(bbTempChain, size);
3325 * Copy the contents of the small block chain to the big block chain
3326 * by small block size increments.
3328 offset.u.LowPart = 0;
3329 offset.u.HighPart = 0;
3330 cbTotalRead.QuadPart = 0;
3332 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3335 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3337 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3340 if (FAILED(resRead))
3345 cbTotalRead.QuadPart += cbRead;
3347 resWrite = BlockChainStream_WriteAt(bbTempChain,
3353 if (FAILED(resWrite))
3356 offset.u.LowPart += cbRead;
3358 } while (cbTotalRead.QuadPart < size.QuadPart);
3359 HeapFree(GetProcessHeap(),0,buffer);
3361 size.u.HighPart = 0;
3364 if (FAILED(resRead) || FAILED(resWrite))
3366 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3367 BlockChainStream_SetSize(bbTempChain, size);
3368 BlockChainStream_Destroy(bbTempChain);
3373 * Destroy the small block chain.
3375 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3376 SmallBlockChainStream_SetSize(*ppsbChain, size);
3377 SmallBlockChainStream_Destroy(*ppsbChain);
3381 * Change the directory entry. This chain is now a big block chain
3382 * and it doesn't reside in the small blocks chain anymore.
3384 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3386 streamEntry.startingBlock = bbHeadOfChain;
3388 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3391 * Destroy the temporary entryless big block chain.
3392 * Create a new big block chain associated with this entry.
3394 BlockChainStream_Destroy(bbTempChain);
3395 bigBlockChain = BlockChainStream_Construct(This,
3399 return bigBlockChain;
3402 /******************************************************************************
3403 * Storage32Impl_BigBlocksToSmallBlocks
3405 * This method will convert a big block chain to a small block chain.
3406 * The big block chain will be destroyed on success.
3408 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3410 BlockChainStream** ppbbChain)
3412 ULARGE_INTEGER size, offset, cbTotalRead;
3413 ULONG cbRead, cbWritten, streamEntryRef, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3414 HRESULT resWrite = S_OK, resRead;
3415 DirEntry streamEntry;
3417 SmallBlockChainStream* sbTempChain;
3419 TRACE("%p %p\n", This, ppbbChain);
3421 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3427 size = BlockChainStream_GetSize(*ppbbChain);
3428 SmallBlockChainStream_SetSize(sbTempChain, size);
3430 offset.u.HighPart = 0;
3431 offset.u.LowPart = 0;
3432 cbTotalRead.QuadPart = 0;
3433 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3436 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3437 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3445 cbTotalRead.QuadPart += cbRead;
3447 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3448 cbRead, buffer, &cbWritten);
3450 if(FAILED(resWrite))
3453 offset.u.LowPart += cbRead;
3455 }while(cbTotalRead.QuadPart < size.QuadPart);
3456 HeapFree(GetProcessHeap(), 0, buffer);
3458 size.u.HighPart = 0;
3461 if(FAILED(resRead) || FAILED(resWrite))
3463 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3464 SmallBlockChainStream_SetSize(sbTempChain, size);
3465 SmallBlockChainStream_Destroy(sbTempChain);
3469 /* destroy the original big block chain */
3470 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3471 BlockChainStream_SetSize(*ppbbChain, size);
3472 BlockChainStream_Destroy(*ppbbChain);
3475 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3476 streamEntry.startingBlock = sbHeadOfChain;
3477 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3479 SmallBlockChainStream_Destroy(sbTempChain);
3480 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3483 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3485 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3487 HeapFree(GetProcessHeap(), 0, This);
3490 /******************************************************************************
3492 ** Storage32InternalImpl_Commit
3495 static HRESULT WINAPI StorageInternalImpl_Commit(
3497 DWORD grfCommitFlags) /* [in] */
3499 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
3503 /******************************************************************************
3505 ** Storage32InternalImpl_Revert
3508 static HRESULT WINAPI StorageInternalImpl_Revert(
3511 FIXME("(%p): stub\n", iface);
3515 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3517 IStorage_Release((IStorage*)This->parentStorage);
3518 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3519 HeapFree(GetProcessHeap(), 0, This);
3522 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3523 IEnumSTATSTG* iface,
3527 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3530 return E_INVALIDARG;
3534 if (IsEqualGUID(&IID_IUnknown, riid) ||
3535 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3538 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3542 return E_NOINTERFACE;
3545 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3546 IEnumSTATSTG* iface)
3548 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3549 return InterlockedIncrement(&This->ref);
3552 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3553 IEnumSTATSTG* iface)
3555 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3559 newRef = InterlockedDecrement(&This->ref);
3563 IEnumSTATSTGImpl_Destroy(This);
3569 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3570 IEnumSTATSTG* iface,
3573 ULONG* pceltFetched)
3575 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3577 DirEntry currentProperty;
3578 STATSTG* currentReturnStruct = rgelt;
3579 ULONG objectFetched = 0;
3580 ULONG currentSearchNode;
3582 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3583 return E_INVALIDARG;
3586 * To avoid the special case, get another pointer to a ULONG value if
3587 * the caller didn't supply one.
3589 if (pceltFetched==0)
3590 pceltFetched = &objectFetched;
3593 * Start the iteration, we will iterate until we hit the end of the
3594 * linked list or until we hit the number of items to iterate through
3599 * Start with the node at the top of the stack.
3601 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3603 while ( ( *pceltFetched < celt) &&
3604 ( currentSearchNode!=DIRENTRY_NULL) )
3607 * Remove the top node from the stack
3609 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3612 * Read the property from the storage.
3614 StorageImpl_ReadDirEntry(This->parentStorage,
3619 * Copy the information to the return buffer.
3621 StorageUtl_CopyDirEntryToSTATSTG(currentReturnStruct,
3626 * Step to the next item in the iteration
3629 currentReturnStruct++;
3632 * Push the next search node in the search stack.
3634 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.rightChild);
3637 * continue the iteration.
3639 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3642 if (*pceltFetched == celt)
3649 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3650 IEnumSTATSTG* iface,
3653 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3655 DirEntry currentProperty;
3656 ULONG objectFetched = 0;
3657 ULONG currentSearchNode;
3660 * Start with the node at the top of the stack.
3662 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3664 while ( (objectFetched < celt) &&
3665 (currentSearchNode!=DIRENTRY_NULL) )
3668 * Remove the top node from the stack
3670 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3673 * Read the property from the storage.
3675 StorageImpl_ReadDirEntry(This->parentStorage,
3680 * Step to the next item in the iteration
3685 * Push the next search node in the search stack.
3687 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.rightChild);
3690 * continue the iteration.
3692 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3695 if (objectFetched == celt)
3701 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3702 IEnumSTATSTG* iface)
3704 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3706 DirEntry rootProperty;
3707 BOOL readSuccessful;
3710 * Re-initialize the search stack to an empty stack
3712 This->stackSize = 0;
3715 * Read the root property from the storage.
3717 readSuccessful = StorageImpl_ReadDirEntry(
3718 This->parentStorage,
3719 This->storageDirEntry,
3724 assert(rootProperty.sizeOfNameString!=0);
3727 * Push the search node in the search stack.
3729 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirRootEntry);
3735 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3736 IEnumSTATSTG* iface,
3737 IEnumSTATSTG** ppenum)
3739 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3741 IEnumSTATSTGImpl* newClone;
3744 * Perform a sanity check on the parameters.
3747 return E_INVALIDARG;
3749 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3750 This->storageDirEntry);
3754 * The new clone enumeration must point to the same current node as
3757 newClone->stackSize = This->stackSize ;
3758 newClone->stackMaxSize = This->stackMaxSize ;
3759 newClone->stackToVisit =
3760 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3763 newClone->stackToVisit,
3765 sizeof(ULONG) * newClone->stackSize);
3767 *ppenum = (IEnumSTATSTG*)newClone;
3770 * Don't forget to nail down a reference to the clone before
3773 IEnumSTATSTGImpl_AddRef(*ppenum);
3778 static void IEnumSTATSTGImpl_PushSearchNode(
3779 IEnumSTATSTGImpl* This,
3782 DirEntry rootProperty;
3783 BOOL readSuccessful;
3786 * First, make sure we're not trying to push an unexisting node.
3788 if (nodeToPush==DIRENTRY_NULL)
3792 * First push the node to the stack
3794 if (This->stackSize == This->stackMaxSize)
3796 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3798 This->stackToVisit = HeapReAlloc(
3802 sizeof(ULONG) * This->stackMaxSize);
3805 This->stackToVisit[This->stackSize] = nodeToPush;
3809 * Read the root property from the storage.
3811 readSuccessful = StorageImpl_ReadDirEntry(
3812 This->parentStorage,
3818 assert(rootProperty.sizeOfNameString!=0);
3821 * Push the previous search node in the search stack.
3823 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.leftChild);
3827 static ULONG IEnumSTATSTGImpl_PopSearchNode(
3828 IEnumSTATSTGImpl* This,
3833 if (This->stackSize == 0)
3834 return DIRENTRY_NULL;
3836 topNode = This->stackToVisit[This->stackSize-1];
3845 * Virtual function table for the IEnumSTATSTGImpl class.
3847 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
3849 IEnumSTATSTGImpl_QueryInterface,
3850 IEnumSTATSTGImpl_AddRef,
3851 IEnumSTATSTGImpl_Release,
3852 IEnumSTATSTGImpl_Next,
3853 IEnumSTATSTGImpl_Skip,
3854 IEnumSTATSTGImpl_Reset,
3855 IEnumSTATSTGImpl_Clone
3858 /******************************************************************************
3859 ** IEnumSTATSTGImpl implementation
3862 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3863 StorageImpl* parentStorage,
3864 ULONG storageDirEntry)
3866 IEnumSTATSTGImpl* newEnumeration;
3868 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3870 if (newEnumeration!=0)
3873 * Set-up the virtual function table and reference count.
3875 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3876 newEnumeration->ref = 0;
3879 * We want to nail-down the reference to the storage in case the
3880 * enumeration out-lives the storage in the client application.
3882 newEnumeration->parentStorage = parentStorage;
3883 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3885 newEnumeration->storageDirEntry = storageDirEntry;
3888 * Initialize the search stack
3890 newEnumeration->stackSize = 0;
3891 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3892 newEnumeration->stackToVisit =
3893 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3896 * Make sure the current node of the iterator is the first one.
3898 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3901 return newEnumeration;
3905 * Virtual function table for the Storage32InternalImpl class.
3907 static const IStorageVtbl Storage32InternalImpl_Vtbl =
3909 StorageBaseImpl_QueryInterface,
3910 StorageBaseImpl_AddRef,
3911 StorageBaseImpl_Release,
3912 StorageBaseImpl_CreateStream,
3913 StorageBaseImpl_OpenStream,
3914 StorageBaseImpl_CreateStorage,
3915 StorageBaseImpl_OpenStorage,
3916 StorageBaseImpl_CopyTo,
3917 StorageBaseImpl_MoveElementTo,
3918 StorageInternalImpl_Commit,
3919 StorageInternalImpl_Revert,
3920 StorageBaseImpl_EnumElements,
3921 StorageBaseImpl_DestroyElement,
3922 StorageBaseImpl_RenameElement,
3923 StorageBaseImpl_SetElementTimes,
3924 StorageBaseImpl_SetClass,
3925 StorageBaseImpl_SetStateBits,
3926 StorageBaseImpl_Stat
3929 /******************************************************************************
3930 ** Storage32InternalImpl implementation
3933 static StorageInternalImpl* StorageInternalImpl_Construct(
3934 StorageImpl* ancestorStorage,
3936 ULONG rootPropertyIndex)
3938 StorageInternalImpl* newStorage;
3940 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
3945 * Initialize the stream list
3947 list_init(&newStorage->base.strmHead);
3950 * Initialize the virtual function table.
3952 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
3953 newStorage->base.v_destructor = StorageInternalImpl_Destroy;
3954 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
3957 * Keep the ancestor storage pointer but do not nail a reference to it.
3959 newStorage->base.ancestorStorage = ancestorStorage;
3962 * Keep the index of the root property set for this storage,
3964 newStorage->base.storageDirEntry = rootPropertyIndex;
3972 /******************************************************************************
3973 ** StorageUtl implementation
3976 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
3980 memcpy(&tmp, buffer+offset, sizeof(WORD));
3981 *value = lendian16toh(tmp);
3984 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
3986 value = htole16(value);
3987 memcpy(buffer+offset, &value, sizeof(WORD));
3990 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
3994 memcpy(&tmp, buffer+offset, sizeof(DWORD));
3995 *value = lendian32toh(tmp);
3998 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4000 value = htole32(value);
4001 memcpy(buffer+offset, &value, sizeof(DWORD));
4004 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4005 ULARGE_INTEGER* value)
4007 #ifdef WORDS_BIGENDIAN
4010 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4011 value->u.LowPart = htole32(tmp.u.HighPart);
4012 value->u.HighPart = htole32(tmp.u.LowPart);
4014 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4018 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4019 const ULARGE_INTEGER *value)
4021 #ifdef WORDS_BIGENDIAN
4024 tmp.u.LowPart = htole32(value->u.HighPart);
4025 tmp.u.HighPart = htole32(value->u.LowPart);
4026 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4028 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4032 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4034 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4035 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4036 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4038 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4041 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4043 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4044 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4045 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4047 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4050 void StorageUtl_CopyDirEntryToSTATSTG(
4051 STATSTG* destination,
4052 const DirEntry* source,
4056 * The copy of the string occurs only when the flag is not set
4058 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4059 (source->name == NULL) ||
4060 (source->name[0] == 0) )
4062 destination->pwcsName = 0;
4066 destination->pwcsName =
4067 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4069 strcpyW(destination->pwcsName, source->name);
4072 switch (source->stgType)
4076 destination->type = STGTY_STORAGE;
4079 destination->type = STGTY_STREAM;
4082 destination->type = STGTY_STREAM;
4086 destination->cbSize = source->size;
4088 currentReturnStruct->mtime = {0}; TODO
4089 currentReturnStruct->ctime = {0};
4090 currentReturnStruct->atime = {0};
4092 destination->grfMode = 0;
4093 destination->grfLocksSupported = 0;
4094 destination->clsid = source->clsid;
4095 destination->grfStateBits = 0;
4096 destination->reserved = 0;
4099 /******************************************************************************
4100 ** BlockChainStream implementation
4103 BlockChainStream* BlockChainStream_Construct(
4104 StorageImpl* parentStorage,
4105 ULONG* headOfStreamPlaceHolder,
4108 BlockChainStream* newStream;
4111 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4113 newStream->parentStorage = parentStorage;
4114 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4115 newStream->ownerDirEntry = dirEntry;
4116 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4117 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4118 newStream->numBlocks = 0;
4120 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4122 while (blockIndex != BLOCK_END_OF_CHAIN)
4124 newStream->numBlocks++;
4125 newStream->tailIndex = blockIndex;
4127 if(FAILED(StorageImpl_GetNextBlockInChain(
4132 HeapFree(GetProcessHeap(), 0, newStream);
4140 void BlockChainStream_Destroy(BlockChainStream* This)
4142 HeapFree(GetProcessHeap(), 0, This);
4145 /******************************************************************************
4146 * BlockChainStream_GetHeadOfChain
4148 * Returns the head of this stream chain.
4149 * Some special chains don't have properties, their heads are kept in
4150 * This->headOfStreamPlaceHolder.
4153 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4155 DirEntry chainProperty;
4156 BOOL readSuccessful;
4158 if (This->headOfStreamPlaceHolder != 0)
4159 return *(This->headOfStreamPlaceHolder);
4161 if (This->ownerDirEntry != DIRENTRY_NULL)
4163 readSuccessful = StorageImpl_ReadDirEntry(
4164 This->parentStorage,
4165 This->ownerDirEntry,
4170 return chainProperty.startingBlock;
4174 return BLOCK_END_OF_CHAIN;
4177 /******************************************************************************
4178 * BlockChainStream_GetCount
4180 * Returns the number of blocks that comprises this chain.
4181 * This is not the size of the stream as the last block may not be full!
4184 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4189 blockIndex = BlockChainStream_GetHeadOfChain(This);
4191 while (blockIndex != BLOCK_END_OF_CHAIN)
4195 if(FAILED(StorageImpl_GetNextBlockInChain(
4196 This->parentStorage,
4205 /******************************************************************************
4206 * BlockChainStream_ReadAt
4208 * Reads a specified number of bytes from this chain at the specified offset.
4209 * bytesRead may be NULL.
4210 * Failure will be returned if the specified number of bytes has not been read.
4212 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4213 ULARGE_INTEGER offset,
4218 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4219 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4220 ULONG bytesToReadInBuffer;
4224 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4227 * Find the first block in the stream that contains part of the buffer.
4229 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4230 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4231 (blockNoInSequence < This->lastBlockNoInSequence) )
4233 blockIndex = BlockChainStream_GetHeadOfChain(This);
4234 This->lastBlockNoInSequence = blockNoInSequence;
4238 ULONG temp = blockNoInSequence;
4240 blockIndex = This->lastBlockNoInSequenceIndex;
4241 blockNoInSequence -= This->lastBlockNoInSequence;
4242 This->lastBlockNoInSequence = temp;
4245 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4247 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4248 return STG_E_DOCFILECORRUPT;
4249 blockNoInSequence--;
4252 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4253 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4255 This->lastBlockNoInSequenceIndex = blockIndex;
4258 * Start reading the buffer.
4261 bufferWalker = buffer;
4263 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4265 ULARGE_INTEGER ulOffset;
4268 * Calculate how many bytes we can copy from this big block.
4270 bytesToReadInBuffer =
4271 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4273 TRACE("block %i\n",blockIndex);
4274 ulOffset.u.HighPart = 0;
4275 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4278 StorageImpl_ReadAt(This->parentStorage,
4281 bytesToReadInBuffer,
4284 * Step to the next big block.
4286 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4287 return STG_E_DOCFILECORRUPT;
4289 bufferWalker += bytesReadAt;
4290 size -= bytesReadAt;
4291 *bytesRead += bytesReadAt;
4292 offsetInBlock = 0; /* There is no offset on the next block */
4294 if (bytesToReadInBuffer != bytesReadAt)
4298 return (size == 0) ? S_OK : STG_E_READFAULT;
4301 /******************************************************************************
4302 * BlockChainStream_WriteAt
4304 * Writes the specified number of bytes to this chain at the specified offset.
4305 * Will fail if not all specified number of bytes have been written.
4307 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4308 ULARGE_INTEGER offset,
4311 ULONG* bytesWritten)
4313 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4314 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4317 const BYTE* bufferWalker;
4320 * Find the first block in the stream that contains part of the buffer.
4322 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4323 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4324 (blockNoInSequence < This->lastBlockNoInSequence) )
4326 blockIndex = BlockChainStream_GetHeadOfChain(This);
4327 This->lastBlockNoInSequence = blockNoInSequence;
4331 ULONG temp = blockNoInSequence;
4333 blockIndex = This->lastBlockNoInSequenceIndex;
4334 blockNoInSequence -= This->lastBlockNoInSequence;
4335 This->lastBlockNoInSequence = temp;
4338 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4340 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4342 return STG_E_DOCFILECORRUPT;
4343 blockNoInSequence--;
4346 This->lastBlockNoInSequenceIndex = blockIndex;
4348 /* BlockChainStream_SetSize should have already been called to ensure we have
4349 * enough blocks in the chain to write into */
4350 if (blockIndex == BLOCK_END_OF_CHAIN)
4352 ERR("not enough blocks in chain to write data\n");
4353 return STG_E_DOCFILECORRUPT;
4357 bufferWalker = buffer;
4359 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4361 ULARGE_INTEGER ulOffset;
4362 DWORD bytesWrittenAt;
4364 * Calculate how many bytes we can copy from this big block.
4367 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4369 TRACE("block %i\n",blockIndex);
4370 ulOffset.u.HighPart = 0;
4371 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4374 StorageImpl_WriteAt(This->parentStorage,
4381 * Step to the next big block.
4383 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4385 return STG_E_DOCFILECORRUPT;
4387 bufferWalker += bytesWrittenAt;
4388 size -= bytesWrittenAt;
4389 *bytesWritten += bytesWrittenAt;
4390 offsetInBlock = 0; /* There is no offset on the next block */
4392 if (bytesWrittenAt != bytesToWrite)
4396 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4399 /******************************************************************************
4400 * BlockChainStream_Shrink
4402 * Shrinks this chain in the big block depot.
4404 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4405 ULARGE_INTEGER newSize)
4407 ULONG blockIndex, extraBlock;
4412 * Reset the last accessed block cache.
4414 This->lastBlockNoInSequence = 0xFFFFFFFF;
4415 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4418 * Figure out how many blocks are needed to contain the new size
4420 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4422 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4425 blockIndex = BlockChainStream_GetHeadOfChain(This);
4428 * Go to the new end of chain
4430 while (count < numBlocks)
4432 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4438 /* Get the next block before marking the new end */
4439 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4443 /* Mark the new end of chain */
4444 StorageImpl_SetNextBlockInChain(
4445 This->parentStorage,
4447 BLOCK_END_OF_CHAIN);
4449 This->tailIndex = blockIndex;
4450 This->numBlocks = numBlocks;
4453 * Mark the extra blocks as free
4455 while (extraBlock != BLOCK_END_OF_CHAIN)
4457 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4460 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4461 extraBlock = blockIndex;
4467 /******************************************************************************
4468 * BlockChainStream_Enlarge
4470 * Grows this chain in the big block depot.
4472 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4473 ULARGE_INTEGER newSize)
4475 ULONG blockIndex, currentBlock;
4477 ULONG oldNumBlocks = 0;
4479 blockIndex = BlockChainStream_GetHeadOfChain(This);
4482 * Empty chain. Create the head.
4484 if (blockIndex == BLOCK_END_OF_CHAIN)
4486 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4487 StorageImpl_SetNextBlockInChain(This->parentStorage,
4489 BLOCK_END_OF_CHAIN);
4491 if (This->headOfStreamPlaceHolder != 0)
4493 *(This->headOfStreamPlaceHolder) = blockIndex;
4498 assert(This->ownerDirEntry != DIRENTRY_NULL);
4500 StorageImpl_ReadDirEntry(
4501 This->parentStorage,
4502 This->ownerDirEntry,
4505 chainProp.startingBlock = blockIndex;
4507 StorageImpl_WriteDirEntry(
4508 This->parentStorage,
4509 This->ownerDirEntry,
4513 This->tailIndex = blockIndex;
4514 This->numBlocks = 1;
4518 * Figure out how many blocks are needed to contain this stream
4520 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4522 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4526 * Go to the current end of chain
4528 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4530 currentBlock = blockIndex;
4532 while (blockIndex != BLOCK_END_OF_CHAIN)
4535 currentBlock = blockIndex;
4537 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4542 This->tailIndex = currentBlock;
4545 currentBlock = This->tailIndex;
4546 oldNumBlocks = This->numBlocks;
4549 * Add new blocks to the chain
4551 if (oldNumBlocks < newNumBlocks)
4553 while (oldNumBlocks < newNumBlocks)
4555 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4557 StorageImpl_SetNextBlockInChain(
4558 This->parentStorage,
4562 StorageImpl_SetNextBlockInChain(
4563 This->parentStorage,
4565 BLOCK_END_OF_CHAIN);
4567 currentBlock = blockIndex;
4571 This->tailIndex = blockIndex;
4572 This->numBlocks = newNumBlocks;
4578 /******************************************************************************
4579 * BlockChainStream_SetSize
4581 * Sets the size of this stream. The big block depot will be updated.
4582 * The file will grow if we grow the chain.
4584 * TODO: Free the actual blocks in the file when we shrink the chain.
4585 * Currently, the blocks are still in the file. So the file size
4586 * doesn't shrink even if we shrink streams.
4588 BOOL BlockChainStream_SetSize(
4589 BlockChainStream* This,
4590 ULARGE_INTEGER newSize)
4592 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4594 if (newSize.u.LowPart == size.u.LowPart)
4597 if (newSize.u.LowPart < size.u.LowPart)
4599 BlockChainStream_Shrink(This, newSize);
4603 BlockChainStream_Enlarge(This, newSize);
4609 /******************************************************************************
4610 * BlockChainStream_GetSize
4612 * Returns the size of this chain.
4613 * Will return the block count if this chain doesn't have a property.
4615 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4617 DirEntry chainProperty;
4619 if(This->headOfStreamPlaceHolder == NULL)
4622 * This chain is a data stream read the property and return
4623 * the appropriate size
4625 StorageImpl_ReadDirEntry(
4626 This->parentStorage,
4627 This->ownerDirEntry,
4630 return chainProperty.size;
4635 * this chain is a chain that does not have a property, figure out the
4636 * size by making the product number of used blocks times the
4639 ULARGE_INTEGER result;
4640 result.u.HighPart = 0;
4643 BlockChainStream_GetCount(This) *
4644 This->parentStorage->bigBlockSize;
4650 /******************************************************************************
4651 ** SmallBlockChainStream implementation
4654 SmallBlockChainStream* SmallBlockChainStream_Construct(
4655 StorageImpl* parentStorage,
4656 ULONG* headOfStreamPlaceHolder,
4659 SmallBlockChainStream* newStream;
4661 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4663 newStream->parentStorage = parentStorage;
4664 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4665 newStream->ownerDirEntry = dirEntry;
4670 void SmallBlockChainStream_Destroy(
4671 SmallBlockChainStream* This)
4673 HeapFree(GetProcessHeap(), 0, This);
4676 /******************************************************************************
4677 * SmallBlockChainStream_GetHeadOfChain
4679 * Returns the head of this chain of small blocks.
4681 static ULONG SmallBlockChainStream_GetHeadOfChain(
4682 SmallBlockChainStream* This)
4684 DirEntry chainProperty;
4685 BOOL readSuccessful;
4687 if (This->headOfStreamPlaceHolder != NULL)
4688 return *(This->headOfStreamPlaceHolder);
4690 if (This->ownerDirEntry)
4692 readSuccessful = StorageImpl_ReadDirEntry(
4693 This->parentStorage,
4694 This->ownerDirEntry,
4699 return chainProperty.startingBlock;
4704 return BLOCK_END_OF_CHAIN;
4707 /******************************************************************************
4708 * SmallBlockChainStream_GetNextBlockInChain
4710 * Returns the index of the next small block in this chain.
4713 * - BLOCK_END_OF_CHAIN: end of this chain
4714 * - BLOCK_UNUSED: small block 'blockIndex' is free
4716 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
4717 SmallBlockChainStream* This,
4719 ULONG* nextBlockInChain)
4721 ULARGE_INTEGER offsetOfBlockInDepot;
4726 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4728 offsetOfBlockInDepot.u.HighPart = 0;
4729 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4732 * Read those bytes in the buffer from the small block file.
4734 res = BlockChainStream_ReadAt(
4735 This->parentStorage->smallBlockDepotChain,
4736 offsetOfBlockInDepot,
4743 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4750 /******************************************************************************
4751 * SmallBlockChainStream_SetNextBlockInChain
4753 * Writes the index of the next block of the specified block in the small
4755 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4756 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4758 static void SmallBlockChainStream_SetNextBlockInChain(
4759 SmallBlockChainStream* This,
4763 ULARGE_INTEGER offsetOfBlockInDepot;
4767 offsetOfBlockInDepot.u.HighPart = 0;
4768 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4770 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4773 * Read those bytes in the buffer from the small block file.
4775 BlockChainStream_WriteAt(
4776 This->parentStorage->smallBlockDepotChain,
4777 offsetOfBlockInDepot,
4783 /******************************************************************************
4784 * SmallBlockChainStream_FreeBlock
4786 * Flag small block 'blockIndex' as free in the small block depot.
4788 static void SmallBlockChainStream_FreeBlock(
4789 SmallBlockChainStream* This,
4792 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4795 /******************************************************************************
4796 * SmallBlockChainStream_GetNextFreeBlock
4798 * Returns the index of a free small block. The small block depot will be
4799 * enlarged if necessary. The small block chain will also be enlarged if
4802 static ULONG SmallBlockChainStream_GetNextFreeBlock(
4803 SmallBlockChainStream* This)
4805 ULARGE_INTEGER offsetOfBlockInDepot;
4808 ULONG blockIndex = 0;
4809 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4811 ULONG smallBlocksPerBigBlock;
4813 offsetOfBlockInDepot.u.HighPart = 0;
4816 * Scan the small block depot for a free block
4818 while (nextBlockIndex != BLOCK_UNUSED)
4820 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4822 res = BlockChainStream_ReadAt(
4823 This->parentStorage->smallBlockDepotChain,
4824 offsetOfBlockInDepot,
4830 * If we run out of space for the small block depot, enlarge it
4834 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
4836 if (nextBlockIndex != BLOCK_UNUSED)
4842 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4844 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4845 ULONG nextBlock, newsbdIndex;
4846 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
4848 nextBlock = sbdIndex;
4849 while (nextBlock != BLOCK_END_OF_CHAIN)
4851 sbdIndex = nextBlock;
4852 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4855 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4856 if (sbdIndex != BLOCK_END_OF_CHAIN)
4857 StorageImpl_SetNextBlockInChain(
4858 This->parentStorage,
4862 StorageImpl_SetNextBlockInChain(
4863 This->parentStorage,
4865 BLOCK_END_OF_CHAIN);
4868 * Initialize all the small blocks to free
4870 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4871 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
4876 * We have just created the small block depot.
4882 * Save it in the header
4884 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4885 StorageImpl_SaveFileHeader(This->parentStorage);
4888 * And allocate the first big block that will contain small blocks
4891 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4893 StorageImpl_SetNextBlockInChain(
4894 This->parentStorage,
4896 BLOCK_END_OF_CHAIN);
4898 StorageImpl_ReadDirEntry(
4899 This->parentStorage,
4900 This->parentStorage->base.storageDirEntry,
4903 rootProp.startingBlock = sbStartIndex;
4904 rootProp.size.u.HighPart = 0;
4905 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
4907 StorageImpl_WriteDirEntry(
4908 This->parentStorage,
4909 This->parentStorage->base.storageDirEntry,
4913 StorageImpl_SaveFileHeader(This->parentStorage);
4917 smallBlocksPerBigBlock =
4918 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4921 * Verify if we have to allocate big blocks to contain small blocks
4923 if (blockIndex % smallBlocksPerBigBlock == 0)
4926 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4928 StorageImpl_ReadDirEntry(
4929 This->parentStorage,
4930 This->parentStorage->base.storageDirEntry,
4933 if (rootProp.size.u.LowPart <
4934 (blocksRequired * This->parentStorage->bigBlockSize))
4936 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
4938 BlockChainStream_SetSize(
4939 This->parentStorage->smallBlockRootChain,
4942 StorageImpl_WriteDirEntry(
4943 This->parentStorage,
4944 This->parentStorage->base.storageDirEntry,
4952 /******************************************************************************
4953 * SmallBlockChainStream_ReadAt
4955 * Reads a specified number of bytes from this chain at the specified offset.
4956 * bytesRead may be NULL.
4957 * Failure will be returned if the specified number of bytes has not been read.
4959 HRESULT SmallBlockChainStream_ReadAt(
4960 SmallBlockChainStream* This,
4961 ULARGE_INTEGER offset,
4967 ULARGE_INTEGER offsetInBigBlockFile;
4968 ULONG blockNoInSequence =
4969 offset.u.LowPart / This->parentStorage->smallBlockSize;
4971 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
4972 ULONG bytesToReadInBuffer;
4974 ULONG bytesReadFromBigBlockFile;
4978 * This should never happen on a small block file.
4980 assert(offset.u.HighPart==0);
4983 * Find the first block in the stream that contains part of the buffer.
4985 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4987 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4989 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
4992 blockNoInSequence--;
4996 * Start reading the buffer.
4999 bufferWalker = buffer;
5001 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5004 * Calculate how many bytes we can copy from this small block.
5006 bytesToReadInBuffer =
5007 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5010 * Calculate the offset of the small block in the small block file.
5012 offsetInBigBlockFile.u.HighPart = 0;
5013 offsetInBigBlockFile.u.LowPart =
5014 blockIndex * This->parentStorage->smallBlockSize;
5016 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5019 * Read those bytes in the buffer from the small block file.
5020 * The small block has already been identified so it shouldn't fail
5021 * unless the file is corrupt.
5023 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5024 offsetInBigBlockFile,
5025 bytesToReadInBuffer,
5027 &bytesReadFromBigBlockFile);
5033 * Step to the next big block.
5035 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5037 return STG_E_DOCFILECORRUPT;
5039 bufferWalker += bytesReadFromBigBlockFile;
5040 size -= bytesReadFromBigBlockFile;
5041 *bytesRead += bytesReadFromBigBlockFile;
5042 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5045 return (size == 0) ? S_OK : STG_E_READFAULT;
5048 /******************************************************************************
5049 * SmallBlockChainStream_WriteAt
5051 * Writes the specified number of bytes to this chain at the specified offset.
5052 * Will fail if not all specified number of bytes have been written.
5054 HRESULT SmallBlockChainStream_WriteAt(
5055 SmallBlockChainStream* This,
5056 ULARGE_INTEGER offset,
5059 ULONG* bytesWritten)
5061 ULARGE_INTEGER offsetInBigBlockFile;
5062 ULONG blockNoInSequence =
5063 offset.u.LowPart / This->parentStorage->smallBlockSize;
5065 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5066 ULONG bytesToWriteInBuffer;
5068 ULONG bytesWrittenToBigBlockFile;
5069 const BYTE* bufferWalker;
5073 * This should never happen on a small block file.
5075 assert(offset.u.HighPart==0);
5078 * Find the first block in the stream that contains part of the buffer.
5080 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5082 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5084 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5085 return STG_E_DOCFILECORRUPT;
5086 blockNoInSequence--;
5090 * Start writing the buffer.
5093 bufferWalker = buffer;
5094 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5097 * Calculate how many bytes we can copy to this small block.
5099 bytesToWriteInBuffer =
5100 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5103 * Calculate the offset of the small block in the small block file.
5105 offsetInBigBlockFile.u.HighPart = 0;
5106 offsetInBigBlockFile.u.LowPart =
5107 blockIndex * This->parentStorage->smallBlockSize;
5109 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5112 * Write those bytes in the buffer to the small block file.
5114 res = BlockChainStream_WriteAt(
5115 This->parentStorage->smallBlockRootChain,
5116 offsetInBigBlockFile,
5117 bytesToWriteInBuffer,
5119 &bytesWrittenToBigBlockFile);
5124 * Step to the next big block.
5126 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5129 bufferWalker += bytesWrittenToBigBlockFile;
5130 size -= bytesWrittenToBigBlockFile;
5131 *bytesWritten += bytesWrittenToBigBlockFile;
5132 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5135 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5138 /******************************************************************************
5139 * SmallBlockChainStream_Shrink
5141 * Shrinks this chain in the small block depot.
5143 static BOOL SmallBlockChainStream_Shrink(
5144 SmallBlockChainStream* This,
5145 ULARGE_INTEGER newSize)
5147 ULONG blockIndex, extraBlock;
5151 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5153 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5156 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5159 * Go to the new end of chain
5161 while (count < numBlocks)
5163 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5170 * If the count is 0, we have a special case, the head of the chain was
5177 StorageImpl_ReadDirEntry(This->parentStorage,
5178 This->ownerDirEntry,
5181 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5183 StorageImpl_WriteDirEntry(This->parentStorage,
5184 This->ownerDirEntry,
5188 * We start freeing the chain at the head block.
5190 extraBlock = blockIndex;
5194 /* Get the next block before marking the new end */
5195 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5199 /* Mark the new end of chain */
5200 SmallBlockChainStream_SetNextBlockInChain(
5203 BLOCK_END_OF_CHAIN);
5207 * Mark the extra blocks as free
5209 while (extraBlock != BLOCK_END_OF_CHAIN)
5211 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5214 SmallBlockChainStream_FreeBlock(This, extraBlock);
5215 extraBlock = blockIndex;
5221 /******************************************************************************
5222 * SmallBlockChainStream_Enlarge
5224 * Grows this chain in the small block depot.
5226 static BOOL SmallBlockChainStream_Enlarge(
5227 SmallBlockChainStream* This,
5228 ULARGE_INTEGER newSize)
5230 ULONG blockIndex, currentBlock;
5232 ULONG oldNumBlocks = 0;
5234 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5237 * Empty chain. Create the head.
5239 if (blockIndex == BLOCK_END_OF_CHAIN)
5241 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5242 SmallBlockChainStream_SetNextBlockInChain(
5245 BLOCK_END_OF_CHAIN);
5247 if (This->headOfStreamPlaceHolder != NULL)
5249 *(This->headOfStreamPlaceHolder) = blockIndex;
5255 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
5258 chainProp.startingBlock = blockIndex;
5260 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
5265 currentBlock = blockIndex;
5268 * Figure out how many blocks are needed to contain this stream
5270 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5272 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5276 * Go to the current end of chain
5278 while (blockIndex != BLOCK_END_OF_CHAIN)
5281 currentBlock = blockIndex;
5282 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5287 * Add new blocks to the chain
5289 while (oldNumBlocks < newNumBlocks)
5291 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5292 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5294 SmallBlockChainStream_SetNextBlockInChain(
5297 BLOCK_END_OF_CHAIN);
5299 currentBlock = blockIndex;
5306 /******************************************************************************
5307 * SmallBlockChainStream_SetSize
5309 * Sets the size of this stream.
5310 * The file will grow if we grow the chain.
5312 * TODO: Free the actual blocks in the file when we shrink the chain.
5313 * Currently, the blocks are still in the file. So the file size
5314 * doesn't shrink even if we shrink streams.
5316 BOOL SmallBlockChainStream_SetSize(
5317 SmallBlockChainStream* This,
5318 ULARGE_INTEGER newSize)
5320 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5322 if (newSize.u.LowPart == size.u.LowPart)
5325 if (newSize.u.LowPart < size.u.LowPart)
5327 SmallBlockChainStream_Shrink(This, newSize);
5331 SmallBlockChainStream_Enlarge(This, newSize);
5337 /******************************************************************************
5338 * SmallBlockChainStream_GetCount
5340 * Returns the number of small blocks that comprises this chain.
5341 * This is not the size of the stream as the last block may not be full!
5344 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5349 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5351 while(blockIndex != BLOCK_END_OF_CHAIN)
5355 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
5356 blockIndex, &blockIndex)))
5363 /******************************************************************************
5364 * SmallBlockChainStream_GetSize
5366 * Returns the size of this chain.
5368 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5370 DirEntry chainProperty;
5372 if(This->headOfStreamPlaceHolder != NULL)
5374 ULARGE_INTEGER result;
5375 result.u.HighPart = 0;
5377 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
5378 This->parentStorage->smallBlockSize;
5383 StorageImpl_ReadDirEntry(
5384 This->parentStorage,
5385 This->ownerDirEntry,
5388 return chainProperty.size;
5391 /******************************************************************************
5392 * StgCreateDocfile [OLE32.@]
5393 * Creates a new compound file storage object
5396 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5397 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5398 * reserved [ ?] unused?, usually 0
5399 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5402 * S_OK if the file was successfully created
5403 * some STG_E_ value if error
5405 * if pwcsName is NULL, create file with new unique name
5406 * the function can returns
5407 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5410 HRESULT WINAPI StgCreateDocfile(
5414 IStorage **ppstgOpen)
5416 StorageImpl* newStorage = 0;
5417 HANDLE hFile = INVALID_HANDLE_VALUE;
5418 HRESULT hr = STG_E_INVALIDFLAG;
5422 DWORD fileAttributes;
5423 WCHAR tempFileName[MAX_PATH];
5425 TRACE("(%s, %x, %d, %p)\n",
5426 debugstr_w(pwcsName), grfMode,
5427 reserved, ppstgOpen);
5430 return STG_E_INVALIDPOINTER;
5432 return STG_E_INVALIDPARAMETER;
5434 /* if no share mode given then DENY_NONE is the default */
5435 if (STGM_SHARE_MODE(grfMode) == 0)
5436 grfMode |= STGM_SHARE_DENY_NONE;
5438 if ( FAILED( validateSTGM(grfMode) ))
5441 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5442 switch(STGM_ACCESS_MODE(grfMode))
5445 case STGM_READWRITE:
5451 /* in direct mode, can only use SHARE_EXCLUSIVE */
5452 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5455 /* but in transacted mode, any share mode is valid */
5458 * Generate a unique name.
5462 WCHAR tempPath[MAX_PATH];
5463 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5465 memset(tempPath, 0, sizeof(tempPath));
5466 memset(tempFileName, 0, sizeof(tempFileName));
5468 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5471 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5472 pwcsName = tempFileName;
5475 hr = STG_E_INSUFFICIENTMEMORY;
5479 creationMode = TRUNCATE_EXISTING;
5483 creationMode = GetCreationModeFromSTGM(grfMode);
5487 * Interpret the STGM value grfMode
5489 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5490 accessMode = GetAccessModeFromSTGM(grfMode);
5492 if (grfMode & STGM_DELETEONRELEASE)
5493 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5495 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5497 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5498 FIXME("Storage share mode not implemented.\n");
5500 if (grfMode & STGM_TRANSACTED)
5501 FIXME("Transacted mode not implemented.\n");
5505 hFile = CreateFileW(pwcsName,
5513 if (hFile == INVALID_HANDLE_VALUE)
5515 if(GetLastError() == ERROR_FILE_EXISTS)
5516 hr = STG_E_FILEALREADYEXISTS;
5523 * Allocate and initialize the new IStorage32object.
5525 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5527 if (newStorage == 0)
5529 hr = STG_E_INSUFFICIENTMEMORY;
5533 hr = StorageImpl_Construct(
5544 HeapFree(GetProcessHeap(), 0, newStorage);
5549 * Get an "out" pointer for the caller.
5551 hr = StorageBaseImpl_QueryInterface(
5552 (IStorage*)newStorage,
5556 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5561 /******************************************************************************
5562 * StgCreateStorageEx [OLE32.@]
5564 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5566 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5567 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5569 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5571 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5572 return STG_E_INVALIDPARAMETER;
5575 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5577 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5578 return STG_E_INVALIDPARAMETER;
5581 if (stgfmt == STGFMT_FILE)
5583 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5584 return STG_E_INVALIDPARAMETER;
5587 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5589 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5590 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5593 ERR("Invalid stgfmt argument\n");
5594 return STG_E_INVALIDPARAMETER;
5597 /******************************************************************************
5598 * StgCreatePropSetStg [OLE32.@]
5600 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5601 IPropertySetStorage **ppPropSetStg)
5605 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5607 hr = STG_E_INVALIDPARAMETER;
5609 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5610 (void**)ppPropSetStg);
5614 /******************************************************************************
5615 * StgOpenStorageEx [OLE32.@]
5617 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5619 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5620 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5622 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5624 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5625 return STG_E_INVALIDPARAMETER;
5631 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5632 return STG_E_INVALIDPARAMETER;
5634 case STGFMT_STORAGE:
5637 case STGFMT_DOCFILE:
5638 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5640 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5641 return STG_E_INVALIDPARAMETER;
5643 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5647 WARN("STGFMT_ANY assuming storage\n");
5651 return STG_E_INVALIDPARAMETER;
5654 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
5658 /******************************************************************************
5659 * StgOpenStorage [OLE32.@]
5661 HRESULT WINAPI StgOpenStorage(
5662 const OLECHAR *pwcsName,
5663 IStorage *pstgPriority,
5667 IStorage **ppstgOpen)
5669 StorageImpl* newStorage = 0;
5674 WCHAR fullname[MAX_PATH];
5676 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5677 debugstr_w(pwcsName), pstgPriority, grfMode,
5678 snbExclude, reserved, ppstgOpen);
5682 hr = STG_E_INVALIDNAME;
5688 hr = STG_E_INVALIDPOINTER;
5694 hr = STG_E_INVALIDPARAMETER;
5698 if (grfMode & STGM_PRIORITY)
5700 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5701 return STG_E_INVALIDFLAG;
5702 if (grfMode & STGM_DELETEONRELEASE)
5703 return STG_E_INVALIDFUNCTION;
5704 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5705 return STG_E_INVALIDFLAG;
5706 grfMode &= ~0xf0; /* remove the existing sharing mode */
5707 grfMode |= STGM_SHARE_DENY_NONE;
5709 /* STGM_PRIORITY stops other IStorage objects on the same file from
5710 * committing until the STGM_PRIORITY IStorage is closed. it also
5711 * stops non-transacted mode StgOpenStorage calls with write access from
5712 * succeeding. obviously, both of these cannot be achieved through just
5713 * file share flags */
5714 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5718 * Validate the sharing mode
5720 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5721 switch(STGM_SHARE_MODE(grfMode))
5723 case STGM_SHARE_EXCLUSIVE:
5724 case STGM_SHARE_DENY_WRITE:
5727 hr = STG_E_INVALIDFLAG;
5731 if ( FAILED( validateSTGM(grfMode) ) ||
5732 (grfMode&STGM_CREATE))
5734 hr = STG_E_INVALIDFLAG;
5738 /* shared reading requires transacted mode */
5739 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5740 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5741 !(grfMode&STGM_TRANSACTED) )
5743 hr = STG_E_INVALIDFLAG;
5748 * Interpret the STGM value grfMode
5750 shareMode = GetShareModeFromSTGM(grfMode);
5751 accessMode = GetAccessModeFromSTGM(grfMode);
5755 hFile = CreateFileW( pwcsName,
5760 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5763 if (hFile==INVALID_HANDLE_VALUE)
5765 DWORD last_error = GetLastError();
5771 case ERROR_FILE_NOT_FOUND:
5772 hr = STG_E_FILENOTFOUND;
5775 case ERROR_PATH_NOT_FOUND:
5776 hr = STG_E_PATHNOTFOUND;
5779 case ERROR_ACCESS_DENIED:
5780 case ERROR_WRITE_PROTECT:
5781 hr = STG_E_ACCESSDENIED;
5784 case ERROR_SHARING_VIOLATION:
5785 hr = STG_E_SHAREVIOLATION;
5796 * Refuse to open the file if it's too small to be a structured storage file
5797 * FIXME: verify the file when reading instead of here
5799 if (GetFileSize(hFile, NULL) < 0x100)
5802 hr = STG_E_FILEALREADYEXISTS;
5807 * Allocate and initialize the new IStorage32object.
5809 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5811 if (newStorage == 0)
5813 hr = STG_E_INSUFFICIENTMEMORY;
5817 /* Initialize the storage */
5818 hr = StorageImpl_Construct(
5829 HeapFree(GetProcessHeap(), 0, newStorage);
5831 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5833 if(hr == STG_E_INVALIDHEADER)
5834 hr = STG_E_FILEALREADYEXISTS;
5838 /* prepare the file name string given in lieu of the root property name */
5839 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5840 memcpy(newStorage->filename, fullname, DIRENTRY_NAME_BUFFER_LEN);
5841 newStorage->filename[DIRENTRY_NAME_BUFFER_LEN-1] = '\0';
5844 * Get an "out" pointer for the caller.
5846 hr = StorageBaseImpl_QueryInterface(
5847 (IStorage*)newStorage,
5852 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5856 /******************************************************************************
5857 * StgCreateDocfileOnILockBytes [OLE32.@]
5859 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5863 IStorage** ppstgOpen)
5865 StorageImpl* newStorage = 0;
5868 if ((ppstgOpen == 0) || (plkbyt == 0))
5869 return STG_E_INVALIDPOINTER;
5872 * Allocate and initialize the new IStorage object.
5874 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5876 if (newStorage == 0)
5877 return STG_E_INSUFFICIENTMEMORY;
5879 hr = StorageImpl_Construct(
5890 HeapFree(GetProcessHeap(), 0, newStorage);
5895 * Get an "out" pointer for the caller.
5897 hr = StorageBaseImpl_QueryInterface(
5898 (IStorage*)newStorage,
5905 /******************************************************************************
5906 * StgOpenStorageOnILockBytes [OLE32.@]
5908 HRESULT WINAPI StgOpenStorageOnILockBytes(
5910 IStorage *pstgPriority,
5914 IStorage **ppstgOpen)
5916 StorageImpl* newStorage = 0;
5919 if ((plkbyt == 0) || (ppstgOpen == 0))
5920 return STG_E_INVALIDPOINTER;
5922 if ( FAILED( validateSTGM(grfMode) ))
5923 return STG_E_INVALIDFLAG;
5928 * Allocate and initialize the new IStorage object.
5930 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5932 if (newStorage == 0)
5933 return STG_E_INSUFFICIENTMEMORY;
5935 hr = StorageImpl_Construct(
5946 HeapFree(GetProcessHeap(), 0, newStorage);
5951 * Get an "out" pointer for the caller.
5953 hr = StorageBaseImpl_QueryInterface(
5954 (IStorage*)newStorage,
5961 /******************************************************************************
5962 * StgSetTimes [ole32.@]
5963 * StgSetTimes [OLE32.@]
5967 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
5968 FILETIME const *patime, FILETIME const *pmtime)
5970 IStorage *stg = NULL;
5973 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
5975 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
5979 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
5980 IStorage_Release(stg);
5986 /******************************************************************************
5987 * StgIsStorageILockBytes [OLE32.@]
5989 * Determines if the ILockBytes contains a storage object.
5991 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5994 ULARGE_INTEGER offset;
5996 offset.u.HighPart = 0;
5997 offset.u.LowPart = 0;
5999 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6001 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6007 /******************************************************************************
6008 * WriteClassStg [OLE32.@]
6010 * This method will store the specified CLSID in the specified storage object
6012 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6017 return E_INVALIDARG;
6020 return STG_E_INVALIDPOINTER;
6022 hRes = IStorage_SetClass(pStg, rclsid);
6027 /***********************************************************************
6028 * ReadClassStg (OLE32.@)
6030 * This method reads the CLSID previously written to a storage object with
6031 * the WriteClassStg.
6034 * pstg [I] IStorage pointer
6035 * pclsid [O] Pointer to where the CLSID is written
6039 * Failure: HRESULT code.
6041 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6046 TRACE("(%p, %p)\n", pstg, pclsid);
6048 if(!pstg || !pclsid)
6049 return E_INVALIDARG;
6052 * read a STATSTG structure (contains the clsid) from the storage
6054 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6057 *pclsid=pstatstg.clsid;
6062 /***********************************************************************
6063 * OleLoadFromStream (OLE32.@)
6065 * This function loads an object from stream
6067 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6071 LPPERSISTSTREAM xstm;
6073 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6075 res=ReadClassStm(pStm,&clsid);
6078 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6081 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6083 IUnknown_Release((IUnknown*)*ppvObj);
6086 res=IPersistStream_Load(xstm,pStm);
6087 IPersistStream_Release(xstm);
6088 /* FIXME: all refcounts ok at this point? I think they should be:
6091 * xstm : 0 (released)
6096 /***********************************************************************
6097 * OleSaveToStream (OLE32.@)
6099 * This function saves an object with the IPersistStream interface on it
6100 * to the specified stream.
6102 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6108 TRACE("(%p,%p)\n",pPStm,pStm);
6110 res=IPersistStream_GetClassID(pPStm,&clsid);
6112 if (SUCCEEDED(res)){
6114 res=WriteClassStm(pStm,&clsid);
6118 res=IPersistStream_Save(pPStm,pStm,TRUE);
6121 TRACE("Finished Save\n");
6125 /****************************************************************************
6126 * This method validate a STGM parameter that can contain the values below
6128 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6129 * The stgm values contained in 0xffff0000 are bitmasks.
6131 * STGM_DIRECT 0x00000000
6132 * STGM_TRANSACTED 0x00010000
6133 * STGM_SIMPLE 0x08000000
6135 * STGM_READ 0x00000000
6136 * STGM_WRITE 0x00000001
6137 * STGM_READWRITE 0x00000002
6139 * STGM_SHARE_DENY_NONE 0x00000040
6140 * STGM_SHARE_DENY_READ 0x00000030
6141 * STGM_SHARE_DENY_WRITE 0x00000020
6142 * STGM_SHARE_EXCLUSIVE 0x00000010
6144 * STGM_PRIORITY 0x00040000
6145 * STGM_DELETEONRELEASE 0x04000000
6147 * STGM_CREATE 0x00001000
6148 * STGM_CONVERT 0x00020000
6149 * STGM_FAILIFTHERE 0x00000000
6151 * STGM_NOSCRATCH 0x00100000
6152 * STGM_NOSNAPSHOT 0x00200000
6154 static HRESULT validateSTGM(DWORD stgm)
6156 DWORD access = STGM_ACCESS_MODE(stgm);
6157 DWORD share = STGM_SHARE_MODE(stgm);
6158 DWORD create = STGM_CREATE_MODE(stgm);
6160 if (stgm&~STGM_KNOWN_FLAGS)
6162 ERR("unknown flags %08x\n", stgm);
6170 case STGM_READWRITE:
6178 case STGM_SHARE_DENY_NONE:
6179 case STGM_SHARE_DENY_READ:
6180 case STGM_SHARE_DENY_WRITE:
6181 case STGM_SHARE_EXCLUSIVE:
6190 case STGM_FAILIFTHERE:
6197 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6199 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6203 * STGM_CREATE | STGM_CONVERT
6204 * if both are false, STGM_FAILIFTHERE is set to TRUE
6206 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6210 * STGM_NOSCRATCH requires STGM_TRANSACTED
6212 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6216 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6217 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6219 if ( (stgm & STGM_NOSNAPSHOT) &&
6220 (!(stgm & STGM_TRANSACTED) ||
6221 share == STGM_SHARE_EXCLUSIVE ||
6222 share == STGM_SHARE_DENY_WRITE) )
6228 /****************************************************************************
6229 * GetShareModeFromSTGM
6231 * This method will return a share mode flag from a STGM value.
6232 * The STGM value is assumed valid.
6234 static DWORD GetShareModeFromSTGM(DWORD stgm)
6236 switch (STGM_SHARE_MODE(stgm))
6238 case STGM_SHARE_DENY_NONE:
6239 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6240 case STGM_SHARE_DENY_READ:
6241 return FILE_SHARE_WRITE;
6242 case STGM_SHARE_DENY_WRITE:
6243 return FILE_SHARE_READ;
6244 case STGM_SHARE_EXCLUSIVE:
6247 ERR("Invalid share mode!\n");
6252 /****************************************************************************
6253 * GetAccessModeFromSTGM
6255 * This method will return an access mode flag from a STGM value.
6256 * The STGM value is assumed valid.
6258 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6260 switch (STGM_ACCESS_MODE(stgm))
6263 return GENERIC_READ;
6265 case STGM_READWRITE:
6266 return GENERIC_READ | GENERIC_WRITE;
6268 ERR("Invalid access mode!\n");
6273 /****************************************************************************
6274 * GetCreationModeFromSTGM
6276 * This method will return a creation mode flag from a STGM value.
6277 * The STGM value is assumed valid.
6279 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6281 switch(STGM_CREATE_MODE(stgm))
6284 return CREATE_ALWAYS;
6286 FIXME("STGM_CONVERT not implemented!\n");
6288 case STGM_FAILIFTHERE:
6291 ERR("Invalid create mode!\n");
6297 /*************************************************************************
6298 * OLECONVERT_LoadOLE10 [Internal]
6300 * Loads the OLE10 STREAM to memory
6303 * pOleStream [I] The OLESTREAM
6304 * pData [I] Data Structure for the OLESTREAM Data
6308 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6309 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6312 * This function is used by OleConvertOLESTREAMToIStorage only.
6314 * Memory allocated for pData must be freed by the caller
6316 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6319 HRESULT hRes = S_OK;
6323 pData->pData = NULL;
6324 pData->pstrOleObjFileName = NULL;
6326 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6329 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6330 if(dwSize != sizeof(pData->dwOleID))
6332 hRes = CONVERT10_E_OLESTREAM_GET;
6334 else if(pData->dwOleID != OLESTREAM_ID)
6336 hRes = CONVERT10_E_OLESTREAM_FMT;
6347 /* Get the TypeID... more info needed for this field */
6348 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6349 if(dwSize != sizeof(pData->dwTypeID))
6351 hRes = CONVERT10_E_OLESTREAM_GET;
6356 if(pData->dwTypeID != 0)
6358 /* Get the length of the OleTypeName */
6359 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6360 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6362 hRes = CONVERT10_E_OLESTREAM_GET;
6367 if(pData->dwOleTypeNameLength > 0)
6369 /* Get the OleTypeName */
6370 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6371 if(dwSize != pData->dwOleTypeNameLength)
6373 hRes = CONVERT10_E_OLESTREAM_GET;
6379 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6380 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6382 hRes = CONVERT10_E_OLESTREAM_GET;
6386 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6387 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6388 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6389 if(pData->pstrOleObjFileName)
6391 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6392 if(dwSize != pData->dwOleObjFileNameLength)
6394 hRes = CONVERT10_E_OLESTREAM_GET;
6398 hRes = CONVERT10_E_OLESTREAM_GET;
6403 /* Get the Width of the Metafile */
6404 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6405 if(dwSize != sizeof(pData->dwMetaFileWidth))
6407 hRes = CONVERT10_E_OLESTREAM_GET;
6411 /* Get the Height of the Metafile */
6412 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6413 if(dwSize != sizeof(pData->dwMetaFileHeight))
6415 hRes = CONVERT10_E_OLESTREAM_GET;
6421 /* Get the Length of the Data */
6422 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6423 if(dwSize != sizeof(pData->dwDataLength))
6425 hRes = CONVERT10_E_OLESTREAM_GET;
6429 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6431 if(!bStrem1) /* if it is a second OLE stream data */
6433 pData->dwDataLength -= 8;
6434 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6435 if(dwSize != sizeof(pData->strUnknown))
6437 hRes = CONVERT10_E_OLESTREAM_GET;
6443 if(pData->dwDataLength > 0)
6445 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6447 /* Get Data (ex. IStorage, Metafile, or BMP) */
6450 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6451 if(dwSize != pData->dwDataLength)
6453 hRes = CONVERT10_E_OLESTREAM_GET;
6458 hRes = CONVERT10_E_OLESTREAM_GET;
6467 /*************************************************************************
6468 * OLECONVERT_SaveOLE10 [Internal]
6470 * Saves the OLE10 STREAM From memory
6473 * pData [I] Data Structure for the OLESTREAM Data
6474 * pOleStream [I] The OLESTREAM to save
6478 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6481 * This function is used by OleConvertIStorageToOLESTREAM only.
6484 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6487 HRESULT hRes = S_OK;
6491 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6492 if(dwSize != sizeof(pData->dwOleID))
6494 hRes = CONVERT10_E_OLESTREAM_PUT;
6499 /* Set the TypeID */
6500 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6501 if(dwSize != sizeof(pData->dwTypeID))
6503 hRes = CONVERT10_E_OLESTREAM_PUT;
6507 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6509 /* Set the Length of the OleTypeName */
6510 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6511 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6513 hRes = CONVERT10_E_OLESTREAM_PUT;
6518 if(pData->dwOleTypeNameLength > 0)
6520 /* Set the OleTypeName */
6521 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6522 if(dwSize != pData->dwOleTypeNameLength)
6524 hRes = CONVERT10_E_OLESTREAM_PUT;
6531 /* Set the width of the Metafile */
6532 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6533 if(dwSize != sizeof(pData->dwMetaFileWidth))
6535 hRes = CONVERT10_E_OLESTREAM_PUT;
6541 /* Set the height of the Metafile */
6542 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6543 if(dwSize != sizeof(pData->dwMetaFileHeight))
6545 hRes = CONVERT10_E_OLESTREAM_PUT;
6551 /* Set the length of the Data */
6552 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6553 if(dwSize != sizeof(pData->dwDataLength))
6555 hRes = CONVERT10_E_OLESTREAM_PUT;
6561 if(pData->dwDataLength > 0)
6563 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6564 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6565 if(dwSize != pData->dwDataLength)
6567 hRes = CONVERT10_E_OLESTREAM_PUT;
6575 /*************************************************************************
6576 * OLECONVERT_GetOLE20FromOLE10[Internal]
6578 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6579 * opens it, and copies the content to the dest IStorage for
6580 * OleConvertOLESTREAMToIStorage
6584 * pDestStorage [I] The IStorage to copy the data to
6585 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6586 * nBufferLength [I] The size of the buffer
6595 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6599 IStorage *pTempStorage;
6600 DWORD dwNumOfBytesWritten;
6601 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6602 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6604 /* Create a temp File */
6605 GetTempPathW(MAX_PATH, wstrTempDir);
6606 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6607 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6609 if(hFile != INVALID_HANDLE_VALUE)
6611 /* Write IStorage Data to File */
6612 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6615 /* Open and copy temp storage to the Dest Storage */
6616 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6619 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6620 IStorage_Release(pTempStorage);
6622 DeleteFileW(wstrTempFile);
6627 /*************************************************************************
6628 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6630 * Saves the OLE10 STREAM From memory
6633 * pStorage [I] The Src IStorage to copy
6634 * pData [I] The Dest Memory to write to.
6637 * The size in bytes allocated for pData
6640 * Memory allocated for pData must be freed by the caller
6642 * Used by OleConvertIStorageToOLESTREAM only.
6645 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6649 DWORD nDataLength = 0;
6650 IStorage *pTempStorage;
6651 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6652 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6656 /* Create temp Storage */
6657 GetTempPathW(MAX_PATH, wstrTempDir);
6658 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6659 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6663 /* Copy Src Storage to the Temp Storage */
6664 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6665 IStorage_Release(pTempStorage);
6667 /* Open Temp Storage as a file and copy to memory */
6668 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6669 if(hFile != INVALID_HANDLE_VALUE)
6671 nDataLength = GetFileSize(hFile, NULL);
6672 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6673 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6676 DeleteFileW(wstrTempFile);
6681 /*************************************************************************
6682 * OLECONVERT_CreateOleStream [Internal]
6684 * Creates the "\001OLE" stream in the IStorage if necessary.
6687 * pStorage [I] Dest storage to create the stream in
6693 * This function is used by OleConvertOLESTREAMToIStorage only.
6695 * This stream is still unknown, MS Word seems to have extra data
6696 * but since the data is stored in the OLESTREAM there should be
6697 * no need to recreate the stream. If the stream is manually
6698 * deleted it will create it with this default data.
6701 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6705 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6706 BYTE pOleStreamHeader [] =
6708 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6709 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6710 0x00, 0x00, 0x00, 0x00
6713 /* Create stream if not present */
6714 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6715 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6719 /* Write default Data */
6720 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6721 IStream_Release(pStream);
6725 /* write a string to a stream, preceded by its length */
6726 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6733 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6734 r = IStream_Write( stm, &len, sizeof(len), NULL);
6739 str = CoTaskMemAlloc( len );
6740 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6741 r = IStream_Write( stm, str, len, NULL);
6742 CoTaskMemFree( str );
6746 /* read a string preceded by its length from a stream */
6747 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6750 DWORD len, count = 0;
6754 r = IStream_Read( stm, &len, sizeof(len), &count );
6757 if( count != sizeof(len) )
6758 return E_OUTOFMEMORY;
6760 TRACE("%d bytes\n",len);
6762 str = CoTaskMemAlloc( len );
6764 return E_OUTOFMEMORY;
6766 r = IStream_Read( stm, str, len, &count );
6771 CoTaskMemFree( str );
6772 return E_OUTOFMEMORY;
6775 TRACE("Read string %s\n",debugstr_an(str,len));
6777 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6778 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6780 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6781 CoTaskMemFree( str );
6789 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6790 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6794 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6796 static const BYTE unknown1[12] =
6797 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6798 0xFF, 0xFF, 0xFF, 0xFF};
6799 static const BYTE unknown2[16] =
6800 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6801 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6803 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6804 debugstr_w(lpszUserType), debugstr_w(szClipName),
6805 debugstr_w(szProgIDName));
6807 /* Create a CompObj stream */
6808 r = IStorage_CreateStream(pstg, szwStreamName,
6809 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6813 /* Write CompObj Structure to stream */
6814 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6816 if( SUCCEEDED( r ) )
6817 r = WriteClassStm( pstm, clsid );
6819 if( SUCCEEDED( r ) )
6820 r = STREAM_WriteString( pstm, lpszUserType );
6821 if( SUCCEEDED( r ) )
6822 r = STREAM_WriteString( pstm, szClipName );
6823 if( SUCCEEDED( r ) )
6824 r = STREAM_WriteString( pstm, szProgIDName );
6825 if( SUCCEEDED( r ) )
6826 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6828 IStream_Release( pstm );
6833 /***********************************************************************
6834 * WriteFmtUserTypeStg (OLE32.@)
6836 HRESULT WINAPI WriteFmtUserTypeStg(
6837 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6840 WCHAR szwClipName[0x40];
6841 CLSID clsid = CLSID_NULL;
6842 LPWSTR wstrProgID = NULL;
6845 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6847 /* get the clipboard format name */
6848 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
6851 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6853 /* FIXME: There's room to save a CLSID and its ProgID, but
6854 the CLSID is not looked up in the registry and in all the
6855 tests I wrote it was CLSID_NULL. Where does it come from?
6858 /* get the real program ID. This may fail, but that's fine */
6859 ProgIDFromCLSID(&clsid, &wstrProgID);
6861 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6863 r = STORAGE_WriteCompObj( pstg, &clsid,
6864 lpszUserType, szwClipName, wstrProgID );
6866 CoTaskMemFree(wstrProgID);
6872 /******************************************************************************
6873 * ReadFmtUserTypeStg [OLE32.@]
6875 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6879 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6880 unsigned char unknown1[12];
6881 unsigned char unknown2[16];
6883 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6886 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6888 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6889 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6892 WARN("Failed to open stream r = %08x\n", r);
6896 /* read the various parts of the structure */
6897 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6898 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6900 r = ReadClassStm( stm, &clsid );
6904 r = STREAM_ReadString( stm, &szCLSIDName );
6908 r = STREAM_ReadString( stm, &szOleTypeName );
6912 r = STREAM_ReadString( stm, &szProgIDName );
6916 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6917 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6920 /* ok, success... now we just need to store what we found */
6922 *pcf = RegisterClipboardFormatW( szOleTypeName );
6923 CoTaskMemFree( szOleTypeName );
6925 if( lplpszUserType )
6926 *lplpszUserType = szCLSIDName;
6927 CoTaskMemFree( szProgIDName );
6930 IStream_Release( stm );
6936 /*************************************************************************
6937 * OLECONVERT_CreateCompObjStream [Internal]
6939 * Creates a "\001CompObj" is the destination IStorage if necessary.
6942 * pStorage [I] The dest IStorage to create the CompObj Stream
6944 * strOleTypeName [I] The ProgID
6948 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6951 * This function is used by OleConvertOLESTREAMToIStorage only.
6953 * The stream data is stored in the OLESTREAM and there should be
6954 * no need to recreate the stream. If the stream is manually
6955 * deleted it will attempt to create it by querying the registry.
6959 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6962 HRESULT hStorageRes, hRes = S_OK;
6963 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6964 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6965 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
6967 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6968 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6970 /* Initialize the CompObj structure */
6971 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6972 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
6973 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
6976 /* Create a CompObj stream if it doesn't exist */
6977 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6978 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6979 if(hStorageRes == S_OK)
6981 /* copy the OleTypeName to the compobj struct */
6982 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6983 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6985 /* copy the OleTypeName to the compobj struct */
6986 /* Note: in the test made, these were Identical */
6987 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6988 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6991 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
6992 bufferW, OLESTREAM_MAX_STR_LEN );
6993 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
6999 /* Get the CLSID Default Name from the Registry */
7000 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7001 if(hErr == ERROR_SUCCESS)
7003 char strTemp[OLESTREAM_MAX_STR_LEN];
7004 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7005 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7006 if(hErr == ERROR_SUCCESS)
7008 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7014 /* Write CompObj Structure to stream */
7015 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7017 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7019 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7020 if(IStorageCompObj.dwCLSIDNameLength > 0)
7022 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7024 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7025 if(IStorageCompObj.dwOleTypeNameLength > 0)
7027 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7029 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7030 if(IStorageCompObj.dwProgIDNameLength > 0)
7032 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7034 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7035 IStream_Release(pStream);
7041 /*************************************************************************
7042 * OLECONVERT_CreateOlePresStream[Internal]
7044 * Creates the "\002OlePres000" Stream with the Metafile data
7047 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7048 * dwExtentX [I] Width of the Metafile
7049 * dwExtentY [I] Height of the Metafile
7050 * pData [I] Metafile data
7051 * dwDataLength [I] Size of the Metafile data
7055 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7058 * This function is used by OleConvertOLESTREAMToIStorage only.
7061 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7065 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7066 BYTE pOlePresStreamHeader [] =
7068 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7069 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7070 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7071 0x00, 0x00, 0x00, 0x00
7074 BYTE pOlePresStreamHeaderEmpty [] =
7076 0x00, 0x00, 0x00, 0x00,
7077 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7078 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7079 0x00, 0x00, 0x00, 0x00
7082 /* Create the OlePres000 Stream */
7083 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7084 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7089 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7091 memset(&OlePres, 0, sizeof(OlePres));
7092 /* Do we have any metafile data to save */
7093 if(dwDataLength > 0)
7095 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7096 nHeaderSize = sizeof(pOlePresStreamHeader);
7100 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7101 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7103 /* Set width and height of the metafile */
7104 OlePres.dwExtentX = dwExtentX;
7105 OlePres.dwExtentY = -dwExtentY;
7107 /* Set Data and Length */
7108 if(dwDataLength > sizeof(METAFILEPICT16))
7110 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7111 OlePres.pData = &(pData[8]);
7113 /* Save OlePres000 Data to Stream */
7114 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7115 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7116 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7117 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7118 if(OlePres.dwSize > 0)
7120 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7122 IStream_Release(pStream);
7126 /*************************************************************************
7127 * OLECONVERT_CreateOle10NativeStream [Internal]
7129 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7132 * pStorage [I] Dest storage to create the stream in
7133 * pData [I] Ole10 Native Data (ex. bmp)
7134 * dwDataLength [I] Size of the Ole10 Native Data
7140 * This function is used by OleConvertOLESTREAMToIStorage only.
7142 * Might need to verify the data and return appropriate error message
7145 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7149 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7151 /* Create the Ole10Native Stream */
7152 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7153 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7157 /* Write info to stream */
7158 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7159 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7160 IStream_Release(pStream);
7165 /*************************************************************************
7166 * OLECONVERT_GetOLE10ProgID [Internal]
7168 * Finds the ProgID (or OleTypeID) from the IStorage
7171 * pStorage [I] The Src IStorage to get the ProgID
7172 * strProgID [I] the ProgID string to get
7173 * dwSize [I] the size of the string
7177 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7180 * This function is used by OleConvertIStorageToOLESTREAM only.
7184 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7188 LARGE_INTEGER iSeekPos;
7189 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7190 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7192 /* Open the CompObj Stream */
7193 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7194 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7198 /*Get the OleType from the CompObj Stream */
7199 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7200 iSeekPos.u.HighPart = 0;
7202 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7203 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7204 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7205 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7206 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7207 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7208 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7210 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7213 IStream_Read(pStream, strProgID, *dwSize, NULL);
7215 IStream_Release(pStream);
7220 LPOLESTR wstrProgID;
7222 /* Get the OleType from the registry */
7223 REFCLSID clsid = &(stat.clsid);
7224 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7225 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7228 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7235 /*************************************************************************
7236 * OLECONVERT_GetOle10PresData [Internal]
7238 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7241 * pStorage [I] Src IStroage
7242 * pOleStream [I] Dest OleStream Mem Struct
7248 * This function is used by OleConvertIStorageToOLESTREAM only.
7250 * Memory allocated for pData must be freed by the caller
7254 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7259 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7261 /* Initialize Default data for OLESTREAM */
7262 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7263 pOleStreamData[0].dwTypeID = 2;
7264 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7265 pOleStreamData[1].dwTypeID = 0;
7266 pOleStreamData[0].dwMetaFileWidth = 0;
7267 pOleStreamData[0].dwMetaFileHeight = 0;
7268 pOleStreamData[0].pData = NULL;
7269 pOleStreamData[1].pData = NULL;
7271 /* Open Ole10Native Stream */
7272 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7273 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7277 /* Read Size and Data */
7278 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7279 if(pOleStreamData->dwDataLength > 0)
7281 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7282 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7284 IStream_Release(pStream);
7290 /*************************************************************************
7291 * OLECONVERT_GetOle20PresData[Internal]
7293 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7296 * pStorage [I] Src IStroage
7297 * pOleStreamData [I] Dest OleStream Mem Struct
7303 * This function is used by OleConvertIStorageToOLESTREAM only.
7305 * Memory allocated for pData must be freed by the caller
7307 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7311 OLECONVERT_ISTORAGE_OLEPRES olePress;
7312 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7314 /* Initialize Default data for OLESTREAM */
7315 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7316 pOleStreamData[0].dwTypeID = 2;
7317 pOleStreamData[0].dwMetaFileWidth = 0;
7318 pOleStreamData[0].dwMetaFileHeight = 0;
7319 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7320 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7321 pOleStreamData[1].dwTypeID = 0;
7322 pOleStreamData[1].dwOleTypeNameLength = 0;
7323 pOleStreamData[1].strOleTypeName[0] = 0;
7324 pOleStreamData[1].dwMetaFileWidth = 0;
7325 pOleStreamData[1].dwMetaFileHeight = 0;
7326 pOleStreamData[1].pData = NULL;
7327 pOleStreamData[1].dwDataLength = 0;
7330 /* Open OlePress000 stream */
7331 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7332 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7335 LARGE_INTEGER iSeekPos;
7336 METAFILEPICT16 MetaFilePict;
7337 static const char strMetafilePictName[] = "METAFILEPICT";
7339 /* Set the TypeID for a Metafile */
7340 pOleStreamData[1].dwTypeID = 5;
7342 /* Set the OleTypeName to Metafile */
7343 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7344 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7346 iSeekPos.u.HighPart = 0;
7347 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7349 /* Get Presentation Data */
7350 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7351 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7352 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7353 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7355 /*Set width and Height */
7356 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7357 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7358 if(olePress.dwSize > 0)
7361 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7363 /* Set MetaFilePict struct */
7364 MetaFilePict.mm = 8;
7365 MetaFilePict.xExt = olePress.dwExtentX;
7366 MetaFilePict.yExt = olePress.dwExtentY;
7367 MetaFilePict.hMF = 0;
7369 /* Get Metafile Data */
7370 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7371 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7372 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7374 IStream_Release(pStream);
7378 /*************************************************************************
7379 * OleConvertOLESTREAMToIStorage [OLE32.@]
7384 * DVTARGETDEVICE parameter is not handled
7385 * Still unsure of some mem fields for OLE 10 Stream
7386 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7387 * and "\001OLE" streams
7390 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7391 LPOLESTREAM pOleStream,
7393 const DVTARGETDEVICE* ptd)
7397 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7399 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7401 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7405 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7408 if(pstg == NULL || pOleStream == NULL)
7410 hRes = E_INVALIDARG;
7415 /* Load the OLESTREAM to Memory */
7416 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7421 /* Load the OLESTREAM to Memory (part 2)*/
7422 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7428 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7430 /* Do we have the IStorage Data in the OLESTREAM */
7431 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7433 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7434 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7438 /* It must be an original OLE 1.0 source */
7439 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7444 /* It must be an original OLE 1.0 source */
7445 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7448 /* Create CompObj Stream if necessary */
7449 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7452 /*Create the Ole Stream if necessary */
7453 OLECONVERT_CreateOleStream(pstg);
7458 /* Free allocated memory */
7459 for(i=0; i < 2; i++)
7461 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7462 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7463 pOleStreamData[i].pstrOleObjFileName = NULL;
7468 /*************************************************************************
7469 * OleConvertIStorageToOLESTREAM [OLE32.@]
7476 * Still unsure of some mem fields for OLE 10 Stream
7477 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7478 * and "\001OLE" streams.
7481 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7483 LPOLESTREAM pOleStream)
7486 HRESULT hRes = S_OK;
7488 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7489 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7491 TRACE("%p %p\n", pstg, pOleStream);
7493 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7495 if(pstg == NULL || pOleStream == NULL)
7497 hRes = E_INVALIDARG;
7501 /* Get the ProgID */
7502 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7503 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7507 /* Was it originally Ole10 */
7508 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7511 IStream_Release(pStream);
7512 /* Get Presentation Data for Ole10Native */
7513 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7517 /* Get Presentation Data (OLE20) */
7518 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7521 /* Save OLESTREAM */
7522 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7525 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7530 /* Free allocated memory */
7531 for(i=0; i < 2; i++)
7533 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7539 /***********************************************************************
7540 * GetConvertStg (OLE32.@)
7542 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7543 FIXME("unimplemented stub!\n");
7547 /******************************************************************************
7548 * StgIsStorageFile [OLE32.@]
7549 * Verify if the file contains a storage object
7555 * S_OK if file has magic bytes as a storage object
7556 * S_FALSE if file is not storage
7559 StgIsStorageFile(LPCOLESTR fn)
7565 TRACE("%s\n", debugstr_w(fn));
7566 hf = CreateFileW(fn, GENERIC_READ,
7567 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7568 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7570 if (hf == INVALID_HANDLE_VALUE)
7571 return STG_E_FILENOTFOUND;
7573 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7575 WARN(" unable to read file\n");
7582 if (bytes_read != 8) {
7583 WARN(" too short\n");
7587 if (!memcmp(magic,STORAGE_magic,8)) {
7592 WARN(" -> Invalid header.\n");
7596 /***********************************************************************
7597 * WriteClassStm (OLE32.@)
7599 * Writes a CLSID to a stream.
7602 * pStm [I] Stream to write to.
7603 * rclsid [I] CLSID to write.
7607 * Failure: HRESULT code.
7609 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7611 TRACE("(%p,%p)\n",pStm,rclsid);
7613 if (!pStm || !rclsid)
7614 return E_INVALIDARG;
7616 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7619 /***********************************************************************
7620 * ReadClassStm (OLE32.@)
7622 * Reads a CLSID from a stream.
7625 * pStm [I] Stream to read from.
7626 * rclsid [O] CLSID to read.
7630 * Failure: HRESULT code.
7632 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7637 TRACE("(%p,%p)\n",pStm,pclsid);
7639 if (!pStm || !pclsid)
7640 return E_INVALIDARG;
7642 /* clear the output args */
7643 *pclsid = CLSID_NULL;
7645 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7650 if (nbByte != sizeof(CLSID))
7651 return STG_E_READFAULT;