winmm: dwBytesRecorded controls how much is played, not dwBufferLength.
[wine/hramrach.git] / dlls / ole32 / storage32.c
blob88e05244fdb8b1e8d702d2b089841ef2017ad5b5
1 /*
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
27 * NOTES
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.
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #define COBJMACROS
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winuser.h"
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
53 #include "winreg.h"
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootEntryName[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base;
82 * Entry in the parent's stream tracking list
84 struct list ParentListEntry;
86 StorageBaseImpl *parentStorage;
88 typedef struct StorageInternalImpl StorageInternalImpl;
90 /* Method definitions for the Storage32InternalImpl class. */
91 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
92 DWORD openFlags, DirRef storageDirEntry);
93 static void StorageImpl_Destroy(StorageBaseImpl* iface);
94 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
95 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
96 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
97 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
98 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
99 static void StorageImpl_SaveFileHeader(StorageImpl* This);
101 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
102 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
103 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
104 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
105 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
107 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
108 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
109 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
111 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
112 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
113 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
114 ULONG blockIndex, ULONG offset, DWORD value);
115 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
116 ULONG blockIndex, ULONG offset, DWORD* value);
118 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
119 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
121 /****************************************************************************
122 * Transacted storage object that reads/writes a snapshot file.
124 typedef struct TransactedSnapshotImpl
126 struct StorageBaseImpl base;
129 * Changes are temporarily saved to the snapshot.
131 StorageBaseImpl *snapshot;
134 * Changes are committed to the transacted parent.
136 StorageBaseImpl *transactedParent;
137 } TransactedSnapshotImpl;
139 /* Generic function to create a transacted wrapper for a direct storage object. */
140 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
142 /* OLESTREAM memory structure to use for Get and Put Routines */
143 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
144 typedef struct
146 DWORD dwOleID;
147 DWORD dwTypeID;
148 DWORD dwOleTypeNameLength;
149 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
150 CHAR *pstrOleObjFileName;
151 DWORD dwOleObjFileNameLength;
152 DWORD dwMetaFileWidth;
153 DWORD dwMetaFileHeight;
154 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
155 DWORD dwDataLength;
156 BYTE *pData;
157 }OLECONVERT_OLESTREAM_DATA;
159 /* CompObj Stream structure */
160 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
161 typedef struct
163 BYTE byUnknown1[12];
164 CLSID clsid;
165 DWORD dwCLSIDNameLength;
166 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
167 DWORD dwOleTypeNameLength;
168 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
169 DWORD dwProgIDNameLength;
170 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
171 BYTE byUnknown2[16];
172 }OLECONVERT_ISTORAGE_COMPOBJ;
175 /* Ole Presentation Stream structure */
176 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
177 typedef struct
179 BYTE byUnknown1[28];
180 DWORD dwExtentX;
181 DWORD dwExtentY;
182 DWORD dwSize;
183 BYTE *pData;
184 }OLECONVERT_ISTORAGE_OLEPRES;
188 /***********************************************************************
189 * Forward declaration of internal functions used by the method DestroyElement
191 static HRESULT deleteStorageContents(
192 StorageBaseImpl *parentStorage,
193 DirRef indexToDelete,
194 DirEntry entryDataToDelete);
196 static HRESULT deleteStreamContents(
197 StorageBaseImpl *parentStorage,
198 DirRef indexToDelete,
199 DirEntry entryDataToDelete);
201 static HRESULT removeFromTree(
202 StorageBaseImpl *This,
203 DirRef parentStorageIndex,
204 DirRef deletedIndex);
206 /***********************************************************************
207 * Declaration of the functions used to manipulate DirEntry
210 static HRESULT insertIntoTree(
211 StorageBaseImpl *This,
212 DirRef parentStorageIndex,
213 DirRef newEntryIndex);
215 static LONG entryNameCmp(
216 const OLECHAR *name1,
217 const OLECHAR *name2);
219 static DirRef findElement(
220 StorageBaseImpl *storage,
221 DirRef storageEntry,
222 const OLECHAR *name,
223 DirEntry *data);
225 static HRESULT findTreeParent(
226 StorageBaseImpl *storage,
227 DirRef storageEntry,
228 const OLECHAR *childName,
229 DirEntry *parentData,
230 DirRef *parentEntry,
231 ULONG *relation);
233 /***********************************************************************
234 * Declaration of miscellaneous functions...
236 static HRESULT validateSTGM(DWORD stgmValue);
238 static DWORD GetShareModeFromSTGM(DWORD stgm);
239 static DWORD GetAccessModeFromSTGM(DWORD stgm);
240 static DWORD GetCreationModeFromSTGM(DWORD stgm);
242 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
245 /****************************************************************************
246 * IEnumSTATSTGImpl definitions.
248 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
249 * This class allows iterating through the content of a storage and to find
250 * specific items inside it.
252 struct IEnumSTATSTGImpl
254 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
255 * since we want to cast this in an IEnumSTATSTG pointer */
257 LONG ref; /* Reference count */
258 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
259 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
261 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
265 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
266 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
268 /************************************************************************
269 ** Block Functions
272 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
274 if (index == 0xffffffff)
275 index = 0;
276 else
277 index ++;
279 return index * BIG_BLOCK_SIZE;
282 /************************************************************************
283 ** Storage32BaseImpl implementation
285 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
286 ULARGE_INTEGER offset,
287 void* buffer,
288 ULONG size,
289 ULONG* bytesRead)
291 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
294 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
295 ULARGE_INTEGER offset,
296 const void* buffer,
297 const ULONG size,
298 ULONG* bytesWritten)
300 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
303 /************************************************************************
304 * Storage32BaseImpl_QueryInterface (IUnknown)
306 * This method implements the common QueryInterface for all IStorage32
307 * implementations contained in this file.
309 * See Windows documentation for more details on IUnknown methods.
311 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
312 IStorage* iface,
313 REFIID riid,
314 void** ppvObject)
316 StorageBaseImpl *This = (StorageBaseImpl *)iface;
318 if ( (This==0) || (ppvObject==0) )
319 return E_INVALIDARG;
321 *ppvObject = 0;
323 if (IsEqualGUID(&IID_IUnknown, riid) ||
324 IsEqualGUID(&IID_IStorage, riid))
326 *ppvObject = This;
328 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
330 *ppvObject = &This->pssVtbl;
333 if ((*ppvObject)==0)
334 return E_NOINTERFACE;
336 IStorage_AddRef(iface);
338 return S_OK;
341 /************************************************************************
342 * Storage32BaseImpl_AddRef (IUnknown)
344 * This method implements the common AddRef for all IStorage32
345 * implementations contained in this file.
347 * See Windows documentation for more details on IUnknown methods.
349 static ULONG WINAPI StorageBaseImpl_AddRef(
350 IStorage* iface)
352 StorageBaseImpl *This = (StorageBaseImpl *)iface;
353 ULONG ref = InterlockedIncrement(&This->ref);
355 TRACE("(%p) AddRef to %d\n", This, ref);
357 return ref;
360 /************************************************************************
361 * Storage32BaseImpl_Release (IUnknown)
363 * This method implements the common Release for all IStorage32
364 * implementations contained in this file.
366 * See Windows documentation for more details on IUnknown methods.
368 static ULONG WINAPI StorageBaseImpl_Release(
369 IStorage* iface)
371 StorageBaseImpl *This = (StorageBaseImpl *)iface;
373 ULONG ref = InterlockedDecrement(&This->ref);
375 TRACE("(%p) ReleaseRef to %d\n", This, ref);
377 if (ref == 0)
380 * Since we are using a system of base-classes, we want to call the
381 * destructor of the appropriate derived class. To do this, we are
382 * using virtual functions to implement the destructor.
384 StorageBaseImpl_Destroy(This);
387 return ref;
390 /************************************************************************
391 * Storage32BaseImpl_OpenStream (IStorage)
393 * This method will open the specified stream object from the current storage.
395 * See Windows documentation for more details on IStorage methods.
397 static HRESULT WINAPI StorageBaseImpl_OpenStream(
398 IStorage* iface,
399 const OLECHAR* pwcsName, /* [string][in] */
400 void* reserved1, /* [unique][in] */
401 DWORD grfMode, /* [in] */
402 DWORD reserved2, /* [in] */
403 IStream** ppstm) /* [out] */
405 StorageBaseImpl *This = (StorageBaseImpl *)iface;
406 StgStreamImpl* newStream;
407 DirEntry currentEntry;
408 DirRef streamEntryRef;
409 HRESULT res = STG_E_UNKNOWN;
411 TRACE("(%p, %s, %p, %x, %d, %p)\n",
412 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
414 if ( (pwcsName==NULL) || (ppstm==0) )
416 res = E_INVALIDARG;
417 goto end;
420 *ppstm = NULL;
422 if ( FAILED( validateSTGM(grfMode) ) ||
423 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
425 res = STG_E_INVALIDFLAG;
426 goto end;
430 * As documented.
432 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
434 res = STG_E_INVALIDFUNCTION;
435 goto end;
438 if (This->reverted)
440 res = STG_E_REVERTED;
441 goto end;
445 * Check that we're compatible with the parent's storage mode, but
446 * only if we are not in transacted mode
448 if(!(This->openFlags & STGM_TRANSACTED)) {
449 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
451 res = STG_E_INVALIDFLAG;
452 goto end;
457 * Search for the element with the given name
459 streamEntryRef = findElement(
460 This,
461 This->storageDirEntry,
462 pwcsName,
463 &currentEntry);
466 * If it was found, construct the stream object and return a pointer to it.
468 if ( (streamEntryRef!=DIRENTRY_NULL) &&
469 (currentEntry.stgType==STGTY_STREAM) )
471 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
473 /* A single stream cannot be opened a second time. */
474 res = STG_E_ACCESSDENIED;
475 goto end;
478 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
480 if (newStream!=0)
482 newStream->grfMode = grfMode;
483 *ppstm = (IStream*)newStream;
485 IStream_AddRef(*ppstm);
487 res = S_OK;
488 goto end;
491 res = E_OUTOFMEMORY;
492 goto end;
495 res = STG_E_FILENOTFOUND;
497 end:
498 if (res == S_OK)
499 TRACE("<-- IStream %p\n", *ppstm);
500 TRACE("<-- %08x\n", res);
501 return res;
504 /************************************************************************
505 * Storage32BaseImpl_OpenStorage (IStorage)
507 * This method will open a new storage object from the current storage.
509 * See Windows documentation for more details on IStorage methods.
511 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
512 IStorage* iface,
513 const OLECHAR* pwcsName, /* [string][unique][in] */
514 IStorage* pstgPriority, /* [unique][in] */
515 DWORD grfMode, /* [in] */
516 SNB snbExclude, /* [unique][in] */
517 DWORD reserved, /* [in] */
518 IStorage** ppstg) /* [out] */
520 StorageBaseImpl *This = (StorageBaseImpl *)iface;
521 StorageInternalImpl* newStorage;
522 StorageBaseImpl* newTransactedStorage;
523 DirEntry currentEntry;
524 DirRef storageEntryRef;
525 HRESULT res = STG_E_UNKNOWN;
527 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
528 iface, debugstr_w(pwcsName), pstgPriority,
529 grfMode, snbExclude, reserved, ppstg);
531 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
533 res = E_INVALIDARG;
534 goto end;
537 if (This->openFlags & STGM_SIMPLE)
539 res = STG_E_INVALIDFUNCTION;
540 goto end;
543 /* as documented */
544 if (snbExclude != NULL)
546 res = STG_E_INVALIDPARAMETER;
547 goto end;
550 if ( FAILED( validateSTGM(grfMode) ))
552 res = STG_E_INVALIDFLAG;
553 goto end;
557 * As documented.
559 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
560 (grfMode & STGM_DELETEONRELEASE) ||
561 (grfMode & STGM_PRIORITY) )
563 res = STG_E_INVALIDFUNCTION;
564 goto end;
567 if (This->reverted)
568 return STG_E_REVERTED;
571 * Check that we're compatible with the parent's storage mode,
572 * but only if we are not transacted
574 if(!(This->openFlags & STGM_TRANSACTED)) {
575 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
577 res = STG_E_ACCESSDENIED;
578 goto end;
582 *ppstg = NULL;
584 storageEntryRef = findElement(
585 This,
586 This->storageDirEntry,
587 pwcsName,
588 &currentEntry);
590 if ( (storageEntryRef!=DIRENTRY_NULL) &&
591 (currentEntry.stgType==STGTY_STORAGE) )
593 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
595 /* A single storage cannot be opened a second time. */
596 res = STG_E_ACCESSDENIED;
597 goto end;
600 newStorage = StorageInternalImpl_Construct(
601 This,
602 grfMode,
603 storageEntryRef);
605 if (newStorage != 0)
607 if (grfMode & STGM_TRANSACTED)
609 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
611 if (FAILED(res))
613 HeapFree(GetProcessHeap(), 0, newStorage);
614 goto end;
617 *ppstg = (IStorage*)newTransactedStorage;
619 else
621 *ppstg = (IStorage*)newStorage;
624 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
626 res = S_OK;
627 goto end;
630 res = STG_E_INSUFFICIENTMEMORY;
631 goto end;
634 res = STG_E_FILENOTFOUND;
636 end:
637 TRACE("<-- %08x\n", res);
638 return res;
641 /************************************************************************
642 * Storage32BaseImpl_EnumElements (IStorage)
644 * This method will create an enumerator object that can be used to
645 * retrieve information about all the elements in the storage object.
647 * See Windows documentation for more details on IStorage methods.
649 static HRESULT WINAPI StorageBaseImpl_EnumElements(
650 IStorage* iface,
651 DWORD reserved1, /* [in] */
652 void* reserved2, /* [size_is][unique][in] */
653 DWORD reserved3, /* [in] */
654 IEnumSTATSTG** ppenum) /* [out] */
656 StorageBaseImpl *This = (StorageBaseImpl *)iface;
657 IEnumSTATSTGImpl* newEnum;
659 TRACE("(%p, %d, %p, %d, %p)\n",
660 iface, reserved1, reserved2, reserved3, ppenum);
662 if ( (This==0) || (ppenum==0))
663 return E_INVALIDARG;
665 if (This->reverted)
666 return STG_E_REVERTED;
668 newEnum = IEnumSTATSTGImpl_Construct(
669 This,
670 This->storageDirEntry);
672 if (newEnum!=0)
674 *ppenum = (IEnumSTATSTG*)newEnum;
676 IEnumSTATSTG_AddRef(*ppenum);
678 return S_OK;
681 return E_OUTOFMEMORY;
684 /************************************************************************
685 * Storage32BaseImpl_Stat (IStorage)
687 * This method will retrieve information about this storage object.
689 * See Windows documentation for more details on IStorage methods.
691 static HRESULT WINAPI StorageBaseImpl_Stat(
692 IStorage* iface,
693 STATSTG* pstatstg, /* [out] */
694 DWORD grfStatFlag) /* [in] */
696 StorageBaseImpl *This = (StorageBaseImpl *)iface;
697 DirEntry currentEntry;
698 HRESULT res = STG_E_UNKNOWN;
700 TRACE("(%p, %p, %x)\n",
701 iface, pstatstg, grfStatFlag);
703 if ( (This==0) || (pstatstg==0))
705 res = E_INVALIDARG;
706 goto end;
709 if (This->reverted)
711 res = STG_E_REVERTED;
712 goto end;
715 res = StorageBaseImpl_ReadDirEntry(
716 This,
717 This->storageDirEntry,
718 &currentEntry);
720 if (SUCCEEDED(res))
722 StorageUtl_CopyDirEntryToSTATSTG(
723 This,
724 pstatstg,
725 &currentEntry,
726 grfStatFlag);
728 pstatstg->grfMode = This->openFlags;
729 pstatstg->grfStateBits = This->stateBits;
732 end:
733 if (res == S_OK)
735 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);
737 TRACE("<-- %08x\n", res);
738 return res;
741 /************************************************************************
742 * Storage32BaseImpl_RenameElement (IStorage)
744 * This method will rename the specified element.
746 * See Windows documentation for more details on IStorage methods.
748 static HRESULT WINAPI StorageBaseImpl_RenameElement(
749 IStorage* iface,
750 const OLECHAR* pwcsOldName, /* [in] */
751 const OLECHAR* pwcsNewName) /* [in] */
753 StorageBaseImpl *This = (StorageBaseImpl *)iface;
754 DirEntry currentEntry;
755 DirRef currentEntryRef;
757 TRACE("(%p, %s, %s)\n",
758 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
760 if (This->reverted)
761 return STG_E_REVERTED;
763 currentEntryRef = findElement(This,
764 This->storageDirEntry,
765 pwcsNewName,
766 &currentEntry);
768 if (currentEntryRef != DIRENTRY_NULL)
771 * There is already an element with the new name
773 return STG_E_FILEALREADYEXISTS;
777 * Search for the old element name
779 currentEntryRef = findElement(This,
780 This->storageDirEntry,
781 pwcsOldName,
782 &currentEntry);
784 if (currentEntryRef != DIRENTRY_NULL)
786 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
787 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
789 WARN("Element is already open; cannot rename.\n");
790 return STG_E_ACCESSDENIED;
793 /* Remove the element from its current position in the tree */
794 removeFromTree(This, This->storageDirEntry,
795 currentEntryRef);
797 /* Change the name of the element */
798 strcpyW(currentEntry.name, pwcsNewName);
800 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
801 &currentEntry);
803 /* Insert the element in a new position in the tree */
804 insertIntoTree(This, This->storageDirEntry,
805 currentEntryRef);
807 else
810 * There is no element with the old name
812 return STG_E_FILENOTFOUND;
815 return S_OK;
818 /************************************************************************
819 * Storage32BaseImpl_CreateStream (IStorage)
821 * This method will create a stream object within this storage
823 * See Windows documentation for more details on IStorage methods.
825 static HRESULT WINAPI StorageBaseImpl_CreateStream(
826 IStorage* iface,
827 const OLECHAR* pwcsName, /* [string][in] */
828 DWORD grfMode, /* [in] */
829 DWORD reserved1, /* [in] */
830 DWORD reserved2, /* [in] */
831 IStream** ppstm) /* [out] */
833 StorageBaseImpl *This = (StorageBaseImpl *)iface;
834 StgStreamImpl* newStream;
835 DirEntry currentEntry, newStreamEntry;
836 DirRef currentEntryRef, newStreamEntryRef;
837 HRESULT hr;
839 TRACE("(%p, %s, %x, %d, %d, %p)\n",
840 iface, debugstr_w(pwcsName), grfMode,
841 reserved1, reserved2, ppstm);
843 if (ppstm == 0)
844 return STG_E_INVALIDPOINTER;
846 if (pwcsName == 0)
847 return STG_E_INVALIDNAME;
849 if (reserved1 || reserved2)
850 return STG_E_INVALIDPARAMETER;
852 if ( FAILED( validateSTGM(grfMode) ))
853 return STG_E_INVALIDFLAG;
855 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
856 return STG_E_INVALIDFLAG;
858 if (This->reverted)
859 return STG_E_REVERTED;
862 * As documented.
864 if ((grfMode & STGM_DELETEONRELEASE) ||
865 (grfMode & STGM_TRANSACTED))
866 return STG_E_INVALIDFUNCTION;
869 * Don't worry about permissions in transacted mode, as we can always write
870 * changes; we just can't always commit them.
872 if(!(This->openFlags & STGM_TRANSACTED)) {
873 /* Can't create a stream on read-only storage */
874 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
875 return STG_E_ACCESSDENIED;
877 /* Can't create a stream with greater access than the parent. */
878 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
879 return STG_E_ACCESSDENIED;
882 if(This->openFlags & STGM_SIMPLE)
883 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
885 *ppstm = 0;
887 currentEntryRef = findElement(This,
888 This->storageDirEntry,
889 pwcsName,
890 &currentEntry);
892 if (currentEntryRef != DIRENTRY_NULL)
895 * An element with this name already exists
897 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
899 IStorage_DestroyElement(iface, pwcsName);
901 else
902 return STG_E_FILEALREADYEXISTS;
906 * memset the empty entry
908 memset(&newStreamEntry, 0, sizeof(DirEntry));
910 newStreamEntry.sizeOfNameString =
911 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
913 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
914 return STG_E_INVALIDNAME;
916 strcpyW(newStreamEntry.name, pwcsName);
918 newStreamEntry.stgType = STGTY_STREAM;
919 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
920 newStreamEntry.size.u.LowPart = 0;
921 newStreamEntry.size.u.HighPart = 0;
923 newStreamEntry.leftChild = DIRENTRY_NULL;
924 newStreamEntry.rightChild = DIRENTRY_NULL;
925 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
927 /* call CoFileTime to get the current time
928 newStreamEntry.ctime
929 newStreamEntry.mtime
932 /* newStreamEntry.clsid */
935 * Create an entry with the new data
937 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
938 if (FAILED(hr))
939 return hr;
942 * Insert the new entry in the parent storage's tree.
944 hr = insertIntoTree(
945 This,
946 This->storageDirEntry,
947 newStreamEntryRef);
948 if (FAILED(hr))
950 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
951 return hr;
955 * Open the stream to return it.
957 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
959 if (newStream != 0)
961 *ppstm = (IStream*)newStream;
963 IStream_AddRef(*ppstm);
965 else
967 return STG_E_INSUFFICIENTMEMORY;
970 return S_OK;
973 /************************************************************************
974 * Storage32BaseImpl_SetClass (IStorage)
976 * This method will write the specified CLSID in the directory entry of this
977 * storage.
979 * See Windows documentation for more details on IStorage methods.
981 static HRESULT WINAPI StorageBaseImpl_SetClass(
982 IStorage* iface,
983 REFCLSID clsid) /* [in] */
985 StorageBaseImpl *This = (StorageBaseImpl *)iface;
986 HRESULT hRes;
987 DirEntry currentEntry;
989 TRACE("(%p, %p)\n", iface, clsid);
991 if (This->reverted)
992 return STG_E_REVERTED;
994 hRes = StorageBaseImpl_ReadDirEntry(This,
995 This->storageDirEntry,
996 &currentEntry);
997 if (SUCCEEDED(hRes))
999 currentEntry.clsid = *clsid;
1001 hRes = StorageBaseImpl_WriteDirEntry(This,
1002 This->storageDirEntry,
1003 &currentEntry);
1006 return hRes;
1009 /************************************************************************
1010 ** Storage32Impl implementation
1013 /************************************************************************
1014 * Storage32BaseImpl_CreateStorage (IStorage)
1016 * This method will create the storage object within the provided storage.
1018 * See Windows documentation for more details on IStorage methods.
1020 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1021 IStorage* iface,
1022 const OLECHAR *pwcsName, /* [string][in] */
1023 DWORD grfMode, /* [in] */
1024 DWORD reserved1, /* [in] */
1025 DWORD reserved2, /* [in] */
1026 IStorage **ppstg) /* [out] */
1028 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1030 DirEntry currentEntry;
1031 DirEntry newEntry;
1032 DirRef currentEntryRef;
1033 DirRef newEntryRef;
1034 HRESULT hr;
1036 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1037 iface, debugstr_w(pwcsName), grfMode,
1038 reserved1, reserved2, ppstg);
1040 if (ppstg == 0)
1041 return STG_E_INVALIDPOINTER;
1043 if (This->openFlags & STGM_SIMPLE)
1045 return STG_E_INVALIDFUNCTION;
1048 if (pwcsName == 0)
1049 return STG_E_INVALIDNAME;
1051 *ppstg = NULL;
1053 if ( FAILED( validateSTGM(grfMode) ) ||
1054 (grfMode & STGM_DELETEONRELEASE) )
1056 WARN("bad grfMode: 0x%x\n", grfMode);
1057 return STG_E_INVALIDFLAG;
1060 if (This->reverted)
1061 return STG_E_REVERTED;
1064 * Check that we're compatible with the parent's storage mode
1066 if ( !(This->openFlags & STGM_TRANSACTED) &&
1067 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1069 WARN("access denied\n");
1070 return STG_E_ACCESSDENIED;
1073 currentEntryRef = findElement(This,
1074 This->storageDirEntry,
1075 pwcsName,
1076 &currentEntry);
1078 if (currentEntryRef != DIRENTRY_NULL)
1081 * An element with this name already exists
1083 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1084 ((This->openFlags & STGM_TRANSACTED) ||
1085 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1087 hr = IStorage_DestroyElement(iface, pwcsName);
1088 if (FAILED(hr))
1089 return hr;
1091 else
1093 WARN("file already exists\n");
1094 return STG_E_FILEALREADYEXISTS;
1097 else if (!(This->openFlags & STGM_TRANSACTED) &&
1098 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1100 WARN("read-only storage\n");
1101 return STG_E_ACCESSDENIED;
1104 memset(&newEntry, 0, sizeof(DirEntry));
1106 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1108 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1110 FIXME("name too long\n");
1111 return STG_E_INVALIDNAME;
1114 strcpyW(newEntry.name, pwcsName);
1116 newEntry.stgType = STGTY_STORAGE;
1117 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1118 newEntry.size.u.LowPart = 0;
1119 newEntry.size.u.HighPart = 0;
1121 newEntry.leftChild = DIRENTRY_NULL;
1122 newEntry.rightChild = DIRENTRY_NULL;
1123 newEntry.dirRootEntry = DIRENTRY_NULL;
1125 /* call CoFileTime to get the current time
1126 newEntry.ctime
1127 newEntry.mtime
1130 /* newEntry.clsid */
1133 * Create a new directory entry for the storage
1135 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1136 if (FAILED(hr))
1137 return hr;
1140 * Insert the new directory entry into the parent storage's tree
1142 hr = insertIntoTree(
1143 This,
1144 This->storageDirEntry,
1145 newEntryRef);
1146 if (FAILED(hr))
1148 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1149 return hr;
1153 * Open it to get a pointer to return.
1155 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1157 if( (hr != S_OK) || (*ppstg == NULL))
1159 return hr;
1163 return S_OK;
1167 /***************************************************************************
1169 * Internal Method
1171 * Reserve a directory entry in the file and initialize it.
1173 static HRESULT StorageImpl_CreateDirEntry(
1174 StorageBaseImpl *base,
1175 const DirEntry *newData,
1176 DirRef *index)
1178 StorageImpl *storage = (StorageImpl*)base;
1179 ULONG currentEntryIndex = 0;
1180 ULONG newEntryIndex = DIRENTRY_NULL;
1181 HRESULT hr = S_OK;
1182 BYTE currentData[RAW_DIRENTRY_SIZE];
1183 WORD sizeOfNameString;
1187 hr = StorageImpl_ReadRawDirEntry(storage,
1188 currentEntryIndex,
1189 currentData);
1191 if (SUCCEEDED(hr))
1193 StorageUtl_ReadWord(
1194 currentData,
1195 OFFSET_PS_NAMELENGTH,
1196 &sizeOfNameString);
1198 if (sizeOfNameString == 0)
1201 * The entry exists and is available, we found it.
1203 newEntryIndex = currentEntryIndex;
1206 else
1209 * We exhausted the directory entries, we will create more space below
1211 newEntryIndex = currentEntryIndex;
1213 currentEntryIndex++;
1215 } while (newEntryIndex == DIRENTRY_NULL);
1218 * grow the directory stream
1220 if (FAILED(hr))
1222 BYTE emptyData[RAW_DIRENTRY_SIZE];
1223 ULARGE_INTEGER newSize;
1224 ULONG entryIndex;
1225 ULONG lastEntry = 0;
1226 ULONG blockCount = 0;
1229 * obtain the new count of blocks in the directory stream
1231 blockCount = BlockChainStream_GetCount(
1232 storage->rootBlockChain)+1;
1235 * initialize the size used by the directory stream
1237 newSize.u.HighPart = 0;
1238 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1241 * add a block to the directory stream
1243 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1246 * memset the empty entry in order to initialize the unused newly
1247 * created entries
1249 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1252 * initialize them
1254 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1256 for(
1257 entryIndex = newEntryIndex + 1;
1258 entryIndex < lastEntry;
1259 entryIndex++)
1261 StorageImpl_WriteRawDirEntry(
1262 storage,
1263 entryIndex,
1264 emptyData);
1268 UpdateRawDirEntry(currentData, newData);
1270 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1272 if (SUCCEEDED(hr))
1273 *index = newEntryIndex;
1275 return hr;
1278 /***************************************************************************
1280 * Internal Method
1282 * Mark a directory entry in the file as free.
1284 static HRESULT StorageImpl_DestroyDirEntry(
1285 StorageBaseImpl *base,
1286 DirRef index)
1288 HRESULT hr;
1289 BYTE emptyData[RAW_DIRENTRY_SIZE];
1290 StorageImpl *storage = (StorageImpl*)base;
1292 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1294 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1296 return hr;
1300 /***************************************************************************
1302 * Internal Method
1304 * Destroy an entry, its attached data, and all entries reachable from it.
1306 static HRESULT DestroyReachableEntries(
1307 StorageBaseImpl *base,
1308 DirRef index)
1310 HRESULT hr = S_OK;
1311 DirEntry data;
1312 ULARGE_INTEGER zero;
1314 zero.QuadPart = 0;
1316 if (index != DIRENTRY_NULL)
1318 hr = StorageBaseImpl_ReadDirEntry(base, index, &data);
1320 if (SUCCEEDED(hr))
1321 hr = DestroyReachableEntries(base, data.dirRootEntry);
1323 if (SUCCEEDED(hr))
1324 hr = DestroyReachableEntries(base, data.leftChild);
1326 if (SUCCEEDED(hr))
1327 hr = DestroyReachableEntries(base, data.rightChild);
1329 if (SUCCEEDED(hr))
1330 hr = StorageBaseImpl_StreamSetSize(base, index, zero);
1332 if (SUCCEEDED(hr))
1333 hr = StorageBaseImpl_DestroyDirEntry(base, index);
1336 return hr;
1340 /****************************************************************************
1342 * Internal Method
1344 * Case insensitive comparison of DirEntry.name by first considering
1345 * their size.
1347 * Returns <0 when name1 < name2
1348 * >0 when name1 > name2
1349 * 0 when name1 == name2
1351 static LONG entryNameCmp(
1352 const OLECHAR *name1,
1353 const OLECHAR *name2)
1355 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1357 while (diff == 0 && *name1 != 0)
1360 * We compare the string themselves only when they are of the same length
1362 diff = toupperW(*name1++) - toupperW(*name2++);
1365 return diff;
1368 /****************************************************************************
1370 * Internal Method
1372 * Add a directory entry to a storage
1374 static HRESULT insertIntoTree(
1375 StorageBaseImpl *This,
1376 DirRef parentStorageIndex,
1377 DirRef newEntryIndex)
1379 DirEntry currentEntry;
1380 DirEntry newEntry;
1383 * Read the inserted entry
1385 StorageBaseImpl_ReadDirEntry(This,
1386 newEntryIndex,
1387 &newEntry);
1390 * Read the storage entry
1392 StorageBaseImpl_ReadDirEntry(This,
1393 parentStorageIndex,
1394 &currentEntry);
1396 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1399 * The root storage contains some element, therefore, start the research
1400 * for the appropriate location.
1402 BOOL found = 0;
1403 DirRef current, next, previous, currentEntryId;
1406 * Keep a reference to the root of the storage's element tree
1408 currentEntryId = currentEntry.dirRootEntry;
1411 * Read
1413 StorageBaseImpl_ReadDirEntry(This,
1414 currentEntry.dirRootEntry,
1415 &currentEntry);
1417 previous = currentEntry.leftChild;
1418 next = currentEntry.rightChild;
1419 current = currentEntryId;
1421 while (found == 0)
1423 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1425 if (diff < 0)
1427 if (previous != DIRENTRY_NULL)
1429 StorageBaseImpl_ReadDirEntry(This,
1430 previous,
1431 &currentEntry);
1432 current = previous;
1434 else
1436 currentEntry.leftChild = newEntryIndex;
1437 StorageBaseImpl_WriteDirEntry(This,
1438 current,
1439 &currentEntry);
1440 found = 1;
1443 else if (diff > 0)
1445 if (next != DIRENTRY_NULL)
1447 StorageBaseImpl_ReadDirEntry(This,
1448 next,
1449 &currentEntry);
1450 current = next;
1452 else
1454 currentEntry.rightChild = newEntryIndex;
1455 StorageBaseImpl_WriteDirEntry(This,
1456 current,
1457 &currentEntry);
1458 found = 1;
1461 else
1464 * Trying to insert an item with the same name in the
1465 * subtree structure.
1467 return STG_E_FILEALREADYEXISTS;
1470 previous = currentEntry.leftChild;
1471 next = currentEntry.rightChild;
1474 else
1477 * The storage is empty, make the new entry the root of its element tree
1479 currentEntry.dirRootEntry = newEntryIndex;
1480 StorageBaseImpl_WriteDirEntry(This,
1481 parentStorageIndex,
1482 &currentEntry);
1485 return S_OK;
1488 /****************************************************************************
1490 * Internal Method
1492 * Find and read the element of a storage with the given name.
1494 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1495 const OLECHAR *name, DirEntry *data)
1497 DirRef currentEntry;
1499 /* Read the storage entry to find the root of the tree. */
1500 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1502 currentEntry = data->dirRootEntry;
1504 while (currentEntry != DIRENTRY_NULL)
1506 LONG cmp;
1508 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1510 cmp = entryNameCmp(name, data->name);
1512 if (cmp == 0)
1513 /* found it */
1514 break;
1516 else if (cmp < 0)
1517 currentEntry = data->leftChild;
1519 else if (cmp > 0)
1520 currentEntry = data->rightChild;
1523 return currentEntry;
1526 /****************************************************************************
1528 * Internal Method
1530 * Find and read the binary tree parent of the element with the given name.
1532 * If there is no such element, find a place where it could be inserted and
1533 * return STG_E_FILENOTFOUND.
1535 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1536 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1537 ULONG *relation)
1539 DirRef childEntry;
1540 DirEntry childData;
1542 /* Read the storage entry to find the root of the tree. */
1543 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1545 *parentEntry = storageEntry;
1546 *relation = DIRENTRY_RELATION_DIR;
1548 childEntry = parentData->dirRootEntry;
1550 while (childEntry != DIRENTRY_NULL)
1552 LONG cmp;
1554 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1556 cmp = entryNameCmp(childName, childData.name);
1558 if (cmp == 0)
1559 /* found it */
1560 break;
1562 else if (cmp < 0)
1564 *parentData = childData;
1565 *parentEntry = childEntry;
1566 *relation = DIRENTRY_RELATION_PREVIOUS;
1568 childEntry = parentData->leftChild;
1571 else if (cmp > 0)
1573 *parentData = childData;
1574 *parentEntry = childEntry;
1575 *relation = DIRENTRY_RELATION_NEXT;
1577 childEntry = parentData->rightChild;
1581 if (childEntry == DIRENTRY_NULL)
1582 return STG_E_FILENOTFOUND;
1583 else
1584 return S_OK;
1588 /*************************************************************************
1589 * CopyTo (IStorage)
1591 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1592 IStorage* iface,
1593 DWORD ciidExclude, /* [in] */
1594 const IID* rgiidExclude, /* [size_is][unique][in] */
1595 SNB snbExclude, /* [unique][in] */
1596 IStorage* pstgDest) /* [unique][in] */
1598 IEnumSTATSTG *elements = 0;
1599 STATSTG curElement, strStat;
1600 HRESULT hr;
1601 IStorage *pstgTmp, *pstgChild;
1602 IStream *pstrTmp, *pstrChild;
1603 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1604 int i;
1606 TRACE("(%p, %d, %p, %p, %p)\n",
1607 iface, ciidExclude, rgiidExclude,
1608 snbExclude, pstgDest);
1610 if ( pstgDest == 0 )
1611 return STG_E_INVALIDPOINTER;
1614 * Enumerate the elements
1616 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1618 if ( hr != S_OK )
1619 return hr;
1622 * set the class ID
1624 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1625 IStorage_SetClass( pstgDest, &curElement.clsid );
1627 for(i = 0; i < ciidExclude; ++i)
1629 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1630 skip_storage = TRUE;
1631 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1632 skip_stream = TRUE;
1633 else
1634 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1640 * Obtain the next element
1642 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1644 if ( hr == S_FALSE )
1646 hr = S_OK; /* done, every element has been copied */
1647 break;
1650 if ( snbExclude )
1652 WCHAR **snb = snbExclude;
1653 skip = FALSE;
1654 while ( *snb != NULL && !skip )
1656 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1657 skip = TRUE;
1658 ++snb;
1662 if ( skip )
1663 goto cleanup;
1665 if (curElement.type == STGTY_STORAGE)
1667 if(skip_storage)
1668 goto cleanup;
1671 * open child source storage
1673 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1674 STGM_READ|STGM_SHARE_EXCLUSIVE,
1675 NULL, 0, &pstgChild );
1677 if (hr != S_OK)
1678 goto cleanup;
1681 * create a new storage in destination storage
1683 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1684 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1685 0, 0,
1686 &pstgTmp );
1688 * if it already exist, don't create a new one use this one
1690 if (hr == STG_E_FILEALREADYEXISTS)
1692 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1693 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1694 NULL, 0, &pstgTmp );
1697 if (hr == S_OK)
1700 * do the copy recursively
1702 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1703 NULL, pstgTmp );
1705 IStorage_Release( pstgTmp );
1708 IStorage_Release( pstgChild );
1710 else if (curElement.type == STGTY_STREAM)
1712 if(skip_stream)
1713 goto cleanup;
1716 * create a new stream in destination storage. If the stream already
1717 * exist, it will be deleted and a new one will be created.
1719 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1720 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1721 0, 0, &pstrTmp );
1723 if (hr != S_OK)
1724 goto cleanup;
1727 * open child stream storage
1729 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1730 STGM_READ|STGM_SHARE_EXCLUSIVE,
1731 0, &pstrChild );
1733 if (hr == S_OK)
1736 * Get the size of the source stream
1738 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1741 * Set the size of the destination stream.
1743 IStream_SetSize(pstrTmp, strStat.cbSize);
1746 * do the copy
1748 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1749 NULL, NULL );
1751 IStream_Release( pstrChild );
1754 IStream_Release( pstrTmp );
1756 else
1758 WARN("unknown element type: %d\n", curElement.type);
1761 cleanup:
1762 CoTaskMemFree(curElement.pwcsName);
1763 } while (hr == S_OK);
1766 * Clean-up
1768 IEnumSTATSTG_Release(elements);
1770 return hr;
1773 /*************************************************************************
1774 * MoveElementTo (IStorage)
1776 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1777 IStorage* iface,
1778 const OLECHAR *pwcsName, /* [string][in] */
1779 IStorage *pstgDest, /* [unique][in] */
1780 const OLECHAR *pwcsNewName,/* [string][in] */
1781 DWORD grfFlags) /* [in] */
1783 FIXME("(%p %s %p %s %u): stub\n", iface,
1784 debugstr_w(pwcsName), pstgDest,
1785 debugstr_w(pwcsNewName), grfFlags);
1786 return E_NOTIMPL;
1789 /*************************************************************************
1790 * Commit (IStorage)
1792 * Ensures that any changes made to a storage object open in transacted mode
1793 * are reflected in the parent storage
1795 * NOTES
1796 * Wine doesn't implement transacted mode, which seems to be a basic
1797 * optimization, so we can ignore this stub for now.
1799 static HRESULT WINAPI StorageImpl_Commit(
1800 IStorage* iface,
1801 DWORD grfCommitFlags)/* [in] */
1803 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1804 return S_OK;
1807 /*************************************************************************
1808 * Revert (IStorage)
1810 * Discard all changes that have been made since the last commit operation
1812 static HRESULT WINAPI StorageImpl_Revert(
1813 IStorage* iface)
1815 TRACE("(%p)\n", iface);
1816 return S_OK;
1819 /*************************************************************************
1820 * DestroyElement (IStorage)
1822 * Strategy: This implementation is built this way for simplicity not for speed.
1823 * I always delete the topmost element of the enumeration and adjust
1824 * the deleted element pointer all the time. This takes longer to
1825 * do but allow to reinvoke DestroyElement whenever we encounter a
1826 * storage object. The optimisation resides in the usage of another
1827 * enumeration strategy that would give all the leaves of a storage
1828 * first. (postfix order)
1830 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1831 IStorage* iface,
1832 const OLECHAR *pwcsName)/* [string][in] */
1834 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1836 HRESULT hr = S_OK;
1837 DirEntry entryToDelete;
1838 DirRef entryToDeleteRef;
1840 TRACE("(%p, %s)\n",
1841 iface, debugstr_w(pwcsName));
1843 if (pwcsName==NULL)
1844 return STG_E_INVALIDPOINTER;
1846 if (This->reverted)
1847 return STG_E_REVERTED;
1849 if ( !(This->openFlags & STGM_TRANSACTED) &&
1850 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1851 return STG_E_ACCESSDENIED;
1853 entryToDeleteRef = findElement(
1854 This,
1855 This->storageDirEntry,
1856 pwcsName,
1857 &entryToDelete);
1859 if ( entryToDeleteRef == DIRENTRY_NULL )
1861 return STG_E_FILENOTFOUND;
1864 if ( entryToDelete.stgType == STGTY_STORAGE )
1866 hr = deleteStorageContents(
1867 This,
1868 entryToDeleteRef,
1869 entryToDelete);
1871 else if ( entryToDelete.stgType == STGTY_STREAM )
1873 hr = deleteStreamContents(
1874 This,
1875 entryToDeleteRef,
1876 entryToDelete);
1879 if (hr!=S_OK)
1880 return hr;
1883 * Remove the entry from its parent storage
1885 hr = removeFromTree(
1886 This,
1887 This->storageDirEntry,
1888 entryToDeleteRef);
1891 * Invalidate the entry
1893 if (SUCCEEDED(hr))
1894 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1896 return hr;
1900 /******************************************************************************
1901 * Internal stream list handlers
1904 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1906 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1907 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1910 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1912 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1913 list_remove(&(strm->StrmListEntry));
1916 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1918 StgStreamImpl *strm;
1920 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1922 if (strm->dirEntry == streamEntry)
1924 return TRUE;
1928 return FALSE;
1931 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1933 StorageInternalImpl *childstg;
1935 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1937 if (childstg->base.storageDirEntry == storageEntry)
1939 return TRUE;
1943 return FALSE;
1946 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1948 struct list *cur, *cur2;
1949 StgStreamImpl *strm=NULL;
1950 StorageInternalImpl *childstg=NULL;
1952 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1953 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1954 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1955 strm->parentStorage = NULL;
1956 list_remove(cur);
1959 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1960 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1961 StorageBaseImpl_Invalidate( &childstg->base );
1964 if (stg->transactedChild)
1966 StorageBaseImpl_Invalidate(stg->transactedChild);
1968 stg->transactedChild = NULL;
1973 /*********************************************************************
1975 * Internal Method
1977 * Delete the contents of a storage entry.
1980 static HRESULT deleteStorageContents(
1981 StorageBaseImpl *parentStorage,
1982 DirRef indexToDelete,
1983 DirEntry entryDataToDelete)
1985 IEnumSTATSTG *elements = 0;
1986 IStorage *childStorage = 0;
1987 STATSTG currentElement;
1988 HRESULT hr;
1989 HRESULT destroyHr = S_OK;
1990 StorageInternalImpl *stg, *stg2;
1992 /* Invalidate any open storage objects. */
1993 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
1995 if (stg->base.storageDirEntry == indexToDelete)
1997 StorageBaseImpl_Invalidate(&stg->base);
2002 * Open the storage and enumerate it
2004 hr = StorageBaseImpl_OpenStorage(
2005 (IStorage*)parentStorage,
2006 entryDataToDelete.name,
2008 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2011 &childStorage);
2013 if (hr != S_OK)
2015 return hr;
2019 * Enumerate the elements
2021 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2026 * Obtain the next element
2028 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2029 if (hr==S_OK)
2031 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2033 CoTaskMemFree(currentElement.pwcsName);
2037 * We need to Reset the enumeration every time because we delete elements
2038 * and the enumeration could be invalid
2040 IEnumSTATSTG_Reset(elements);
2042 } while ((hr == S_OK) && (destroyHr == S_OK));
2044 IStorage_Release(childStorage);
2045 IEnumSTATSTG_Release(elements);
2047 return destroyHr;
2050 /*********************************************************************
2052 * Internal Method
2054 * Perform the deletion of a stream's data
2057 static HRESULT deleteStreamContents(
2058 StorageBaseImpl *parentStorage,
2059 DirRef indexToDelete,
2060 DirEntry entryDataToDelete)
2062 IStream *pis;
2063 HRESULT hr;
2064 ULARGE_INTEGER size;
2065 StgStreamImpl *strm, *strm2;
2067 /* Invalidate any open stream objects. */
2068 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2070 if (strm->dirEntry == indexToDelete)
2072 TRACE("Stream deleted %p\n", strm);
2073 strm->parentStorage = NULL;
2074 list_remove(&strm->StrmListEntry);
2078 size.u.HighPart = 0;
2079 size.u.LowPart = 0;
2081 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2082 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2084 if (hr!=S_OK)
2086 return(hr);
2090 * Zap the stream
2092 hr = IStream_SetSize(pis, size);
2094 if(hr != S_OK)
2096 return hr;
2100 * Release the stream object.
2102 IStream_Release(pis);
2104 return S_OK;
2107 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2109 switch (relation)
2111 case DIRENTRY_RELATION_PREVIOUS:
2112 entry->leftChild = new_target;
2113 break;
2114 case DIRENTRY_RELATION_NEXT:
2115 entry->rightChild = new_target;
2116 break;
2117 case DIRENTRY_RELATION_DIR:
2118 entry->dirRootEntry = new_target;
2119 break;
2120 default:
2121 assert(0);
2125 /*************************************************************************
2127 * Internal Method
2129 * This method removes a directory entry from its parent storage tree without
2130 * freeing any resources attached to it.
2132 static HRESULT removeFromTree(
2133 StorageBaseImpl *This,
2134 DirRef parentStorageIndex,
2135 DirRef deletedIndex)
2137 HRESULT hr = S_OK;
2138 DirEntry entryToDelete;
2139 DirEntry parentEntry;
2140 DirRef parentEntryRef;
2141 ULONG typeOfRelation;
2143 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2145 if (hr != S_OK)
2146 return hr;
2149 * Find the element that links to the one we want to delete.
2151 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2152 &parentEntry, &parentEntryRef, &typeOfRelation);
2154 if (hr != S_OK)
2155 return hr;
2157 if (entryToDelete.leftChild != DIRENTRY_NULL)
2160 * Replace the deleted entry with its left child
2162 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2164 hr = StorageBaseImpl_WriteDirEntry(
2165 This,
2166 parentEntryRef,
2167 &parentEntry);
2168 if(FAILED(hr))
2170 return hr;
2173 if (entryToDelete.rightChild != DIRENTRY_NULL)
2176 * We need to reinsert the right child somewhere. We already know it and
2177 * its children are greater than everything in the left tree, so we
2178 * insert it at the rightmost point in the left tree.
2180 DirRef newRightChildParent = entryToDelete.leftChild;
2181 DirEntry newRightChildParentEntry;
2185 hr = StorageBaseImpl_ReadDirEntry(
2186 This,
2187 newRightChildParent,
2188 &newRightChildParentEntry);
2189 if (FAILED(hr))
2191 return hr;
2194 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2195 newRightChildParent = newRightChildParentEntry.rightChild;
2196 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2198 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2200 hr = StorageBaseImpl_WriteDirEntry(
2201 This,
2202 newRightChildParent,
2203 &newRightChildParentEntry);
2204 if (FAILED(hr))
2206 return hr;
2210 else
2213 * Replace the deleted entry with its right child
2215 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2217 hr = StorageBaseImpl_WriteDirEntry(
2218 This,
2219 parentEntryRef,
2220 &parentEntry);
2221 if(FAILED(hr))
2223 return hr;
2227 return hr;
2231 /******************************************************************************
2232 * SetElementTimes (IStorage)
2234 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2235 IStorage* iface,
2236 const OLECHAR *pwcsName,/* [string][in] */
2237 const FILETIME *pctime, /* [in] */
2238 const FILETIME *patime, /* [in] */
2239 const FILETIME *pmtime) /* [in] */
2241 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2242 return S_OK;
2245 /******************************************************************************
2246 * SetStateBits (IStorage)
2248 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2249 IStorage* iface,
2250 DWORD grfStateBits,/* [in] */
2251 DWORD grfMask) /* [in] */
2253 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2255 if (This->reverted)
2256 return STG_E_REVERTED;
2258 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2259 return S_OK;
2262 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2263 DirRef index, const DirEntry *data)
2265 StorageImpl *This = (StorageImpl*)base;
2266 return StorageImpl_WriteDirEntry(This, index, data);
2269 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2270 DirRef index, DirEntry *data)
2272 StorageImpl *This = (StorageImpl*)base;
2273 return StorageImpl_ReadDirEntry(This, index, data);
2276 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2278 int i;
2280 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2282 if (!This->blockChainCache[i])
2284 return &This->blockChainCache[i];
2288 i = This->blockChainToEvict;
2290 BlockChainStream_Destroy(This->blockChainCache[i]);
2291 This->blockChainCache[i] = NULL;
2293 This->blockChainToEvict++;
2294 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2295 This->blockChainToEvict = 0;
2297 return &This->blockChainCache[i];
2300 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2301 DirRef index)
2303 int i, free_index=-1;
2305 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2307 if (!This->blockChainCache[i])
2309 if (free_index == -1) free_index = i;
2311 else if (This->blockChainCache[i]->ownerDirEntry == index)
2313 return &This->blockChainCache[i];
2317 if (free_index == -1)
2319 free_index = This->blockChainToEvict;
2321 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2322 This->blockChainCache[free_index] = NULL;
2324 This->blockChainToEvict++;
2325 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2326 This->blockChainToEvict = 0;
2329 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2330 return &This->blockChainCache[free_index];
2333 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2334 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2336 StorageImpl *This = (StorageImpl*)base;
2337 DirEntry data;
2338 HRESULT hr;
2339 ULONG bytesToRead;
2341 hr = StorageImpl_ReadDirEntry(This, index, &data);
2342 if (FAILED(hr)) return hr;
2344 if (data.size.QuadPart == 0)
2346 *bytesRead = 0;
2347 return S_OK;
2350 if (offset.QuadPart + size > data.size.QuadPart)
2352 bytesToRead = data.size.QuadPart - offset.QuadPart;
2354 else
2356 bytesToRead = size;
2359 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2361 SmallBlockChainStream *stream;
2363 stream = SmallBlockChainStream_Construct(This, NULL, index);
2364 if (!stream) return E_OUTOFMEMORY;
2366 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2368 SmallBlockChainStream_Destroy(stream);
2370 return hr;
2372 else
2374 BlockChainStream *stream = NULL;
2376 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2377 if (!stream) return E_OUTOFMEMORY;
2379 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2381 return hr;
2385 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2386 ULARGE_INTEGER newsize)
2388 StorageImpl *This = (StorageImpl*)base;
2389 DirEntry data;
2390 HRESULT hr;
2391 SmallBlockChainStream *smallblock=NULL;
2392 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2394 hr = StorageImpl_ReadDirEntry(This, index, &data);
2395 if (FAILED(hr)) return hr;
2397 /* In simple mode keep the stream size above the small block limit */
2398 if (This->base.openFlags & STGM_SIMPLE)
2399 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2401 if (data.size.QuadPart == newsize.QuadPart)
2402 return S_OK;
2404 /* Create a block chain object of the appropriate type */
2405 if (data.size.QuadPart == 0)
2407 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2409 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2410 if (!smallblock) return E_OUTOFMEMORY;
2412 else
2414 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2415 bigblock = *pbigblock;
2416 if (!bigblock) return E_OUTOFMEMORY;
2419 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2421 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2422 if (!smallblock) return E_OUTOFMEMORY;
2424 else
2426 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2427 bigblock = *pbigblock;
2428 if (!bigblock) return E_OUTOFMEMORY;
2431 /* Change the block chain type if necessary. */
2432 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2434 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2435 if (!bigblock)
2437 SmallBlockChainStream_Destroy(smallblock);
2438 return E_FAIL;
2441 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2442 *pbigblock = bigblock;
2444 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2446 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2447 if (!smallblock)
2448 return E_FAIL;
2451 /* Set the size of the block chain. */
2452 if (smallblock)
2454 SmallBlockChainStream_SetSize(smallblock, newsize);
2455 SmallBlockChainStream_Destroy(smallblock);
2457 else
2459 BlockChainStream_SetSize(bigblock, newsize);
2462 /* Set the size in the directory entry. */
2463 hr = StorageImpl_ReadDirEntry(This, index, &data);
2464 if (SUCCEEDED(hr))
2466 data.size = newsize;
2468 hr = StorageImpl_WriteDirEntry(This, index, &data);
2470 return hr;
2473 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2474 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2476 StorageImpl *This = (StorageImpl*)base;
2477 DirEntry data;
2478 HRESULT hr;
2479 ULARGE_INTEGER newSize;
2481 hr = StorageImpl_ReadDirEntry(This, index, &data);
2482 if (FAILED(hr)) return hr;
2484 /* Grow the stream if necessary */
2485 newSize.QuadPart = 0;
2486 newSize.QuadPart = offset.QuadPart + size;
2488 if (newSize.QuadPart > data.size.QuadPart)
2490 hr = StorageImpl_StreamSetSize(base, index, newSize);
2491 if (FAILED(hr))
2492 return hr;
2494 hr = StorageImpl_ReadDirEntry(This, index, &data);
2495 if (FAILED(hr)) return hr;
2498 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2500 SmallBlockChainStream *stream;
2502 stream = SmallBlockChainStream_Construct(This, NULL, index);
2503 if (!stream) return E_OUTOFMEMORY;
2505 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2507 SmallBlockChainStream_Destroy(stream);
2509 return hr;
2511 else
2513 BlockChainStream *stream;
2515 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2516 if (!stream) return E_OUTOFMEMORY;
2518 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2520 return hr;
2525 * Virtual function table for the IStorage32Impl class.
2527 static const IStorageVtbl Storage32Impl_Vtbl =
2529 StorageBaseImpl_QueryInterface,
2530 StorageBaseImpl_AddRef,
2531 StorageBaseImpl_Release,
2532 StorageBaseImpl_CreateStream,
2533 StorageBaseImpl_OpenStream,
2534 StorageBaseImpl_CreateStorage,
2535 StorageBaseImpl_OpenStorage,
2536 StorageBaseImpl_CopyTo,
2537 StorageBaseImpl_MoveElementTo,
2538 StorageImpl_Commit,
2539 StorageImpl_Revert,
2540 StorageBaseImpl_EnumElements,
2541 StorageBaseImpl_DestroyElement,
2542 StorageBaseImpl_RenameElement,
2543 StorageBaseImpl_SetElementTimes,
2544 StorageBaseImpl_SetClass,
2545 StorageBaseImpl_SetStateBits,
2546 StorageBaseImpl_Stat
2549 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2551 StorageImpl_Destroy,
2552 StorageImpl_Invalidate,
2553 StorageImpl_CreateDirEntry,
2554 StorageImpl_BaseWriteDirEntry,
2555 StorageImpl_BaseReadDirEntry,
2556 StorageImpl_DestroyDirEntry,
2557 StorageImpl_StreamReadAt,
2558 StorageImpl_StreamWriteAt,
2559 StorageImpl_StreamSetSize
2562 static HRESULT StorageImpl_Construct(
2563 HANDLE hFile,
2564 LPCOLESTR pwcsName,
2565 ILockBytes* pLkbyt,
2566 DWORD openFlags,
2567 BOOL fileBased,
2568 BOOL create,
2569 StorageImpl** result)
2571 StorageImpl* This;
2572 HRESULT hr = S_OK;
2573 DirEntry currentEntry;
2574 DirRef currentEntryRef;
2575 WCHAR fullpath[MAX_PATH];
2577 if ( FAILED( validateSTGM(openFlags) ))
2578 return STG_E_INVALIDFLAG;
2580 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2581 if (!This)
2582 return E_OUTOFMEMORY;
2584 memset(This, 0, sizeof(StorageImpl));
2586 list_init(&This->base.strmHead);
2588 list_init(&This->base.storageHead);
2590 This->base.lpVtbl = &Storage32Impl_Vtbl;
2591 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2592 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2593 This->base.openFlags = (openFlags & ~STGM_CREATE);
2594 This->base.ref = 1;
2595 This->base.create = create;
2597 This->base.reverted = 0;
2599 This->hFile = hFile;
2601 if(pwcsName) {
2602 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2604 lstrcpynW(fullpath, pwcsName, MAX_PATH);
2606 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2607 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2608 if (!This->pwcsName)
2610 hr = STG_E_INSUFFICIENTMEMORY;
2611 goto end;
2613 strcpyW(This->pwcsName, fullpath);
2614 This->base.filename = This->pwcsName;
2618 * Initialize the big block cache.
2620 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2621 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2622 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2623 pLkbyt,
2624 openFlags,
2625 This->bigBlockSize,
2626 fileBased);
2628 if (This->bigBlockFile == 0)
2630 hr = E_FAIL;
2631 goto end;
2634 if (create)
2636 ULARGE_INTEGER size;
2637 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2640 * Initialize all header variables:
2641 * - The big block depot consists of one block and it is at block 0
2642 * - The directory table starts at block 1
2643 * - There is no small block depot
2645 memset( This->bigBlockDepotStart,
2646 BLOCK_UNUSED,
2647 sizeof(This->bigBlockDepotStart));
2649 This->bigBlockDepotCount = 1;
2650 This->bigBlockDepotStart[0] = 0;
2651 This->rootStartBlock = 1;
2652 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2653 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2654 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2655 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2656 This->extBigBlockDepotCount = 0;
2658 StorageImpl_SaveFileHeader(This);
2661 * Add one block for the big block depot and one block for the directory table
2663 size.u.HighPart = 0;
2664 size.u.LowPart = This->bigBlockSize * 3;
2665 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2668 * Initialize the big block depot
2670 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2671 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2672 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2673 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2675 else
2678 * Load the header for the file.
2680 hr = StorageImpl_LoadFileHeader(This);
2682 if (FAILED(hr))
2684 goto end;
2689 * There is no block depot cached yet.
2691 This->indexBlockDepotCached = 0xFFFFFFFF;
2694 * Start searching for free blocks with block 0.
2696 This->prevFreeBlock = 0;
2699 * Create the block chain abstractions.
2701 if(!(This->rootBlockChain =
2702 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2704 hr = STG_E_READFAULT;
2705 goto end;
2708 if(!(This->smallBlockDepotChain =
2709 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2710 DIRENTRY_NULL)))
2712 hr = STG_E_READFAULT;
2713 goto end;
2717 * Write the root storage entry (memory only)
2719 if (create)
2721 DirEntry rootEntry;
2723 * Initialize the directory table
2725 memset(&rootEntry, 0, sizeof(rootEntry));
2726 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2727 sizeof(rootEntry.name)/sizeof(WCHAR) );
2728 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2729 rootEntry.stgType = STGTY_ROOT;
2730 rootEntry.leftChild = DIRENTRY_NULL;
2731 rootEntry.rightChild = DIRENTRY_NULL;
2732 rootEntry.dirRootEntry = DIRENTRY_NULL;
2733 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2734 rootEntry.size.u.HighPart = 0;
2735 rootEntry.size.u.LowPart = 0;
2737 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2741 * Find the ID of the root storage.
2743 currentEntryRef = 0;
2747 hr = StorageImpl_ReadDirEntry(
2748 This,
2749 currentEntryRef,
2750 &currentEntry);
2752 if (SUCCEEDED(hr))
2754 if ( (currentEntry.sizeOfNameString != 0 ) &&
2755 (currentEntry.stgType == STGTY_ROOT) )
2757 This->base.storageDirEntry = currentEntryRef;
2761 currentEntryRef++;
2763 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2765 if (FAILED(hr))
2767 hr = STG_E_READFAULT;
2768 goto end;
2772 * Create the block chain abstraction for the small block root chain.
2774 if(!(This->smallBlockRootChain =
2775 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2777 hr = STG_E_READFAULT;
2780 end:
2781 if (FAILED(hr))
2783 IStorage_Release((IStorage*)This);
2784 *result = NULL;
2786 else
2787 *result = This;
2789 return hr;
2792 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2794 StorageImpl *This = (StorageImpl*) iface;
2796 StorageBaseImpl_DeleteAll(&This->base);
2798 This->base.reverted = 1;
2801 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2803 StorageImpl *This = (StorageImpl*) iface;
2804 int i;
2805 TRACE("(%p)\n", This);
2807 StorageImpl_Invalidate(iface);
2809 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2811 BlockChainStream_Destroy(This->smallBlockRootChain);
2812 BlockChainStream_Destroy(This->rootBlockChain);
2813 BlockChainStream_Destroy(This->smallBlockDepotChain);
2815 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2816 BlockChainStream_Destroy(This->blockChainCache[i]);
2818 if (This->bigBlockFile)
2819 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2820 HeapFree(GetProcessHeap(), 0, This);
2823 /******************************************************************************
2824 * Storage32Impl_GetNextFreeBigBlock
2826 * Returns the index of the next free big block.
2827 * If the big block depot is filled, this method will enlarge it.
2830 static ULONG StorageImpl_GetNextFreeBigBlock(
2831 StorageImpl* This)
2833 ULONG depotBlockIndexPos;
2834 BYTE depotBuffer[BIG_BLOCK_SIZE];
2835 BOOL success;
2836 ULONG depotBlockOffset;
2837 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2838 ULONG nextBlockIndex = BLOCK_SPECIAL;
2839 int depotIndex = 0;
2840 ULONG freeBlock = BLOCK_UNUSED;
2842 depotIndex = This->prevFreeBlock / blocksPerDepot;
2843 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2846 * Scan the entire big block depot until we find a block marked free
2848 while (nextBlockIndex != BLOCK_UNUSED)
2850 if (depotIndex < COUNT_BBDEPOTINHEADER)
2852 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2855 * Grow the primary depot.
2857 if (depotBlockIndexPos == BLOCK_UNUSED)
2859 depotBlockIndexPos = depotIndex*blocksPerDepot;
2862 * Add a block depot.
2864 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2865 This->bigBlockDepotCount++;
2866 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2869 * Flag it as a block depot.
2871 StorageImpl_SetNextBlockInChain(This,
2872 depotBlockIndexPos,
2873 BLOCK_SPECIAL);
2875 /* Save new header information.
2877 StorageImpl_SaveFileHeader(This);
2880 else
2882 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2884 if (depotBlockIndexPos == BLOCK_UNUSED)
2887 * Grow the extended depot.
2889 ULONG extIndex = BLOCK_UNUSED;
2890 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2891 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2893 if (extBlockOffset == 0)
2895 /* We need an extended block.
2897 extIndex = Storage32Impl_AddExtBlockDepot(This);
2898 This->extBigBlockDepotCount++;
2899 depotBlockIndexPos = extIndex + 1;
2901 else
2902 depotBlockIndexPos = depotIndex * blocksPerDepot;
2905 * Add a block depot and mark it in the extended block.
2907 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2908 This->bigBlockDepotCount++;
2909 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2911 /* Flag the block depot.
2913 StorageImpl_SetNextBlockInChain(This,
2914 depotBlockIndexPos,
2915 BLOCK_SPECIAL);
2917 /* If necessary, flag the extended depot block.
2919 if (extIndex != BLOCK_UNUSED)
2920 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2922 /* Save header information.
2924 StorageImpl_SaveFileHeader(This);
2928 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2930 if (success)
2932 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2933 ( nextBlockIndex != BLOCK_UNUSED))
2935 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2937 if (nextBlockIndex == BLOCK_UNUSED)
2939 freeBlock = (depotIndex * blocksPerDepot) +
2940 (depotBlockOffset/sizeof(ULONG));
2943 depotBlockOffset += sizeof(ULONG);
2947 depotIndex++;
2948 depotBlockOffset = 0;
2952 * make sure that the block physically exists before using it
2954 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2956 This->prevFreeBlock = freeBlock;
2958 return freeBlock;
2961 /******************************************************************************
2962 * Storage32Impl_AddBlockDepot
2964 * This will create a depot block, essentially it is a block initialized
2965 * to BLOCK_UNUSEDs.
2967 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2969 BYTE blockBuffer[BIG_BLOCK_SIZE];
2972 * Initialize blocks as free
2974 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2975 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2978 /******************************************************************************
2979 * Storage32Impl_GetExtDepotBlock
2981 * Returns the index of the block that corresponds to the specified depot
2982 * index. This method is only for depot indexes equal or greater than
2983 * COUNT_BBDEPOTINHEADER.
2985 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2987 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2988 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2989 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2990 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2991 ULONG blockIndex = BLOCK_UNUSED;
2992 ULONG extBlockIndex = This->extBigBlockDepotStart;
2994 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2996 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2997 return BLOCK_UNUSED;
2999 while (extBlockCount > 0)
3001 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3002 extBlockCount--;
3005 if (extBlockIndex != BLOCK_UNUSED)
3006 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3007 extBlockOffset * sizeof(ULONG), &blockIndex);
3009 return blockIndex;
3012 /******************************************************************************
3013 * Storage32Impl_SetExtDepotBlock
3015 * Associates the specified block index to the specified depot index.
3016 * This method is only for depot indexes equal or greater than
3017 * COUNT_BBDEPOTINHEADER.
3019 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3021 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3022 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3023 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3024 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3025 ULONG extBlockIndex = This->extBigBlockDepotStart;
3027 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3029 while (extBlockCount > 0)
3031 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3032 extBlockCount--;
3035 if (extBlockIndex != BLOCK_UNUSED)
3037 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3038 extBlockOffset * sizeof(ULONG),
3039 blockIndex);
3043 /******************************************************************************
3044 * Storage32Impl_AddExtBlockDepot
3046 * Creates an extended depot block.
3048 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3050 ULONG numExtBlocks = This->extBigBlockDepotCount;
3051 ULONG nextExtBlock = This->extBigBlockDepotStart;
3052 BYTE depotBuffer[BIG_BLOCK_SIZE];
3053 ULONG index = BLOCK_UNUSED;
3054 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3055 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3056 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3058 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3059 blocksPerDepotBlock;
3061 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3064 * The first extended block.
3066 This->extBigBlockDepotStart = index;
3068 else
3070 unsigned int i;
3072 * Follow the chain to the last one.
3074 for (i = 0; i < (numExtBlocks - 1); i++)
3076 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3080 * Add the new extended block to the chain.
3082 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3083 index);
3087 * Initialize this block.
3089 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3090 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3092 return index;
3095 /******************************************************************************
3096 * Storage32Impl_FreeBigBlock
3098 * This method will flag the specified block as free in the big block depot.
3100 static void StorageImpl_FreeBigBlock(
3101 StorageImpl* This,
3102 ULONG blockIndex)
3104 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3106 if (blockIndex < This->prevFreeBlock)
3107 This->prevFreeBlock = blockIndex;
3110 /************************************************************************
3111 * Storage32Impl_GetNextBlockInChain
3113 * This method will retrieve the block index of the next big block in
3114 * in the chain.
3116 * Params: This - Pointer to the Storage object.
3117 * blockIndex - Index of the block to retrieve the chain
3118 * for.
3119 * nextBlockIndex - receives the return value.
3121 * Returns: This method returns the index of the next block in the chain.
3122 * It will return the constants:
3123 * BLOCK_SPECIAL - If the block given was not part of a
3124 * chain.
3125 * BLOCK_END_OF_CHAIN - If the block given was the last in
3126 * a chain.
3127 * BLOCK_UNUSED - If the block given was not past of a chain
3128 * and is available.
3129 * BLOCK_EXTBBDEPOT - This block is part of the extended
3130 * big block depot.
3132 * See Windows documentation for more details on IStorage methods.
3134 static HRESULT StorageImpl_GetNextBlockInChain(
3135 StorageImpl* This,
3136 ULONG blockIndex,
3137 ULONG* nextBlockIndex)
3139 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3140 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3141 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3142 BYTE depotBuffer[BIG_BLOCK_SIZE];
3143 BOOL success;
3144 ULONG depotBlockIndexPos;
3145 int index;
3147 *nextBlockIndex = BLOCK_SPECIAL;
3149 if(depotBlockCount >= This->bigBlockDepotCount)
3151 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3152 This->bigBlockDepotCount);
3153 return STG_E_READFAULT;
3157 * Cache the currently accessed depot block.
3159 if (depotBlockCount != This->indexBlockDepotCached)
3161 This->indexBlockDepotCached = depotBlockCount;
3163 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3165 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3167 else
3170 * We have to look in the extended depot.
3172 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3175 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3177 if (!success)
3178 return STG_E_READFAULT;
3180 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
3182 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3183 This->blockDepotCached[index] = *nextBlockIndex;
3187 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3189 return S_OK;
3192 /******************************************************************************
3193 * Storage32Impl_GetNextExtendedBlock
3195 * Given an extended block this method will return the next extended block.
3197 * NOTES:
3198 * The last ULONG of an extended block is the block index of the next
3199 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3200 * depot.
3202 * Return values:
3203 * - The index of the next extended block
3204 * - BLOCK_UNUSED: there is no next extended block.
3205 * - Any other return values denotes failure.
3207 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3209 ULONG nextBlockIndex = BLOCK_SPECIAL;
3210 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3212 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3213 &nextBlockIndex);
3215 return nextBlockIndex;
3218 /******************************************************************************
3219 * Storage32Impl_SetNextBlockInChain
3221 * This method will write the index of the specified block's next block
3222 * in the big block depot.
3224 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3225 * do the following
3227 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3228 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3229 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3232 static void StorageImpl_SetNextBlockInChain(
3233 StorageImpl* This,
3234 ULONG blockIndex,
3235 ULONG nextBlock)
3237 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3238 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3239 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3240 ULONG depotBlockIndexPos;
3242 assert(depotBlockCount < This->bigBlockDepotCount);
3243 assert(blockIndex != nextBlock);
3245 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3247 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3249 else
3252 * We have to look in the extended depot.
3254 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3257 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3258 nextBlock);
3260 * Update the cached block depot, if necessary.
3262 if (depotBlockCount == This->indexBlockDepotCached)
3264 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3268 /******************************************************************************
3269 * Storage32Impl_LoadFileHeader
3271 * This method will read in the file header, i.e. big block index -1.
3273 static HRESULT StorageImpl_LoadFileHeader(
3274 StorageImpl* This)
3276 HRESULT hr = STG_E_FILENOTFOUND;
3277 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3278 BOOL success;
3279 int index;
3281 TRACE("\n");
3283 * Get a pointer to the big block of data containing the header.
3285 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3288 * Extract the information from the header.
3290 if (success)
3293 * Check for the "magic number" signature and return an error if it is not
3294 * found.
3296 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3298 return STG_E_OLDFORMAT;
3301 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3303 return STG_E_INVALIDHEADER;
3306 StorageUtl_ReadWord(
3307 headerBigBlock,
3308 OFFSET_BIGBLOCKSIZEBITS,
3309 &This->bigBlockSizeBits);
3311 StorageUtl_ReadWord(
3312 headerBigBlock,
3313 OFFSET_SMALLBLOCKSIZEBITS,
3314 &This->smallBlockSizeBits);
3316 StorageUtl_ReadDWord(
3317 headerBigBlock,
3318 OFFSET_BBDEPOTCOUNT,
3319 &This->bigBlockDepotCount);
3321 StorageUtl_ReadDWord(
3322 headerBigBlock,
3323 OFFSET_ROOTSTARTBLOCK,
3324 &This->rootStartBlock);
3326 StorageUtl_ReadDWord(
3327 headerBigBlock,
3328 OFFSET_SBDEPOTSTART,
3329 &This->smallBlockDepotStart);
3331 StorageUtl_ReadDWord(
3332 headerBigBlock,
3333 OFFSET_EXTBBDEPOTSTART,
3334 &This->extBigBlockDepotStart);
3336 StorageUtl_ReadDWord(
3337 headerBigBlock,
3338 OFFSET_EXTBBDEPOTCOUNT,
3339 &This->extBigBlockDepotCount);
3341 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3343 StorageUtl_ReadDWord(
3344 headerBigBlock,
3345 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3346 &(This->bigBlockDepotStart[index]));
3350 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3352 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3353 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3356 * Right now, the code is making some assumptions about the size of the
3357 * blocks, just make sure they are what we're expecting.
3359 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3360 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3362 WARN("Broken OLE storage file\n");
3363 hr = STG_E_INVALIDHEADER;
3365 else
3366 hr = S_OK;
3369 return hr;
3372 /******************************************************************************
3373 * Storage32Impl_SaveFileHeader
3375 * This method will save to the file the header, i.e. big block -1.
3377 static void StorageImpl_SaveFileHeader(
3378 StorageImpl* This)
3380 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3381 int index;
3382 BOOL success;
3385 * Get a pointer to the big block of data containing the header.
3387 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3390 * If the block read failed, the file is probably new.
3392 if (!success)
3395 * Initialize for all unknown fields.
3397 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3400 * Initialize the magic number.
3402 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3405 * And a bunch of things we don't know what they mean
3407 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3408 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3409 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3410 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3414 * Write the information to the header.
3416 StorageUtl_WriteWord(
3417 headerBigBlock,
3418 OFFSET_BIGBLOCKSIZEBITS,
3419 This->bigBlockSizeBits);
3421 StorageUtl_WriteWord(
3422 headerBigBlock,
3423 OFFSET_SMALLBLOCKSIZEBITS,
3424 This->smallBlockSizeBits);
3426 StorageUtl_WriteDWord(
3427 headerBigBlock,
3428 OFFSET_BBDEPOTCOUNT,
3429 This->bigBlockDepotCount);
3431 StorageUtl_WriteDWord(
3432 headerBigBlock,
3433 OFFSET_ROOTSTARTBLOCK,
3434 This->rootStartBlock);
3436 StorageUtl_WriteDWord(
3437 headerBigBlock,
3438 OFFSET_SBDEPOTSTART,
3439 This->smallBlockDepotStart);
3441 StorageUtl_WriteDWord(
3442 headerBigBlock,
3443 OFFSET_SBDEPOTCOUNT,
3444 This->smallBlockDepotChain ?
3445 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3447 StorageUtl_WriteDWord(
3448 headerBigBlock,
3449 OFFSET_EXTBBDEPOTSTART,
3450 This->extBigBlockDepotStart);
3452 StorageUtl_WriteDWord(
3453 headerBigBlock,
3454 OFFSET_EXTBBDEPOTCOUNT,
3455 This->extBigBlockDepotCount);
3457 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3459 StorageUtl_WriteDWord(
3460 headerBigBlock,
3461 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3462 (This->bigBlockDepotStart[index]));
3466 * Write the big block back to the file.
3468 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3471 /******************************************************************************
3472 * StorageImpl_ReadRawDirEntry
3474 * This method will read the raw data from a directory entry in the file.
3476 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3478 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3480 ULARGE_INTEGER offset;
3481 HRESULT hr;
3482 ULONG bytesRead;
3484 offset.u.HighPart = 0;
3485 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3487 hr = BlockChainStream_ReadAt(
3488 This->rootBlockChain,
3489 offset,
3490 RAW_DIRENTRY_SIZE,
3491 buffer,
3492 &bytesRead);
3494 return hr;
3497 /******************************************************************************
3498 * StorageImpl_WriteRawDirEntry
3500 * This method will write the raw data from a directory entry in the file.
3502 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3504 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3506 ULARGE_INTEGER offset;
3507 HRESULT hr;
3508 ULONG bytesRead;
3510 offset.u.HighPart = 0;
3511 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3513 hr = BlockChainStream_WriteAt(
3514 This->rootBlockChain,
3515 offset,
3516 RAW_DIRENTRY_SIZE,
3517 buffer,
3518 &bytesRead);
3520 return hr;
3523 /******************************************************************************
3524 * UpdateRawDirEntry
3526 * Update raw directory entry data from the fields in newData.
3528 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3530 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3532 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3534 memcpy(
3535 buffer + OFFSET_PS_NAME,
3536 newData->name,
3537 DIRENTRY_NAME_BUFFER_LEN );
3539 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3541 StorageUtl_WriteWord(
3542 buffer,
3543 OFFSET_PS_NAMELENGTH,
3544 newData->sizeOfNameString);
3546 StorageUtl_WriteDWord(
3547 buffer,
3548 OFFSET_PS_LEFTCHILD,
3549 newData->leftChild);
3551 StorageUtl_WriteDWord(
3552 buffer,
3553 OFFSET_PS_RIGHTCHILD,
3554 newData->rightChild);
3556 StorageUtl_WriteDWord(
3557 buffer,
3558 OFFSET_PS_DIRROOT,
3559 newData->dirRootEntry);
3561 StorageUtl_WriteGUID(
3562 buffer,
3563 OFFSET_PS_GUID,
3564 &newData->clsid);
3566 StorageUtl_WriteDWord(
3567 buffer,
3568 OFFSET_PS_CTIMELOW,
3569 newData->ctime.dwLowDateTime);
3571 StorageUtl_WriteDWord(
3572 buffer,
3573 OFFSET_PS_CTIMEHIGH,
3574 newData->ctime.dwHighDateTime);
3576 StorageUtl_WriteDWord(
3577 buffer,
3578 OFFSET_PS_MTIMELOW,
3579 newData->mtime.dwLowDateTime);
3581 StorageUtl_WriteDWord(
3582 buffer,
3583 OFFSET_PS_MTIMEHIGH,
3584 newData->ctime.dwHighDateTime);
3586 StorageUtl_WriteDWord(
3587 buffer,
3588 OFFSET_PS_STARTBLOCK,
3589 newData->startingBlock);
3591 StorageUtl_WriteDWord(
3592 buffer,
3593 OFFSET_PS_SIZE,
3594 newData->size.u.LowPart);
3597 /******************************************************************************
3598 * Storage32Impl_ReadDirEntry
3600 * This method will read the specified directory entry.
3602 HRESULT StorageImpl_ReadDirEntry(
3603 StorageImpl* This,
3604 DirRef index,
3605 DirEntry* buffer)
3607 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3608 HRESULT readRes;
3610 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3612 if (SUCCEEDED(readRes))
3614 memset(buffer->name, 0, sizeof(buffer->name));
3615 memcpy(
3616 buffer->name,
3617 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3618 DIRENTRY_NAME_BUFFER_LEN );
3619 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3621 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3623 StorageUtl_ReadWord(
3624 currentEntry,
3625 OFFSET_PS_NAMELENGTH,
3626 &buffer->sizeOfNameString);
3628 StorageUtl_ReadDWord(
3629 currentEntry,
3630 OFFSET_PS_LEFTCHILD,
3631 &buffer->leftChild);
3633 StorageUtl_ReadDWord(
3634 currentEntry,
3635 OFFSET_PS_RIGHTCHILD,
3636 &buffer->rightChild);
3638 StorageUtl_ReadDWord(
3639 currentEntry,
3640 OFFSET_PS_DIRROOT,
3641 &buffer->dirRootEntry);
3643 StorageUtl_ReadGUID(
3644 currentEntry,
3645 OFFSET_PS_GUID,
3646 &buffer->clsid);
3648 StorageUtl_ReadDWord(
3649 currentEntry,
3650 OFFSET_PS_CTIMELOW,
3651 &buffer->ctime.dwLowDateTime);
3653 StorageUtl_ReadDWord(
3654 currentEntry,
3655 OFFSET_PS_CTIMEHIGH,
3656 &buffer->ctime.dwHighDateTime);
3658 StorageUtl_ReadDWord(
3659 currentEntry,
3660 OFFSET_PS_MTIMELOW,
3661 &buffer->mtime.dwLowDateTime);
3663 StorageUtl_ReadDWord(
3664 currentEntry,
3665 OFFSET_PS_MTIMEHIGH,
3666 &buffer->mtime.dwHighDateTime);
3668 StorageUtl_ReadDWord(
3669 currentEntry,
3670 OFFSET_PS_STARTBLOCK,
3671 &buffer->startingBlock);
3673 StorageUtl_ReadDWord(
3674 currentEntry,
3675 OFFSET_PS_SIZE,
3676 &buffer->size.u.LowPart);
3678 buffer->size.u.HighPart = 0;
3681 return readRes;
3684 /*********************************************************************
3685 * Write the specified directory entry to the file
3687 HRESULT StorageImpl_WriteDirEntry(
3688 StorageImpl* This,
3689 DirRef index,
3690 const DirEntry* buffer)
3692 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3693 HRESULT writeRes;
3695 UpdateRawDirEntry(currentEntry, buffer);
3697 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3698 return writeRes;
3701 static BOOL StorageImpl_ReadBigBlock(
3702 StorageImpl* This,
3703 ULONG blockIndex,
3704 void* buffer)
3706 ULARGE_INTEGER ulOffset;
3707 DWORD read;
3709 ulOffset.u.HighPart = 0;
3710 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3712 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3713 return (read == This->bigBlockSize);
3716 static BOOL StorageImpl_ReadDWordFromBigBlock(
3717 StorageImpl* This,
3718 ULONG blockIndex,
3719 ULONG offset,
3720 DWORD* value)
3722 ULARGE_INTEGER ulOffset;
3723 DWORD read;
3724 DWORD tmp;
3726 ulOffset.u.HighPart = 0;
3727 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3728 ulOffset.u.LowPart += offset;
3730 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3731 *value = lendian32toh(tmp);
3732 return (read == sizeof(DWORD));
3735 static BOOL StorageImpl_WriteBigBlock(
3736 StorageImpl* This,
3737 ULONG blockIndex,
3738 const void* buffer)
3740 ULARGE_INTEGER ulOffset;
3741 DWORD wrote;
3743 ulOffset.u.HighPart = 0;
3744 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3746 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3747 return (wrote == This->bigBlockSize);
3750 static BOOL StorageImpl_WriteDWordToBigBlock(
3751 StorageImpl* This,
3752 ULONG blockIndex,
3753 ULONG offset,
3754 DWORD value)
3756 ULARGE_INTEGER ulOffset;
3757 DWORD wrote;
3759 ulOffset.u.HighPart = 0;
3760 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3761 ulOffset.u.LowPart += offset;
3763 value = htole32(value);
3764 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3765 return (wrote == sizeof(DWORD));
3768 /******************************************************************************
3769 * Storage32Impl_SmallBlocksToBigBlocks
3771 * This method will convert a small block chain to a big block chain.
3772 * The small block chain will be destroyed.
3774 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3775 StorageImpl* This,
3776 SmallBlockChainStream** ppsbChain)
3778 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3779 ULARGE_INTEGER size, offset;
3780 ULONG cbRead, cbWritten;
3781 ULARGE_INTEGER cbTotalRead;
3782 DirRef streamEntryRef;
3783 HRESULT resWrite = S_OK;
3784 HRESULT resRead;
3785 DirEntry streamEntry;
3786 BYTE *buffer;
3787 BlockChainStream *bbTempChain = NULL;
3788 BlockChainStream *bigBlockChain = NULL;
3791 * Create a temporary big block chain that doesn't have
3792 * an associated directory entry. This temporary chain will be
3793 * used to copy data from small blocks to big blocks.
3795 bbTempChain = BlockChainStream_Construct(This,
3796 &bbHeadOfChain,
3797 DIRENTRY_NULL);
3798 if(!bbTempChain) return NULL;
3800 * Grow the big block chain.
3802 size = SmallBlockChainStream_GetSize(*ppsbChain);
3803 BlockChainStream_SetSize(bbTempChain, size);
3806 * Copy the contents of the small block chain to the big block chain
3807 * by small block size increments.
3809 offset.u.LowPart = 0;
3810 offset.u.HighPart = 0;
3811 cbTotalRead.QuadPart = 0;
3813 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3816 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3817 offset,
3818 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3819 buffer,
3820 &cbRead);
3821 if (FAILED(resRead))
3822 break;
3824 if (cbRead > 0)
3826 cbTotalRead.QuadPart += cbRead;
3828 resWrite = BlockChainStream_WriteAt(bbTempChain,
3829 offset,
3830 cbRead,
3831 buffer,
3832 &cbWritten);
3834 if (FAILED(resWrite))
3835 break;
3837 offset.u.LowPart += cbRead;
3839 } while (cbTotalRead.QuadPart < size.QuadPart);
3840 HeapFree(GetProcessHeap(),0,buffer);
3842 size.u.HighPart = 0;
3843 size.u.LowPart = 0;
3845 if (FAILED(resRead) || FAILED(resWrite))
3847 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3848 BlockChainStream_SetSize(bbTempChain, size);
3849 BlockChainStream_Destroy(bbTempChain);
3850 return NULL;
3854 * Destroy the small block chain.
3856 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3857 SmallBlockChainStream_SetSize(*ppsbChain, size);
3858 SmallBlockChainStream_Destroy(*ppsbChain);
3859 *ppsbChain = 0;
3862 * Change the directory entry. This chain is now a big block chain
3863 * and it doesn't reside in the small blocks chain anymore.
3865 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3867 streamEntry.startingBlock = bbHeadOfChain;
3869 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3872 * Destroy the temporary entryless big block chain.
3873 * Create a new big block chain associated with this entry.
3875 BlockChainStream_Destroy(bbTempChain);
3876 bigBlockChain = BlockChainStream_Construct(This,
3877 NULL,
3878 streamEntryRef);
3880 return bigBlockChain;
3883 /******************************************************************************
3884 * Storage32Impl_BigBlocksToSmallBlocks
3886 * This method will convert a big block chain to a small block chain.
3887 * The big block chain will be destroyed on success.
3889 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3890 StorageImpl* This,
3891 BlockChainStream** ppbbChain)
3893 ULARGE_INTEGER size, offset, cbTotalRead;
3894 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3895 DirRef streamEntryRef;
3896 HRESULT resWrite = S_OK, resRead;
3897 DirEntry streamEntry;
3898 BYTE* buffer;
3899 SmallBlockChainStream* sbTempChain;
3901 TRACE("%p %p\n", This, ppbbChain);
3903 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3904 DIRENTRY_NULL);
3906 if(!sbTempChain)
3907 return NULL;
3909 size = BlockChainStream_GetSize(*ppbbChain);
3910 SmallBlockChainStream_SetSize(sbTempChain, size);
3912 offset.u.HighPart = 0;
3913 offset.u.LowPart = 0;
3914 cbTotalRead.QuadPart = 0;
3915 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3918 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3919 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3920 buffer, &cbRead);
3922 if(FAILED(resRead))
3923 break;
3925 if(cbRead > 0)
3927 cbTotalRead.QuadPart += cbRead;
3929 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3930 cbRead, buffer, &cbWritten);
3932 if(FAILED(resWrite))
3933 break;
3935 offset.u.LowPart += cbRead;
3937 }while(cbTotalRead.QuadPart < size.QuadPart);
3938 HeapFree(GetProcessHeap(), 0, buffer);
3940 size.u.HighPart = 0;
3941 size.u.LowPart = 0;
3943 if(FAILED(resRead) || FAILED(resWrite))
3945 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3946 SmallBlockChainStream_SetSize(sbTempChain, size);
3947 SmallBlockChainStream_Destroy(sbTempChain);
3948 return NULL;
3951 /* destroy the original big block chain */
3952 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3953 BlockChainStream_SetSize(*ppbbChain, size);
3954 BlockChainStream_Destroy(*ppbbChain);
3955 *ppbbChain = NULL;
3957 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3958 streamEntry.startingBlock = sbHeadOfChain;
3959 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3961 SmallBlockChainStream_Destroy(sbTempChain);
3962 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3965 static HRESULT CreateSnapshotFile(StorageBaseImpl* original, StorageBaseImpl **snapshot)
3967 HRESULT hr;
3968 DirEntry parentData, snapshotData;
3970 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DELETEONRELEASE,
3971 0, (IStorage**)snapshot);
3973 if (SUCCEEDED(hr))
3975 hr = StorageBaseImpl_ReadDirEntry(original,
3976 original->storageDirEntry, &parentData);
3978 if (SUCCEEDED(hr))
3979 hr = StorageBaseImpl_ReadDirEntry((*snapshot),
3980 (*snapshot)->storageDirEntry, &snapshotData);
3982 if (SUCCEEDED(hr))
3984 memcpy(snapshotData.name, parentData.name, sizeof(snapshotData.name));
3985 snapshotData.sizeOfNameString = parentData.sizeOfNameString;
3986 snapshotData.stgType = parentData.stgType;
3987 snapshotData.clsid = parentData.clsid;
3988 snapshotData.ctime = parentData.ctime;
3989 snapshotData.mtime = parentData.mtime;
3990 hr = StorageBaseImpl_WriteDirEntry((*snapshot),
3991 (*snapshot)->storageDirEntry, &snapshotData);
3994 if (SUCCEEDED(hr))
3995 hr = IStorage_CopyTo((IStorage*)original, 0, NULL, NULL,
3996 (IStorage*)(*snapshot));
3998 if (FAILED(hr)) IStorage_Release((IStorage*)(*snapshot));
4001 return hr;
4004 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4005 IStorage* iface,
4006 DWORD grfCommitFlags) /* [in] */
4008 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4009 HRESULT hr;
4010 DirEntry data, tempStorageData, snapshotRootData;
4011 DirRef tempStorageEntry, oldDirRoot;
4012 StorageInternalImpl *tempStorage;
4014 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4016 /* Cannot commit a read-only transacted storage */
4017 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4018 return STG_E_ACCESSDENIED;
4020 /* To prevent data loss, we create the new structure in the file before we
4021 * delete the old one, so that in case of errors the old data is intact. We
4022 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4023 * needed in the rare situation where we have just enough free disk space to
4024 * overwrite the existing data. */
4026 /* Create an orphaned storage in the parent for the new directory structure. */
4027 memset(&data, 0, sizeof(data));
4028 data.name[0] = 'D';
4029 data.sizeOfNameString = 1;
4030 data.stgType = STGTY_STORAGE;
4031 data.leftChild = DIRENTRY_NULL;
4032 data.rightChild = DIRENTRY_NULL;
4033 data.dirRootEntry = DIRENTRY_NULL;
4034 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &data, &tempStorageEntry);
4036 if (FAILED(hr)) return hr;
4038 tempStorage = StorageInternalImpl_Construct(This->transactedParent,
4039 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, tempStorageEntry);
4040 if (tempStorage)
4042 hr = IStorage_CopyTo((IStorage*)This->snapshot, 0, NULL, NULL,
4043 (IStorage*)tempStorage);
4045 list_init(&tempStorage->ParentListEntry);
4047 IStorage_Release((IStorage*) tempStorage);
4049 else
4050 hr = E_OUTOFMEMORY;
4052 if (FAILED(hr))
4054 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4055 return hr;
4058 /* Update the storage to use the new data in one step. */
4059 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4060 This->transactedParent->storageDirEntry, &data);
4062 if (SUCCEEDED(hr))
4064 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4065 tempStorageEntry, &tempStorageData);
4068 if (SUCCEEDED(hr))
4070 hr = StorageBaseImpl_ReadDirEntry(This->snapshot,
4071 This->snapshot->storageDirEntry, &snapshotRootData);
4074 if (SUCCEEDED(hr))
4076 oldDirRoot = data.dirRootEntry;
4077 data.dirRootEntry = tempStorageData.dirRootEntry;
4078 data.clsid = snapshotRootData.clsid;
4079 data.ctime = snapshotRootData.ctime;
4080 data.mtime = snapshotRootData.mtime;
4082 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4083 This->transactedParent->storageDirEntry, &data);
4086 if (SUCCEEDED(hr))
4088 /* Destroy the old now-orphaned data. */
4089 DestroyReachableEntries(This->transactedParent, oldDirRoot);
4090 StorageBaseImpl_DestroyDirEntry(This->transactedParent, tempStorageEntry);
4092 else
4094 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4097 return hr;
4100 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4101 IStorage* iface)
4103 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4104 StorageBaseImpl *newSnapshot;
4105 HRESULT hr;
4107 TRACE("(%p)\n", iface);
4109 /* Create a new copy of the parent data. */
4110 hr = CreateSnapshotFile(This->transactedParent, &newSnapshot);
4111 if (FAILED(hr)) return hr;
4113 /* Destroy the open objects. */
4114 StorageBaseImpl_DeleteAll(&This->base);
4116 /* Replace our current snapshot. */
4117 IStorage_Release((IStorage*)This->snapshot);
4118 This->snapshot = newSnapshot;
4120 return S_OK;
4123 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4125 if (!This->reverted)
4127 TRACE("Storage invalidated (stg=%p)\n", This);
4129 This->reverted = 1;
4131 StorageBaseImpl_DeleteAll(This);
4135 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4137 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4139 TransactedSnapshotImpl_Invalidate(iface);
4141 IStorage_Release((IStorage*)This->transactedParent);
4143 IStorage_Release((IStorage*)This->snapshot);
4145 HeapFree(GetProcessHeap(), 0, This);
4148 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4149 const DirEntry *newData, DirRef *index)
4151 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4153 return StorageBaseImpl_CreateDirEntry(This->snapshot,
4154 newData, index);
4157 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4158 DirRef index, const DirEntry *data)
4160 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4162 return StorageBaseImpl_WriteDirEntry(This->snapshot,
4163 index, data);
4166 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4167 DirRef index, DirEntry *data)
4169 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4171 return StorageBaseImpl_ReadDirEntry(This->snapshot,
4172 index, data);
4175 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4176 DirRef index)
4178 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4180 return StorageBaseImpl_DestroyDirEntry(This->snapshot,
4181 index);
4184 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4185 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4187 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4189 return StorageBaseImpl_StreamReadAt(This->snapshot,
4190 index, offset, size, buffer, bytesRead);
4193 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4194 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4196 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4198 return StorageBaseImpl_StreamWriteAt(This->snapshot,
4199 index, offset, size, buffer, bytesWritten);
4202 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4203 DirRef index, ULARGE_INTEGER newsize)
4205 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4207 return StorageBaseImpl_StreamSetSize(This->snapshot,
4208 index, newsize);
4211 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4213 StorageBaseImpl_QueryInterface,
4214 StorageBaseImpl_AddRef,
4215 StorageBaseImpl_Release,
4216 StorageBaseImpl_CreateStream,
4217 StorageBaseImpl_OpenStream,
4218 StorageBaseImpl_CreateStorage,
4219 StorageBaseImpl_OpenStorage,
4220 StorageBaseImpl_CopyTo,
4221 StorageBaseImpl_MoveElementTo,
4222 TransactedSnapshotImpl_Commit,
4223 TransactedSnapshotImpl_Revert,
4224 StorageBaseImpl_EnumElements,
4225 StorageBaseImpl_DestroyElement,
4226 StorageBaseImpl_RenameElement,
4227 StorageBaseImpl_SetElementTimes,
4228 StorageBaseImpl_SetClass,
4229 StorageBaseImpl_SetStateBits,
4230 StorageBaseImpl_Stat
4233 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4235 TransactedSnapshotImpl_Destroy,
4236 TransactedSnapshotImpl_Invalidate,
4237 TransactedSnapshotImpl_CreateDirEntry,
4238 TransactedSnapshotImpl_WriteDirEntry,
4239 TransactedSnapshotImpl_ReadDirEntry,
4240 TransactedSnapshotImpl_DestroyDirEntry,
4241 TransactedSnapshotImpl_StreamReadAt,
4242 TransactedSnapshotImpl_StreamWriteAt,
4243 TransactedSnapshotImpl_StreamSetSize
4246 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4247 TransactedSnapshotImpl** result)
4249 HRESULT hr;
4251 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4252 if (*result)
4254 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4256 /* This is OK because the property set storage functions use the IStorage functions. */
4257 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4259 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4261 list_init(&(*result)->base.strmHead);
4263 list_init(&(*result)->base.storageHead);
4265 (*result)->base.ref = 1;
4267 (*result)->base.openFlags = parentStorage->openFlags;
4269 (*result)->base.filename = parentStorage->filename;
4271 /* Create a new temporary storage to act as the snapshot */
4272 hr = CreateSnapshotFile(parentStorage, &(*result)->snapshot);
4274 if (SUCCEEDED(hr))
4276 (*result)->base.storageDirEntry = (*result)->snapshot->storageDirEntry;
4278 /* parentStorage already has 1 reference, which we take over here. */
4279 (*result)->transactedParent = parentStorage;
4281 parentStorage->transactedChild = (StorageBaseImpl*)*result;
4284 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4286 return hr;
4288 else
4289 return E_OUTOFMEMORY;
4292 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
4293 StorageBaseImpl** result)
4295 static int fixme=0;
4297 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
4299 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
4302 return TransactedSnapshotImpl_Construct(parentStorage,
4303 (TransactedSnapshotImpl**)result);
4306 static HRESULT Storage_Construct(
4307 HANDLE hFile,
4308 LPCOLESTR pwcsName,
4309 ILockBytes* pLkbyt,
4310 DWORD openFlags,
4311 BOOL fileBased,
4312 BOOL create,
4313 StorageBaseImpl** result)
4315 StorageImpl *newStorage;
4316 StorageBaseImpl *newTransactedStorage;
4317 HRESULT hr;
4319 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, &newStorage);
4320 if (FAILED(hr)) goto end;
4322 if (openFlags & STGM_TRANSACTED)
4324 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
4325 if (FAILED(hr))
4326 IStorage_Release((IStorage*)newStorage);
4327 else
4328 *result = newTransactedStorage;
4330 else
4331 *result = &newStorage->base;
4333 end:
4334 return hr;
4337 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
4339 StorageInternalImpl* This = (StorageInternalImpl*) base;
4341 if (!This->base.reverted)
4343 TRACE("Storage invalidated (stg=%p)\n", This);
4345 This->base.reverted = 1;
4347 This->parentStorage = NULL;
4349 StorageBaseImpl_DeleteAll(&This->base);
4351 list_remove(&This->ParentListEntry);
4355 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
4357 StorageInternalImpl* This = (StorageInternalImpl*) iface;
4359 StorageInternalImpl_Invalidate(&This->base);
4361 HeapFree(GetProcessHeap(), 0, This);
4364 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
4365 const DirEntry *newData, DirRef *index)
4367 StorageInternalImpl* This = (StorageInternalImpl*) base;
4369 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
4370 newData, index);
4373 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
4374 DirRef index, const DirEntry *data)
4376 StorageInternalImpl* This = (StorageInternalImpl*) base;
4378 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
4379 index, data);
4382 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
4383 DirRef index, DirEntry *data)
4385 StorageInternalImpl* This = (StorageInternalImpl*) base;
4387 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
4388 index, data);
4391 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
4392 DirRef index)
4394 StorageInternalImpl* This = (StorageInternalImpl*) base;
4396 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
4397 index);
4400 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
4401 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4403 StorageInternalImpl* This = (StorageInternalImpl*) base;
4405 return StorageBaseImpl_StreamReadAt(This->parentStorage,
4406 index, offset, size, buffer, bytesRead);
4409 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
4410 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4412 StorageInternalImpl* This = (StorageInternalImpl*) base;
4414 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
4415 index, offset, size, buffer, bytesWritten);
4418 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
4419 DirRef index, ULARGE_INTEGER newsize)
4421 StorageInternalImpl* This = (StorageInternalImpl*) base;
4423 return StorageBaseImpl_StreamSetSize(This->parentStorage,
4424 index, newsize);
4427 /******************************************************************************
4429 ** Storage32InternalImpl_Commit
4432 static HRESULT WINAPI StorageInternalImpl_Commit(
4433 IStorage* iface,
4434 DWORD grfCommitFlags) /* [in] */
4436 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
4437 return S_OK;
4440 /******************************************************************************
4442 ** Storage32InternalImpl_Revert
4445 static HRESULT WINAPI StorageInternalImpl_Revert(
4446 IStorage* iface)
4448 FIXME("(%p): stub\n", iface);
4449 return S_OK;
4452 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
4454 IStorage_Release((IStorage*)This->parentStorage);
4455 HeapFree(GetProcessHeap(), 0, This);
4458 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
4459 IEnumSTATSTG* iface,
4460 REFIID riid,
4461 void** ppvObject)
4463 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4465 if (ppvObject==0)
4466 return E_INVALIDARG;
4468 *ppvObject = 0;
4470 if (IsEqualGUID(&IID_IUnknown, riid) ||
4471 IsEqualGUID(&IID_IEnumSTATSTG, riid))
4473 *ppvObject = This;
4474 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
4475 return S_OK;
4478 return E_NOINTERFACE;
4481 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
4482 IEnumSTATSTG* iface)
4484 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4485 return InterlockedIncrement(&This->ref);
4488 static ULONG WINAPI IEnumSTATSTGImpl_Release(
4489 IEnumSTATSTG* iface)
4491 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4493 ULONG newRef;
4495 newRef = InterlockedDecrement(&This->ref);
4497 if (newRef==0)
4499 IEnumSTATSTGImpl_Destroy(This);
4502 return newRef;
4505 static HRESULT IEnumSTATSTGImpl_GetNextRef(
4506 IEnumSTATSTGImpl* This,
4507 DirRef *ref)
4509 DirRef result = DIRENTRY_NULL;
4510 DirRef searchNode;
4511 DirEntry entry;
4512 HRESULT hr;
4513 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
4515 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
4516 This->parentStorage->storageDirEntry, &entry);
4517 searchNode = entry.dirRootEntry;
4519 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
4521 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
4523 if (SUCCEEDED(hr))
4525 LONG diff = entryNameCmp( entry.name, This->name);
4527 if (diff <= 0)
4529 searchNode = entry.rightChild;
4531 else
4533 result = searchNode;
4534 memcpy(result_name, entry.name, sizeof(result_name));
4535 searchNode = entry.leftChild;
4540 if (SUCCEEDED(hr))
4542 *ref = result;
4543 if (result != DIRENTRY_NULL)
4544 memcpy(This->name, result_name, sizeof(result_name));
4547 return hr;
4550 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
4551 IEnumSTATSTG* iface,
4552 ULONG celt,
4553 STATSTG* rgelt,
4554 ULONG* pceltFetched)
4556 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4558 DirEntry currentEntry;
4559 STATSTG* currentReturnStruct = rgelt;
4560 ULONG objectFetched = 0;
4561 DirRef currentSearchNode;
4562 HRESULT hr=S_OK;
4564 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
4565 return E_INVALIDARG;
4567 if (This->parentStorage->reverted)
4568 return STG_E_REVERTED;
4571 * To avoid the special case, get another pointer to a ULONG value if
4572 * the caller didn't supply one.
4574 if (pceltFetched==0)
4575 pceltFetched = &objectFetched;
4578 * Start the iteration, we will iterate until we hit the end of the
4579 * linked list or until we hit the number of items to iterate through
4581 *pceltFetched = 0;
4583 while ( *pceltFetched < celt )
4585 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
4587 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
4588 break;
4591 * Read the entry from the storage.
4593 StorageBaseImpl_ReadDirEntry(This->parentStorage,
4594 currentSearchNode,
4595 &currentEntry);
4598 * Copy the information to the return buffer.
4600 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
4601 currentReturnStruct,
4602 &currentEntry,
4603 STATFLAG_DEFAULT);
4606 * Step to the next item in the iteration
4608 (*pceltFetched)++;
4609 currentReturnStruct++;
4612 if (SUCCEEDED(hr) && *pceltFetched != celt)
4613 hr = S_FALSE;
4615 return hr;
4619 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
4620 IEnumSTATSTG* iface,
4621 ULONG celt)
4623 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4625 ULONG objectFetched = 0;
4626 DirRef currentSearchNode;
4627 HRESULT hr=S_OK;
4629 if (This->parentStorage->reverted)
4630 return STG_E_REVERTED;
4632 while ( (objectFetched < celt) )
4634 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
4636 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
4637 break;
4639 objectFetched++;
4642 if (SUCCEEDED(hr) && objectFetched != celt)
4643 return S_FALSE;
4645 return hr;
4648 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
4649 IEnumSTATSTG* iface)
4651 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4653 if (This->parentStorage->reverted)
4654 return STG_E_REVERTED;
4656 This->name[0] = 0;
4658 return S_OK;
4661 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
4662 IEnumSTATSTG* iface,
4663 IEnumSTATSTG** ppenum)
4665 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4667 IEnumSTATSTGImpl* newClone;
4669 if (This->parentStorage->reverted)
4670 return STG_E_REVERTED;
4673 * Perform a sanity check on the parameters.
4675 if (ppenum==0)
4676 return E_INVALIDARG;
4678 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
4679 This->storageDirEntry);
4683 * The new clone enumeration must point to the same current node as
4684 * the ole one.
4686 memcpy(newClone->name, This->name, sizeof(newClone->name));
4688 *ppenum = (IEnumSTATSTG*)newClone;
4691 * Don't forget to nail down a reference to the clone before
4692 * returning it.
4694 IEnumSTATSTGImpl_AddRef(*ppenum);
4696 return S_OK;
4700 * Virtual function table for the IEnumSTATSTGImpl class.
4702 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4704 IEnumSTATSTGImpl_QueryInterface,
4705 IEnumSTATSTGImpl_AddRef,
4706 IEnumSTATSTGImpl_Release,
4707 IEnumSTATSTGImpl_Next,
4708 IEnumSTATSTGImpl_Skip,
4709 IEnumSTATSTGImpl_Reset,
4710 IEnumSTATSTGImpl_Clone
4713 /******************************************************************************
4714 ** IEnumSTATSTGImpl implementation
4717 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4718 StorageBaseImpl* parentStorage,
4719 DirRef storageDirEntry)
4721 IEnumSTATSTGImpl* newEnumeration;
4723 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4725 if (newEnumeration!=0)
4728 * Set-up the virtual function table and reference count.
4730 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4731 newEnumeration->ref = 0;
4734 * We want to nail-down the reference to the storage in case the
4735 * enumeration out-lives the storage in the client application.
4737 newEnumeration->parentStorage = parentStorage;
4738 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4740 newEnumeration->storageDirEntry = storageDirEntry;
4743 * Make sure the current node of the iterator is the first one.
4745 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4748 return newEnumeration;
4752 * Virtual function table for the Storage32InternalImpl class.
4754 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4756 StorageBaseImpl_QueryInterface,
4757 StorageBaseImpl_AddRef,
4758 StorageBaseImpl_Release,
4759 StorageBaseImpl_CreateStream,
4760 StorageBaseImpl_OpenStream,
4761 StorageBaseImpl_CreateStorage,
4762 StorageBaseImpl_OpenStorage,
4763 StorageBaseImpl_CopyTo,
4764 StorageBaseImpl_MoveElementTo,
4765 StorageInternalImpl_Commit,
4766 StorageInternalImpl_Revert,
4767 StorageBaseImpl_EnumElements,
4768 StorageBaseImpl_DestroyElement,
4769 StorageBaseImpl_RenameElement,
4770 StorageBaseImpl_SetElementTimes,
4771 StorageBaseImpl_SetClass,
4772 StorageBaseImpl_SetStateBits,
4773 StorageBaseImpl_Stat
4776 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
4778 StorageInternalImpl_Destroy,
4779 StorageInternalImpl_Invalidate,
4780 StorageInternalImpl_CreateDirEntry,
4781 StorageInternalImpl_WriteDirEntry,
4782 StorageInternalImpl_ReadDirEntry,
4783 StorageInternalImpl_DestroyDirEntry,
4784 StorageInternalImpl_StreamReadAt,
4785 StorageInternalImpl_StreamWriteAt,
4786 StorageInternalImpl_StreamSetSize
4789 /******************************************************************************
4790 ** Storage32InternalImpl implementation
4793 static StorageInternalImpl* StorageInternalImpl_Construct(
4794 StorageBaseImpl* parentStorage,
4795 DWORD openFlags,
4796 DirRef storageDirEntry)
4798 StorageInternalImpl* newStorage;
4800 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4802 if (newStorage!=0)
4804 list_init(&newStorage->base.strmHead);
4806 list_init(&newStorage->base.storageHead);
4809 * Initialize the virtual function table.
4811 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4812 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
4813 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4815 newStorage->base.reverted = 0;
4817 newStorage->base.ref = 1;
4819 newStorage->parentStorage = parentStorage;
4822 * Keep a reference to the directory entry of this storage
4824 newStorage->base.storageDirEntry = storageDirEntry;
4826 newStorage->base.create = 0;
4828 return newStorage;
4831 return 0;
4834 /******************************************************************************
4835 ** StorageUtl implementation
4838 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4840 WORD tmp;
4842 memcpy(&tmp, buffer+offset, sizeof(WORD));
4843 *value = lendian16toh(tmp);
4846 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4848 value = htole16(value);
4849 memcpy(buffer+offset, &value, sizeof(WORD));
4852 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4854 DWORD tmp;
4856 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4857 *value = lendian32toh(tmp);
4860 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4862 value = htole32(value);
4863 memcpy(buffer+offset, &value, sizeof(DWORD));
4866 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4867 ULARGE_INTEGER* value)
4869 #ifdef WORDS_BIGENDIAN
4870 ULARGE_INTEGER tmp;
4872 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4873 value->u.LowPart = htole32(tmp.u.HighPart);
4874 value->u.HighPart = htole32(tmp.u.LowPart);
4875 #else
4876 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4877 #endif
4880 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4881 const ULARGE_INTEGER *value)
4883 #ifdef WORDS_BIGENDIAN
4884 ULARGE_INTEGER tmp;
4886 tmp.u.LowPart = htole32(value->u.HighPart);
4887 tmp.u.HighPart = htole32(value->u.LowPart);
4888 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4889 #else
4890 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4891 #endif
4894 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4896 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4897 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4898 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4900 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4903 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4905 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4906 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4907 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4909 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4912 void StorageUtl_CopyDirEntryToSTATSTG(
4913 StorageBaseImpl* storage,
4914 STATSTG* destination,
4915 const DirEntry* source,
4916 int statFlags)
4918 LPCWSTR entryName;
4920 if (source->stgType == STGTY_ROOT)
4922 /* replace the name of root entry (often "Root Entry") by the file name */
4923 entryName = storage->filename;
4925 else
4927 entryName = source->name;
4931 * The copy of the string occurs only when the flag is not set
4933 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4934 (entryName == NULL) ||
4935 (entryName[0] == 0) )
4937 destination->pwcsName = 0;
4939 else
4941 destination->pwcsName =
4942 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
4944 strcpyW(destination->pwcsName, entryName);
4947 switch (source->stgType)
4949 case STGTY_STORAGE:
4950 case STGTY_ROOT:
4951 destination->type = STGTY_STORAGE;
4952 break;
4953 case STGTY_STREAM:
4954 destination->type = STGTY_STREAM;
4955 break;
4956 default:
4957 destination->type = STGTY_STREAM;
4958 break;
4961 destination->cbSize = source->size;
4963 currentReturnStruct->mtime = {0}; TODO
4964 currentReturnStruct->ctime = {0};
4965 currentReturnStruct->atime = {0};
4967 destination->grfMode = 0;
4968 destination->grfLocksSupported = 0;
4969 destination->clsid = source->clsid;
4970 destination->grfStateBits = 0;
4971 destination->reserved = 0;
4974 /******************************************************************************
4975 ** BlockChainStream implementation
4978 BlockChainStream* BlockChainStream_Construct(
4979 StorageImpl* parentStorage,
4980 ULONG* headOfStreamPlaceHolder,
4981 DirRef dirEntry)
4983 BlockChainStream* newStream;
4984 ULONG blockIndex;
4986 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4988 newStream->parentStorage = parentStorage;
4989 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4990 newStream->ownerDirEntry = dirEntry;
4991 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4992 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4993 newStream->numBlocks = 0;
4995 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4997 while (blockIndex != BLOCK_END_OF_CHAIN)
4999 newStream->numBlocks++;
5000 newStream->tailIndex = blockIndex;
5002 if(FAILED(StorageImpl_GetNextBlockInChain(
5003 parentStorage,
5004 blockIndex,
5005 &blockIndex)))
5007 HeapFree(GetProcessHeap(), 0, newStream);
5008 return NULL;
5012 return newStream;
5015 void BlockChainStream_Destroy(BlockChainStream* This)
5017 HeapFree(GetProcessHeap(), 0, This);
5020 /******************************************************************************
5021 * BlockChainStream_GetHeadOfChain
5023 * Returns the head of this stream chain.
5024 * Some special chains don't have directory entries, their heads are kept in
5025 * This->headOfStreamPlaceHolder.
5028 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5030 DirEntry chainEntry;
5031 HRESULT hr;
5033 if (This->headOfStreamPlaceHolder != 0)
5034 return *(This->headOfStreamPlaceHolder);
5036 if (This->ownerDirEntry != DIRENTRY_NULL)
5038 hr = StorageImpl_ReadDirEntry(
5039 This->parentStorage,
5040 This->ownerDirEntry,
5041 &chainEntry);
5043 if (SUCCEEDED(hr))
5045 return chainEntry.startingBlock;
5049 return BLOCK_END_OF_CHAIN;
5052 /******************************************************************************
5053 * BlockChainStream_GetCount
5055 * Returns the number of blocks that comprises this chain.
5056 * This is not the size of the stream as the last block may not be full!
5059 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5061 ULONG blockIndex;
5062 ULONG count = 0;
5064 blockIndex = BlockChainStream_GetHeadOfChain(This);
5066 while (blockIndex != BLOCK_END_OF_CHAIN)
5068 count++;
5070 if(FAILED(StorageImpl_GetNextBlockInChain(
5071 This->parentStorage,
5072 blockIndex,
5073 &blockIndex)))
5074 return 0;
5077 return count;
5080 /******************************************************************************
5081 * BlockChainStream_ReadAt
5083 * Reads a specified number of bytes from this chain at the specified offset.
5084 * bytesRead may be NULL.
5085 * Failure will be returned if the specified number of bytes has not been read.
5087 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5088 ULARGE_INTEGER offset,
5089 ULONG size,
5090 void* buffer,
5091 ULONG* bytesRead)
5093 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5094 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5095 ULONG bytesToReadInBuffer;
5096 ULONG blockIndex;
5097 BYTE* bufferWalker;
5099 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5102 * Find the first block in the stream that contains part of the buffer.
5104 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5105 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5106 (blockNoInSequence < This->lastBlockNoInSequence) )
5108 blockIndex = BlockChainStream_GetHeadOfChain(This);
5109 This->lastBlockNoInSequence = blockNoInSequence;
5111 else
5113 ULONG temp = blockNoInSequence;
5115 blockIndex = This->lastBlockNoInSequenceIndex;
5116 blockNoInSequence -= This->lastBlockNoInSequence;
5117 This->lastBlockNoInSequence = temp;
5120 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5122 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5123 return STG_E_DOCFILECORRUPT;
5124 blockNoInSequence--;
5127 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
5128 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
5130 This->lastBlockNoInSequenceIndex = blockIndex;
5133 * Start reading the buffer.
5135 *bytesRead = 0;
5136 bufferWalker = buffer;
5138 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5140 ULARGE_INTEGER ulOffset;
5141 DWORD bytesReadAt;
5143 * Calculate how many bytes we can copy from this big block.
5145 bytesToReadInBuffer =
5146 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5148 TRACE("block %i\n",blockIndex);
5149 ulOffset.u.HighPart = 0;
5150 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5151 offsetInBlock;
5153 StorageImpl_ReadAt(This->parentStorage,
5154 ulOffset,
5155 bufferWalker,
5156 bytesToReadInBuffer,
5157 &bytesReadAt);
5159 * Step to the next big block.
5161 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5162 return STG_E_DOCFILECORRUPT;
5164 bufferWalker += bytesReadAt;
5165 size -= bytesReadAt;
5166 *bytesRead += bytesReadAt;
5167 offsetInBlock = 0; /* There is no offset on the next block */
5169 if (bytesToReadInBuffer != bytesReadAt)
5170 break;
5173 return (size == 0) ? S_OK : STG_E_READFAULT;
5176 /******************************************************************************
5177 * BlockChainStream_WriteAt
5179 * Writes the specified number of bytes to this chain at the specified offset.
5180 * Will fail if not all specified number of bytes have been written.
5182 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5183 ULARGE_INTEGER offset,
5184 ULONG size,
5185 const void* buffer,
5186 ULONG* bytesWritten)
5188 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5189 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5190 ULONG bytesToWrite;
5191 ULONG blockIndex;
5192 const BYTE* bufferWalker;
5195 * Find the first block in the stream that contains part of the buffer.
5197 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5198 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5199 (blockNoInSequence < This->lastBlockNoInSequence) )
5201 blockIndex = BlockChainStream_GetHeadOfChain(This);
5202 This->lastBlockNoInSequence = blockNoInSequence;
5204 else
5206 ULONG temp = blockNoInSequence;
5208 blockIndex = This->lastBlockNoInSequenceIndex;
5209 blockNoInSequence -= This->lastBlockNoInSequence;
5210 This->lastBlockNoInSequence = temp;
5213 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5215 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5216 &blockIndex)))
5217 return STG_E_DOCFILECORRUPT;
5218 blockNoInSequence--;
5221 This->lastBlockNoInSequenceIndex = blockIndex;
5223 /* BlockChainStream_SetSize should have already been called to ensure we have
5224 * enough blocks in the chain to write into */
5225 if (blockIndex == BLOCK_END_OF_CHAIN)
5227 ERR("not enough blocks in chain to write data\n");
5228 return STG_E_DOCFILECORRUPT;
5231 *bytesWritten = 0;
5232 bufferWalker = buffer;
5234 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5236 ULARGE_INTEGER ulOffset;
5237 DWORD bytesWrittenAt;
5239 * Calculate how many bytes we can copy from this big block.
5241 bytesToWrite =
5242 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5244 TRACE("block %i\n",blockIndex);
5245 ulOffset.u.HighPart = 0;
5246 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5247 offsetInBlock;
5249 StorageImpl_WriteAt(This->parentStorage,
5250 ulOffset,
5251 bufferWalker,
5252 bytesToWrite,
5253 &bytesWrittenAt);
5256 * Step to the next big block.
5258 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5259 &blockIndex)))
5260 return STG_E_DOCFILECORRUPT;
5262 bufferWalker += bytesWrittenAt;
5263 size -= bytesWrittenAt;
5264 *bytesWritten += bytesWrittenAt;
5265 offsetInBlock = 0; /* There is no offset on the next block */
5267 if (bytesWrittenAt != bytesToWrite)
5268 break;
5271 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5274 /******************************************************************************
5275 * BlockChainStream_Shrink
5277 * Shrinks this chain in the big block depot.
5279 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
5280 ULARGE_INTEGER newSize)
5282 ULONG blockIndex, extraBlock;
5283 ULONG numBlocks;
5284 ULONG count = 1;
5287 * Reset the last accessed block cache.
5289 This->lastBlockNoInSequence = 0xFFFFFFFF;
5290 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
5293 * Figure out how many blocks are needed to contain the new size
5295 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5297 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5298 numBlocks++;
5300 blockIndex = BlockChainStream_GetHeadOfChain(This);
5303 * Go to the new end of chain
5305 while (count < numBlocks)
5307 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5308 &blockIndex)))
5309 return FALSE;
5310 count++;
5313 /* Get the next block before marking the new end */
5314 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5315 &extraBlock)))
5316 return FALSE;
5318 /* Mark the new end of chain */
5319 StorageImpl_SetNextBlockInChain(
5320 This->parentStorage,
5321 blockIndex,
5322 BLOCK_END_OF_CHAIN);
5324 This->tailIndex = blockIndex;
5325 This->numBlocks = numBlocks;
5328 * Mark the extra blocks as free
5330 while (extraBlock != BLOCK_END_OF_CHAIN)
5332 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
5333 &blockIndex)))
5334 return FALSE;
5335 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
5336 extraBlock = blockIndex;
5339 return TRUE;
5342 /******************************************************************************
5343 * BlockChainStream_Enlarge
5345 * Grows this chain in the big block depot.
5347 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
5348 ULARGE_INTEGER newSize)
5350 ULONG blockIndex, currentBlock;
5351 ULONG newNumBlocks;
5352 ULONG oldNumBlocks = 0;
5354 blockIndex = BlockChainStream_GetHeadOfChain(This);
5357 * Empty chain. Create the head.
5359 if (blockIndex == BLOCK_END_OF_CHAIN)
5361 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5362 StorageImpl_SetNextBlockInChain(This->parentStorage,
5363 blockIndex,
5364 BLOCK_END_OF_CHAIN);
5366 if (This->headOfStreamPlaceHolder != 0)
5368 *(This->headOfStreamPlaceHolder) = blockIndex;
5370 else
5372 DirEntry chainEntry;
5373 assert(This->ownerDirEntry != DIRENTRY_NULL);
5375 StorageImpl_ReadDirEntry(
5376 This->parentStorage,
5377 This->ownerDirEntry,
5378 &chainEntry);
5380 chainEntry.startingBlock = blockIndex;
5382 StorageImpl_WriteDirEntry(
5383 This->parentStorage,
5384 This->ownerDirEntry,
5385 &chainEntry);
5388 This->tailIndex = blockIndex;
5389 This->numBlocks = 1;
5393 * Figure out how many blocks are needed to contain this stream
5395 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5397 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5398 newNumBlocks++;
5401 * Go to the current end of chain
5403 if (This->tailIndex == BLOCK_END_OF_CHAIN)
5405 currentBlock = blockIndex;
5407 while (blockIndex != BLOCK_END_OF_CHAIN)
5409 This->numBlocks++;
5410 currentBlock = blockIndex;
5412 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
5413 &blockIndex)))
5414 return FALSE;
5417 This->tailIndex = currentBlock;
5420 currentBlock = This->tailIndex;
5421 oldNumBlocks = This->numBlocks;
5424 * Add new blocks to the chain
5426 if (oldNumBlocks < newNumBlocks)
5428 while (oldNumBlocks < newNumBlocks)
5430 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5432 StorageImpl_SetNextBlockInChain(
5433 This->parentStorage,
5434 currentBlock,
5435 blockIndex);
5437 StorageImpl_SetNextBlockInChain(
5438 This->parentStorage,
5439 blockIndex,
5440 BLOCK_END_OF_CHAIN);
5442 currentBlock = blockIndex;
5443 oldNumBlocks++;
5446 This->tailIndex = blockIndex;
5447 This->numBlocks = newNumBlocks;
5450 return TRUE;
5453 /******************************************************************************
5454 * BlockChainStream_SetSize
5456 * Sets the size of this stream. The big block depot will be updated.
5457 * The file will grow if we grow the chain.
5459 * TODO: Free the actual blocks in the file when we shrink the chain.
5460 * Currently, the blocks are still in the file. So the file size
5461 * doesn't shrink even if we shrink streams.
5463 BOOL BlockChainStream_SetSize(
5464 BlockChainStream* This,
5465 ULARGE_INTEGER newSize)
5467 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
5469 if (newSize.u.LowPart == size.u.LowPart)
5470 return TRUE;
5472 if (newSize.u.LowPart < size.u.LowPart)
5474 BlockChainStream_Shrink(This, newSize);
5476 else
5478 BlockChainStream_Enlarge(This, newSize);
5481 return TRUE;
5484 /******************************************************************************
5485 * BlockChainStream_GetSize
5487 * Returns the size of this chain.
5488 * Will return the block count if this chain doesn't have a directory entry.
5490 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
5492 DirEntry chainEntry;
5494 if(This->headOfStreamPlaceHolder == NULL)
5497 * This chain has a directory entry so use the size value from there.
5499 StorageImpl_ReadDirEntry(
5500 This->parentStorage,
5501 This->ownerDirEntry,
5502 &chainEntry);
5504 return chainEntry.size;
5506 else
5509 * this chain is a chain that does not have a directory entry, figure out the
5510 * size by making the product number of used blocks times the
5511 * size of them
5513 ULARGE_INTEGER result;
5514 result.u.HighPart = 0;
5516 result.u.LowPart =
5517 BlockChainStream_GetCount(This) *
5518 This->parentStorage->bigBlockSize;
5520 return result;
5524 /******************************************************************************
5525 ** SmallBlockChainStream implementation
5528 SmallBlockChainStream* SmallBlockChainStream_Construct(
5529 StorageImpl* parentStorage,
5530 ULONG* headOfStreamPlaceHolder,
5531 DirRef dirEntry)
5533 SmallBlockChainStream* newStream;
5535 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
5537 newStream->parentStorage = parentStorage;
5538 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5539 newStream->ownerDirEntry = dirEntry;
5541 return newStream;
5544 void SmallBlockChainStream_Destroy(
5545 SmallBlockChainStream* This)
5547 HeapFree(GetProcessHeap(), 0, This);
5550 /******************************************************************************
5551 * SmallBlockChainStream_GetHeadOfChain
5553 * Returns the head of this chain of small blocks.
5555 static ULONG SmallBlockChainStream_GetHeadOfChain(
5556 SmallBlockChainStream* This)
5558 DirEntry chainEntry;
5559 HRESULT hr;
5561 if (This->headOfStreamPlaceHolder != NULL)
5562 return *(This->headOfStreamPlaceHolder);
5564 if (This->ownerDirEntry)
5566 hr = StorageImpl_ReadDirEntry(
5567 This->parentStorage,
5568 This->ownerDirEntry,
5569 &chainEntry);
5571 if (SUCCEEDED(hr))
5573 return chainEntry.startingBlock;
5578 return BLOCK_END_OF_CHAIN;
5581 /******************************************************************************
5582 * SmallBlockChainStream_GetNextBlockInChain
5584 * Returns the index of the next small block in this chain.
5586 * Return Values:
5587 * - BLOCK_END_OF_CHAIN: end of this chain
5588 * - BLOCK_UNUSED: small block 'blockIndex' is free
5590 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5591 SmallBlockChainStream* This,
5592 ULONG blockIndex,
5593 ULONG* nextBlockInChain)
5595 ULARGE_INTEGER offsetOfBlockInDepot;
5596 DWORD buffer;
5597 ULONG bytesRead;
5598 HRESULT res;
5600 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5602 offsetOfBlockInDepot.u.HighPart = 0;
5603 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5606 * Read those bytes in the buffer from the small block file.
5608 res = BlockChainStream_ReadAt(
5609 This->parentStorage->smallBlockDepotChain,
5610 offsetOfBlockInDepot,
5611 sizeof(DWORD),
5612 &buffer,
5613 &bytesRead);
5615 if (SUCCEEDED(res))
5617 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5618 return S_OK;
5621 return res;
5624 /******************************************************************************
5625 * SmallBlockChainStream_SetNextBlockInChain
5627 * Writes the index of the next block of the specified block in the small
5628 * block depot.
5629 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5630 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5632 static void SmallBlockChainStream_SetNextBlockInChain(
5633 SmallBlockChainStream* This,
5634 ULONG blockIndex,
5635 ULONG nextBlock)
5637 ULARGE_INTEGER offsetOfBlockInDepot;
5638 DWORD buffer;
5639 ULONG bytesWritten;
5641 offsetOfBlockInDepot.u.HighPart = 0;
5642 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5644 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5647 * Read those bytes in the buffer from the small block file.
5649 BlockChainStream_WriteAt(
5650 This->parentStorage->smallBlockDepotChain,
5651 offsetOfBlockInDepot,
5652 sizeof(DWORD),
5653 &buffer,
5654 &bytesWritten);
5657 /******************************************************************************
5658 * SmallBlockChainStream_FreeBlock
5660 * Flag small block 'blockIndex' as free in the small block depot.
5662 static void SmallBlockChainStream_FreeBlock(
5663 SmallBlockChainStream* This,
5664 ULONG blockIndex)
5666 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5669 /******************************************************************************
5670 * SmallBlockChainStream_GetNextFreeBlock
5672 * Returns the index of a free small block. The small block depot will be
5673 * enlarged if necessary. The small block chain will also be enlarged if
5674 * necessary.
5676 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5677 SmallBlockChainStream* This)
5679 ULARGE_INTEGER offsetOfBlockInDepot;
5680 DWORD buffer;
5681 ULONG bytesRead;
5682 ULONG blockIndex = 0;
5683 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5684 HRESULT res = S_OK;
5685 ULONG smallBlocksPerBigBlock;
5687 offsetOfBlockInDepot.u.HighPart = 0;
5690 * Scan the small block depot for a free block
5692 while (nextBlockIndex != BLOCK_UNUSED)
5694 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5696 res = BlockChainStream_ReadAt(
5697 This->parentStorage->smallBlockDepotChain,
5698 offsetOfBlockInDepot,
5699 sizeof(DWORD),
5700 &buffer,
5701 &bytesRead);
5704 * If we run out of space for the small block depot, enlarge it
5706 if (SUCCEEDED(res))
5708 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5710 if (nextBlockIndex != BLOCK_UNUSED)
5711 blockIndex++;
5713 else
5715 ULONG count =
5716 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5718 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5719 ULONG nextBlock, newsbdIndex;
5720 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5722 nextBlock = sbdIndex;
5723 while (nextBlock != BLOCK_END_OF_CHAIN)
5725 sbdIndex = nextBlock;
5726 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5729 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5730 if (sbdIndex != BLOCK_END_OF_CHAIN)
5731 StorageImpl_SetNextBlockInChain(
5732 This->parentStorage,
5733 sbdIndex,
5734 newsbdIndex);
5736 StorageImpl_SetNextBlockInChain(
5737 This->parentStorage,
5738 newsbdIndex,
5739 BLOCK_END_OF_CHAIN);
5742 * Initialize all the small blocks to free
5744 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5745 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5747 if (count == 0)
5750 * We have just created the small block depot.
5752 DirEntry rootEntry;
5753 ULONG sbStartIndex;
5756 * Save it in the header
5758 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5759 StorageImpl_SaveFileHeader(This->parentStorage);
5762 * And allocate the first big block that will contain small blocks
5764 sbStartIndex =
5765 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5767 StorageImpl_SetNextBlockInChain(
5768 This->parentStorage,
5769 sbStartIndex,
5770 BLOCK_END_OF_CHAIN);
5772 StorageImpl_ReadDirEntry(
5773 This->parentStorage,
5774 This->parentStorage->base.storageDirEntry,
5775 &rootEntry);
5777 rootEntry.startingBlock = sbStartIndex;
5778 rootEntry.size.u.HighPart = 0;
5779 rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
5781 StorageImpl_WriteDirEntry(
5782 This->parentStorage,
5783 This->parentStorage->base.storageDirEntry,
5784 &rootEntry);
5786 else
5787 StorageImpl_SaveFileHeader(This->parentStorage);
5791 smallBlocksPerBigBlock =
5792 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5795 * Verify if we have to allocate big blocks to contain small blocks
5797 if (blockIndex % smallBlocksPerBigBlock == 0)
5799 DirEntry rootEntry;
5800 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5802 StorageImpl_ReadDirEntry(
5803 This->parentStorage,
5804 This->parentStorage->base.storageDirEntry,
5805 &rootEntry);
5807 if (rootEntry.size.u.LowPart <
5808 (blocksRequired * This->parentStorage->bigBlockSize))
5810 rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5812 BlockChainStream_SetSize(
5813 This->parentStorage->smallBlockRootChain,
5814 rootEntry.size);
5816 StorageImpl_WriteDirEntry(
5817 This->parentStorage,
5818 This->parentStorage->base.storageDirEntry,
5819 &rootEntry);
5823 return blockIndex;
5826 /******************************************************************************
5827 * SmallBlockChainStream_ReadAt
5829 * Reads a specified number of bytes from this chain at the specified offset.
5830 * bytesRead may be NULL.
5831 * Failure will be returned if the specified number of bytes has not been read.
5833 HRESULT SmallBlockChainStream_ReadAt(
5834 SmallBlockChainStream* This,
5835 ULARGE_INTEGER offset,
5836 ULONG size,
5837 void* buffer,
5838 ULONG* bytesRead)
5840 HRESULT rc = S_OK;
5841 ULARGE_INTEGER offsetInBigBlockFile;
5842 ULONG blockNoInSequence =
5843 offset.u.LowPart / This->parentStorage->smallBlockSize;
5845 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5846 ULONG bytesToReadInBuffer;
5847 ULONG blockIndex;
5848 ULONG bytesReadFromBigBlockFile;
5849 BYTE* bufferWalker;
5852 * This should never happen on a small block file.
5854 assert(offset.u.HighPart==0);
5857 * Find the first block in the stream that contains part of the buffer.
5859 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5861 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5863 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5864 if(FAILED(rc))
5865 return rc;
5866 blockNoInSequence--;
5870 * Start reading the buffer.
5872 *bytesRead = 0;
5873 bufferWalker = buffer;
5875 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5878 * Calculate how many bytes we can copy from this small block.
5880 bytesToReadInBuffer =
5881 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5884 * Calculate the offset of the small block in the small block file.
5886 offsetInBigBlockFile.u.HighPart = 0;
5887 offsetInBigBlockFile.u.LowPart =
5888 blockIndex * This->parentStorage->smallBlockSize;
5890 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5893 * Read those bytes in the buffer from the small block file.
5894 * The small block has already been identified so it shouldn't fail
5895 * unless the file is corrupt.
5897 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5898 offsetInBigBlockFile,
5899 bytesToReadInBuffer,
5900 bufferWalker,
5901 &bytesReadFromBigBlockFile);
5903 if (FAILED(rc))
5904 return rc;
5907 * Step to the next big block.
5909 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5910 if(FAILED(rc))
5911 return STG_E_DOCFILECORRUPT;
5913 bufferWalker += bytesReadFromBigBlockFile;
5914 size -= bytesReadFromBigBlockFile;
5915 *bytesRead += bytesReadFromBigBlockFile;
5916 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5919 return (size == 0) ? S_OK : STG_E_READFAULT;
5922 /******************************************************************************
5923 * SmallBlockChainStream_WriteAt
5925 * Writes the specified number of bytes to this chain at the specified offset.
5926 * Will fail if not all specified number of bytes have been written.
5928 HRESULT SmallBlockChainStream_WriteAt(
5929 SmallBlockChainStream* This,
5930 ULARGE_INTEGER offset,
5931 ULONG size,
5932 const void* buffer,
5933 ULONG* bytesWritten)
5935 ULARGE_INTEGER offsetInBigBlockFile;
5936 ULONG blockNoInSequence =
5937 offset.u.LowPart / This->parentStorage->smallBlockSize;
5939 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5940 ULONG bytesToWriteInBuffer;
5941 ULONG blockIndex;
5942 ULONG bytesWrittenToBigBlockFile;
5943 const BYTE* bufferWalker;
5944 HRESULT res;
5947 * This should never happen on a small block file.
5949 assert(offset.u.HighPart==0);
5952 * Find the first block in the stream that contains part of the buffer.
5954 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5956 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5958 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5959 return STG_E_DOCFILECORRUPT;
5960 blockNoInSequence--;
5964 * Start writing the buffer.
5966 *bytesWritten = 0;
5967 bufferWalker = buffer;
5968 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5971 * Calculate how many bytes we can copy to this small block.
5973 bytesToWriteInBuffer =
5974 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5977 * Calculate the offset of the small block in the small block file.
5979 offsetInBigBlockFile.u.HighPart = 0;
5980 offsetInBigBlockFile.u.LowPart =
5981 blockIndex * This->parentStorage->smallBlockSize;
5983 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5986 * Write those bytes in the buffer to the small block file.
5988 res = BlockChainStream_WriteAt(
5989 This->parentStorage->smallBlockRootChain,
5990 offsetInBigBlockFile,
5991 bytesToWriteInBuffer,
5992 bufferWalker,
5993 &bytesWrittenToBigBlockFile);
5994 if (FAILED(res))
5995 return res;
5998 * Step to the next big block.
6000 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6001 &blockIndex)))
6002 return FALSE;
6003 bufferWalker += bytesWrittenToBigBlockFile;
6004 size -= bytesWrittenToBigBlockFile;
6005 *bytesWritten += bytesWrittenToBigBlockFile;
6006 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6009 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6012 /******************************************************************************
6013 * SmallBlockChainStream_Shrink
6015 * Shrinks this chain in the small block depot.
6017 static BOOL SmallBlockChainStream_Shrink(
6018 SmallBlockChainStream* This,
6019 ULARGE_INTEGER newSize)
6021 ULONG blockIndex, extraBlock;
6022 ULONG numBlocks;
6023 ULONG count = 0;
6025 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6027 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6028 numBlocks++;
6030 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6033 * Go to the new end of chain
6035 while (count < numBlocks)
6037 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6038 &blockIndex)))
6039 return FALSE;
6040 count++;
6044 * If the count is 0, we have a special case, the head of the chain was
6045 * just freed.
6047 if (count == 0)
6049 DirEntry chainEntry;
6051 StorageImpl_ReadDirEntry(This->parentStorage,
6052 This->ownerDirEntry,
6053 &chainEntry);
6055 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6057 StorageImpl_WriteDirEntry(This->parentStorage,
6058 This->ownerDirEntry,
6059 &chainEntry);
6062 * We start freeing the chain at the head block.
6064 extraBlock = blockIndex;
6066 else
6068 /* Get the next block before marking the new end */
6069 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6070 &extraBlock)))
6071 return FALSE;
6073 /* Mark the new end of chain */
6074 SmallBlockChainStream_SetNextBlockInChain(
6075 This,
6076 blockIndex,
6077 BLOCK_END_OF_CHAIN);
6081 * Mark the extra blocks as free
6083 while (extraBlock != BLOCK_END_OF_CHAIN)
6085 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6086 &blockIndex)))
6087 return FALSE;
6088 SmallBlockChainStream_FreeBlock(This, extraBlock);
6089 extraBlock = blockIndex;
6092 return TRUE;
6095 /******************************************************************************
6096 * SmallBlockChainStream_Enlarge
6098 * Grows this chain in the small block depot.
6100 static BOOL SmallBlockChainStream_Enlarge(
6101 SmallBlockChainStream* This,
6102 ULARGE_INTEGER newSize)
6104 ULONG blockIndex, currentBlock;
6105 ULONG newNumBlocks;
6106 ULONG oldNumBlocks = 0;
6108 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6111 * Empty chain. Create the head.
6113 if (blockIndex == BLOCK_END_OF_CHAIN)
6115 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6116 SmallBlockChainStream_SetNextBlockInChain(
6117 This,
6118 blockIndex,
6119 BLOCK_END_OF_CHAIN);
6121 if (This->headOfStreamPlaceHolder != NULL)
6123 *(This->headOfStreamPlaceHolder) = blockIndex;
6125 else
6127 DirEntry chainEntry;
6129 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6130 &chainEntry);
6132 chainEntry.startingBlock = blockIndex;
6134 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6135 &chainEntry);
6139 currentBlock = blockIndex;
6142 * Figure out how many blocks are needed to contain this stream
6144 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6146 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6147 newNumBlocks++;
6150 * Go to the current end of chain
6152 while (blockIndex != BLOCK_END_OF_CHAIN)
6154 oldNumBlocks++;
6155 currentBlock = blockIndex;
6156 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6157 return FALSE;
6161 * Add new blocks to the chain
6163 while (oldNumBlocks < newNumBlocks)
6165 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6166 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6168 SmallBlockChainStream_SetNextBlockInChain(
6169 This,
6170 blockIndex,
6171 BLOCK_END_OF_CHAIN);
6173 currentBlock = blockIndex;
6174 oldNumBlocks++;
6177 return TRUE;
6180 /******************************************************************************
6181 * SmallBlockChainStream_SetSize
6183 * Sets the size of this stream.
6184 * The file will grow if we grow the chain.
6186 * TODO: Free the actual blocks in the file when we shrink the chain.
6187 * Currently, the blocks are still in the file. So the file size
6188 * doesn't shrink even if we shrink streams.
6190 BOOL SmallBlockChainStream_SetSize(
6191 SmallBlockChainStream* This,
6192 ULARGE_INTEGER newSize)
6194 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6196 if (newSize.u.LowPart == size.u.LowPart)
6197 return TRUE;
6199 if (newSize.u.LowPart < size.u.LowPart)
6201 SmallBlockChainStream_Shrink(This, newSize);
6203 else
6205 SmallBlockChainStream_Enlarge(This, newSize);
6208 return TRUE;
6211 /******************************************************************************
6212 * SmallBlockChainStream_GetCount
6214 * Returns the number of small blocks that comprises this chain.
6215 * This is not the size of the stream as the last block may not be full!
6218 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6220 ULONG blockIndex;
6221 ULONG count = 0;
6223 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6225 while(blockIndex != BLOCK_END_OF_CHAIN)
6227 count++;
6229 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6230 blockIndex, &blockIndex)))
6231 return 0;
6234 return count;
6237 /******************************************************************************
6238 * SmallBlockChainStream_GetSize
6240 * Returns the size of this chain.
6242 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6244 DirEntry chainEntry;
6246 if(This->headOfStreamPlaceHolder != NULL)
6248 ULARGE_INTEGER result;
6249 result.u.HighPart = 0;
6251 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
6252 This->parentStorage->smallBlockSize;
6254 return result;
6257 StorageImpl_ReadDirEntry(
6258 This->parentStorage,
6259 This->ownerDirEntry,
6260 &chainEntry);
6262 return chainEntry.size;
6265 /******************************************************************************
6266 * StgCreateDocfile [OLE32.@]
6267 * Creates a new compound file storage object
6269 * PARAMS
6270 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6271 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6272 * reserved [ ?] unused?, usually 0
6273 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6275 * RETURNS
6276 * S_OK if the file was successfully created
6277 * some STG_E_ value if error
6278 * NOTES
6279 * if pwcsName is NULL, create file with new unique name
6280 * the function can returns
6281 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6282 * (unrealized now)
6284 HRESULT WINAPI StgCreateDocfile(
6285 LPCOLESTR pwcsName,
6286 DWORD grfMode,
6287 DWORD reserved,
6288 IStorage **ppstgOpen)
6290 StorageBaseImpl* newStorage = 0;
6291 HANDLE hFile = INVALID_HANDLE_VALUE;
6292 HRESULT hr = STG_E_INVALIDFLAG;
6293 DWORD shareMode;
6294 DWORD accessMode;
6295 DWORD creationMode;
6296 DWORD fileAttributes;
6297 WCHAR tempFileName[MAX_PATH];
6299 TRACE("(%s, %x, %d, %p)\n",
6300 debugstr_w(pwcsName), grfMode,
6301 reserved, ppstgOpen);
6303 if (ppstgOpen == 0)
6304 return STG_E_INVALIDPOINTER;
6305 if (reserved != 0)
6306 return STG_E_INVALIDPARAMETER;
6308 /* if no share mode given then DENY_NONE is the default */
6309 if (STGM_SHARE_MODE(grfMode) == 0)
6310 grfMode |= STGM_SHARE_DENY_NONE;
6312 if ( FAILED( validateSTGM(grfMode) ))
6313 goto end;
6315 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6316 switch(STGM_ACCESS_MODE(grfMode))
6318 case STGM_WRITE:
6319 case STGM_READWRITE:
6320 break;
6321 default:
6322 goto end;
6325 /* in direct mode, can only use SHARE_EXCLUSIVE */
6326 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
6327 goto end;
6329 /* but in transacted mode, any share mode is valid */
6332 * Generate a unique name.
6334 if (pwcsName == 0)
6336 WCHAR tempPath[MAX_PATH];
6337 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
6339 memset(tempPath, 0, sizeof(tempPath));
6340 memset(tempFileName, 0, sizeof(tempFileName));
6342 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
6343 tempPath[0] = '.';
6345 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
6346 pwcsName = tempFileName;
6347 else
6349 hr = STG_E_INSUFFICIENTMEMORY;
6350 goto end;
6353 creationMode = TRUNCATE_EXISTING;
6355 else
6357 creationMode = GetCreationModeFromSTGM(grfMode);
6361 * Interpret the STGM value grfMode
6363 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6364 accessMode = GetAccessModeFromSTGM(grfMode);
6366 if (grfMode & STGM_DELETEONRELEASE)
6367 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
6368 else
6369 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
6371 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
6373 static int fixme;
6374 if (!fixme++)
6375 FIXME("Storage share mode not implemented.\n");
6378 *ppstgOpen = 0;
6380 hFile = CreateFileW(pwcsName,
6381 accessMode,
6382 shareMode,
6383 NULL,
6384 creationMode,
6385 fileAttributes,
6388 if (hFile == INVALID_HANDLE_VALUE)
6390 if(GetLastError() == ERROR_FILE_EXISTS)
6391 hr = STG_E_FILEALREADYEXISTS;
6392 else
6393 hr = E_FAIL;
6394 goto end;
6398 * Allocate and initialize the new IStorage32object.
6400 hr = Storage_Construct(
6401 hFile,
6402 pwcsName,
6403 NULL,
6404 grfMode,
6405 TRUE,
6406 TRUE,
6407 &newStorage);
6409 if (FAILED(hr))
6411 goto end;
6415 * Get an "out" pointer for the caller.
6417 *ppstgOpen = (IStorage*)newStorage;
6419 end:
6420 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
6422 return hr;
6425 /******************************************************************************
6426 * StgCreateStorageEx [OLE32.@]
6428 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6430 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6431 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6433 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
6435 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6436 return STG_E_INVALIDPARAMETER;
6439 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
6441 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6442 return STG_E_INVALIDPARAMETER;
6445 if (stgfmt == STGFMT_FILE)
6447 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6448 return STG_E_INVALIDPARAMETER;
6451 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
6453 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6454 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
6457 ERR("Invalid stgfmt argument\n");
6458 return STG_E_INVALIDPARAMETER;
6461 /******************************************************************************
6462 * StgCreatePropSetStg [OLE32.@]
6464 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
6465 IPropertySetStorage **ppPropSetStg)
6467 HRESULT hr;
6469 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
6470 if (reserved)
6471 hr = STG_E_INVALIDPARAMETER;
6472 else
6473 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
6474 (void**)ppPropSetStg);
6475 return hr;
6478 /******************************************************************************
6479 * StgOpenStorageEx [OLE32.@]
6481 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6483 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6484 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6486 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
6488 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6489 return STG_E_INVALIDPARAMETER;
6492 switch (stgfmt)
6494 case STGFMT_FILE:
6495 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6496 return STG_E_INVALIDPARAMETER;
6498 case STGFMT_STORAGE:
6499 break;
6501 case STGFMT_DOCFILE:
6502 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
6504 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6505 return STG_E_INVALIDPARAMETER;
6507 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6508 break;
6510 case STGFMT_ANY:
6511 WARN("STGFMT_ANY assuming storage\n");
6512 break;
6514 default:
6515 return STG_E_INVALIDPARAMETER;
6518 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
6522 /******************************************************************************
6523 * StgOpenStorage [OLE32.@]
6525 HRESULT WINAPI StgOpenStorage(
6526 const OLECHAR *pwcsName,
6527 IStorage *pstgPriority,
6528 DWORD grfMode,
6529 SNB snbExclude,
6530 DWORD reserved,
6531 IStorage **ppstgOpen)
6533 StorageBaseImpl* newStorage = 0;
6534 HRESULT hr = S_OK;
6535 HANDLE hFile = 0;
6536 DWORD shareMode;
6537 DWORD accessMode;
6539 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6540 debugstr_w(pwcsName), pstgPriority, grfMode,
6541 snbExclude, reserved, ppstgOpen);
6543 if (pwcsName == 0)
6545 hr = STG_E_INVALIDNAME;
6546 goto end;
6549 if (ppstgOpen == 0)
6551 hr = STG_E_INVALIDPOINTER;
6552 goto end;
6555 if (reserved)
6557 hr = STG_E_INVALIDPARAMETER;
6558 goto end;
6561 if (grfMode & STGM_PRIORITY)
6563 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
6564 return STG_E_INVALIDFLAG;
6565 if (grfMode & STGM_DELETEONRELEASE)
6566 return STG_E_INVALIDFUNCTION;
6567 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
6568 return STG_E_INVALIDFLAG;
6569 grfMode &= ~0xf0; /* remove the existing sharing mode */
6570 grfMode |= STGM_SHARE_DENY_NONE;
6572 /* STGM_PRIORITY stops other IStorage objects on the same file from
6573 * committing until the STGM_PRIORITY IStorage is closed. it also
6574 * stops non-transacted mode StgOpenStorage calls with write access from
6575 * succeeding. obviously, both of these cannot be achieved through just
6576 * file share flags */
6577 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6581 * Validate the sharing mode
6583 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
6584 switch(STGM_SHARE_MODE(grfMode))
6586 case STGM_SHARE_EXCLUSIVE:
6587 case STGM_SHARE_DENY_WRITE:
6588 break;
6589 default:
6590 hr = STG_E_INVALIDFLAG;
6591 goto end;
6594 if ( FAILED( validateSTGM(grfMode) ) ||
6595 (grfMode&STGM_CREATE))
6597 hr = STG_E_INVALIDFLAG;
6598 goto end;
6601 /* shared reading requires transacted mode */
6602 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6603 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6604 !(grfMode&STGM_TRANSACTED) )
6606 hr = STG_E_INVALIDFLAG;
6607 goto end;
6611 * Interpret the STGM value grfMode
6613 shareMode = GetShareModeFromSTGM(grfMode);
6614 accessMode = GetAccessModeFromSTGM(grfMode);
6616 *ppstgOpen = 0;
6618 hFile = CreateFileW( pwcsName,
6619 accessMode,
6620 shareMode,
6621 NULL,
6622 OPEN_EXISTING,
6623 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6626 if (hFile==INVALID_HANDLE_VALUE)
6628 DWORD last_error = GetLastError();
6630 hr = E_FAIL;
6632 switch (last_error)
6634 case ERROR_FILE_NOT_FOUND:
6635 hr = STG_E_FILENOTFOUND;
6636 break;
6638 case ERROR_PATH_NOT_FOUND:
6639 hr = STG_E_PATHNOTFOUND;
6640 break;
6642 case ERROR_ACCESS_DENIED:
6643 case ERROR_WRITE_PROTECT:
6644 hr = STG_E_ACCESSDENIED;
6645 break;
6647 case ERROR_SHARING_VIOLATION:
6648 hr = STG_E_SHAREVIOLATION;
6649 break;
6651 default:
6652 hr = E_FAIL;
6655 goto end;
6659 * Refuse to open the file if it's too small to be a structured storage file
6660 * FIXME: verify the file when reading instead of here
6662 if (GetFileSize(hFile, NULL) < 0x100)
6664 CloseHandle(hFile);
6665 hr = STG_E_FILEALREADYEXISTS;
6666 goto end;
6670 * Allocate and initialize the new IStorage32object.
6672 hr = Storage_Construct(
6673 hFile,
6674 pwcsName,
6675 NULL,
6676 grfMode,
6677 TRUE,
6678 FALSE,
6679 &newStorage);
6681 if (FAILED(hr))
6684 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6686 if(hr == STG_E_INVALIDHEADER)
6687 hr = STG_E_FILEALREADYEXISTS;
6688 goto end;
6692 * Get an "out" pointer for the caller.
6694 *ppstgOpen = (IStorage*)newStorage;
6696 end:
6697 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6698 return hr;
6701 /******************************************************************************
6702 * StgCreateDocfileOnILockBytes [OLE32.@]
6704 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6705 ILockBytes *plkbyt,
6706 DWORD grfMode,
6707 DWORD reserved,
6708 IStorage** ppstgOpen)
6710 StorageBaseImpl* newStorage = 0;
6711 HRESULT hr = S_OK;
6713 if ((ppstgOpen == 0) || (plkbyt == 0))
6714 return STG_E_INVALIDPOINTER;
6717 * Allocate and initialize the new IStorage object.
6719 hr = Storage_Construct(
6722 plkbyt,
6723 grfMode,
6724 FALSE,
6725 TRUE,
6726 &newStorage);
6728 if (FAILED(hr))
6730 return hr;
6734 * Get an "out" pointer for the caller.
6736 *ppstgOpen = (IStorage*)newStorage;
6738 return hr;
6741 /******************************************************************************
6742 * StgOpenStorageOnILockBytes [OLE32.@]
6744 HRESULT WINAPI StgOpenStorageOnILockBytes(
6745 ILockBytes *plkbyt,
6746 IStorage *pstgPriority,
6747 DWORD grfMode,
6748 SNB snbExclude,
6749 DWORD reserved,
6750 IStorage **ppstgOpen)
6752 StorageBaseImpl* newStorage = 0;
6753 HRESULT hr = S_OK;
6755 if ((plkbyt == 0) || (ppstgOpen == 0))
6756 return STG_E_INVALIDPOINTER;
6758 if ( FAILED( validateSTGM(grfMode) ))
6759 return STG_E_INVALIDFLAG;
6761 *ppstgOpen = 0;
6764 * Allocate and initialize the new IStorage object.
6766 hr = Storage_Construct(
6769 plkbyt,
6770 grfMode,
6771 FALSE,
6772 FALSE,
6773 &newStorage);
6775 if (FAILED(hr))
6777 return hr;
6781 * Get an "out" pointer for the caller.
6783 *ppstgOpen = (IStorage*)newStorage;
6785 return hr;
6788 /******************************************************************************
6789 * StgSetTimes [ole32.@]
6790 * StgSetTimes [OLE32.@]
6794 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6795 FILETIME const *patime, FILETIME const *pmtime)
6797 IStorage *stg = NULL;
6798 HRESULT r;
6800 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6802 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6803 0, 0, &stg);
6804 if( SUCCEEDED(r) )
6806 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6807 IStorage_Release(stg);
6810 return r;
6813 /******************************************************************************
6814 * StgIsStorageILockBytes [OLE32.@]
6816 * Determines if the ILockBytes contains a storage object.
6818 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6820 BYTE sig[8];
6821 ULARGE_INTEGER offset;
6823 offset.u.HighPart = 0;
6824 offset.u.LowPart = 0;
6826 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6828 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6829 return S_OK;
6831 return S_FALSE;
6834 /******************************************************************************
6835 * WriteClassStg [OLE32.@]
6837 * This method will store the specified CLSID in the specified storage object
6839 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6841 HRESULT hRes;
6843 if(!pStg)
6844 return E_INVALIDARG;
6846 if(!rclsid)
6847 return STG_E_INVALIDPOINTER;
6849 hRes = IStorage_SetClass(pStg, rclsid);
6851 return hRes;
6854 /***********************************************************************
6855 * ReadClassStg (OLE32.@)
6857 * This method reads the CLSID previously written to a storage object with
6858 * the WriteClassStg.
6860 * PARAMS
6861 * pstg [I] IStorage pointer
6862 * pclsid [O] Pointer to where the CLSID is written
6864 * RETURNS
6865 * Success: S_OK.
6866 * Failure: HRESULT code.
6868 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6870 STATSTG pstatstg;
6871 HRESULT hRes;
6873 TRACE("(%p, %p)\n", pstg, pclsid);
6875 if(!pstg || !pclsid)
6876 return E_INVALIDARG;
6879 * read a STATSTG structure (contains the clsid) from the storage
6881 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6883 if(SUCCEEDED(hRes))
6884 *pclsid=pstatstg.clsid;
6886 return hRes;
6889 /***********************************************************************
6890 * OleLoadFromStream (OLE32.@)
6892 * This function loads an object from stream
6894 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6896 CLSID clsid;
6897 HRESULT res;
6898 LPPERSISTSTREAM xstm;
6900 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6902 res=ReadClassStm(pStm,&clsid);
6903 if (FAILED(res))
6904 return res;
6905 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6906 if (FAILED(res))
6907 return res;
6908 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6909 if (FAILED(res)) {
6910 IUnknown_Release((IUnknown*)*ppvObj);
6911 return res;
6913 res=IPersistStream_Load(xstm,pStm);
6914 IPersistStream_Release(xstm);
6915 /* FIXME: all refcounts ok at this point? I think they should be:
6916 * pStm : unchanged
6917 * ppvObj : 1
6918 * xstm : 0 (released)
6920 return res;
6923 /***********************************************************************
6924 * OleSaveToStream (OLE32.@)
6926 * This function saves an object with the IPersistStream interface on it
6927 * to the specified stream.
6929 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6932 CLSID clsid;
6933 HRESULT res;
6935 TRACE("(%p,%p)\n",pPStm,pStm);
6937 res=IPersistStream_GetClassID(pPStm,&clsid);
6939 if (SUCCEEDED(res)){
6941 res=WriteClassStm(pStm,&clsid);
6943 if (SUCCEEDED(res))
6945 res=IPersistStream_Save(pPStm,pStm,TRUE);
6948 TRACE("Finished Save\n");
6949 return res;
6952 /****************************************************************************
6953 * This method validate a STGM parameter that can contain the values below
6955 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6956 * The stgm values contained in 0xffff0000 are bitmasks.
6958 * STGM_DIRECT 0x00000000
6959 * STGM_TRANSACTED 0x00010000
6960 * STGM_SIMPLE 0x08000000
6962 * STGM_READ 0x00000000
6963 * STGM_WRITE 0x00000001
6964 * STGM_READWRITE 0x00000002
6966 * STGM_SHARE_DENY_NONE 0x00000040
6967 * STGM_SHARE_DENY_READ 0x00000030
6968 * STGM_SHARE_DENY_WRITE 0x00000020
6969 * STGM_SHARE_EXCLUSIVE 0x00000010
6971 * STGM_PRIORITY 0x00040000
6972 * STGM_DELETEONRELEASE 0x04000000
6974 * STGM_CREATE 0x00001000
6975 * STGM_CONVERT 0x00020000
6976 * STGM_FAILIFTHERE 0x00000000
6978 * STGM_NOSCRATCH 0x00100000
6979 * STGM_NOSNAPSHOT 0x00200000
6981 static HRESULT validateSTGM(DWORD stgm)
6983 DWORD access = STGM_ACCESS_MODE(stgm);
6984 DWORD share = STGM_SHARE_MODE(stgm);
6985 DWORD create = STGM_CREATE_MODE(stgm);
6987 if (stgm&~STGM_KNOWN_FLAGS)
6989 ERR("unknown flags %08x\n", stgm);
6990 return E_FAIL;
6993 switch (access)
6995 case STGM_READ:
6996 case STGM_WRITE:
6997 case STGM_READWRITE:
6998 break;
6999 default:
7000 return E_FAIL;
7003 switch (share)
7005 case STGM_SHARE_DENY_NONE:
7006 case STGM_SHARE_DENY_READ:
7007 case STGM_SHARE_DENY_WRITE:
7008 case STGM_SHARE_EXCLUSIVE:
7009 break;
7010 default:
7011 return E_FAIL;
7014 switch (create)
7016 case STGM_CREATE:
7017 case STGM_FAILIFTHERE:
7018 break;
7019 default:
7020 return E_FAIL;
7024 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7026 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7027 return E_FAIL;
7030 * STGM_CREATE | STGM_CONVERT
7031 * if both are false, STGM_FAILIFTHERE is set to TRUE
7033 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7034 return E_FAIL;
7037 * STGM_NOSCRATCH requires STGM_TRANSACTED
7039 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7040 return E_FAIL;
7043 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7044 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7046 if ( (stgm & STGM_NOSNAPSHOT) &&
7047 (!(stgm & STGM_TRANSACTED) ||
7048 share == STGM_SHARE_EXCLUSIVE ||
7049 share == STGM_SHARE_DENY_WRITE) )
7050 return E_FAIL;
7052 return S_OK;
7055 /****************************************************************************
7056 * GetShareModeFromSTGM
7058 * This method will return a share mode flag from a STGM value.
7059 * The STGM value is assumed valid.
7061 static DWORD GetShareModeFromSTGM(DWORD stgm)
7063 switch (STGM_SHARE_MODE(stgm))
7065 case STGM_SHARE_DENY_NONE:
7066 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7067 case STGM_SHARE_DENY_READ:
7068 return FILE_SHARE_WRITE;
7069 case STGM_SHARE_DENY_WRITE:
7070 return FILE_SHARE_READ;
7071 case STGM_SHARE_EXCLUSIVE:
7072 return 0;
7074 ERR("Invalid share mode!\n");
7075 assert(0);
7076 return 0;
7079 /****************************************************************************
7080 * GetAccessModeFromSTGM
7082 * This method will return an access mode flag from a STGM value.
7083 * The STGM value is assumed valid.
7085 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7087 switch (STGM_ACCESS_MODE(stgm))
7089 case STGM_READ:
7090 return GENERIC_READ;
7091 case STGM_WRITE:
7092 case STGM_READWRITE:
7093 return GENERIC_READ | GENERIC_WRITE;
7095 ERR("Invalid access mode!\n");
7096 assert(0);
7097 return 0;
7100 /****************************************************************************
7101 * GetCreationModeFromSTGM
7103 * This method will return a creation mode flag from a STGM value.
7104 * The STGM value is assumed valid.
7106 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7108 switch(STGM_CREATE_MODE(stgm))
7110 case STGM_CREATE:
7111 return CREATE_ALWAYS;
7112 case STGM_CONVERT:
7113 FIXME("STGM_CONVERT not implemented!\n");
7114 return CREATE_NEW;
7115 case STGM_FAILIFTHERE:
7116 return CREATE_NEW;
7118 ERR("Invalid create mode!\n");
7119 assert(0);
7120 return 0;
7124 /*************************************************************************
7125 * OLECONVERT_LoadOLE10 [Internal]
7127 * Loads the OLE10 STREAM to memory
7129 * PARAMS
7130 * pOleStream [I] The OLESTREAM
7131 * pData [I] Data Structure for the OLESTREAM Data
7133 * RETURNS
7134 * Success: S_OK
7135 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7136 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7138 * NOTES
7139 * This function is used by OleConvertOLESTREAMToIStorage only.
7141 * Memory allocated for pData must be freed by the caller
7143 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7145 DWORD dwSize;
7146 HRESULT hRes = S_OK;
7147 int nTryCnt=0;
7148 int max_try = 6;
7150 pData->pData = NULL;
7151 pData->pstrOleObjFileName = NULL;
7153 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7155 /* Get the OleID */
7156 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7157 if(dwSize != sizeof(pData->dwOleID))
7159 hRes = CONVERT10_E_OLESTREAM_GET;
7161 else if(pData->dwOleID != OLESTREAM_ID)
7163 hRes = CONVERT10_E_OLESTREAM_FMT;
7165 else
7167 hRes = S_OK;
7168 break;
7172 if(hRes == S_OK)
7174 /* Get the TypeID... more info needed for this field */
7175 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7176 if(dwSize != sizeof(pData->dwTypeID))
7178 hRes = CONVERT10_E_OLESTREAM_GET;
7181 if(hRes == S_OK)
7183 if(pData->dwTypeID != 0)
7185 /* Get the length of the OleTypeName */
7186 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7187 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7189 hRes = CONVERT10_E_OLESTREAM_GET;
7192 if(hRes == S_OK)
7194 if(pData->dwOleTypeNameLength > 0)
7196 /* Get the OleTypeName */
7197 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7198 if(dwSize != pData->dwOleTypeNameLength)
7200 hRes = CONVERT10_E_OLESTREAM_GET;
7204 if(bStrem1)
7206 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7207 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7209 hRes = CONVERT10_E_OLESTREAM_GET;
7211 if(hRes == S_OK)
7213 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7214 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7215 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7216 if(pData->pstrOleObjFileName)
7218 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7219 if(dwSize != pData->dwOleObjFileNameLength)
7221 hRes = CONVERT10_E_OLESTREAM_GET;
7224 else
7225 hRes = CONVERT10_E_OLESTREAM_GET;
7228 else
7230 /* Get the Width of the Metafile */
7231 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7232 if(dwSize != sizeof(pData->dwMetaFileWidth))
7234 hRes = CONVERT10_E_OLESTREAM_GET;
7236 if(hRes == S_OK)
7238 /* Get the Height of the Metafile */
7239 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7240 if(dwSize != sizeof(pData->dwMetaFileHeight))
7242 hRes = CONVERT10_E_OLESTREAM_GET;
7246 if(hRes == S_OK)
7248 /* Get the Length of the Data */
7249 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7250 if(dwSize != sizeof(pData->dwDataLength))
7252 hRes = CONVERT10_E_OLESTREAM_GET;
7256 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7258 if(!bStrem1) /* if it is a second OLE stream data */
7260 pData->dwDataLength -= 8;
7261 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7262 if(dwSize != sizeof(pData->strUnknown))
7264 hRes = CONVERT10_E_OLESTREAM_GET;
7268 if(hRes == S_OK)
7270 if(pData->dwDataLength > 0)
7272 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
7274 /* Get Data (ex. IStorage, Metafile, or BMP) */
7275 if(pData->pData)
7277 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
7278 if(dwSize != pData->dwDataLength)
7280 hRes = CONVERT10_E_OLESTREAM_GET;
7283 else
7285 hRes = CONVERT10_E_OLESTREAM_GET;
7291 return hRes;
7294 /*************************************************************************
7295 * OLECONVERT_SaveOLE10 [Internal]
7297 * Saves the OLE10 STREAM From memory
7299 * PARAMS
7300 * pData [I] Data Structure for the OLESTREAM Data
7301 * pOleStream [I] The OLESTREAM to save
7303 * RETURNS
7304 * Success: S_OK
7305 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7307 * NOTES
7308 * This function is used by OleConvertIStorageToOLESTREAM only.
7311 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
7313 DWORD dwSize;
7314 HRESULT hRes = S_OK;
7317 /* Set the OleID */
7318 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7319 if(dwSize != sizeof(pData->dwOleID))
7321 hRes = CONVERT10_E_OLESTREAM_PUT;
7324 if(hRes == S_OK)
7326 /* Set the TypeID */
7327 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7328 if(dwSize != sizeof(pData->dwTypeID))
7330 hRes = CONVERT10_E_OLESTREAM_PUT;
7334 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
7336 /* Set the Length of the OleTypeName */
7337 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7338 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7340 hRes = CONVERT10_E_OLESTREAM_PUT;
7343 if(hRes == S_OK)
7345 if(pData->dwOleTypeNameLength > 0)
7347 /* Set the OleTypeName */
7348 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7349 if(dwSize != pData->dwOleTypeNameLength)
7351 hRes = CONVERT10_E_OLESTREAM_PUT;
7356 if(hRes == S_OK)
7358 /* Set the width of the Metafile */
7359 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7360 if(dwSize != sizeof(pData->dwMetaFileWidth))
7362 hRes = CONVERT10_E_OLESTREAM_PUT;
7366 if(hRes == S_OK)
7368 /* Set the height of the Metafile */
7369 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7370 if(dwSize != sizeof(pData->dwMetaFileHeight))
7372 hRes = CONVERT10_E_OLESTREAM_PUT;
7376 if(hRes == S_OK)
7378 /* Set the length of the Data */
7379 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7380 if(dwSize != sizeof(pData->dwDataLength))
7382 hRes = CONVERT10_E_OLESTREAM_PUT;
7386 if(hRes == S_OK)
7388 if(pData->dwDataLength > 0)
7390 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7391 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
7392 if(dwSize != pData->dwDataLength)
7394 hRes = CONVERT10_E_OLESTREAM_PUT;
7399 return hRes;
7402 /*************************************************************************
7403 * OLECONVERT_GetOLE20FromOLE10[Internal]
7405 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7406 * opens it, and copies the content to the dest IStorage for
7407 * OleConvertOLESTREAMToIStorage
7410 * PARAMS
7411 * pDestStorage [I] The IStorage to copy the data to
7412 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7413 * nBufferLength [I] The size of the buffer
7415 * RETURNS
7416 * Nothing
7418 * NOTES
7422 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
7424 HRESULT hRes;
7425 HANDLE hFile;
7426 IStorage *pTempStorage;
7427 DWORD dwNumOfBytesWritten;
7428 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7429 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7431 /* Create a temp File */
7432 GetTempPathW(MAX_PATH, wstrTempDir);
7433 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7434 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
7436 if(hFile != INVALID_HANDLE_VALUE)
7438 /* Write IStorage Data to File */
7439 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
7440 CloseHandle(hFile);
7442 /* Open and copy temp storage to the Dest Storage */
7443 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
7444 if(hRes == S_OK)
7446 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
7447 IStorage_Release(pTempStorage);
7449 DeleteFileW(wstrTempFile);
7454 /*************************************************************************
7455 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7457 * Saves the OLE10 STREAM From memory
7459 * PARAMS
7460 * pStorage [I] The Src IStorage to copy
7461 * pData [I] The Dest Memory to write to.
7463 * RETURNS
7464 * The size in bytes allocated for pData
7466 * NOTES
7467 * Memory allocated for pData must be freed by the caller
7469 * Used by OleConvertIStorageToOLESTREAM only.
7472 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
7474 HANDLE hFile;
7475 HRESULT hRes;
7476 DWORD nDataLength = 0;
7477 IStorage *pTempStorage;
7478 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7479 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7481 *pData = NULL;
7483 /* Create temp Storage */
7484 GetTempPathW(MAX_PATH, wstrTempDir);
7485 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7486 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
7488 if(hRes == S_OK)
7490 /* Copy Src Storage to the Temp Storage */
7491 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
7492 IStorage_Release(pTempStorage);
7494 /* Open Temp Storage as a file and copy to memory */
7495 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7496 if(hFile != INVALID_HANDLE_VALUE)
7498 nDataLength = GetFileSize(hFile, NULL);
7499 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
7500 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
7501 CloseHandle(hFile);
7503 DeleteFileW(wstrTempFile);
7505 return nDataLength;
7508 /*************************************************************************
7509 * OLECONVERT_CreateOleStream [Internal]
7511 * Creates the "\001OLE" stream in the IStorage if necessary.
7513 * PARAMS
7514 * pStorage [I] Dest storage to create the stream in
7516 * RETURNS
7517 * Nothing
7519 * NOTES
7520 * This function is used by OleConvertOLESTREAMToIStorage only.
7522 * This stream is still unknown, MS Word seems to have extra data
7523 * but since the data is stored in the OLESTREAM there should be
7524 * no need to recreate the stream. If the stream is manually
7525 * deleted it will create it with this default data.
7528 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7530 HRESULT hRes;
7531 IStream *pStream;
7532 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7533 BYTE pOleStreamHeader [] =
7535 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7536 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7537 0x00, 0x00, 0x00, 0x00
7540 /* Create stream if not present */
7541 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7542 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7544 if(hRes == S_OK)
7546 /* Write default Data */
7547 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7548 IStream_Release(pStream);
7552 /* write a string to a stream, preceded by its length */
7553 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7555 HRESULT r;
7556 LPSTR str;
7557 DWORD len = 0;
7559 if( string )
7560 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7561 r = IStream_Write( stm, &len, sizeof(len), NULL);
7562 if( FAILED( r ) )
7563 return r;
7564 if(len == 0)
7565 return r;
7566 str = CoTaskMemAlloc( len );
7567 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7568 r = IStream_Write( stm, str, len, NULL);
7569 CoTaskMemFree( str );
7570 return r;
7573 /* read a string preceded by its length from a stream */
7574 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7576 HRESULT r;
7577 DWORD len, count = 0;
7578 LPSTR str;
7579 LPWSTR wstr;
7581 r = IStream_Read( stm, &len, sizeof(len), &count );
7582 if( FAILED( r ) )
7583 return r;
7584 if( count != sizeof(len) )
7585 return E_OUTOFMEMORY;
7587 TRACE("%d bytes\n",len);
7589 str = CoTaskMemAlloc( len );
7590 if( !str )
7591 return E_OUTOFMEMORY;
7592 count = 0;
7593 r = IStream_Read( stm, str, len, &count );
7594 if( FAILED( r ) )
7595 return r;
7596 if( count != len )
7598 CoTaskMemFree( str );
7599 return E_OUTOFMEMORY;
7602 TRACE("Read string %s\n",debugstr_an(str,len));
7604 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7605 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7606 if( wstr )
7607 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7608 CoTaskMemFree( str );
7610 *string = wstr;
7612 return r;
7616 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7617 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7619 IStream *pstm;
7620 HRESULT r = S_OK;
7621 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7623 static const BYTE unknown1[12] =
7624 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7625 0xFF, 0xFF, 0xFF, 0xFF};
7626 static const BYTE unknown2[16] =
7627 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7628 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7630 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7631 debugstr_w(lpszUserType), debugstr_w(szClipName),
7632 debugstr_w(szProgIDName));
7634 /* Create a CompObj stream */
7635 r = IStorage_CreateStream(pstg, szwStreamName,
7636 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7637 if( FAILED (r) )
7638 return r;
7640 /* Write CompObj Structure to stream */
7641 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7643 if( SUCCEEDED( r ) )
7644 r = WriteClassStm( pstm, clsid );
7646 if( SUCCEEDED( r ) )
7647 r = STREAM_WriteString( pstm, lpszUserType );
7648 if( SUCCEEDED( r ) )
7649 r = STREAM_WriteString( pstm, szClipName );
7650 if( SUCCEEDED( r ) )
7651 r = STREAM_WriteString( pstm, szProgIDName );
7652 if( SUCCEEDED( r ) )
7653 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7655 IStream_Release( pstm );
7657 return r;
7660 /***********************************************************************
7661 * WriteFmtUserTypeStg (OLE32.@)
7663 HRESULT WINAPI WriteFmtUserTypeStg(
7664 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7666 HRESULT r;
7667 WCHAR szwClipName[0x40];
7668 CLSID clsid = CLSID_NULL;
7669 LPWSTR wstrProgID = NULL;
7670 DWORD n;
7672 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7674 /* get the clipboard format name */
7675 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7676 szwClipName[n]=0;
7678 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7680 /* FIXME: There's room to save a CLSID and its ProgID, but
7681 the CLSID is not looked up in the registry and in all the
7682 tests I wrote it was CLSID_NULL. Where does it come from?
7685 /* get the real program ID. This may fail, but that's fine */
7686 ProgIDFromCLSID(&clsid, &wstrProgID);
7688 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7690 r = STORAGE_WriteCompObj( pstg, &clsid,
7691 lpszUserType, szwClipName, wstrProgID );
7693 CoTaskMemFree(wstrProgID);
7695 return r;
7699 /******************************************************************************
7700 * ReadFmtUserTypeStg [OLE32.@]
7702 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7704 HRESULT r;
7705 IStream *stm = 0;
7706 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7707 unsigned char unknown1[12];
7708 unsigned char unknown2[16];
7709 DWORD count;
7710 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7711 CLSID clsid;
7713 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7715 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7716 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7717 if( FAILED ( r ) )
7719 WARN("Failed to open stream r = %08x\n", r);
7720 return r;
7723 /* read the various parts of the structure */
7724 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7725 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7726 goto end;
7727 r = ReadClassStm( stm, &clsid );
7728 if( FAILED( r ) )
7729 goto end;
7731 r = STREAM_ReadString( stm, &szCLSIDName );
7732 if( FAILED( r ) )
7733 goto end;
7735 r = STREAM_ReadString( stm, &szOleTypeName );
7736 if( FAILED( r ) )
7737 goto end;
7739 r = STREAM_ReadString( stm, &szProgIDName );
7740 if( FAILED( r ) )
7741 goto end;
7743 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7744 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7745 goto end;
7747 /* ok, success... now we just need to store what we found */
7748 if( pcf )
7749 *pcf = RegisterClipboardFormatW( szOleTypeName );
7750 CoTaskMemFree( szOleTypeName );
7752 if( lplpszUserType )
7753 *lplpszUserType = szCLSIDName;
7754 CoTaskMemFree( szProgIDName );
7756 end:
7757 IStream_Release( stm );
7759 return r;
7763 /*************************************************************************
7764 * OLECONVERT_CreateCompObjStream [Internal]
7766 * Creates a "\001CompObj" is the destination IStorage if necessary.
7768 * PARAMS
7769 * pStorage [I] The dest IStorage to create the CompObj Stream
7770 * if necessary.
7771 * strOleTypeName [I] The ProgID
7773 * RETURNS
7774 * Success: S_OK
7775 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7777 * NOTES
7778 * This function is used by OleConvertOLESTREAMToIStorage only.
7780 * The stream data is stored in the OLESTREAM and there should be
7781 * no need to recreate the stream. If the stream is manually
7782 * deleted it will attempt to create it by querying the registry.
7786 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7788 IStream *pStream;
7789 HRESULT hStorageRes, hRes = S_OK;
7790 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7791 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7792 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7794 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7795 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7797 /* Initialize the CompObj structure */
7798 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7799 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7800 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7803 /* Create a CompObj stream if it doesn't exist */
7804 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7805 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7806 if(hStorageRes == S_OK)
7808 /* copy the OleTypeName to the compobj struct */
7809 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7810 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7812 /* copy the OleTypeName to the compobj struct */
7813 /* Note: in the test made, these were Identical */
7814 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7815 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7817 /* Get the CLSID */
7818 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7819 bufferW, OLESTREAM_MAX_STR_LEN );
7820 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7822 if(hRes == S_OK)
7824 HKEY hKey;
7825 LONG hErr;
7826 /* Get the CLSID Default Name from the Registry */
7827 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7828 if(hErr == ERROR_SUCCESS)
7830 char strTemp[OLESTREAM_MAX_STR_LEN];
7831 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7832 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7833 if(hErr == ERROR_SUCCESS)
7835 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7837 RegCloseKey(hKey);
7841 /* Write CompObj Structure to stream */
7842 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7844 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7846 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7847 if(IStorageCompObj.dwCLSIDNameLength > 0)
7849 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7851 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7852 if(IStorageCompObj.dwOleTypeNameLength > 0)
7854 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7856 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7857 if(IStorageCompObj.dwProgIDNameLength > 0)
7859 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7861 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7862 IStream_Release(pStream);
7864 return hRes;
7868 /*************************************************************************
7869 * OLECONVERT_CreateOlePresStream[Internal]
7871 * Creates the "\002OlePres000" Stream with the Metafile data
7873 * PARAMS
7874 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7875 * dwExtentX [I] Width of the Metafile
7876 * dwExtentY [I] Height of the Metafile
7877 * pData [I] Metafile data
7878 * dwDataLength [I] Size of the Metafile data
7880 * RETURNS
7881 * Success: S_OK
7882 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7884 * NOTES
7885 * This function is used by OleConvertOLESTREAMToIStorage only.
7888 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7890 HRESULT hRes;
7891 IStream *pStream;
7892 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7893 BYTE pOlePresStreamHeader [] =
7895 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7896 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7897 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7898 0x00, 0x00, 0x00, 0x00
7901 BYTE pOlePresStreamHeaderEmpty [] =
7903 0x00, 0x00, 0x00, 0x00,
7904 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7905 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7906 0x00, 0x00, 0x00, 0x00
7909 /* Create the OlePres000 Stream */
7910 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7911 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7913 if(hRes == S_OK)
7915 DWORD nHeaderSize;
7916 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7918 memset(&OlePres, 0, sizeof(OlePres));
7919 /* Do we have any metafile data to save */
7920 if(dwDataLength > 0)
7922 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7923 nHeaderSize = sizeof(pOlePresStreamHeader);
7925 else
7927 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7928 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7930 /* Set width and height of the metafile */
7931 OlePres.dwExtentX = dwExtentX;
7932 OlePres.dwExtentY = -dwExtentY;
7934 /* Set Data and Length */
7935 if(dwDataLength > sizeof(METAFILEPICT16))
7937 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7938 OlePres.pData = &(pData[8]);
7940 /* Save OlePres000 Data to Stream */
7941 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7942 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7943 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7944 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7945 if(OlePres.dwSize > 0)
7947 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7949 IStream_Release(pStream);
7953 /*************************************************************************
7954 * OLECONVERT_CreateOle10NativeStream [Internal]
7956 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7958 * PARAMS
7959 * pStorage [I] Dest storage to create the stream in
7960 * pData [I] Ole10 Native Data (ex. bmp)
7961 * dwDataLength [I] Size of the Ole10 Native Data
7963 * RETURNS
7964 * Nothing
7966 * NOTES
7967 * This function is used by OleConvertOLESTREAMToIStorage only.
7969 * Might need to verify the data and return appropriate error message
7972 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7974 HRESULT hRes;
7975 IStream *pStream;
7976 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7978 /* Create the Ole10Native Stream */
7979 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7980 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7982 if(hRes == S_OK)
7984 /* Write info to stream */
7985 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7986 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7987 IStream_Release(pStream);
7992 /*************************************************************************
7993 * OLECONVERT_GetOLE10ProgID [Internal]
7995 * Finds the ProgID (or OleTypeID) from the IStorage
7997 * PARAMS
7998 * pStorage [I] The Src IStorage to get the ProgID
7999 * strProgID [I] the ProgID string to get
8000 * dwSize [I] the size of the string
8002 * RETURNS
8003 * Success: S_OK
8004 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8006 * NOTES
8007 * This function is used by OleConvertIStorageToOLESTREAM only.
8011 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8013 HRESULT hRes;
8014 IStream *pStream;
8015 LARGE_INTEGER iSeekPos;
8016 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8017 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8019 /* Open the CompObj Stream */
8020 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8021 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8022 if(hRes == S_OK)
8025 /*Get the OleType from the CompObj Stream */
8026 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8027 iSeekPos.u.HighPart = 0;
8029 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8030 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8031 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8032 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8033 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8034 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8035 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8037 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8038 if(*dwSize > 0)
8040 IStream_Read(pStream, strProgID, *dwSize, NULL);
8042 IStream_Release(pStream);
8044 else
8046 STATSTG stat;
8047 LPOLESTR wstrProgID;
8049 /* Get the OleType from the registry */
8050 REFCLSID clsid = &(stat.clsid);
8051 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8052 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8053 if(hRes == S_OK)
8055 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8059 return hRes;
8062 /*************************************************************************
8063 * OLECONVERT_GetOle10PresData [Internal]
8065 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8067 * PARAMS
8068 * pStorage [I] Src IStroage
8069 * pOleStream [I] Dest OleStream Mem Struct
8071 * RETURNS
8072 * Nothing
8074 * NOTES
8075 * This function is used by OleConvertIStorageToOLESTREAM only.
8077 * Memory allocated for pData must be freed by the caller
8081 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8084 HRESULT hRes;
8085 IStream *pStream;
8086 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8088 /* Initialize Default data for OLESTREAM */
8089 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8090 pOleStreamData[0].dwTypeID = 2;
8091 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8092 pOleStreamData[1].dwTypeID = 0;
8093 pOleStreamData[0].dwMetaFileWidth = 0;
8094 pOleStreamData[0].dwMetaFileHeight = 0;
8095 pOleStreamData[0].pData = NULL;
8096 pOleStreamData[1].pData = NULL;
8098 /* Open Ole10Native Stream */
8099 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8100 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8101 if(hRes == S_OK)
8104 /* Read Size and Data */
8105 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8106 if(pOleStreamData->dwDataLength > 0)
8108 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8109 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8111 IStream_Release(pStream);
8117 /*************************************************************************
8118 * OLECONVERT_GetOle20PresData[Internal]
8120 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8122 * PARAMS
8123 * pStorage [I] Src IStroage
8124 * pOleStreamData [I] Dest OleStream Mem Struct
8126 * RETURNS
8127 * Nothing
8129 * NOTES
8130 * This function is used by OleConvertIStorageToOLESTREAM only.
8132 * Memory allocated for pData must be freed by the caller
8134 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8136 HRESULT hRes;
8137 IStream *pStream;
8138 OLECONVERT_ISTORAGE_OLEPRES olePress;
8139 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8141 /* Initialize Default data for OLESTREAM */
8142 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8143 pOleStreamData[0].dwTypeID = 2;
8144 pOleStreamData[0].dwMetaFileWidth = 0;
8145 pOleStreamData[0].dwMetaFileHeight = 0;
8146 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8147 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8148 pOleStreamData[1].dwTypeID = 0;
8149 pOleStreamData[1].dwOleTypeNameLength = 0;
8150 pOleStreamData[1].strOleTypeName[0] = 0;
8151 pOleStreamData[1].dwMetaFileWidth = 0;
8152 pOleStreamData[1].dwMetaFileHeight = 0;
8153 pOleStreamData[1].pData = NULL;
8154 pOleStreamData[1].dwDataLength = 0;
8157 /* Open OlePress000 stream */
8158 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8159 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8160 if(hRes == S_OK)
8162 LARGE_INTEGER iSeekPos;
8163 METAFILEPICT16 MetaFilePict;
8164 static const char strMetafilePictName[] = "METAFILEPICT";
8166 /* Set the TypeID for a Metafile */
8167 pOleStreamData[1].dwTypeID = 5;
8169 /* Set the OleTypeName to Metafile */
8170 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8171 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8173 iSeekPos.u.HighPart = 0;
8174 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8176 /* Get Presentation Data */
8177 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8178 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8179 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8180 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8182 /*Set width and Height */
8183 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8184 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8185 if(olePress.dwSize > 0)
8187 /* Set Length */
8188 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8190 /* Set MetaFilePict struct */
8191 MetaFilePict.mm = 8;
8192 MetaFilePict.xExt = olePress.dwExtentX;
8193 MetaFilePict.yExt = olePress.dwExtentY;
8194 MetaFilePict.hMF = 0;
8196 /* Get Metafile Data */
8197 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8198 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8199 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8201 IStream_Release(pStream);
8205 /*************************************************************************
8206 * OleConvertOLESTREAMToIStorage [OLE32.@]
8208 * Read info on MSDN
8210 * TODO
8211 * DVTARGETDEVICE parameter is not handled
8212 * Still unsure of some mem fields for OLE 10 Stream
8213 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8214 * and "\001OLE" streams
8217 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8218 LPOLESTREAM pOleStream,
8219 LPSTORAGE pstg,
8220 const DVTARGETDEVICE* ptd)
8222 int i;
8223 HRESULT hRes=S_OK;
8224 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8226 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8228 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8230 if(ptd != NULL)
8232 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8235 if(pstg == NULL || pOleStream == NULL)
8237 hRes = E_INVALIDARG;
8240 if(hRes == S_OK)
8242 /* Load the OLESTREAM to Memory */
8243 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8246 if(hRes == S_OK)
8248 /* Load the OLESTREAM to Memory (part 2)*/
8249 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8252 if(hRes == S_OK)
8255 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8257 /* Do we have the IStorage Data in the OLESTREAM */
8258 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8260 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8261 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8263 else
8265 /* It must be an original OLE 1.0 source */
8266 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8269 else
8271 /* It must be an original OLE 1.0 source */
8272 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8275 /* Create CompObj Stream if necessary */
8276 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
8277 if(hRes == S_OK)
8279 /*Create the Ole Stream if necessary */
8280 OLECONVERT_CreateOleStream(pstg);
8285 /* Free allocated memory */
8286 for(i=0; i < 2; i++)
8288 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8289 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
8290 pOleStreamData[i].pstrOleObjFileName = NULL;
8292 return hRes;
8295 /*************************************************************************
8296 * OleConvertIStorageToOLESTREAM [OLE32.@]
8298 * Read info on MSDN
8300 * Read info on MSDN
8302 * TODO
8303 * Still unsure of some mem fields for OLE 10 Stream
8304 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8305 * and "\001OLE" streams.
8308 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
8309 LPSTORAGE pstg,
8310 LPOLESTREAM pOleStream)
8312 int i;
8313 HRESULT hRes = S_OK;
8314 IStream *pStream;
8315 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8316 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8318 TRACE("%p %p\n", pstg, pOleStream);
8320 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8322 if(pstg == NULL || pOleStream == NULL)
8324 hRes = E_INVALIDARG;
8326 if(hRes == S_OK)
8328 /* Get the ProgID */
8329 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
8330 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
8332 if(hRes == S_OK)
8334 /* Was it originally Ole10 */
8335 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
8336 if(hRes == S_OK)
8338 IStream_Release(pStream);
8339 /* Get Presentation Data for Ole10Native */
8340 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
8342 else
8344 /* Get Presentation Data (OLE20) */
8345 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
8348 /* Save OLESTREAM */
8349 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
8350 if(hRes == S_OK)
8352 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
8357 /* Free allocated memory */
8358 for(i=0; i < 2; i++)
8360 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8363 return hRes;
8366 /***********************************************************************
8367 * GetConvertStg (OLE32.@)
8369 HRESULT WINAPI GetConvertStg(IStorage *stg) {
8370 FIXME("unimplemented stub!\n");
8371 return E_FAIL;
8374 /******************************************************************************
8375 * StgIsStorageFile [OLE32.@]
8376 * Verify if the file contains a storage object
8378 * PARAMS
8379 * fn [ I] Filename
8381 * RETURNS
8382 * S_OK if file has magic bytes as a storage object
8383 * S_FALSE if file is not storage
8385 HRESULT WINAPI
8386 StgIsStorageFile(LPCOLESTR fn)
8388 HANDLE hf;
8389 BYTE magic[8];
8390 DWORD bytes_read;
8392 TRACE("%s\n", debugstr_w(fn));
8393 hf = CreateFileW(fn, GENERIC_READ,
8394 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
8395 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8397 if (hf == INVALID_HANDLE_VALUE)
8398 return STG_E_FILENOTFOUND;
8400 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
8402 WARN(" unable to read file\n");
8403 CloseHandle(hf);
8404 return S_FALSE;
8407 CloseHandle(hf);
8409 if (bytes_read != 8) {
8410 TRACE(" too short\n");
8411 return S_FALSE;
8414 if (!memcmp(magic,STORAGE_magic,8)) {
8415 TRACE(" -> YES\n");
8416 return S_OK;
8419 TRACE(" -> Invalid header.\n");
8420 return S_FALSE;
8423 /***********************************************************************
8424 * WriteClassStm (OLE32.@)
8426 * Writes a CLSID to a stream.
8428 * PARAMS
8429 * pStm [I] Stream to write to.
8430 * rclsid [I] CLSID to write.
8432 * RETURNS
8433 * Success: S_OK.
8434 * Failure: HRESULT code.
8436 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
8438 TRACE("(%p,%p)\n",pStm,rclsid);
8440 if (!pStm || !rclsid)
8441 return E_INVALIDARG;
8443 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
8446 /***********************************************************************
8447 * ReadClassStm (OLE32.@)
8449 * Reads a CLSID from a stream.
8451 * PARAMS
8452 * pStm [I] Stream to read from.
8453 * rclsid [O] CLSID to read.
8455 * RETURNS
8456 * Success: S_OK.
8457 * Failure: HRESULT code.
8459 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
8461 ULONG nbByte;
8462 HRESULT res;
8464 TRACE("(%p,%p)\n",pStm,pclsid);
8466 if (!pStm || !pclsid)
8467 return E_INVALIDARG;
8469 /* clear the output args */
8470 *pclsid = CLSID_NULL;
8472 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
8474 if (FAILED(res))
8475 return res;
8477 if (nbByte != sizeof(CLSID))
8478 return STG_E_READFAULT;
8479 else
8480 return S_OK;