ole32: Always move unmodified streams instead of copying on commit.
[wine/hacks.git] / dlls / ole32 / storage32.c
blob8bae897a77cec1806ccf235b12bdec1ad2317036
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 typedef struct TransactedDirEntry
123 /* If applicable, a reference to the original DirEntry in the transacted
124 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
125 DirRef transactedParentEntry;
127 /* True if this entry is being used. */
128 int inuse;
130 /* True if data is up to date. */
131 int read;
133 /* True if this entry has been modified. */
134 int dirty;
136 /* True if this entry's stream has been modified. */
137 int stream_dirty;
139 /* True if this entry has been deleted in the transacted storage, but the
140 * delete has not yet been committed. */
141 int deleted;
143 /* If this entry's stream has been modified, a reference to where the stream
144 * is stored in the snapshot file. */
145 DirRef stream_entry;
147 /* This directory entry's data, including any changes that have been made. */
148 DirEntry data;
150 /* A reference to the parent of this node. This is only valid while we are
151 * committing changes. */
152 DirRef parent;
154 /* A reference to a newly-created entry in the transacted parent. This is
155 * always equal to transactedParentEntry except when committing changes. */
156 DirRef newTransactedParentEntry;
157 } TransactedDirEntry;
159 /****************************************************************************
160 * Transacted storage object.
162 typedef struct TransactedSnapshotImpl
164 struct StorageBaseImpl base;
167 * Modified streams are temporarily saved to the scratch file.
169 StorageBaseImpl *scratch;
171 /* The directory structure is kept here, so that we can track how these
172 * entries relate to those in the parent storage. */
173 TransactedDirEntry *entries;
174 ULONG entries_size;
175 ULONG firstFreeEntry;
178 * Changes are committed to the transacted parent.
180 StorageBaseImpl *transactedParent;
181 } TransactedSnapshotImpl;
183 /* Generic function to create a transacted wrapper for a direct storage object. */
184 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
186 /* OLESTREAM memory structure to use for Get and Put Routines */
187 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
188 typedef struct
190 DWORD dwOleID;
191 DWORD dwTypeID;
192 DWORD dwOleTypeNameLength;
193 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
194 CHAR *pstrOleObjFileName;
195 DWORD dwOleObjFileNameLength;
196 DWORD dwMetaFileWidth;
197 DWORD dwMetaFileHeight;
198 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
199 DWORD dwDataLength;
200 BYTE *pData;
201 }OLECONVERT_OLESTREAM_DATA;
203 /* CompObj Stream structure */
204 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
205 typedef struct
207 BYTE byUnknown1[12];
208 CLSID clsid;
209 DWORD dwCLSIDNameLength;
210 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
211 DWORD dwOleTypeNameLength;
212 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
213 DWORD dwProgIDNameLength;
214 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
215 BYTE byUnknown2[16];
216 }OLECONVERT_ISTORAGE_COMPOBJ;
219 /* Ole Presentation Stream structure */
220 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
221 typedef struct
223 BYTE byUnknown1[28];
224 DWORD dwExtentX;
225 DWORD dwExtentY;
226 DWORD dwSize;
227 BYTE *pData;
228 }OLECONVERT_ISTORAGE_OLEPRES;
232 /***********************************************************************
233 * Forward declaration of internal functions used by the method DestroyElement
235 static HRESULT deleteStorageContents(
236 StorageBaseImpl *parentStorage,
237 DirRef indexToDelete,
238 DirEntry entryDataToDelete);
240 static HRESULT deleteStreamContents(
241 StorageBaseImpl *parentStorage,
242 DirRef indexToDelete,
243 DirEntry entryDataToDelete);
245 static HRESULT removeFromTree(
246 StorageBaseImpl *This,
247 DirRef parentStorageIndex,
248 DirRef deletedIndex);
250 /***********************************************************************
251 * Declaration of the functions used to manipulate DirEntry
254 static HRESULT insertIntoTree(
255 StorageBaseImpl *This,
256 DirRef parentStorageIndex,
257 DirRef newEntryIndex);
259 static LONG entryNameCmp(
260 const OLECHAR *name1,
261 const OLECHAR *name2);
263 static DirRef findElement(
264 StorageBaseImpl *storage,
265 DirRef storageEntry,
266 const OLECHAR *name,
267 DirEntry *data);
269 static HRESULT findTreeParent(
270 StorageBaseImpl *storage,
271 DirRef storageEntry,
272 const OLECHAR *childName,
273 DirEntry *parentData,
274 DirRef *parentEntry,
275 ULONG *relation);
277 /***********************************************************************
278 * Declaration of miscellaneous functions...
280 static HRESULT validateSTGM(DWORD stgmValue);
282 static DWORD GetShareModeFromSTGM(DWORD stgm);
283 static DWORD GetAccessModeFromSTGM(DWORD stgm);
284 static DWORD GetCreationModeFromSTGM(DWORD stgm);
286 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
289 /****************************************************************************
290 * IEnumSTATSTGImpl definitions.
292 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
293 * This class allows iterating through the content of a storage and to find
294 * specific items inside it.
296 struct IEnumSTATSTGImpl
298 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
299 * since we want to cast this in an IEnumSTATSTG pointer */
301 LONG ref; /* Reference count */
302 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
303 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
305 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
309 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
310 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
312 /************************************************************************
313 ** Block Functions
316 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
318 return (index+1) * This->bigBlockSize;
321 /************************************************************************
322 ** Storage32BaseImpl implementation
324 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
325 ULARGE_INTEGER offset,
326 void* buffer,
327 ULONG size,
328 ULONG* bytesRead)
330 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
333 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
334 ULARGE_INTEGER offset,
335 const void* buffer,
336 const ULONG size,
337 ULONG* bytesWritten)
339 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
342 /************************************************************************
343 * Storage32BaseImpl_QueryInterface (IUnknown)
345 * This method implements the common QueryInterface for all IStorage32
346 * implementations contained in this file.
348 * See Windows documentation for more details on IUnknown methods.
350 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
351 IStorage* iface,
352 REFIID riid,
353 void** ppvObject)
355 StorageBaseImpl *This = (StorageBaseImpl *)iface;
357 if ( (This==0) || (ppvObject==0) )
358 return E_INVALIDARG;
360 *ppvObject = 0;
362 if (IsEqualGUID(&IID_IUnknown, riid) ||
363 IsEqualGUID(&IID_IStorage, riid))
365 *ppvObject = This;
367 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
369 *ppvObject = &This->pssVtbl;
372 if ((*ppvObject)==0)
373 return E_NOINTERFACE;
375 IStorage_AddRef(iface);
377 return S_OK;
380 /************************************************************************
381 * Storage32BaseImpl_AddRef (IUnknown)
383 * This method implements the common AddRef for all IStorage32
384 * implementations contained in this file.
386 * See Windows documentation for more details on IUnknown methods.
388 static ULONG WINAPI StorageBaseImpl_AddRef(
389 IStorage* iface)
391 StorageBaseImpl *This = (StorageBaseImpl *)iface;
392 ULONG ref = InterlockedIncrement(&This->ref);
394 TRACE("(%p) AddRef to %d\n", This, ref);
396 return ref;
399 /************************************************************************
400 * Storage32BaseImpl_Release (IUnknown)
402 * This method implements the common Release for all IStorage32
403 * implementations contained in this file.
405 * See Windows documentation for more details on IUnknown methods.
407 static ULONG WINAPI StorageBaseImpl_Release(
408 IStorage* iface)
410 StorageBaseImpl *This = (StorageBaseImpl *)iface;
412 ULONG ref = InterlockedDecrement(&This->ref);
414 TRACE("(%p) ReleaseRef to %d\n", This, ref);
416 if (ref == 0)
419 * Since we are using a system of base-classes, we want to call the
420 * destructor of the appropriate derived class. To do this, we are
421 * using virtual functions to implement the destructor.
423 StorageBaseImpl_Destroy(This);
426 return ref;
429 /************************************************************************
430 * Storage32BaseImpl_OpenStream (IStorage)
432 * This method will open the specified stream object from the current storage.
434 * See Windows documentation for more details on IStorage methods.
436 static HRESULT WINAPI StorageBaseImpl_OpenStream(
437 IStorage* iface,
438 const OLECHAR* pwcsName, /* [string][in] */
439 void* reserved1, /* [unique][in] */
440 DWORD grfMode, /* [in] */
441 DWORD reserved2, /* [in] */
442 IStream** ppstm) /* [out] */
444 StorageBaseImpl *This = (StorageBaseImpl *)iface;
445 StgStreamImpl* newStream;
446 DirEntry currentEntry;
447 DirRef streamEntryRef;
448 HRESULT res = STG_E_UNKNOWN;
450 TRACE("(%p, %s, %p, %x, %d, %p)\n",
451 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
453 if ( (pwcsName==NULL) || (ppstm==0) )
455 res = E_INVALIDARG;
456 goto end;
459 *ppstm = NULL;
461 if ( FAILED( validateSTGM(grfMode) ) ||
462 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
464 res = STG_E_INVALIDFLAG;
465 goto end;
469 * As documented.
471 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
473 res = STG_E_INVALIDFUNCTION;
474 goto end;
477 if (This->reverted)
479 res = STG_E_REVERTED;
480 goto end;
484 * Check that we're compatible with the parent's storage mode, but
485 * only if we are not in transacted mode
487 if(!(This->openFlags & STGM_TRANSACTED)) {
488 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
490 res = STG_E_INVALIDFLAG;
491 goto end;
496 * Search for the element with the given name
498 streamEntryRef = findElement(
499 This,
500 This->storageDirEntry,
501 pwcsName,
502 &currentEntry);
505 * If it was found, construct the stream object and return a pointer to it.
507 if ( (streamEntryRef!=DIRENTRY_NULL) &&
508 (currentEntry.stgType==STGTY_STREAM) )
510 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
512 /* A single stream cannot be opened a second time. */
513 res = STG_E_ACCESSDENIED;
514 goto end;
517 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
519 if (newStream!=0)
521 newStream->grfMode = grfMode;
522 *ppstm = (IStream*)newStream;
524 IStream_AddRef(*ppstm);
526 res = S_OK;
527 goto end;
530 res = E_OUTOFMEMORY;
531 goto end;
534 res = STG_E_FILENOTFOUND;
536 end:
537 if (res == S_OK)
538 TRACE("<-- IStream %p\n", *ppstm);
539 TRACE("<-- %08x\n", res);
540 return res;
543 /************************************************************************
544 * Storage32BaseImpl_OpenStorage (IStorage)
546 * This method will open a new storage object from the current storage.
548 * See Windows documentation for more details on IStorage methods.
550 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
551 IStorage* iface,
552 const OLECHAR* pwcsName, /* [string][unique][in] */
553 IStorage* pstgPriority, /* [unique][in] */
554 DWORD grfMode, /* [in] */
555 SNB snbExclude, /* [unique][in] */
556 DWORD reserved, /* [in] */
557 IStorage** ppstg) /* [out] */
559 StorageBaseImpl *This = (StorageBaseImpl *)iface;
560 StorageInternalImpl* newStorage;
561 StorageBaseImpl* newTransactedStorage;
562 DirEntry currentEntry;
563 DirRef storageEntryRef;
564 HRESULT res = STG_E_UNKNOWN;
566 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
567 iface, debugstr_w(pwcsName), pstgPriority,
568 grfMode, snbExclude, reserved, ppstg);
570 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
572 res = E_INVALIDARG;
573 goto end;
576 if (This->openFlags & STGM_SIMPLE)
578 res = STG_E_INVALIDFUNCTION;
579 goto end;
582 /* as documented */
583 if (snbExclude != NULL)
585 res = STG_E_INVALIDPARAMETER;
586 goto end;
589 if ( FAILED( validateSTGM(grfMode) ))
591 res = STG_E_INVALIDFLAG;
592 goto end;
596 * As documented.
598 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
599 (grfMode & STGM_DELETEONRELEASE) ||
600 (grfMode & STGM_PRIORITY) )
602 res = STG_E_INVALIDFUNCTION;
603 goto end;
606 if (This->reverted)
607 return STG_E_REVERTED;
610 * Check that we're compatible with the parent's storage mode,
611 * but only if we are not transacted
613 if(!(This->openFlags & STGM_TRANSACTED)) {
614 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
616 res = STG_E_ACCESSDENIED;
617 goto end;
621 *ppstg = NULL;
623 storageEntryRef = findElement(
624 This,
625 This->storageDirEntry,
626 pwcsName,
627 &currentEntry);
629 if ( (storageEntryRef!=DIRENTRY_NULL) &&
630 (currentEntry.stgType==STGTY_STORAGE) )
632 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
634 /* A single storage cannot be opened a second time. */
635 res = STG_E_ACCESSDENIED;
636 goto end;
639 newStorage = StorageInternalImpl_Construct(
640 This,
641 grfMode,
642 storageEntryRef);
644 if (newStorage != 0)
646 if (grfMode & STGM_TRANSACTED)
648 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
650 if (FAILED(res))
652 HeapFree(GetProcessHeap(), 0, newStorage);
653 goto end;
656 *ppstg = (IStorage*)newTransactedStorage;
658 else
660 *ppstg = (IStorage*)newStorage;
663 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
665 res = S_OK;
666 goto end;
669 res = STG_E_INSUFFICIENTMEMORY;
670 goto end;
673 res = STG_E_FILENOTFOUND;
675 end:
676 TRACE("<-- %08x\n", res);
677 return res;
680 /************************************************************************
681 * Storage32BaseImpl_EnumElements (IStorage)
683 * This method will create an enumerator object that can be used to
684 * retrieve information about all the elements in the storage object.
686 * See Windows documentation for more details on IStorage methods.
688 static HRESULT WINAPI StorageBaseImpl_EnumElements(
689 IStorage* iface,
690 DWORD reserved1, /* [in] */
691 void* reserved2, /* [size_is][unique][in] */
692 DWORD reserved3, /* [in] */
693 IEnumSTATSTG** ppenum) /* [out] */
695 StorageBaseImpl *This = (StorageBaseImpl *)iface;
696 IEnumSTATSTGImpl* newEnum;
698 TRACE("(%p, %d, %p, %d, %p)\n",
699 iface, reserved1, reserved2, reserved3, ppenum);
701 if ( (This==0) || (ppenum==0))
702 return E_INVALIDARG;
704 if (This->reverted)
705 return STG_E_REVERTED;
707 newEnum = IEnumSTATSTGImpl_Construct(
708 This,
709 This->storageDirEntry);
711 if (newEnum!=0)
713 *ppenum = (IEnumSTATSTG*)newEnum;
715 IEnumSTATSTG_AddRef(*ppenum);
717 return S_OK;
720 return E_OUTOFMEMORY;
723 /************************************************************************
724 * Storage32BaseImpl_Stat (IStorage)
726 * This method will retrieve information about this storage object.
728 * See Windows documentation for more details on IStorage methods.
730 static HRESULT WINAPI StorageBaseImpl_Stat(
731 IStorage* iface,
732 STATSTG* pstatstg, /* [out] */
733 DWORD grfStatFlag) /* [in] */
735 StorageBaseImpl *This = (StorageBaseImpl *)iface;
736 DirEntry currentEntry;
737 HRESULT res = STG_E_UNKNOWN;
739 TRACE("(%p, %p, %x)\n",
740 iface, pstatstg, grfStatFlag);
742 if ( (This==0) || (pstatstg==0))
744 res = E_INVALIDARG;
745 goto end;
748 if (This->reverted)
750 res = STG_E_REVERTED;
751 goto end;
754 res = StorageBaseImpl_ReadDirEntry(
755 This,
756 This->storageDirEntry,
757 &currentEntry);
759 if (SUCCEEDED(res))
761 StorageUtl_CopyDirEntryToSTATSTG(
762 This,
763 pstatstg,
764 &currentEntry,
765 grfStatFlag);
767 pstatstg->grfMode = This->openFlags;
768 pstatstg->grfStateBits = This->stateBits;
771 end:
772 if (res == S_OK)
774 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);
776 TRACE("<-- %08x\n", res);
777 return res;
780 /************************************************************************
781 * Storage32BaseImpl_RenameElement (IStorage)
783 * This method will rename the specified element.
785 * See Windows documentation for more details on IStorage methods.
787 static HRESULT WINAPI StorageBaseImpl_RenameElement(
788 IStorage* iface,
789 const OLECHAR* pwcsOldName, /* [in] */
790 const OLECHAR* pwcsNewName) /* [in] */
792 StorageBaseImpl *This = (StorageBaseImpl *)iface;
793 DirEntry currentEntry;
794 DirRef currentEntryRef;
796 TRACE("(%p, %s, %s)\n",
797 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
799 if (This->reverted)
800 return STG_E_REVERTED;
802 currentEntryRef = findElement(This,
803 This->storageDirEntry,
804 pwcsNewName,
805 &currentEntry);
807 if (currentEntryRef != DIRENTRY_NULL)
810 * There is already an element with the new name
812 return STG_E_FILEALREADYEXISTS;
816 * Search for the old element name
818 currentEntryRef = findElement(This,
819 This->storageDirEntry,
820 pwcsOldName,
821 &currentEntry);
823 if (currentEntryRef != DIRENTRY_NULL)
825 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
826 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
828 WARN("Element is already open; cannot rename.\n");
829 return STG_E_ACCESSDENIED;
832 /* Remove the element from its current position in the tree */
833 removeFromTree(This, This->storageDirEntry,
834 currentEntryRef);
836 /* Change the name of the element */
837 strcpyW(currentEntry.name, pwcsNewName);
839 /* Delete any sibling links */
840 currentEntry.leftChild = DIRENTRY_NULL;
841 currentEntry.rightChild = DIRENTRY_NULL;
843 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
844 &currentEntry);
846 /* Insert the element in a new position in the tree */
847 insertIntoTree(This, This->storageDirEntry,
848 currentEntryRef);
850 else
853 * There is no element with the old name
855 return STG_E_FILENOTFOUND;
858 return S_OK;
861 /************************************************************************
862 * Storage32BaseImpl_CreateStream (IStorage)
864 * This method will create a stream object within this storage
866 * See Windows documentation for more details on IStorage methods.
868 static HRESULT WINAPI StorageBaseImpl_CreateStream(
869 IStorage* iface,
870 const OLECHAR* pwcsName, /* [string][in] */
871 DWORD grfMode, /* [in] */
872 DWORD reserved1, /* [in] */
873 DWORD reserved2, /* [in] */
874 IStream** ppstm) /* [out] */
876 StorageBaseImpl *This = (StorageBaseImpl *)iface;
877 StgStreamImpl* newStream;
878 DirEntry currentEntry, newStreamEntry;
879 DirRef currentEntryRef, newStreamEntryRef;
880 HRESULT hr;
882 TRACE("(%p, %s, %x, %d, %d, %p)\n",
883 iface, debugstr_w(pwcsName), grfMode,
884 reserved1, reserved2, ppstm);
886 if (ppstm == 0)
887 return STG_E_INVALIDPOINTER;
889 if (pwcsName == 0)
890 return STG_E_INVALIDNAME;
892 if (reserved1 || reserved2)
893 return STG_E_INVALIDPARAMETER;
895 if ( FAILED( validateSTGM(grfMode) ))
896 return STG_E_INVALIDFLAG;
898 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
899 return STG_E_INVALIDFLAG;
901 if (This->reverted)
902 return STG_E_REVERTED;
905 * As documented.
907 if ((grfMode & STGM_DELETEONRELEASE) ||
908 (grfMode & STGM_TRANSACTED))
909 return STG_E_INVALIDFUNCTION;
912 * Don't worry about permissions in transacted mode, as we can always write
913 * changes; we just can't always commit them.
915 if(!(This->openFlags & STGM_TRANSACTED)) {
916 /* Can't create a stream on read-only storage */
917 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
918 return STG_E_ACCESSDENIED;
920 /* Can't create a stream with greater access than the parent. */
921 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
922 return STG_E_ACCESSDENIED;
925 if(This->openFlags & STGM_SIMPLE)
926 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
928 *ppstm = 0;
930 currentEntryRef = findElement(This,
931 This->storageDirEntry,
932 pwcsName,
933 &currentEntry);
935 if (currentEntryRef != DIRENTRY_NULL)
938 * An element with this name already exists
940 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
942 IStorage_DestroyElement(iface, pwcsName);
944 else
945 return STG_E_FILEALREADYEXISTS;
949 * memset the empty entry
951 memset(&newStreamEntry, 0, sizeof(DirEntry));
953 newStreamEntry.sizeOfNameString =
954 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
956 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
957 return STG_E_INVALIDNAME;
959 strcpyW(newStreamEntry.name, pwcsName);
961 newStreamEntry.stgType = STGTY_STREAM;
962 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
963 newStreamEntry.size.u.LowPart = 0;
964 newStreamEntry.size.u.HighPart = 0;
966 newStreamEntry.leftChild = DIRENTRY_NULL;
967 newStreamEntry.rightChild = DIRENTRY_NULL;
968 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
970 /* call CoFileTime to get the current time
971 newStreamEntry.ctime
972 newStreamEntry.mtime
975 /* newStreamEntry.clsid */
978 * Create an entry with the new data
980 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
981 if (FAILED(hr))
982 return hr;
985 * Insert the new entry in the parent storage's tree.
987 hr = insertIntoTree(
988 This,
989 This->storageDirEntry,
990 newStreamEntryRef);
991 if (FAILED(hr))
993 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
994 return hr;
998 * Open the stream to return it.
1000 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1002 if (newStream != 0)
1004 *ppstm = (IStream*)newStream;
1006 IStream_AddRef(*ppstm);
1008 else
1010 return STG_E_INSUFFICIENTMEMORY;
1013 return S_OK;
1016 /************************************************************************
1017 * Storage32BaseImpl_SetClass (IStorage)
1019 * This method will write the specified CLSID in the directory entry of this
1020 * storage.
1022 * See Windows documentation for more details on IStorage methods.
1024 static HRESULT WINAPI StorageBaseImpl_SetClass(
1025 IStorage* iface,
1026 REFCLSID clsid) /* [in] */
1028 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1029 HRESULT hRes;
1030 DirEntry currentEntry;
1032 TRACE("(%p, %p)\n", iface, clsid);
1034 if (This->reverted)
1035 return STG_E_REVERTED;
1037 hRes = StorageBaseImpl_ReadDirEntry(This,
1038 This->storageDirEntry,
1039 &currentEntry);
1040 if (SUCCEEDED(hRes))
1042 currentEntry.clsid = *clsid;
1044 hRes = StorageBaseImpl_WriteDirEntry(This,
1045 This->storageDirEntry,
1046 &currentEntry);
1049 return hRes;
1052 /************************************************************************
1053 ** Storage32Impl implementation
1056 /************************************************************************
1057 * Storage32BaseImpl_CreateStorage (IStorage)
1059 * This method will create the storage object within the provided storage.
1061 * See Windows documentation for more details on IStorage methods.
1063 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1064 IStorage* iface,
1065 const OLECHAR *pwcsName, /* [string][in] */
1066 DWORD grfMode, /* [in] */
1067 DWORD reserved1, /* [in] */
1068 DWORD reserved2, /* [in] */
1069 IStorage **ppstg) /* [out] */
1071 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1073 DirEntry currentEntry;
1074 DirEntry newEntry;
1075 DirRef currentEntryRef;
1076 DirRef newEntryRef;
1077 HRESULT hr;
1079 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1080 iface, debugstr_w(pwcsName), grfMode,
1081 reserved1, reserved2, ppstg);
1083 if (ppstg == 0)
1084 return STG_E_INVALIDPOINTER;
1086 if (This->openFlags & STGM_SIMPLE)
1088 return STG_E_INVALIDFUNCTION;
1091 if (pwcsName == 0)
1092 return STG_E_INVALIDNAME;
1094 *ppstg = NULL;
1096 if ( FAILED( validateSTGM(grfMode) ) ||
1097 (grfMode & STGM_DELETEONRELEASE) )
1099 WARN("bad grfMode: 0x%x\n", grfMode);
1100 return STG_E_INVALIDFLAG;
1103 if (This->reverted)
1104 return STG_E_REVERTED;
1107 * Check that we're compatible with the parent's storage mode
1109 if ( !(This->openFlags & STGM_TRANSACTED) &&
1110 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1112 WARN("access denied\n");
1113 return STG_E_ACCESSDENIED;
1116 currentEntryRef = findElement(This,
1117 This->storageDirEntry,
1118 pwcsName,
1119 &currentEntry);
1121 if (currentEntryRef != DIRENTRY_NULL)
1124 * An element with this name already exists
1126 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1127 ((This->openFlags & STGM_TRANSACTED) ||
1128 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1130 hr = IStorage_DestroyElement(iface, pwcsName);
1131 if (FAILED(hr))
1132 return hr;
1134 else
1136 WARN("file already exists\n");
1137 return STG_E_FILEALREADYEXISTS;
1140 else if (!(This->openFlags & STGM_TRANSACTED) &&
1141 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1143 WARN("read-only storage\n");
1144 return STG_E_ACCESSDENIED;
1147 memset(&newEntry, 0, sizeof(DirEntry));
1149 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1151 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1153 FIXME("name too long\n");
1154 return STG_E_INVALIDNAME;
1157 strcpyW(newEntry.name, pwcsName);
1159 newEntry.stgType = STGTY_STORAGE;
1160 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1161 newEntry.size.u.LowPart = 0;
1162 newEntry.size.u.HighPart = 0;
1164 newEntry.leftChild = DIRENTRY_NULL;
1165 newEntry.rightChild = DIRENTRY_NULL;
1166 newEntry.dirRootEntry = DIRENTRY_NULL;
1168 /* call CoFileTime to get the current time
1169 newEntry.ctime
1170 newEntry.mtime
1173 /* newEntry.clsid */
1176 * Create a new directory entry for the storage
1178 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1179 if (FAILED(hr))
1180 return hr;
1183 * Insert the new directory entry into the parent storage's tree
1185 hr = insertIntoTree(
1186 This,
1187 This->storageDirEntry,
1188 newEntryRef);
1189 if (FAILED(hr))
1191 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1192 return hr;
1196 * Open it to get a pointer to return.
1198 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1200 if( (hr != S_OK) || (*ppstg == NULL))
1202 return hr;
1206 return S_OK;
1210 /***************************************************************************
1212 * Internal Method
1214 * Reserve a directory entry in the file and initialize it.
1216 static HRESULT StorageImpl_CreateDirEntry(
1217 StorageBaseImpl *base,
1218 const DirEntry *newData,
1219 DirRef *index)
1221 StorageImpl *storage = (StorageImpl*)base;
1222 ULONG currentEntryIndex = 0;
1223 ULONG newEntryIndex = DIRENTRY_NULL;
1224 HRESULT hr = S_OK;
1225 BYTE currentData[RAW_DIRENTRY_SIZE];
1226 WORD sizeOfNameString;
1230 hr = StorageImpl_ReadRawDirEntry(storage,
1231 currentEntryIndex,
1232 currentData);
1234 if (SUCCEEDED(hr))
1236 StorageUtl_ReadWord(
1237 currentData,
1238 OFFSET_PS_NAMELENGTH,
1239 &sizeOfNameString);
1241 if (sizeOfNameString == 0)
1244 * The entry exists and is available, we found it.
1246 newEntryIndex = currentEntryIndex;
1249 else
1252 * We exhausted the directory entries, we will create more space below
1254 newEntryIndex = currentEntryIndex;
1256 currentEntryIndex++;
1258 } while (newEntryIndex == DIRENTRY_NULL);
1261 * grow the directory stream
1263 if (FAILED(hr))
1265 BYTE emptyData[RAW_DIRENTRY_SIZE];
1266 ULARGE_INTEGER newSize;
1267 ULONG entryIndex;
1268 ULONG lastEntry = 0;
1269 ULONG blockCount = 0;
1272 * obtain the new count of blocks in the directory stream
1274 blockCount = BlockChainStream_GetCount(
1275 storage->rootBlockChain)+1;
1278 * initialize the size used by the directory stream
1280 newSize.u.HighPart = 0;
1281 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1284 * add a block to the directory stream
1286 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1289 * memset the empty entry in order to initialize the unused newly
1290 * created entries
1292 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1295 * initialize them
1297 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1299 for(
1300 entryIndex = newEntryIndex + 1;
1301 entryIndex < lastEntry;
1302 entryIndex++)
1304 StorageImpl_WriteRawDirEntry(
1305 storage,
1306 entryIndex,
1307 emptyData);
1311 UpdateRawDirEntry(currentData, newData);
1313 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1315 if (SUCCEEDED(hr))
1316 *index = newEntryIndex;
1318 return hr;
1321 /***************************************************************************
1323 * Internal Method
1325 * Mark a directory entry in the file as free.
1327 static HRESULT StorageImpl_DestroyDirEntry(
1328 StorageBaseImpl *base,
1329 DirRef index)
1331 HRESULT hr;
1332 BYTE emptyData[RAW_DIRENTRY_SIZE];
1333 StorageImpl *storage = (StorageImpl*)base;
1335 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1337 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1339 return hr;
1343 /****************************************************************************
1345 * Internal Method
1347 * Case insensitive comparison of DirEntry.name by first considering
1348 * their size.
1350 * Returns <0 when name1 < name2
1351 * >0 when name1 > name2
1352 * 0 when name1 == name2
1354 static LONG entryNameCmp(
1355 const OLECHAR *name1,
1356 const OLECHAR *name2)
1358 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1360 while (diff == 0 && *name1 != 0)
1363 * We compare the string themselves only when they are of the same length
1365 diff = toupperW(*name1++) - toupperW(*name2++);
1368 return diff;
1371 /****************************************************************************
1373 * Internal Method
1375 * Add a directory entry to a storage
1377 static HRESULT insertIntoTree(
1378 StorageBaseImpl *This,
1379 DirRef parentStorageIndex,
1380 DirRef newEntryIndex)
1382 DirEntry currentEntry;
1383 DirEntry newEntry;
1386 * Read the inserted entry
1388 StorageBaseImpl_ReadDirEntry(This,
1389 newEntryIndex,
1390 &newEntry);
1393 * Read the storage entry
1395 StorageBaseImpl_ReadDirEntry(This,
1396 parentStorageIndex,
1397 &currentEntry);
1399 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1402 * The root storage contains some element, therefore, start the research
1403 * for the appropriate location.
1405 BOOL found = 0;
1406 DirRef current, next, previous, currentEntryId;
1409 * Keep a reference to the root of the storage's element tree
1411 currentEntryId = currentEntry.dirRootEntry;
1414 * Read
1416 StorageBaseImpl_ReadDirEntry(This,
1417 currentEntry.dirRootEntry,
1418 &currentEntry);
1420 previous = currentEntry.leftChild;
1421 next = currentEntry.rightChild;
1422 current = currentEntryId;
1424 while (found == 0)
1426 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1428 if (diff < 0)
1430 if (previous != DIRENTRY_NULL)
1432 StorageBaseImpl_ReadDirEntry(This,
1433 previous,
1434 &currentEntry);
1435 current = previous;
1437 else
1439 currentEntry.leftChild = newEntryIndex;
1440 StorageBaseImpl_WriteDirEntry(This,
1441 current,
1442 &currentEntry);
1443 found = 1;
1446 else if (diff > 0)
1448 if (next != DIRENTRY_NULL)
1450 StorageBaseImpl_ReadDirEntry(This,
1451 next,
1452 &currentEntry);
1453 current = next;
1455 else
1457 currentEntry.rightChild = newEntryIndex;
1458 StorageBaseImpl_WriteDirEntry(This,
1459 current,
1460 &currentEntry);
1461 found = 1;
1464 else
1467 * Trying to insert an item with the same name in the
1468 * subtree structure.
1470 return STG_E_FILEALREADYEXISTS;
1473 previous = currentEntry.leftChild;
1474 next = currentEntry.rightChild;
1477 else
1480 * The storage is empty, make the new entry the root of its element tree
1482 currentEntry.dirRootEntry = newEntryIndex;
1483 StorageBaseImpl_WriteDirEntry(This,
1484 parentStorageIndex,
1485 &currentEntry);
1488 return S_OK;
1491 /****************************************************************************
1493 * Internal Method
1495 * Find and read the element of a storage with the given name.
1497 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1498 const OLECHAR *name, DirEntry *data)
1500 DirRef currentEntry;
1502 /* Read the storage entry to find the root of the tree. */
1503 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1505 currentEntry = data->dirRootEntry;
1507 while (currentEntry != DIRENTRY_NULL)
1509 LONG cmp;
1511 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1513 cmp = entryNameCmp(name, data->name);
1515 if (cmp == 0)
1516 /* found it */
1517 break;
1519 else if (cmp < 0)
1520 currentEntry = data->leftChild;
1522 else if (cmp > 0)
1523 currentEntry = data->rightChild;
1526 return currentEntry;
1529 /****************************************************************************
1531 * Internal Method
1533 * Find and read the binary tree parent of the element with the given name.
1535 * If there is no such element, find a place where it could be inserted and
1536 * return STG_E_FILENOTFOUND.
1538 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1539 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1540 ULONG *relation)
1542 DirRef childEntry;
1543 DirEntry childData;
1545 /* Read the storage entry to find the root of the tree. */
1546 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1548 *parentEntry = storageEntry;
1549 *relation = DIRENTRY_RELATION_DIR;
1551 childEntry = parentData->dirRootEntry;
1553 while (childEntry != DIRENTRY_NULL)
1555 LONG cmp;
1557 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1559 cmp = entryNameCmp(childName, childData.name);
1561 if (cmp == 0)
1562 /* found it */
1563 break;
1565 else if (cmp < 0)
1567 *parentData = childData;
1568 *parentEntry = childEntry;
1569 *relation = DIRENTRY_RELATION_PREVIOUS;
1571 childEntry = parentData->leftChild;
1574 else if (cmp > 0)
1576 *parentData = childData;
1577 *parentEntry = childEntry;
1578 *relation = DIRENTRY_RELATION_NEXT;
1580 childEntry = parentData->rightChild;
1584 if (childEntry == DIRENTRY_NULL)
1585 return STG_E_FILENOTFOUND;
1586 else
1587 return S_OK;
1591 /*************************************************************************
1592 * CopyTo (IStorage)
1594 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1595 IStorage* iface,
1596 DWORD ciidExclude, /* [in] */
1597 const IID* rgiidExclude, /* [size_is][unique][in] */
1598 SNB snbExclude, /* [unique][in] */
1599 IStorage* pstgDest) /* [unique][in] */
1601 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1603 IEnumSTATSTG *elements = 0;
1604 STATSTG curElement, strStat;
1605 HRESULT hr;
1606 IStorage *pstgTmp, *pstgChild;
1607 IStream *pstrTmp, *pstrChild;
1608 DirRef srcEntryRef;
1609 DirEntry srcEntry;
1610 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1611 int i;
1613 TRACE("(%p, %d, %p, %p, %p)\n",
1614 iface, ciidExclude, rgiidExclude,
1615 snbExclude, pstgDest);
1617 if ( pstgDest == 0 )
1618 return STG_E_INVALIDPOINTER;
1621 * Enumerate the elements
1623 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1625 if ( hr != S_OK )
1626 return hr;
1629 * set the class ID
1631 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1632 IStorage_SetClass( pstgDest, &curElement.clsid );
1634 for(i = 0; i < ciidExclude; ++i)
1636 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1637 skip_storage = TRUE;
1638 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1639 skip_stream = TRUE;
1640 else
1641 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1647 * Obtain the next element
1649 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1651 if ( hr == S_FALSE )
1653 hr = S_OK; /* done, every element has been copied */
1654 break;
1657 if ( snbExclude )
1659 WCHAR **snb = snbExclude;
1660 skip = FALSE;
1661 while ( *snb != NULL && !skip )
1663 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1664 skip = TRUE;
1665 ++snb;
1669 if ( skip )
1670 goto cleanup;
1672 if (curElement.type == STGTY_STORAGE)
1674 if(skip_storage)
1675 goto cleanup;
1678 * open child source storage
1680 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1681 STGM_READ|STGM_SHARE_EXCLUSIVE,
1682 NULL, 0, &pstgChild );
1684 if (hr != S_OK)
1685 goto cleanup;
1688 * create a new storage in destination storage
1690 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1691 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1692 0, 0,
1693 &pstgTmp );
1695 * if it already exist, don't create a new one use this one
1697 if (hr == STG_E_FILEALREADYEXISTS)
1699 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1700 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1701 NULL, 0, &pstgTmp );
1704 if (hr == S_OK)
1707 * do the copy recursively
1709 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1710 NULL, pstgTmp );
1712 IStorage_Release( pstgTmp );
1715 IStorage_Release( pstgChild );
1717 else if (curElement.type == STGTY_STREAM)
1719 if(skip_stream)
1720 goto cleanup;
1723 * create a new stream in destination storage. If the stream already
1724 * exist, it will be deleted and a new one will be created.
1726 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1727 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1728 0, 0, &pstrTmp );
1730 if (hr != S_OK)
1731 goto cleanup;
1734 * open child stream storage. This operation must succeed even if the
1735 * stream is already open, so we use internal functions to do it.
1737 srcEntryRef = findElement( This, This->storageDirEntry, curElement.pwcsName,
1738 &srcEntry);
1739 if (!srcEntryRef)
1741 ERR("source stream not found\n");
1742 hr = STG_E_DOCFILECORRUPT;
1745 if (hr == S_OK)
1747 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntryRef);
1748 if (pstrChild)
1749 IStream_AddRef(pstrChild);
1750 else
1751 hr = E_OUTOFMEMORY;
1754 if (hr == S_OK)
1757 * Get the size of the source stream
1759 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1762 * Set the size of the destination stream.
1764 IStream_SetSize(pstrTmp, strStat.cbSize);
1767 * do the copy
1769 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1770 NULL, NULL );
1772 IStream_Release( pstrChild );
1775 IStream_Release( pstrTmp );
1777 else
1779 WARN("unknown element type: %d\n", curElement.type);
1782 cleanup:
1783 CoTaskMemFree(curElement.pwcsName);
1784 } while (hr == S_OK);
1787 * Clean-up
1789 IEnumSTATSTG_Release(elements);
1791 return hr;
1794 /*************************************************************************
1795 * MoveElementTo (IStorage)
1797 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1798 IStorage* iface,
1799 const OLECHAR *pwcsName, /* [string][in] */
1800 IStorage *pstgDest, /* [unique][in] */
1801 const OLECHAR *pwcsNewName,/* [string][in] */
1802 DWORD grfFlags) /* [in] */
1804 FIXME("(%p %s %p %s %u): stub\n", iface,
1805 debugstr_w(pwcsName), pstgDest,
1806 debugstr_w(pwcsNewName), grfFlags);
1807 return E_NOTIMPL;
1810 /*************************************************************************
1811 * Commit (IStorage)
1813 * Ensures that any changes made to a storage object open in transacted mode
1814 * are reflected in the parent storage
1816 * NOTES
1817 * Wine doesn't implement transacted mode, which seems to be a basic
1818 * optimization, so we can ignore this stub for now.
1820 static HRESULT WINAPI StorageImpl_Commit(
1821 IStorage* iface,
1822 DWORD grfCommitFlags)/* [in] */
1824 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1825 return S_OK;
1828 /*************************************************************************
1829 * Revert (IStorage)
1831 * Discard all changes that have been made since the last commit operation
1833 static HRESULT WINAPI StorageImpl_Revert(
1834 IStorage* iface)
1836 TRACE("(%p)\n", iface);
1837 return S_OK;
1840 /*************************************************************************
1841 * DestroyElement (IStorage)
1843 * Strategy: This implementation is built this way for simplicity not for speed.
1844 * I always delete the topmost element of the enumeration and adjust
1845 * the deleted element pointer all the time. This takes longer to
1846 * do but allow to reinvoke DestroyElement whenever we encounter a
1847 * storage object. The optimisation resides in the usage of another
1848 * enumeration strategy that would give all the leaves of a storage
1849 * first. (postfix order)
1851 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1852 IStorage* iface,
1853 const OLECHAR *pwcsName)/* [string][in] */
1855 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1857 HRESULT hr = S_OK;
1858 DirEntry entryToDelete;
1859 DirRef entryToDeleteRef;
1861 TRACE("(%p, %s)\n",
1862 iface, debugstr_w(pwcsName));
1864 if (pwcsName==NULL)
1865 return STG_E_INVALIDPOINTER;
1867 if (This->reverted)
1868 return STG_E_REVERTED;
1870 if ( !(This->openFlags & STGM_TRANSACTED) &&
1871 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1872 return STG_E_ACCESSDENIED;
1874 entryToDeleteRef = findElement(
1875 This,
1876 This->storageDirEntry,
1877 pwcsName,
1878 &entryToDelete);
1880 if ( entryToDeleteRef == DIRENTRY_NULL )
1882 return STG_E_FILENOTFOUND;
1885 if ( entryToDelete.stgType == STGTY_STORAGE )
1887 hr = deleteStorageContents(
1888 This,
1889 entryToDeleteRef,
1890 entryToDelete);
1892 else if ( entryToDelete.stgType == STGTY_STREAM )
1894 hr = deleteStreamContents(
1895 This,
1896 entryToDeleteRef,
1897 entryToDelete);
1900 if (hr!=S_OK)
1901 return hr;
1904 * Remove the entry from its parent storage
1906 hr = removeFromTree(
1907 This,
1908 This->storageDirEntry,
1909 entryToDeleteRef);
1912 * Invalidate the entry
1914 if (SUCCEEDED(hr))
1915 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1917 return hr;
1921 /******************************************************************************
1922 * Internal stream list handlers
1925 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1927 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1928 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1931 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1933 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1934 list_remove(&(strm->StrmListEntry));
1937 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1939 StgStreamImpl *strm;
1941 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1943 if (strm->dirEntry == streamEntry)
1945 return TRUE;
1949 return FALSE;
1952 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1954 StorageInternalImpl *childstg;
1956 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1958 if (childstg->base.storageDirEntry == storageEntry)
1960 return TRUE;
1964 return FALSE;
1967 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1969 struct list *cur, *cur2;
1970 StgStreamImpl *strm=NULL;
1971 StorageInternalImpl *childstg=NULL;
1973 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1974 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1975 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1976 strm->parentStorage = NULL;
1977 list_remove(cur);
1980 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1981 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1982 StorageBaseImpl_Invalidate( &childstg->base );
1985 if (stg->transactedChild)
1987 StorageBaseImpl_Invalidate(stg->transactedChild);
1989 stg->transactedChild = NULL;
1994 /*********************************************************************
1996 * Internal Method
1998 * Delete the contents of a storage entry.
2001 static HRESULT deleteStorageContents(
2002 StorageBaseImpl *parentStorage,
2003 DirRef indexToDelete,
2004 DirEntry entryDataToDelete)
2006 IEnumSTATSTG *elements = 0;
2007 IStorage *childStorage = 0;
2008 STATSTG currentElement;
2009 HRESULT hr;
2010 HRESULT destroyHr = S_OK;
2011 StorageInternalImpl *stg, *stg2;
2013 /* Invalidate any open storage objects. */
2014 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2016 if (stg->base.storageDirEntry == indexToDelete)
2018 StorageBaseImpl_Invalidate(&stg->base);
2023 * Open the storage and enumerate it
2025 hr = StorageBaseImpl_OpenStorage(
2026 (IStorage*)parentStorage,
2027 entryDataToDelete.name,
2029 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2032 &childStorage);
2034 if (hr != S_OK)
2036 return hr;
2040 * Enumerate the elements
2042 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2047 * Obtain the next element
2049 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2050 if (hr==S_OK)
2052 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2054 CoTaskMemFree(currentElement.pwcsName);
2058 * We need to Reset the enumeration every time because we delete elements
2059 * and the enumeration could be invalid
2061 IEnumSTATSTG_Reset(elements);
2063 } while ((hr == S_OK) && (destroyHr == S_OK));
2065 IStorage_Release(childStorage);
2066 IEnumSTATSTG_Release(elements);
2068 return destroyHr;
2071 /*********************************************************************
2073 * Internal Method
2075 * Perform the deletion of a stream's data
2078 static HRESULT deleteStreamContents(
2079 StorageBaseImpl *parentStorage,
2080 DirRef indexToDelete,
2081 DirEntry entryDataToDelete)
2083 IStream *pis;
2084 HRESULT hr;
2085 ULARGE_INTEGER size;
2086 StgStreamImpl *strm, *strm2;
2088 /* Invalidate any open stream objects. */
2089 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2091 if (strm->dirEntry == indexToDelete)
2093 TRACE("Stream deleted %p\n", strm);
2094 strm->parentStorage = NULL;
2095 list_remove(&strm->StrmListEntry);
2099 size.u.HighPart = 0;
2100 size.u.LowPart = 0;
2102 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2103 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2105 if (hr!=S_OK)
2107 return(hr);
2111 * Zap the stream
2113 hr = IStream_SetSize(pis, size);
2115 if(hr != S_OK)
2117 return hr;
2121 * Release the stream object.
2123 IStream_Release(pis);
2125 return S_OK;
2128 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2130 switch (relation)
2132 case DIRENTRY_RELATION_PREVIOUS:
2133 entry->leftChild = new_target;
2134 break;
2135 case DIRENTRY_RELATION_NEXT:
2136 entry->rightChild = new_target;
2137 break;
2138 case DIRENTRY_RELATION_DIR:
2139 entry->dirRootEntry = new_target;
2140 break;
2141 default:
2142 assert(0);
2146 /*************************************************************************
2148 * Internal Method
2150 * This method removes a directory entry from its parent storage tree without
2151 * freeing any resources attached to it.
2153 static HRESULT removeFromTree(
2154 StorageBaseImpl *This,
2155 DirRef parentStorageIndex,
2156 DirRef deletedIndex)
2158 HRESULT hr = S_OK;
2159 DirEntry entryToDelete;
2160 DirEntry parentEntry;
2161 DirRef parentEntryRef;
2162 ULONG typeOfRelation;
2164 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2166 if (hr != S_OK)
2167 return hr;
2170 * Find the element that links to the one we want to delete.
2172 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2173 &parentEntry, &parentEntryRef, &typeOfRelation);
2175 if (hr != S_OK)
2176 return hr;
2178 if (entryToDelete.leftChild != DIRENTRY_NULL)
2181 * Replace the deleted entry with its left child
2183 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2185 hr = StorageBaseImpl_WriteDirEntry(
2186 This,
2187 parentEntryRef,
2188 &parentEntry);
2189 if(FAILED(hr))
2191 return hr;
2194 if (entryToDelete.rightChild != DIRENTRY_NULL)
2197 * We need to reinsert the right child somewhere. We already know it and
2198 * its children are greater than everything in the left tree, so we
2199 * insert it at the rightmost point in the left tree.
2201 DirRef newRightChildParent = entryToDelete.leftChild;
2202 DirEntry newRightChildParentEntry;
2206 hr = StorageBaseImpl_ReadDirEntry(
2207 This,
2208 newRightChildParent,
2209 &newRightChildParentEntry);
2210 if (FAILED(hr))
2212 return hr;
2215 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2216 newRightChildParent = newRightChildParentEntry.rightChild;
2217 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2219 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2221 hr = StorageBaseImpl_WriteDirEntry(
2222 This,
2223 newRightChildParent,
2224 &newRightChildParentEntry);
2225 if (FAILED(hr))
2227 return hr;
2231 else
2234 * Replace the deleted entry with its right child
2236 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2238 hr = StorageBaseImpl_WriteDirEntry(
2239 This,
2240 parentEntryRef,
2241 &parentEntry);
2242 if(FAILED(hr))
2244 return hr;
2248 return hr;
2252 /******************************************************************************
2253 * SetElementTimes (IStorage)
2255 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2256 IStorage* iface,
2257 const OLECHAR *pwcsName,/* [string][in] */
2258 const FILETIME *pctime, /* [in] */
2259 const FILETIME *patime, /* [in] */
2260 const FILETIME *pmtime) /* [in] */
2262 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2263 return S_OK;
2266 /******************************************************************************
2267 * SetStateBits (IStorage)
2269 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2270 IStorage* iface,
2271 DWORD grfStateBits,/* [in] */
2272 DWORD grfMask) /* [in] */
2274 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2276 if (This->reverted)
2277 return STG_E_REVERTED;
2279 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2280 return S_OK;
2283 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2284 DirRef index, const DirEntry *data)
2286 StorageImpl *This = (StorageImpl*)base;
2287 return StorageImpl_WriteDirEntry(This, index, data);
2290 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2291 DirRef index, DirEntry *data)
2293 StorageImpl *This = (StorageImpl*)base;
2294 return StorageImpl_ReadDirEntry(This, index, data);
2297 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2299 int i;
2301 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2303 if (!This->blockChainCache[i])
2305 return &This->blockChainCache[i];
2309 i = This->blockChainToEvict;
2311 BlockChainStream_Destroy(This->blockChainCache[i]);
2312 This->blockChainCache[i] = NULL;
2314 This->blockChainToEvict++;
2315 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2316 This->blockChainToEvict = 0;
2318 return &This->blockChainCache[i];
2321 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2322 DirRef index)
2324 int i, free_index=-1;
2326 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2328 if (!This->blockChainCache[i])
2330 if (free_index == -1) free_index = i;
2332 else if (This->blockChainCache[i]->ownerDirEntry == index)
2334 return &This->blockChainCache[i];
2338 if (free_index == -1)
2340 free_index = This->blockChainToEvict;
2342 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2343 This->blockChainCache[free_index] = NULL;
2345 This->blockChainToEvict++;
2346 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2347 This->blockChainToEvict = 0;
2350 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2351 return &This->blockChainCache[free_index];
2354 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2356 int i;
2358 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2360 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2362 BlockChainStream_Destroy(This->blockChainCache[i]);
2363 This->blockChainCache[i] = NULL;
2364 return;
2369 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2370 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2372 StorageImpl *This = (StorageImpl*)base;
2373 DirEntry data;
2374 HRESULT hr;
2375 ULONG bytesToRead;
2377 hr = StorageImpl_ReadDirEntry(This, index, &data);
2378 if (FAILED(hr)) return hr;
2380 if (data.size.QuadPart == 0)
2382 *bytesRead = 0;
2383 return S_OK;
2386 if (offset.QuadPart + size > data.size.QuadPart)
2388 bytesToRead = data.size.QuadPart - offset.QuadPart;
2390 else
2392 bytesToRead = size;
2395 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2397 SmallBlockChainStream *stream;
2399 stream = SmallBlockChainStream_Construct(This, NULL, index);
2400 if (!stream) return E_OUTOFMEMORY;
2402 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2404 SmallBlockChainStream_Destroy(stream);
2406 return hr;
2408 else
2410 BlockChainStream *stream = NULL;
2412 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2413 if (!stream) return E_OUTOFMEMORY;
2415 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2417 return hr;
2421 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2422 ULARGE_INTEGER newsize)
2424 StorageImpl *This = (StorageImpl*)base;
2425 DirEntry data;
2426 HRESULT hr;
2427 SmallBlockChainStream *smallblock=NULL;
2428 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2430 hr = StorageImpl_ReadDirEntry(This, index, &data);
2431 if (FAILED(hr)) return hr;
2433 /* In simple mode keep the stream size above the small block limit */
2434 if (This->base.openFlags & STGM_SIMPLE)
2435 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2437 if (data.size.QuadPart == newsize.QuadPart)
2438 return S_OK;
2440 /* Create a block chain object of the appropriate type */
2441 if (data.size.QuadPart == 0)
2443 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2445 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2446 if (!smallblock) return E_OUTOFMEMORY;
2448 else
2450 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2451 bigblock = *pbigblock;
2452 if (!bigblock) return E_OUTOFMEMORY;
2455 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2457 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2458 if (!smallblock) return E_OUTOFMEMORY;
2460 else
2462 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2463 bigblock = *pbigblock;
2464 if (!bigblock) return E_OUTOFMEMORY;
2467 /* Change the block chain type if necessary. */
2468 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2470 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2471 if (!bigblock)
2473 SmallBlockChainStream_Destroy(smallblock);
2474 return E_FAIL;
2477 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2478 *pbigblock = bigblock;
2480 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2482 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2483 if (!smallblock)
2484 return E_FAIL;
2487 /* Set the size of the block chain. */
2488 if (smallblock)
2490 SmallBlockChainStream_SetSize(smallblock, newsize);
2491 SmallBlockChainStream_Destroy(smallblock);
2493 else
2495 BlockChainStream_SetSize(bigblock, newsize);
2498 /* Set the size in the directory entry. */
2499 hr = StorageImpl_ReadDirEntry(This, index, &data);
2500 if (SUCCEEDED(hr))
2502 data.size = newsize;
2504 hr = StorageImpl_WriteDirEntry(This, index, &data);
2506 return hr;
2509 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2510 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2512 StorageImpl *This = (StorageImpl*)base;
2513 DirEntry data;
2514 HRESULT hr;
2515 ULARGE_INTEGER newSize;
2517 hr = StorageImpl_ReadDirEntry(This, index, &data);
2518 if (FAILED(hr)) return hr;
2520 /* Grow the stream if necessary */
2521 newSize.QuadPart = 0;
2522 newSize.QuadPart = offset.QuadPart + size;
2524 if (newSize.QuadPart > data.size.QuadPart)
2526 hr = StorageImpl_StreamSetSize(base, index, newSize);
2527 if (FAILED(hr))
2528 return hr;
2530 hr = StorageImpl_ReadDirEntry(This, index, &data);
2531 if (FAILED(hr)) return hr;
2534 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2536 SmallBlockChainStream *stream;
2538 stream = SmallBlockChainStream_Construct(This, NULL, index);
2539 if (!stream) return E_OUTOFMEMORY;
2541 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2543 SmallBlockChainStream_Destroy(stream);
2545 return hr;
2547 else
2549 BlockChainStream *stream;
2551 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2552 if (!stream) return E_OUTOFMEMORY;
2554 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2556 return hr;
2560 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2561 DirRef src)
2563 StorageImpl *This = (StorageImpl*)base;
2564 DirEntry dst_data, src_data;
2565 HRESULT hr;
2567 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2569 if (SUCCEEDED(hr))
2570 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2572 if (SUCCEEDED(hr))
2574 StorageImpl_DeleteCachedBlockChainStream(This, src);
2575 dst_data.startingBlock = src_data.startingBlock;
2576 dst_data.size = src_data.size;
2578 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2581 return hr;
2585 * Virtual function table for the IStorage32Impl class.
2587 static const IStorageVtbl Storage32Impl_Vtbl =
2589 StorageBaseImpl_QueryInterface,
2590 StorageBaseImpl_AddRef,
2591 StorageBaseImpl_Release,
2592 StorageBaseImpl_CreateStream,
2593 StorageBaseImpl_OpenStream,
2594 StorageBaseImpl_CreateStorage,
2595 StorageBaseImpl_OpenStorage,
2596 StorageBaseImpl_CopyTo,
2597 StorageBaseImpl_MoveElementTo,
2598 StorageImpl_Commit,
2599 StorageImpl_Revert,
2600 StorageBaseImpl_EnumElements,
2601 StorageBaseImpl_DestroyElement,
2602 StorageBaseImpl_RenameElement,
2603 StorageBaseImpl_SetElementTimes,
2604 StorageBaseImpl_SetClass,
2605 StorageBaseImpl_SetStateBits,
2606 StorageBaseImpl_Stat
2609 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2611 StorageImpl_Destroy,
2612 StorageImpl_Invalidate,
2613 StorageImpl_CreateDirEntry,
2614 StorageImpl_BaseWriteDirEntry,
2615 StorageImpl_BaseReadDirEntry,
2616 StorageImpl_DestroyDirEntry,
2617 StorageImpl_StreamReadAt,
2618 StorageImpl_StreamWriteAt,
2619 StorageImpl_StreamSetSize,
2620 StorageImpl_StreamLink
2623 static HRESULT StorageImpl_Construct(
2624 HANDLE hFile,
2625 LPCOLESTR pwcsName,
2626 ILockBytes* pLkbyt,
2627 DWORD openFlags,
2628 BOOL fileBased,
2629 BOOL create,
2630 ULONG sector_size,
2631 StorageImpl** result)
2633 StorageImpl* This;
2634 HRESULT hr = S_OK;
2635 DirEntry currentEntry;
2636 DirRef currentEntryRef;
2637 WCHAR fullpath[MAX_PATH];
2639 if ( FAILED( validateSTGM(openFlags) ))
2640 return STG_E_INVALIDFLAG;
2642 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2643 if (!This)
2644 return E_OUTOFMEMORY;
2646 memset(This, 0, sizeof(StorageImpl));
2648 list_init(&This->base.strmHead);
2650 list_init(&This->base.storageHead);
2652 This->base.lpVtbl = &Storage32Impl_Vtbl;
2653 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2654 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2655 This->base.openFlags = (openFlags & ~STGM_CREATE);
2656 This->base.ref = 1;
2657 This->base.create = create;
2659 This->base.reverted = 0;
2661 This->hFile = hFile;
2663 if(pwcsName) {
2664 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2666 lstrcpynW(fullpath, pwcsName, MAX_PATH);
2668 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2669 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2670 if (!This->pwcsName)
2672 hr = STG_E_INSUFFICIENTMEMORY;
2673 goto end;
2675 strcpyW(This->pwcsName, fullpath);
2676 This->base.filename = This->pwcsName;
2680 * Initialize the big block cache.
2682 This->bigBlockSize = sector_size;
2683 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2684 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2685 pLkbyt,
2686 openFlags,
2687 fileBased);
2689 if (This->bigBlockFile == 0)
2691 hr = E_FAIL;
2692 goto end;
2695 if (create)
2697 ULARGE_INTEGER size;
2698 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2701 * Initialize all header variables:
2702 * - The big block depot consists of one block and it is at block 0
2703 * - The directory table starts at block 1
2704 * - There is no small block depot
2706 memset( This->bigBlockDepotStart,
2707 BLOCK_UNUSED,
2708 sizeof(This->bigBlockDepotStart));
2710 This->bigBlockDepotCount = 1;
2711 This->bigBlockDepotStart[0] = 0;
2712 This->rootStartBlock = 1;
2713 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2714 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2715 if (sector_size == 4096)
2716 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2717 else
2718 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2719 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2720 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2721 This->extBigBlockDepotCount = 0;
2723 StorageImpl_SaveFileHeader(This);
2726 * Add one block for the big block depot and one block for the directory table
2728 size.u.HighPart = 0;
2729 size.u.LowPart = This->bigBlockSize * 3;
2730 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2733 * Initialize the big block depot
2735 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2736 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2737 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2738 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2740 else
2743 * Load the header for the file.
2745 hr = StorageImpl_LoadFileHeader(This);
2747 if (FAILED(hr))
2749 goto end;
2754 * There is no block depot cached yet.
2756 This->indexBlockDepotCached = 0xFFFFFFFF;
2759 * Start searching for free blocks with block 0.
2761 This->prevFreeBlock = 0;
2763 This->firstFreeSmallBlock = 0;
2766 * Create the block chain abstractions.
2768 if(!(This->rootBlockChain =
2769 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2771 hr = STG_E_READFAULT;
2772 goto end;
2775 if(!(This->smallBlockDepotChain =
2776 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2777 DIRENTRY_NULL)))
2779 hr = STG_E_READFAULT;
2780 goto end;
2784 * Write the root storage entry (memory only)
2786 if (create)
2788 DirEntry rootEntry;
2790 * Initialize the directory table
2792 memset(&rootEntry, 0, sizeof(rootEntry));
2793 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2794 sizeof(rootEntry.name)/sizeof(WCHAR) );
2795 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2796 rootEntry.stgType = STGTY_ROOT;
2797 rootEntry.leftChild = DIRENTRY_NULL;
2798 rootEntry.rightChild = DIRENTRY_NULL;
2799 rootEntry.dirRootEntry = DIRENTRY_NULL;
2800 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2801 rootEntry.size.u.HighPart = 0;
2802 rootEntry.size.u.LowPart = 0;
2804 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2808 * Find the ID of the root storage.
2810 currentEntryRef = 0;
2814 hr = StorageImpl_ReadDirEntry(
2815 This,
2816 currentEntryRef,
2817 &currentEntry);
2819 if (SUCCEEDED(hr))
2821 if ( (currentEntry.sizeOfNameString != 0 ) &&
2822 (currentEntry.stgType == STGTY_ROOT) )
2824 This->base.storageDirEntry = currentEntryRef;
2828 currentEntryRef++;
2830 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2832 if (FAILED(hr))
2834 hr = STG_E_READFAULT;
2835 goto end;
2839 * Create the block chain abstraction for the small block root chain.
2841 if(!(This->smallBlockRootChain =
2842 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2844 hr = STG_E_READFAULT;
2847 end:
2848 if (FAILED(hr))
2850 IStorage_Release((IStorage*)This);
2851 *result = NULL;
2853 else
2854 *result = This;
2856 return hr;
2859 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2861 StorageImpl *This = (StorageImpl*) iface;
2863 StorageBaseImpl_DeleteAll(&This->base);
2865 This->base.reverted = 1;
2868 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2870 StorageImpl *This = (StorageImpl*) iface;
2871 int i;
2872 TRACE("(%p)\n", This);
2874 StorageImpl_Invalidate(iface);
2876 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2878 BlockChainStream_Destroy(This->smallBlockRootChain);
2879 BlockChainStream_Destroy(This->rootBlockChain);
2880 BlockChainStream_Destroy(This->smallBlockDepotChain);
2882 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2883 BlockChainStream_Destroy(This->blockChainCache[i]);
2885 if (This->bigBlockFile)
2886 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2887 HeapFree(GetProcessHeap(), 0, This);
2890 /******************************************************************************
2891 * Storage32Impl_GetNextFreeBigBlock
2893 * Returns the index of the next free big block.
2894 * If the big block depot is filled, this method will enlarge it.
2897 static ULONG StorageImpl_GetNextFreeBigBlock(
2898 StorageImpl* This)
2900 ULONG depotBlockIndexPos;
2901 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
2902 BOOL success;
2903 ULONG depotBlockOffset;
2904 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2905 ULONG nextBlockIndex = BLOCK_SPECIAL;
2906 int depotIndex = 0;
2907 ULONG freeBlock = BLOCK_UNUSED;
2908 ULARGE_INTEGER neededSize;
2910 depotIndex = This->prevFreeBlock / blocksPerDepot;
2911 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2914 * Scan the entire big block depot until we find a block marked free
2916 while (nextBlockIndex != BLOCK_UNUSED)
2918 if (depotIndex < COUNT_BBDEPOTINHEADER)
2920 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2923 * Grow the primary depot.
2925 if (depotBlockIndexPos == BLOCK_UNUSED)
2927 depotBlockIndexPos = depotIndex*blocksPerDepot;
2930 * Add a block depot.
2932 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2933 This->bigBlockDepotCount++;
2934 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2937 * Flag it as a block depot.
2939 StorageImpl_SetNextBlockInChain(This,
2940 depotBlockIndexPos,
2941 BLOCK_SPECIAL);
2943 /* Save new header information.
2945 StorageImpl_SaveFileHeader(This);
2948 else
2950 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2952 if (depotBlockIndexPos == BLOCK_UNUSED)
2955 * Grow the extended depot.
2957 ULONG extIndex = BLOCK_UNUSED;
2958 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2959 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2961 if (extBlockOffset == 0)
2963 /* We need an extended block.
2965 extIndex = Storage32Impl_AddExtBlockDepot(This);
2966 This->extBigBlockDepotCount++;
2967 depotBlockIndexPos = extIndex + 1;
2969 else
2970 depotBlockIndexPos = depotIndex * blocksPerDepot;
2973 * Add a block depot and mark it in the extended block.
2975 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2976 This->bigBlockDepotCount++;
2977 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2979 /* Flag the block depot.
2981 StorageImpl_SetNextBlockInChain(This,
2982 depotBlockIndexPos,
2983 BLOCK_SPECIAL);
2985 /* If necessary, flag the extended depot block.
2987 if (extIndex != BLOCK_UNUSED)
2988 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2990 /* Save header information.
2992 StorageImpl_SaveFileHeader(This);
2996 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2998 if (success)
3000 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3001 ( nextBlockIndex != BLOCK_UNUSED))
3003 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3005 if (nextBlockIndex == BLOCK_UNUSED)
3007 freeBlock = (depotIndex * blocksPerDepot) +
3008 (depotBlockOffset/sizeof(ULONG));
3011 depotBlockOffset += sizeof(ULONG);
3015 depotIndex++;
3016 depotBlockOffset = 0;
3020 * make sure that the block physically exists before using it
3022 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3023 BIGBLOCKFILE_Expand(This->bigBlockFile, neededSize);
3025 This->prevFreeBlock = freeBlock;
3027 return freeBlock;
3030 /******************************************************************************
3031 * Storage32Impl_AddBlockDepot
3033 * This will create a depot block, essentially it is a block initialized
3034 * to BLOCK_UNUSEDs.
3036 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3038 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3041 * Initialize blocks as free
3043 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3044 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3047 /******************************************************************************
3048 * Storage32Impl_GetExtDepotBlock
3050 * Returns the index of the block that corresponds to the specified depot
3051 * index. This method is only for depot indexes equal or greater than
3052 * COUNT_BBDEPOTINHEADER.
3054 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3056 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3057 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3058 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3059 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3060 ULONG blockIndex = BLOCK_UNUSED;
3061 ULONG extBlockIndex = This->extBigBlockDepotStart;
3063 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3065 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
3066 return BLOCK_UNUSED;
3068 while (extBlockCount > 0)
3070 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3071 extBlockCount--;
3074 if (extBlockIndex != BLOCK_UNUSED)
3075 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3076 extBlockOffset * sizeof(ULONG), &blockIndex);
3078 return blockIndex;
3081 /******************************************************************************
3082 * Storage32Impl_SetExtDepotBlock
3084 * Associates the specified block index to the specified depot index.
3085 * This method is only for depot indexes equal or greater than
3086 * COUNT_BBDEPOTINHEADER.
3088 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3090 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3091 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3092 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3093 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3094 ULONG extBlockIndex = This->extBigBlockDepotStart;
3096 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3098 while (extBlockCount > 0)
3100 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3101 extBlockCount--;
3104 if (extBlockIndex != BLOCK_UNUSED)
3106 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3107 extBlockOffset * sizeof(ULONG),
3108 blockIndex);
3112 /******************************************************************************
3113 * Storage32Impl_AddExtBlockDepot
3115 * Creates an extended depot block.
3117 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3119 ULONG numExtBlocks = This->extBigBlockDepotCount;
3120 ULONG nextExtBlock = This->extBigBlockDepotStart;
3121 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3122 ULONG index = BLOCK_UNUSED;
3123 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3124 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3125 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3127 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3128 blocksPerDepotBlock;
3130 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3133 * The first extended block.
3135 This->extBigBlockDepotStart = index;
3137 else
3139 unsigned int i;
3141 * Follow the chain to the last one.
3143 for (i = 0; i < (numExtBlocks - 1); i++)
3145 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3149 * Add the new extended block to the chain.
3151 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3152 index);
3156 * Initialize this block.
3158 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3159 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3161 return index;
3164 /******************************************************************************
3165 * Storage32Impl_FreeBigBlock
3167 * This method will flag the specified block as free in the big block depot.
3169 static void StorageImpl_FreeBigBlock(
3170 StorageImpl* This,
3171 ULONG blockIndex)
3173 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3175 if (blockIndex < This->prevFreeBlock)
3176 This->prevFreeBlock = blockIndex;
3179 /************************************************************************
3180 * Storage32Impl_GetNextBlockInChain
3182 * This method will retrieve the block index of the next big block in
3183 * in the chain.
3185 * Params: This - Pointer to the Storage object.
3186 * blockIndex - Index of the block to retrieve the chain
3187 * for.
3188 * nextBlockIndex - receives the return value.
3190 * Returns: This method returns the index of the next block in the chain.
3191 * It will return the constants:
3192 * BLOCK_SPECIAL - If the block given was not part of a
3193 * chain.
3194 * BLOCK_END_OF_CHAIN - If the block given was the last in
3195 * a chain.
3196 * BLOCK_UNUSED - If the block given was not past of a chain
3197 * and is available.
3198 * BLOCK_EXTBBDEPOT - This block is part of the extended
3199 * big block depot.
3201 * See Windows documentation for more details on IStorage methods.
3203 static HRESULT StorageImpl_GetNextBlockInChain(
3204 StorageImpl* This,
3205 ULONG blockIndex,
3206 ULONG* nextBlockIndex)
3208 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3209 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3210 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3211 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3212 BOOL success;
3213 ULONG depotBlockIndexPos;
3214 int index, num_blocks;
3216 *nextBlockIndex = BLOCK_SPECIAL;
3218 if(depotBlockCount >= This->bigBlockDepotCount)
3220 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3221 This->bigBlockDepotCount);
3222 return STG_E_READFAULT;
3226 * Cache the currently accessed depot block.
3228 if (depotBlockCount != This->indexBlockDepotCached)
3230 This->indexBlockDepotCached = depotBlockCount;
3232 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3234 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3236 else
3239 * We have to look in the extended depot.
3241 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3244 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3246 if (!success)
3247 return STG_E_READFAULT;
3249 num_blocks = This->bigBlockSize / 4;
3251 for (index = 0; index < num_blocks; index++)
3253 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3254 This->blockDepotCached[index] = *nextBlockIndex;
3258 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3260 return S_OK;
3263 /******************************************************************************
3264 * Storage32Impl_GetNextExtendedBlock
3266 * Given an extended block this method will return the next extended block.
3268 * NOTES:
3269 * The last ULONG of an extended block is the block index of the next
3270 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3271 * depot.
3273 * Return values:
3274 * - The index of the next extended block
3275 * - BLOCK_UNUSED: there is no next extended block.
3276 * - Any other return values denotes failure.
3278 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3280 ULONG nextBlockIndex = BLOCK_SPECIAL;
3281 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3283 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3284 &nextBlockIndex);
3286 return nextBlockIndex;
3289 /******************************************************************************
3290 * Storage32Impl_SetNextBlockInChain
3292 * This method will write the index of the specified block's next block
3293 * in the big block depot.
3295 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3296 * do the following
3298 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3299 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3300 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3303 static void StorageImpl_SetNextBlockInChain(
3304 StorageImpl* This,
3305 ULONG blockIndex,
3306 ULONG nextBlock)
3308 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3309 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3310 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3311 ULONG depotBlockIndexPos;
3313 assert(depotBlockCount < This->bigBlockDepotCount);
3314 assert(blockIndex != nextBlock);
3316 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3318 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3320 else
3323 * We have to look in the extended depot.
3325 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3328 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3329 nextBlock);
3331 * Update the cached block depot, if necessary.
3333 if (depotBlockCount == This->indexBlockDepotCached)
3335 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3339 /******************************************************************************
3340 * Storage32Impl_LoadFileHeader
3342 * This method will read in the file header
3344 static HRESULT StorageImpl_LoadFileHeader(
3345 StorageImpl* This)
3347 HRESULT hr;
3348 BYTE headerBigBlock[HEADER_SIZE];
3349 int index;
3350 ULARGE_INTEGER offset;
3351 DWORD bytes_read;
3353 TRACE("\n");
3355 * Get a pointer to the big block of data containing the header.
3357 offset.u.HighPart = 0;
3358 offset.u.LowPart = 0;
3359 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3360 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3361 hr = STG_E_FILENOTFOUND;
3364 * Extract the information from the header.
3366 if (SUCCEEDED(hr))
3369 * Check for the "magic number" signature and return an error if it is not
3370 * found.
3372 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3374 return STG_E_OLDFORMAT;
3377 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3379 return STG_E_INVALIDHEADER;
3382 StorageUtl_ReadWord(
3383 headerBigBlock,
3384 OFFSET_BIGBLOCKSIZEBITS,
3385 &This->bigBlockSizeBits);
3387 StorageUtl_ReadWord(
3388 headerBigBlock,
3389 OFFSET_SMALLBLOCKSIZEBITS,
3390 &This->smallBlockSizeBits);
3392 StorageUtl_ReadDWord(
3393 headerBigBlock,
3394 OFFSET_BBDEPOTCOUNT,
3395 &This->bigBlockDepotCount);
3397 StorageUtl_ReadDWord(
3398 headerBigBlock,
3399 OFFSET_ROOTSTARTBLOCK,
3400 &This->rootStartBlock);
3402 StorageUtl_ReadDWord(
3403 headerBigBlock,
3404 OFFSET_SMALLBLOCKLIMIT,
3405 &This->smallBlockLimit);
3407 StorageUtl_ReadDWord(
3408 headerBigBlock,
3409 OFFSET_SBDEPOTSTART,
3410 &This->smallBlockDepotStart);
3412 StorageUtl_ReadDWord(
3413 headerBigBlock,
3414 OFFSET_EXTBBDEPOTSTART,
3415 &This->extBigBlockDepotStart);
3417 StorageUtl_ReadDWord(
3418 headerBigBlock,
3419 OFFSET_EXTBBDEPOTCOUNT,
3420 &This->extBigBlockDepotCount);
3422 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3424 StorageUtl_ReadDWord(
3425 headerBigBlock,
3426 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3427 &(This->bigBlockDepotStart[index]));
3431 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3433 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3434 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3437 * Right now, the code is making some assumptions about the size of the
3438 * blocks, just make sure they are what we're expecting.
3440 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3441 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3442 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3444 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3445 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3446 hr = STG_E_INVALIDHEADER;
3448 else
3449 hr = S_OK;
3452 return hr;
3455 /******************************************************************************
3456 * Storage32Impl_SaveFileHeader
3458 * This method will save to the file the header
3460 static void StorageImpl_SaveFileHeader(
3461 StorageImpl* This)
3463 BYTE headerBigBlock[HEADER_SIZE];
3464 int index;
3465 HRESULT hr;
3466 ULARGE_INTEGER offset;
3467 DWORD bytes_read, bytes_written;
3470 * Get a pointer to the big block of data containing the header.
3472 offset.u.HighPart = 0;
3473 offset.u.LowPart = 0;
3474 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3475 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3476 hr = STG_E_FILENOTFOUND;
3479 * If the block read failed, the file is probably new.
3481 if (FAILED(hr))
3484 * Initialize for all unknown fields.
3486 memset(headerBigBlock, 0, HEADER_SIZE);
3489 * Initialize the magic number.
3491 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3494 * And a bunch of things we don't know what they mean
3496 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3497 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3498 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3502 * Write the information to the header.
3504 StorageUtl_WriteWord(
3505 headerBigBlock,
3506 OFFSET_BIGBLOCKSIZEBITS,
3507 This->bigBlockSizeBits);
3509 StorageUtl_WriteWord(
3510 headerBigBlock,
3511 OFFSET_SMALLBLOCKSIZEBITS,
3512 This->smallBlockSizeBits);
3514 StorageUtl_WriteDWord(
3515 headerBigBlock,
3516 OFFSET_BBDEPOTCOUNT,
3517 This->bigBlockDepotCount);
3519 StorageUtl_WriteDWord(
3520 headerBigBlock,
3521 OFFSET_ROOTSTARTBLOCK,
3522 This->rootStartBlock);
3524 StorageUtl_WriteDWord(
3525 headerBigBlock,
3526 OFFSET_SMALLBLOCKLIMIT,
3527 This->smallBlockLimit);
3529 StorageUtl_WriteDWord(
3530 headerBigBlock,
3531 OFFSET_SBDEPOTSTART,
3532 This->smallBlockDepotStart);
3534 StorageUtl_WriteDWord(
3535 headerBigBlock,
3536 OFFSET_SBDEPOTCOUNT,
3537 This->smallBlockDepotChain ?
3538 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3540 StorageUtl_WriteDWord(
3541 headerBigBlock,
3542 OFFSET_EXTBBDEPOTSTART,
3543 This->extBigBlockDepotStart);
3545 StorageUtl_WriteDWord(
3546 headerBigBlock,
3547 OFFSET_EXTBBDEPOTCOUNT,
3548 This->extBigBlockDepotCount);
3550 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3552 StorageUtl_WriteDWord(
3553 headerBigBlock,
3554 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3555 (This->bigBlockDepotStart[index]));
3559 * Write the big block back to the file.
3561 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3564 /******************************************************************************
3565 * StorageImpl_ReadRawDirEntry
3567 * This method will read the raw data from a directory entry in the file.
3569 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3571 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3573 ULARGE_INTEGER offset;
3574 HRESULT hr;
3575 ULONG bytesRead;
3577 offset.u.HighPart = 0;
3578 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3580 hr = BlockChainStream_ReadAt(
3581 This->rootBlockChain,
3582 offset,
3583 RAW_DIRENTRY_SIZE,
3584 buffer,
3585 &bytesRead);
3587 return hr;
3590 /******************************************************************************
3591 * StorageImpl_WriteRawDirEntry
3593 * This method will write the raw data from a directory entry in the file.
3595 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3597 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3599 ULARGE_INTEGER offset;
3600 HRESULT hr;
3601 ULONG bytesRead;
3603 offset.u.HighPart = 0;
3604 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3606 hr = BlockChainStream_WriteAt(
3607 This->rootBlockChain,
3608 offset,
3609 RAW_DIRENTRY_SIZE,
3610 buffer,
3611 &bytesRead);
3613 return hr;
3616 /******************************************************************************
3617 * UpdateRawDirEntry
3619 * Update raw directory entry data from the fields in newData.
3621 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3623 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3625 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3627 memcpy(
3628 buffer + OFFSET_PS_NAME,
3629 newData->name,
3630 DIRENTRY_NAME_BUFFER_LEN );
3632 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3634 StorageUtl_WriteWord(
3635 buffer,
3636 OFFSET_PS_NAMELENGTH,
3637 newData->sizeOfNameString);
3639 StorageUtl_WriteDWord(
3640 buffer,
3641 OFFSET_PS_LEFTCHILD,
3642 newData->leftChild);
3644 StorageUtl_WriteDWord(
3645 buffer,
3646 OFFSET_PS_RIGHTCHILD,
3647 newData->rightChild);
3649 StorageUtl_WriteDWord(
3650 buffer,
3651 OFFSET_PS_DIRROOT,
3652 newData->dirRootEntry);
3654 StorageUtl_WriteGUID(
3655 buffer,
3656 OFFSET_PS_GUID,
3657 &newData->clsid);
3659 StorageUtl_WriteDWord(
3660 buffer,
3661 OFFSET_PS_CTIMELOW,
3662 newData->ctime.dwLowDateTime);
3664 StorageUtl_WriteDWord(
3665 buffer,
3666 OFFSET_PS_CTIMEHIGH,
3667 newData->ctime.dwHighDateTime);
3669 StorageUtl_WriteDWord(
3670 buffer,
3671 OFFSET_PS_MTIMELOW,
3672 newData->mtime.dwLowDateTime);
3674 StorageUtl_WriteDWord(
3675 buffer,
3676 OFFSET_PS_MTIMEHIGH,
3677 newData->ctime.dwHighDateTime);
3679 StorageUtl_WriteDWord(
3680 buffer,
3681 OFFSET_PS_STARTBLOCK,
3682 newData->startingBlock);
3684 StorageUtl_WriteDWord(
3685 buffer,
3686 OFFSET_PS_SIZE,
3687 newData->size.u.LowPart);
3690 /******************************************************************************
3691 * Storage32Impl_ReadDirEntry
3693 * This method will read the specified directory entry.
3695 HRESULT StorageImpl_ReadDirEntry(
3696 StorageImpl* This,
3697 DirRef index,
3698 DirEntry* buffer)
3700 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3701 HRESULT readRes;
3703 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3705 if (SUCCEEDED(readRes))
3707 memset(buffer->name, 0, sizeof(buffer->name));
3708 memcpy(
3709 buffer->name,
3710 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3711 DIRENTRY_NAME_BUFFER_LEN );
3712 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3714 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3716 StorageUtl_ReadWord(
3717 currentEntry,
3718 OFFSET_PS_NAMELENGTH,
3719 &buffer->sizeOfNameString);
3721 StorageUtl_ReadDWord(
3722 currentEntry,
3723 OFFSET_PS_LEFTCHILD,
3724 &buffer->leftChild);
3726 StorageUtl_ReadDWord(
3727 currentEntry,
3728 OFFSET_PS_RIGHTCHILD,
3729 &buffer->rightChild);
3731 StorageUtl_ReadDWord(
3732 currentEntry,
3733 OFFSET_PS_DIRROOT,
3734 &buffer->dirRootEntry);
3736 StorageUtl_ReadGUID(
3737 currentEntry,
3738 OFFSET_PS_GUID,
3739 &buffer->clsid);
3741 StorageUtl_ReadDWord(
3742 currentEntry,
3743 OFFSET_PS_CTIMELOW,
3744 &buffer->ctime.dwLowDateTime);
3746 StorageUtl_ReadDWord(
3747 currentEntry,
3748 OFFSET_PS_CTIMEHIGH,
3749 &buffer->ctime.dwHighDateTime);
3751 StorageUtl_ReadDWord(
3752 currentEntry,
3753 OFFSET_PS_MTIMELOW,
3754 &buffer->mtime.dwLowDateTime);
3756 StorageUtl_ReadDWord(
3757 currentEntry,
3758 OFFSET_PS_MTIMEHIGH,
3759 &buffer->mtime.dwHighDateTime);
3761 StorageUtl_ReadDWord(
3762 currentEntry,
3763 OFFSET_PS_STARTBLOCK,
3764 &buffer->startingBlock);
3766 StorageUtl_ReadDWord(
3767 currentEntry,
3768 OFFSET_PS_SIZE,
3769 &buffer->size.u.LowPart);
3771 buffer->size.u.HighPart = 0;
3774 return readRes;
3777 /*********************************************************************
3778 * Write the specified directory entry to the file
3780 HRESULT StorageImpl_WriteDirEntry(
3781 StorageImpl* This,
3782 DirRef index,
3783 const DirEntry* buffer)
3785 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3786 HRESULT writeRes;
3788 UpdateRawDirEntry(currentEntry, buffer);
3790 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3791 return writeRes;
3794 static BOOL StorageImpl_ReadBigBlock(
3795 StorageImpl* This,
3796 ULONG blockIndex,
3797 void* buffer)
3799 ULARGE_INTEGER ulOffset;
3800 DWORD read;
3802 ulOffset.u.HighPart = 0;
3803 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3805 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3806 return (read == This->bigBlockSize);
3809 static BOOL StorageImpl_ReadDWordFromBigBlock(
3810 StorageImpl* This,
3811 ULONG blockIndex,
3812 ULONG offset,
3813 DWORD* value)
3815 ULARGE_INTEGER ulOffset;
3816 DWORD read;
3817 DWORD tmp;
3819 ulOffset.u.HighPart = 0;
3820 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3821 ulOffset.u.LowPart += offset;
3823 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3824 *value = lendian32toh(tmp);
3825 return (read == sizeof(DWORD));
3828 static BOOL StorageImpl_WriteBigBlock(
3829 StorageImpl* This,
3830 ULONG blockIndex,
3831 const void* buffer)
3833 ULARGE_INTEGER ulOffset;
3834 DWORD wrote;
3836 ulOffset.u.HighPart = 0;
3837 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3839 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3840 return (wrote == This->bigBlockSize);
3843 static BOOL StorageImpl_WriteDWordToBigBlock(
3844 StorageImpl* This,
3845 ULONG blockIndex,
3846 ULONG offset,
3847 DWORD value)
3849 ULARGE_INTEGER ulOffset;
3850 DWORD wrote;
3852 ulOffset.u.HighPart = 0;
3853 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3854 ulOffset.u.LowPart += offset;
3856 value = htole32(value);
3857 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3858 return (wrote == sizeof(DWORD));
3861 /******************************************************************************
3862 * Storage32Impl_SmallBlocksToBigBlocks
3864 * This method will convert a small block chain to a big block chain.
3865 * The small block chain will be destroyed.
3867 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3868 StorageImpl* This,
3869 SmallBlockChainStream** ppsbChain)
3871 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3872 ULARGE_INTEGER size, offset;
3873 ULONG cbRead, cbWritten;
3874 ULARGE_INTEGER cbTotalRead;
3875 DirRef streamEntryRef;
3876 HRESULT resWrite = S_OK;
3877 HRESULT resRead;
3878 DirEntry streamEntry;
3879 BYTE *buffer;
3880 BlockChainStream *bbTempChain = NULL;
3881 BlockChainStream *bigBlockChain = NULL;
3884 * Create a temporary big block chain that doesn't have
3885 * an associated directory entry. This temporary chain will be
3886 * used to copy data from small blocks to big blocks.
3888 bbTempChain = BlockChainStream_Construct(This,
3889 &bbHeadOfChain,
3890 DIRENTRY_NULL);
3891 if(!bbTempChain) return NULL;
3893 * Grow the big block chain.
3895 size = SmallBlockChainStream_GetSize(*ppsbChain);
3896 BlockChainStream_SetSize(bbTempChain, size);
3899 * Copy the contents of the small block chain to the big block chain
3900 * by small block size increments.
3902 offset.u.LowPart = 0;
3903 offset.u.HighPart = 0;
3904 cbTotalRead.QuadPart = 0;
3906 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3909 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3910 offset,
3911 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3912 buffer,
3913 &cbRead);
3914 if (FAILED(resRead))
3915 break;
3917 if (cbRead > 0)
3919 cbTotalRead.QuadPart += cbRead;
3921 resWrite = BlockChainStream_WriteAt(bbTempChain,
3922 offset,
3923 cbRead,
3924 buffer,
3925 &cbWritten);
3927 if (FAILED(resWrite))
3928 break;
3930 offset.u.LowPart += cbRead;
3932 } while (cbTotalRead.QuadPart < size.QuadPart);
3933 HeapFree(GetProcessHeap(),0,buffer);
3935 size.u.HighPart = 0;
3936 size.u.LowPart = 0;
3938 if (FAILED(resRead) || FAILED(resWrite))
3940 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3941 BlockChainStream_SetSize(bbTempChain, size);
3942 BlockChainStream_Destroy(bbTempChain);
3943 return NULL;
3947 * Destroy the small block chain.
3949 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3950 SmallBlockChainStream_SetSize(*ppsbChain, size);
3951 SmallBlockChainStream_Destroy(*ppsbChain);
3952 *ppsbChain = 0;
3955 * Change the directory entry. This chain is now a big block chain
3956 * and it doesn't reside in the small blocks chain anymore.
3958 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3960 streamEntry.startingBlock = bbHeadOfChain;
3962 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3965 * Destroy the temporary entryless big block chain.
3966 * Create a new big block chain associated with this entry.
3968 BlockChainStream_Destroy(bbTempChain);
3969 bigBlockChain = BlockChainStream_Construct(This,
3970 NULL,
3971 streamEntryRef);
3973 return bigBlockChain;
3976 /******************************************************************************
3977 * Storage32Impl_BigBlocksToSmallBlocks
3979 * This method will convert a big block chain to a small block chain.
3980 * The big block chain will be destroyed on success.
3982 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3983 StorageImpl* This,
3984 BlockChainStream** ppbbChain)
3986 ULARGE_INTEGER size, offset, cbTotalRead;
3987 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3988 DirRef streamEntryRef;
3989 HRESULT resWrite = S_OK, resRead;
3990 DirEntry streamEntry;
3991 BYTE* buffer;
3992 SmallBlockChainStream* sbTempChain;
3994 TRACE("%p %p\n", This, ppbbChain);
3996 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3997 DIRENTRY_NULL);
3999 if(!sbTempChain)
4000 return NULL;
4002 size = BlockChainStream_GetSize(*ppbbChain);
4003 SmallBlockChainStream_SetSize(sbTempChain, size);
4005 offset.u.HighPart = 0;
4006 offset.u.LowPart = 0;
4007 cbTotalRead.QuadPart = 0;
4008 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4011 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4012 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4013 buffer, &cbRead);
4015 if(FAILED(resRead))
4016 break;
4018 if(cbRead > 0)
4020 cbTotalRead.QuadPart += cbRead;
4022 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4023 cbRead, buffer, &cbWritten);
4025 if(FAILED(resWrite))
4026 break;
4028 offset.u.LowPart += cbRead;
4030 }while(cbTotalRead.QuadPart < size.QuadPart);
4031 HeapFree(GetProcessHeap(), 0, buffer);
4033 size.u.HighPart = 0;
4034 size.u.LowPart = 0;
4036 if(FAILED(resRead) || FAILED(resWrite))
4038 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4039 SmallBlockChainStream_SetSize(sbTempChain, size);
4040 SmallBlockChainStream_Destroy(sbTempChain);
4041 return NULL;
4044 /* destroy the original big block chain */
4045 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4046 BlockChainStream_SetSize(*ppbbChain, size);
4047 BlockChainStream_Destroy(*ppbbChain);
4048 *ppbbChain = NULL;
4050 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4051 streamEntry.startingBlock = sbHeadOfChain;
4052 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4054 SmallBlockChainStream_Destroy(sbTempChain);
4055 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4058 static HRESULT StorageBaseImpl_CopyStream(
4059 StorageBaseImpl *dst, DirRef dst_entry,
4060 StorageBaseImpl *src, DirRef src_entry)
4062 HRESULT hr;
4063 BYTE data[4096];
4064 DirEntry srcdata;
4065 ULARGE_INTEGER bytes_copied;
4066 ULONG bytestocopy, bytesread, byteswritten;
4068 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4070 if (SUCCEEDED(hr))
4072 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4074 bytes_copied.QuadPart = 0;
4075 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4077 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4079 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4080 data, &bytesread);
4081 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4083 if (SUCCEEDED(hr))
4084 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4085 data, &byteswritten);
4086 if (SUCCEEDED(hr) && byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4088 bytes_copied.QuadPart += byteswritten;
4092 return hr;
4095 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4097 DirRef result=This->firstFreeEntry;
4099 while (result < This->entries_size && This->entries[result].inuse)
4100 result++;
4102 if (result == This->entries_size)
4104 ULONG new_size = This->entries_size * 2;
4105 TransactedDirEntry *new_entries;
4107 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4108 if (!new_entries) return DIRENTRY_NULL;
4110 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4111 HeapFree(GetProcessHeap(), 0, This->entries);
4113 This->entries = new_entries;
4114 This->entries_size = new_size;
4117 This->entries[result].inuse = 1;
4119 This->firstFreeEntry = result+1;
4121 return result;
4124 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4125 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4127 DirRef stubEntryRef;
4128 TransactedDirEntry *entry;
4130 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4132 if (stubEntryRef != DIRENTRY_NULL)
4134 entry = &This->entries[stubEntryRef];
4136 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4138 entry->read = 0;
4141 return stubEntryRef;
4144 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4145 TransactedSnapshotImpl *This, DirRef entry)
4147 HRESULT hr=S_OK;
4149 if (!This->entries[entry].read)
4151 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4152 This->entries[entry].transactedParentEntry,
4153 &This->entries[entry].data);
4155 if (SUCCEEDED(hr) && This->entries[entry].data.leftChild != DIRENTRY_NULL)
4157 This->entries[entry].data.leftChild =
4158 TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].data.leftChild);
4160 if (This->entries[entry].data.leftChild == DIRENTRY_NULL)
4161 hr = E_OUTOFMEMORY;
4164 if (SUCCEEDED(hr) && This->entries[entry].data.rightChild != DIRENTRY_NULL)
4166 This->entries[entry].data.rightChild =
4167 TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].data.rightChild);
4169 if (This->entries[entry].data.rightChild == DIRENTRY_NULL)
4170 hr = E_OUTOFMEMORY;
4173 if (SUCCEEDED(hr) && This->entries[entry].data.dirRootEntry != DIRENTRY_NULL)
4175 This->entries[entry].data.dirRootEntry =
4176 TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].data.dirRootEntry);
4178 if (This->entries[entry].data.dirRootEntry == DIRENTRY_NULL)
4179 hr = E_OUTOFMEMORY;
4182 if (SUCCEEDED(hr))
4183 This->entries[entry].read = 1;
4186 return hr;
4189 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4190 TransactedSnapshotImpl *This, DirRef entry)
4192 HRESULT hr = S_OK;
4194 if (!This->entries[entry].stream_dirty)
4196 DirEntry new_entrydata;
4198 memset(&new_entrydata, 0, sizeof(DirEntry));
4199 new_entrydata.name[0] = 'S';
4200 new_entrydata.sizeOfNameString = 1;
4201 new_entrydata.stgType = STGTY_STREAM;
4202 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4203 new_entrydata.leftChild = DIRENTRY_NULL;
4204 new_entrydata.rightChild = DIRENTRY_NULL;
4205 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4207 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4208 &This->entries[entry].stream_entry);
4210 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4212 hr = StorageBaseImpl_CopyStream(
4213 This->scratch, This->entries[entry].stream_entry,
4214 This->transactedParent, This->entries[entry].transactedParentEntry);
4216 if (FAILED(hr))
4217 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4220 if (SUCCEEDED(hr))
4221 This->entries[entry].stream_dirty = 1;
4223 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4225 /* Since this entry is modified, and we aren't using its stream data, we
4226 * no longer care about the original entry. */
4227 DirRef delete_ref;
4228 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4230 if (delete_ref != DIRENTRY_NULL)
4231 This->entries[delete_ref].deleted = 1;
4233 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4237 return hr;
4240 /* Find the first entry in a depth-first traversal. */
4241 static DirRef TransactedSnapshotImpl_FindFirstChild(
4242 TransactedSnapshotImpl* This, DirRef parent)
4244 DirRef cursor, prev;
4245 TransactedDirEntry *entry;
4247 cursor = parent;
4248 entry = &This->entries[cursor];
4249 while (entry->read)
4251 if (entry->data.leftChild != DIRENTRY_NULL)
4253 prev = cursor;
4254 cursor = entry->data.leftChild;
4255 entry = &This->entries[cursor];
4256 entry->parent = prev;
4258 else if (entry->data.rightChild != DIRENTRY_NULL)
4260 prev = cursor;
4261 cursor = entry->data.rightChild;
4262 entry = &This->entries[cursor];
4263 entry->parent = prev;
4265 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4267 prev = cursor;
4268 cursor = entry->data.dirRootEntry;
4269 entry = &This->entries[cursor];
4270 entry->parent = prev;
4272 else
4273 break;
4276 return cursor;
4279 /* Find the next entry in a depth-first traversal. */
4280 static DirRef TransactedSnapshotImpl_FindNextChild(
4281 TransactedSnapshotImpl* This, DirRef current)
4283 DirRef parent;
4284 TransactedDirEntry *parent_entry;
4286 parent = This->entries[current].parent;
4287 parent_entry = &This->entries[parent];
4289 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4291 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4293 This->entries[parent_entry->data.rightChild].parent = parent;
4294 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4297 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4299 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4300 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4304 return parent;
4307 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4308 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4309 TransactedSnapshotImpl* This, DirRef entry)
4311 return entry != DIRENTRY_NULL &&
4312 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4315 /* Destroy the entries created by CopyTree. */
4316 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4317 TransactedSnapshotImpl* This, DirRef stop)
4319 DirRef cursor;
4320 TransactedDirEntry *entry;
4321 ULARGE_INTEGER zero;
4323 zero.QuadPart = 0;
4325 if (!This->entries[This->base.storageDirEntry].read)
4326 return;
4328 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4330 if (cursor == DIRENTRY_NULL)
4331 return;
4333 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4335 while (cursor != DIRENTRY_NULL && cursor != stop)
4337 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4339 entry = &This->entries[cursor];
4341 if (entry->stream_dirty)
4342 StorageBaseImpl_StreamSetSize(This->transactedParent,
4343 entry->newTransactedParentEntry, zero);
4345 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4346 entry->newTransactedParentEntry);
4348 entry->newTransactedParentEntry = entry->transactedParentEntry;
4351 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4355 /* Make a copy of our edited tree that we can use in the parent. */
4356 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4358 DirRef cursor;
4359 TransactedDirEntry *entry;
4360 HRESULT hr;
4362 cursor = This->base.storageDirEntry;
4363 entry = &This->entries[cursor];
4364 entry->parent = DIRENTRY_NULL;
4365 entry->newTransactedParentEntry = entry->transactedParentEntry;
4367 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4368 return S_OK;
4370 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4372 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4373 entry = &This->entries[cursor];
4375 while (cursor != DIRENTRY_NULL)
4377 /* Make a copy of this entry in the transacted parent. */
4378 if (!entry->read ||
4379 (!entry->dirty && !entry->stream_dirty &&
4380 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4381 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4382 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4383 entry->newTransactedParentEntry = entry->transactedParentEntry;
4384 else
4386 DirEntry newData;
4388 memcpy(&newData, &entry->data, sizeof(DirEntry));
4390 newData.size.QuadPart = 0;
4391 newData.startingBlock = BLOCK_END_OF_CHAIN;
4393 if (newData.leftChild != DIRENTRY_NULL)
4394 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4396 if (newData.rightChild != DIRENTRY_NULL)
4397 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4399 if (newData.dirRootEntry != DIRENTRY_NULL)
4400 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4402 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4403 &entry->newTransactedParentEntry);
4404 if (FAILED(hr))
4406 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4407 return hr;
4410 if (entry->stream_dirty)
4412 hr = StorageBaseImpl_CopyStream(
4413 This->transactedParent, entry->newTransactedParentEntry,
4414 This->scratch, entry->stream_entry);
4416 else if (entry->data.size.QuadPart)
4418 hr = StorageBaseImpl_StreamLink(
4419 This->transactedParent, entry->newTransactedParentEntry,
4420 entry->transactedParentEntry);
4423 if (FAILED(hr))
4425 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4426 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4427 return hr;
4431 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4432 entry = &This->entries[cursor];
4435 return hr;
4438 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4439 IStorage* iface,
4440 DWORD grfCommitFlags) /* [in] */
4442 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4443 TransactedDirEntry *root_entry;
4444 DirRef i, dir_root_ref;
4445 DirEntry data;
4446 ULARGE_INTEGER zero;
4447 HRESULT hr;
4449 zero.QuadPart = 0;
4451 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4453 /* Cannot commit a read-only transacted storage */
4454 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4455 return STG_E_ACCESSDENIED;
4457 /* To prevent data loss, we create the new structure in the file before we
4458 * delete the old one, so that in case of errors the old data is intact. We
4459 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4460 * needed in the rare situation where we have just enough free disk space to
4461 * overwrite the existing data. */
4463 root_entry = &This->entries[This->base.storageDirEntry];
4465 if (!root_entry->read)
4466 return S_OK;
4468 hr = TransactedSnapshotImpl_CopyTree(This);
4469 if (FAILED(hr)) return hr;
4471 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4472 dir_root_ref = DIRENTRY_NULL;
4473 else
4474 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4476 /* Update the storage to use the new data in one step. */
4477 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4478 root_entry->transactedParentEntry, &data);
4480 if (SUCCEEDED(hr))
4482 data.dirRootEntry = dir_root_ref;
4483 data.clsid = root_entry->data.clsid;
4484 data.ctime = root_entry->data.ctime;
4485 data.mtime = root_entry->data.mtime;
4487 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4488 root_entry->transactedParentEntry, &data);
4491 if (SUCCEEDED(hr))
4493 /* Destroy the old now-orphaned data. */
4494 for (i=0; i<This->entries_size; i++)
4496 TransactedDirEntry *entry = &This->entries[i];
4497 if (entry->inuse)
4499 if (entry->deleted)
4501 StorageBaseImpl_StreamSetSize(This->transactedParent,
4502 entry->transactedParentEntry, zero);
4503 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4504 entry->transactedParentEntry);
4505 memset(entry, 0, sizeof(TransactedDirEntry));
4506 This->firstFreeEntry = min(i, This->firstFreeEntry);
4508 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4510 if (entry->transactedParentEntry != DIRENTRY_NULL)
4511 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4512 entry->transactedParentEntry);
4513 if (entry->stream_dirty)
4515 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4516 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4517 entry->stream_dirty = 0;
4519 entry->dirty = 0;
4520 entry->transactedParentEntry = entry->newTransactedParentEntry;
4525 else
4527 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4530 return hr;
4533 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4534 IStorage* iface)
4536 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4537 ULARGE_INTEGER zero;
4538 ULONG i;
4540 TRACE("(%p)\n", iface);
4542 /* Destroy the open objects. */
4543 StorageBaseImpl_DeleteAll(&This->base);
4545 /* Clear out the scratch file. */
4546 zero.QuadPart = 0;
4547 for (i=0; i<This->entries_size; i++)
4549 if (This->entries[i].stream_dirty)
4551 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4552 zero);
4554 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4558 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4560 This->firstFreeEntry = 0;
4561 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4563 return S_OK;
4566 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4568 if (!This->reverted)
4570 TRACE("Storage invalidated (stg=%p)\n", This);
4572 This->reverted = 1;
4574 StorageBaseImpl_DeleteAll(This);
4578 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4580 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4582 TransactedSnapshotImpl_Revert((IStorage*)iface);
4584 IStorage_Release((IStorage*)This->transactedParent);
4586 IStorage_Release((IStorage*)This->scratch);
4588 HeapFree(GetProcessHeap(), 0, This->entries);
4590 HeapFree(GetProcessHeap(), 0, This);
4593 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4594 const DirEntry *newData, DirRef *index)
4596 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4597 DirRef new_ref;
4598 TransactedDirEntry *new_entry;
4600 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4601 if (new_ref == DIRENTRY_NULL)
4602 return E_OUTOFMEMORY;
4604 new_entry = &This->entries[new_ref];
4606 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4607 new_entry->read = 1;
4608 new_entry->dirty = 1;
4609 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4611 *index = new_ref;
4613 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4615 return S_OK;
4618 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4619 DirRef index, const DirEntry *data)
4621 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4622 HRESULT hr;
4624 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4626 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4627 if (FAILED(hr)) return hr;
4629 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4631 if (index != This->base.storageDirEntry)
4633 This->entries[index].dirty = 1;
4635 if (data->size.QuadPart == 0 &&
4636 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4638 /* Since this entry is modified, and we aren't using its stream data, we
4639 * no longer care about the original entry. */
4640 DirRef delete_ref;
4641 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4643 if (delete_ref != DIRENTRY_NULL)
4644 This->entries[delete_ref].deleted = 1;
4646 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4650 return S_OK;
4653 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4654 DirRef index, DirEntry *data)
4656 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4657 HRESULT hr;
4659 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4660 if (FAILED(hr)) return hr;
4662 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4664 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4666 return S_OK;
4669 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4670 DirRef index)
4672 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4674 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4675 This->entries[index].data.size.QuadPart != 0)
4677 /* If we deleted this entry while it has stream data. We must have left the
4678 * data because some other entry is using it, and we need to leave the
4679 * original entry alone. */
4680 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4681 This->firstFreeEntry = min(index, This->firstFreeEntry);
4683 else
4685 This->entries[index].deleted = 1;
4688 return S_OK;
4691 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4692 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4694 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4696 if (This->entries[index].stream_dirty)
4698 return StorageBaseImpl_StreamReadAt(This->scratch,
4699 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4701 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4703 /* This stream doesn't live in the parent, and we haven't allocated storage
4704 * for it yet */
4705 *bytesRead = 0;
4706 return S_OK;
4708 else
4710 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4711 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4715 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4716 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4718 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4719 HRESULT hr;
4721 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4722 if (FAILED(hr)) return hr;
4724 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4725 if (FAILED(hr)) return hr;
4727 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4728 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4730 if (SUCCEEDED(hr) && size != 0)
4731 This->entries[index].data.size.QuadPart = max(
4732 This->entries[index].data.size.QuadPart,
4733 offset.QuadPart + size);
4735 return hr;
4738 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4739 DirRef index, ULARGE_INTEGER newsize)
4741 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4742 HRESULT hr;
4744 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4745 if (FAILED(hr)) return hr;
4747 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4748 return S_OK;
4750 if (newsize.QuadPart == 0)
4752 /* Destroy any parent references or entries in the scratch file. */
4753 if (This->entries[index].stream_dirty)
4755 ULARGE_INTEGER zero;
4756 zero.QuadPart = 0;
4757 StorageBaseImpl_StreamSetSize(This->scratch,
4758 This->entries[index].stream_entry, zero);
4759 StorageBaseImpl_DestroyDirEntry(This->scratch,
4760 This->entries[index].stream_entry);
4761 This->entries[index].stream_dirty = 0;
4763 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4765 DirRef delete_ref;
4766 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4768 if (delete_ref != DIRENTRY_NULL)
4769 This->entries[delete_ref].deleted = 1;
4771 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4774 else
4776 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4777 if (FAILED(hr)) return hr;
4779 hr = StorageBaseImpl_StreamSetSize(This->scratch,
4780 This->entries[index].stream_entry, newsize);
4783 if (SUCCEEDED(hr))
4784 This->entries[index].data.size = newsize;
4786 return hr;
4789 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
4790 DirRef dst, DirRef src)
4792 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4793 HRESULT hr;
4794 TransactedDirEntry *dst_entry, *src_entry;
4796 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
4797 if (FAILED(hr)) return hr;
4799 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
4800 if (FAILED(hr)) return hr;
4802 dst_entry = &This->entries[dst];
4803 src_entry = &This->entries[src];
4805 dst_entry->stream_dirty = src_entry->stream_dirty;
4806 dst_entry->stream_entry = src_entry->stream_entry;
4807 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
4808 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
4809 dst_entry->data.size = src_entry->data.size;
4811 return S_OK;
4814 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4816 StorageBaseImpl_QueryInterface,
4817 StorageBaseImpl_AddRef,
4818 StorageBaseImpl_Release,
4819 StorageBaseImpl_CreateStream,
4820 StorageBaseImpl_OpenStream,
4821 StorageBaseImpl_CreateStorage,
4822 StorageBaseImpl_OpenStorage,
4823 StorageBaseImpl_CopyTo,
4824 StorageBaseImpl_MoveElementTo,
4825 TransactedSnapshotImpl_Commit,
4826 TransactedSnapshotImpl_Revert,
4827 StorageBaseImpl_EnumElements,
4828 StorageBaseImpl_DestroyElement,
4829 StorageBaseImpl_RenameElement,
4830 StorageBaseImpl_SetElementTimes,
4831 StorageBaseImpl_SetClass,
4832 StorageBaseImpl_SetStateBits,
4833 StorageBaseImpl_Stat
4836 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4838 TransactedSnapshotImpl_Destroy,
4839 TransactedSnapshotImpl_Invalidate,
4840 TransactedSnapshotImpl_CreateDirEntry,
4841 TransactedSnapshotImpl_WriteDirEntry,
4842 TransactedSnapshotImpl_ReadDirEntry,
4843 TransactedSnapshotImpl_DestroyDirEntry,
4844 TransactedSnapshotImpl_StreamReadAt,
4845 TransactedSnapshotImpl_StreamWriteAt,
4846 TransactedSnapshotImpl_StreamSetSize,
4847 TransactedSnapshotImpl_StreamLink
4850 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4851 TransactedSnapshotImpl** result)
4853 HRESULT hr;
4855 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4856 if (*result)
4858 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4860 /* This is OK because the property set storage functions use the IStorage functions. */
4861 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4863 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4865 list_init(&(*result)->base.strmHead);
4867 list_init(&(*result)->base.storageHead);
4869 (*result)->base.ref = 1;
4871 (*result)->base.openFlags = parentStorage->openFlags;
4873 (*result)->base.filename = parentStorage->filename;
4875 /* Create a new temporary storage to act as the scratch file. */
4876 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
4877 0, (IStorage**)&(*result)->scratch);
4879 if (SUCCEEDED(hr))
4881 ULONG num_entries = 20;
4883 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
4885 (*result)->entries_size = num_entries;
4887 (*result)->firstFreeEntry = 0;
4889 if ((*result)->entries)
4891 /* parentStorage already has 1 reference, which we take over here. */
4892 (*result)->transactedParent = parentStorage;
4894 parentStorage->transactedChild = (StorageBaseImpl*)*result;
4896 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
4898 else
4900 IStorage_Release((IStorage*)(*result)->scratch);
4902 hr = E_OUTOFMEMORY;
4906 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4908 return hr;
4910 else
4911 return E_OUTOFMEMORY;
4914 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
4915 StorageBaseImpl** result)
4917 static int fixme=0;
4919 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
4921 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
4924 return TransactedSnapshotImpl_Construct(parentStorage,
4925 (TransactedSnapshotImpl**)result);
4928 static HRESULT Storage_Construct(
4929 HANDLE hFile,
4930 LPCOLESTR pwcsName,
4931 ILockBytes* pLkbyt,
4932 DWORD openFlags,
4933 BOOL fileBased,
4934 BOOL create,
4935 ULONG sector_size,
4936 StorageBaseImpl** result)
4938 StorageImpl *newStorage;
4939 StorageBaseImpl *newTransactedStorage;
4940 HRESULT hr;
4942 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
4943 if (FAILED(hr)) goto end;
4945 if (openFlags & STGM_TRANSACTED)
4947 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
4948 if (FAILED(hr))
4949 IStorage_Release((IStorage*)newStorage);
4950 else
4951 *result = newTransactedStorage;
4953 else
4954 *result = &newStorage->base;
4956 end:
4957 return hr;
4960 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
4962 StorageInternalImpl* This = (StorageInternalImpl*) base;
4964 if (!This->base.reverted)
4966 TRACE("Storage invalidated (stg=%p)\n", This);
4968 This->base.reverted = 1;
4970 This->parentStorage = NULL;
4972 StorageBaseImpl_DeleteAll(&This->base);
4974 list_remove(&This->ParentListEntry);
4978 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
4980 StorageInternalImpl* This = (StorageInternalImpl*) iface;
4982 StorageInternalImpl_Invalidate(&This->base);
4984 HeapFree(GetProcessHeap(), 0, This);
4987 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
4988 const DirEntry *newData, DirRef *index)
4990 StorageInternalImpl* This = (StorageInternalImpl*) base;
4992 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
4993 newData, index);
4996 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
4997 DirRef index, const DirEntry *data)
4999 StorageInternalImpl* This = (StorageInternalImpl*) base;
5001 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5002 index, data);
5005 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5006 DirRef index, DirEntry *data)
5008 StorageInternalImpl* This = (StorageInternalImpl*) base;
5010 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5011 index, data);
5014 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5015 DirRef index)
5017 StorageInternalImpl* This = (StorageInternalImpl*) base;
5019 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5020 index);
5023 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5024 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5026 StorageInternalImpl* This = (StorageInternalImpl*) base;
5028 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5029 index, offset, size, buffer, bytesRead);
5032 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5033 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5035 StorageInternalImpl* This = (StorageInternalImpl*) base;
5037 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5038 index, offset, size, buffer, bytesWritten);
5041 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5042 DirRef index, ULARGE_INTEGER newsize)
5044 StorageInternalImpl* This = (StorageInternalImpl*) base;
5046 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5047 index, newsize);
5050 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5051 DirRef dst, DirRef src)
5053 StorageInternalImpl* This = (StorageInternalImpl*) base;
5055 return StorageBaseImpl_StreamLink(This->parentStorage,
5056 dst, src);
5059 /******************************************************************************
5061 ** Storage32InternalImpl_Commit
5064 static HRESULT WINAPI StorageInternalImpl_Commit(
5065 IStorage* iface,
5066 DWORD grfCommitFlags) /* [in] */
5068 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
5069 return S_OK;
5072 /******************************************************************************
5074 ** Storage32InternalImpl_Revert
5077 static HRESULT WINAPI StorageInternalImpl_Revert(
5078 IStorage* iface)
5080 FIXME("(%p): stub\n", iface);
5081 return S_OK;
5084 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5086 IStorage_Release((IStorage*)This->parentStorage);
5087 HeapFree(GetProcessHeap(), 0, This);
5090 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5091 IEnumSTATSTG* iface,
5092 REFIID riid,
5093 void** ppvObject)
5095 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5097 if (ppvObject==0)
5098 return E_INVALIDARG;
5100 *ppvObject = 0;
5102 if (IsEqualGUID(&IID_IUnknown, riid) ||
5103 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5105 *ppvObject = This;
5106 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
5107 return S_OK;
5110 return E_NOINTERFACE;
5113 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5114 IEnumSTATSTG* iface)
5116 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5117 return InterlockedIncrement(&This->ref);
5120 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5121 IEnumSTATSTG* iface)
5123 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5125 ULONG newRef;
5127 newRef = InterlockedDecrement(&This->ref);
5129 if (newRef==0)
5131 IEnumSTATSTGImpl_Destroy(This);
5134 return newRef;
5137 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5138 IEnumSTATSTGImpl* This,
5139 DirRef *ref)
5141 DirRef result = DIRENTRY_NULL;
5142 DirRef searchNode;
5143 DirEntry entry;
5144 HRESULT hr;
5145 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5147 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5148 This->parentStorage->storageDirEntry, &entry);
5149 searchNode = entry.dirRootEntry;
5151 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5153 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5155 if (SUCCEEDED(hr))
5157 LONG diff = entryNameCmp( entry.name, This->name);
5159 if (diff <= 0)
5161 searchNode = entry.rightChild;
5163 else
5165 result = searchNode;
5166 memcpy(result_name, entry.name, sizeof(result_name));
5167 searchNode = entry.leftChild;
5172 if (SUCCEEDED(hr))
5174 *ref = result;
5175 if (result != DIRENTRY_NULL)
5176 memcpy(This->name, result_name, sizeof(result_name));
5179 return hr;
5182 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5183 IEnumSTATSTG* iface,
5184 ULONG celt,
5185 STATSTG* rgelt,
5186 ULONG* pceltFetched)
5188 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5190 DirEntry currentEntry;
5191 STATSTG* currentReturnStruct = rgelt;
5192 ULONG objectFetched = 0;
5193 DirRef currentSearchNode;
5194 HRESULT hr=S_OK;
5196 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5197 return E_INVALIDARG;
5199 if (This->parentStorage->reverted)
5200 return STG_E_REVERTED;
5203 * To avoid the special case, get another pointer to a ULONG value if
5204 * the caller didn't supply one.
5206 if (pceltFetched==0)
5207 pceltFetched = &objectFetched;
5210 * Start the iteration, we will iterate until we hit the end of the
5211 * linked list or until we hit the number of items to iterate through
5213 *pceltFetched = 0;
5215 while ( *pceltFetched < celt )
5217 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5219 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5220 break;
5223 * Read the entry from the storage.
5225 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5226 currentSearchNode,
5227 &currentEntry);
5230 * Copy the information to the return buffer.
5232 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5233 currentReturnStruct,
5234 &currentEntry,
5235 STATFLAG_DEFAULT);
5238 * Step to the next item in the iteration
5240 (*pceltFetched)++;
5241 currentReturnStruct++;
5244 if (SUCCEEDED(hr) && *pceltFetched != celt)
5245 hr = S_FALSE;
5247 return hr;
5251 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5252 IEnumSTATSTG* iface,
5253 ULONG celt)
5255 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5257 ULONG objectFetched = 0;
5258 DirRef currentSearchNode;
5259 HRESULT hr=S_OK;
5261 if (This->parentStorage->reverted)
5262 return STG_E_REVERTED;
5264 while ( (objectFetched < celt) )
5266 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5268 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5269 break;
5271 objectFetched++;
5274 if (SUCCEEDED(hr) && objectFetched != celt)
5275 return S_FALSE;
5277 return hr;
5280 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5281 IEnumSTATSTG* iface)
5283 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5285 if (This->parentStorage->reverted)
5286 return STG_E_REVERTED;
5288 This->name[0] = 0;
5290 return S_OK;
5293 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5294 IEnumSTATSTG* iface,
5295 IEnumSTATSTG** ppenum)
5297 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5299 IEnumSTATSTGImpl* newClone;
5301 if (This->parentStorage->reverted)
5302 return STG_E_REVERTED;
5305 * Perform a sanity check on the parameters.
5307 if (ppenum==0)
5308 return E_INVALIDARG;
5310 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5311 This->storageDirEntry);
5315 * The new clone enumeration must point to the same current node as
5316 * the ole one.
5318 memcpy(newClone->name, This->name, sizeof(newClone->name));
5320 *ppenum = (IEnumSTATSTG*)newClone;
5323 * Don't forget to nail down a reference to the clone before
5324 * returning it.
5326 IEnumSTATSTGImpl_AddRef(*ppenum);
5328 return S_OK;
5332 * Virtual function table for the IEnumSTATSTGImpl class.
5334 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5336 IEnumSTATSTGImpl_QueryInterface,
5337 IEnumSTATSTGImpl_AddRef,
5338 IEnumSTATSTGImpl_Release,
5339 IEnumSTATSTGImpl_Next,
5340 IEnumSTATSTGImpl_Skip,
5341 IEnumSTATSTGImpl_Reset,
5342 IEnumSTATSTGImpl_Clone
5345 /******************************************************************************
5346 ** IEnumSTATSTGImpl implementation
5349 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5350 StorageBaseImpl* parentStorage,
5351 DirRef storageDirEntry)
5353 IEnumSTATSTGImpl* newEnumeration;
5355 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5357 if (newEnumeration!=0)
5360 * Set-up the virtual function table and reference count.
5362 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5363 newEnumeration->ref = 0;
5366 * We want to nail-down the reference to the storage in case the
5367 * enumeration out-lives the storage in the client application.
5369 newEnumeration->parentStorage = parentStorage;
5370 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
5372 newEnumeration->storageDirEntry = storageDirEntry;
5375 * Make sure the current node of the iterator is the first one.
5377 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
5380 return newEnumeration;
5384 * Virtual function table for the Storage32InternalImpl class.
5386 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5388 StorageBaseImpl_QueryInterface,
5389 StorageBaseImpl_AddRef,
5390 StorageBaseImpl_Release,
5391 StorageBaseImpl_CreateStream,
5392 StorageBaseImpl_OpenStream,
5393 StorageBaseImpl_CreateStorage,
5394 StorageBaseImpl_OpenStorage,
5395 StorageBaseImpl_CopyTo,
5396 StorageBaseImpl_MoveElementTo,
5397 StorageInternalImpl_Commit,
5398 StorageInternalImpl_Revert,
5399 StorageBaseImpl_EnumElements,
5400 StorageBaseImpl_DestroyElement,
5401 StorageBaseImpl_RenameElement,
5402 StorageBaseImpl_SetElementTimes,
5403 StorageBaseImpl_SetClass,
5404 StorageBaseImpl_SetStateBits,
5405 StorageBaseImpl_Stat
5408 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5410 StorageInternalImpl_Destroy,
5411 StorageInternalImpl_Invalidate,
5412 StorageInternalImpl_CreateDirEntry,
5413 StorageInternalImpl_WriteDirEntry,
5414 StorageInternalImpl_ReadDirEntry,
5415 StorageInternalImpl_DestroyDirEntry,
5416 StorageInternalImpl_StreamReadAt,
5417 StorageInternalImpl_StreamWriteAt,
5418 StorageInternalImpl_StreamSetSize,
5419 StorageInternalImpl_StreamLink
5422 /******************************************************************************
5423 ** Storage32InternalImpl implementation
5426 static StorageInternalImpl* StorageInternalImpl_Construct(
5427 StorageBaseImpl* parentStorage,
5428 DWORD openFlags,
5429 DirRef storageDirEntry)
5431 StorageInternalImpl* newStorage;
5433 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5435 if (newStorage!=0)
5437 list_init(&newStorage->base.strmHead);
5439 list_init(&newStorage->base.storageHead);
5442 * Initialize the virtual function table.
5444 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
5445 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
5446 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5447 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5449 newStorage->base.reverted = 0;
5451 newStorage->base.ref = 1;
5453 newStorage->parentStorage = parentStorage;
5456 * Keep a reference to the directory entry of this storage
5458 newStorage->base.storageDirEntry = storageDirEntry;
5460 newStorage->base.create = 0;
5462 return newStorage;
5465 return 0;
5468 /******************************************************************************
5469 ** StorageUtl implementation
5472 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5474 WORD tmp;
5476 memcpy(&tmp, buffer+offset, sizeof(WORD));
5477 *value = lendian16toh(tmp);
5480 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5482 value = htole16(value);
5483 memcpy(buffer+offset, &value, sizeof(WORD));
5486 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5488 DWORD tmp;
5490 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5491 *value = lendian32toh(tmp);
5494 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5496 value = htole32(value);
5497 memcpy(buffer+offset, &value, sizeof(DWORD));
5500 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5501 ULARGE_INTEGER* value)
5503 #ifdef WORDS_BIGENDIAN
5504 ULARGE_INTEGER tmp;
5506 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5507 value->u.LowPart = htole32(tmp.u.HighPart);
5508 value->u.HighPart = htole32(tmp.u.LowPart);
5509 #else
5510 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5511 #endif
5514 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5515 const ULARGE_INTEGER *value)
5517 #ifdef WORDS_BIGENDIAN
5518 ULARGE_INTEGER tmp;
5520 tmp.u.LowPart = htole32(value->u.HighPart);
5521 tmp.u.HighPart = htole32(value->u.LowPart);
5522 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5523 #else
5524 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5525 #endif
5528 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5530 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5531 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5532 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5534 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5537 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5539 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5540 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5541 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5543 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5546 void StorageUtl_CopyDirEntryToSTATSTG(
5547 StorageBaseImpl* storage,
5548 STATSTG* destination,
5549 const DirEntry* source,
5550 int statFlags)
5552 LPCWSTR entryName;
5554 if (source->stgType == STGTY_ROOT)
5556 /* replace the name of root entry (often "Root Entry") by the file name */
5557 entryName = storage->filename;
5559 else
5561 entryName = source->name;
5565 * The copy of the string occurs only when the flag is not set
5567 if( ((statFlags & STATFLAG_NONAME) != 0) ||
5568 (entryName == NULL) ||
5569 (entryName[0] == 0) )
5571 destination->pwcsName = 0;
5573 else
5575 destination->pwcsName =
5576 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
5578 strcpyW(destination->pwcsName, entryName);
5581 switch (source->stgType)
5583 case STGTY_STORAGE:
5584 case STGTY_ROOT:
5585 destination->type = STGTY_STORAGE;
5586 break;
5587 case STGTY_STREAM:
5588 destination->type = STGTY_STREAM;
5589 break;
5590 default:
5591 destination->type = STGTY_STREAM;
5592 break;
5595 destination->cbSize = source->size;
5597 currentReturnStruct->mtime = {0}; TODO
5598 currentReturnStruct->ctime = {0};
5599 currentReturnStruct->atime = {0};
5601 destination->grfMode = 0;
5602 destination->grfLocksSupported = 0;
5603 destination->clsid = source->clsid;
5604 destination->grfStateBits = 0;
5605 destination->reserved = 0;
5608 /******************************************************************************
5609 ** BlockChainStream implementation
5612 /* Read and save the index of all blocks in this stream. */
5613 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5615 ULONG next_sector, next_offset;
5616 HRESULT hr;
5617 struct BlockChainRun *last_run;
5619 if (This->indexCacheLen == 0)
5621 last_run = NULL;
5622 next_offset = 0;
5623 next_sector = BlockChainStream_GetHeadOfChain(This);
5625 else
5627 last_run = &This->indexCache[This->indexCacheLen-1];
5628 next_offset = last_run->lastOffset+1;
5629 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5630 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5631 &next_sector);
5632 if (FAILED(hr)) return hr;
5635 while (next_sector != BLOCK_END_OF_CHAIN)
5637 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5639 /* Add the current block to the cache. */
5640 if (This->indexCacheSize == 0)
5642 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5643 if (!This->indexCache) return E_OUTOFMEMORY;
5644 This->indexCacheSize = 16;
5646 else if (This->indexCacheSize == This->indexCacheLen)
5648 struct BlockChainRun *new_cache;
5649 ULONG new_size;
5651 new_size = This->indexCacheSize * 2;
5652 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5653 if (!new_cache) return E_OUTOFMEMORY;
5654 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5656 HeapFree(GetProcessHeap(), 0, This->indexCache);
5657 This->indexCache = new_cache;
5658 This->indexCacheSize = new_size;
5661 This->indexCacheLen++;
5662 last_run = &This->indexCache[This->indexCacheLen-1];
5663 last_run->firstSector = next_sector;
5664 last_run->firstOffset = next_offset;
5667 last_run->lastOffset = next_offset;
5669 /* Find the next block. */
5670 next_offset++;
5671 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5672 if (FAILED(hr)) return hr;
5675 if (This->indexCacheLen)
5677 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5678 This->numBlocks = last_run->lastOffset+1;
5680 else
5682 This->tailIndex = BLOCK_END_OF_CHAIN;
5683 This->numBlocks = 0;
5686 return S_OK;
5689 /* Locate the nth block in this stream. */
5690 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5692 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5693 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5695 if (offset >= This->numBlocks)
5696 return BLOCK_END_OF_CHAIN;
5698 while (min_run < max_run)
5700 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5701 if (offset < This->indexCache[run_to_check].firstOffset)
5703 max_offset = This->indexCache[run_to_check].firstOffset-1;
5704 max_run = run_to_check-1;
5706 else if (offset > This->indexCache[run_to_check].lastOffset)
5708 min_offset = This->indexCache[run_to_check].lastOffset+1;
5709 min_run = run_to_check+1;
5711 else
5712 /* Block is in this run. */
5713 min_run = max_run = run_to_check;
5716 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5719 BlockChainStream* BlockChainStream_Construct(
5720 StorageImpl* parentStorage,
5721 ULONG* headOfStreamPlaceHolder,
5722 DirRef dirEntry)
5724 BlockChainStream* newStream;
5726 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5728 newStream->parentStorage = parentStorage;
5729 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5730 newStream->ownerDirEntry = dirEntry;
5731 newStream->indexCache = NULL;
5732 newStream->indexCacheLen = 0;
5733 newStream->indexCacheSize = 0;
5735 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
5737 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
5738 HeapFree(GetProcessHeap(), 0, newStream);
5739 return NULL;
5742 return newStream;
5745 void BlockChainStream_Destroy(BlockChainStream* This)
5747 if (This)
5748 HeapFree(GetProcessHeap(), 0, This->indexCache);
5749 HeapFree(GetProcessHeap(), 0, This);
5752 /******************************************************************************
5753 * BlockChainStream_GetHeadOfChain
5755 * Returns the head of this stream chain.
5756 * Some special chains don't have directory entries, their heads are kept in
5757 * This->headOfStreamPlaceHolder.
5760 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5762 DirEntry chainEntry;
5763 HRESULT hr;
5765 if (This->headOfStreamPlaceHolder != 0)
5766 return *(This->headOfStreamPlaceHolder);
5768 if (This->ownerDirEntry != DIRENTRY_NULL)
5770 hr = StorageImpl_ReadDirEntry(
5771 This->parentStorage,
5772 This->ownerDirEntry,
5773 &chainEntry);
5775 if (SUCCEEDED(hr))
5777 return chainEntry.startingBlock;
5781 return BLOCK_END_OF_CHAIN;
5784 /******************************************************************************
5785 * BlockChainStream_GetCount
5787 * Returns the number of blocks that comprises this chain.
5788 * This is not the size of the stream as the last block may not be full!
5790 * FIXME: Use the cache to get this information.
5792 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5794 ULONG blockIndex;
5795 ULONG count = 0;
5797 blockIndex = BlockChainStream_GetHeadOfChain(This);
5799 while (blockIndex != BLOCK_END_OF_CHAIN)
5801 count++;
5803 if(FAILED(StorageImpl_GetNextBlockInChain(
5804 This->parentStorage,
5805 blockIndex,
5806 &blockIndex)))
5807 return 0;
5810 return count;
5813 /******************************************************************************
5814 * BlockChainStream_ReadAt
5816 * Reads a specified number of bytes from this chain at the specified offset.
5817 * bytesRead may be NULL.
5818 * Failure will be returned if the specified number of bytes has not been read.
5820 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5821 ULARGE_INTEGER offset,
5822 ULONG size,
5823 void* buffer,
5824 ULONG* bytesRead)
5826 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5827 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5828 ULONG bytesToReadInBuffer;
5829 ULONG blockIndex;
5830 BYTE* bufferWalker;
5832 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5835 * Find the first block in the stream that contains part of the buffer.
5837 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
5839 if (blockIndex == BLOCK_END_OF_CHAIN)
5840 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
5843 * Start reading the buffer.
5845 *bytesRead = 0;
5846 bufferWalker = buffer;
5848 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5850 ULARGE_INTEGER ulOffset;
5851 DWORD bytesReadAt;
5853 * Calculate how many bytes we can copy from this big block.
5855 bytesToReadInBuffer =
5856 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5858 TRACE("block %i\n",blockIndex);
5859 ulOffset.u.HighPart = 0;
5860 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
5861 offsetInBlock;
5863 StorageImpl_ReadAt(This->parentStorage,
5864 ulOffset,
5865 bufferWalker,
5866 bytesToReadInBuffer,
5867 &bytesReadAt);
5869 * Step to the next big block.
5871 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5872 return STG_E_DOCFILECORRUPT;
5874 bufferWalker += bytesReadAt;
5875 size -= bytesReadAt;
5876 *bytesRead += bytesReadAt;
5877 offsetInBlock = 0; /* There is no offset on the next block */
5879 if (bytesToReadInBuffer != bytesReadAt)
5880 break;
5883 return (size == 0) ? S_OK : STG_E_READFAULT;
5886 /******************************************************************************
5887 * BlockChainStream_WriteAt
5889 * Writes the specified number of bytes to this chain at the specified offset.
5890 * Will fail if not all specified number of bytes have been written.
5892 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5893 ULARGE_INTEGER offset,
5894 ULONG size,
5895 const void* buffer,
5896 ULONG* bytesWritten)
5898 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5899 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5900 ULONG bytesToWrite;
5901 ULONG blockIndex;
5902 const BYTE* bufferWalker;
5905 * Find the first block in the stream that contains part of the buffer.
5907 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
5909 /* BlockChainStream_SetSize should have already been called to ensure we have
5910 * enough blocks in the chain to write into */
5911 if (blockIndex == BLOCK_END_OF_CHAIN)
5913 ERR("not enough blocks in chain to write data\n");
5914 return STG_E_DOCFILECORRUPT;
5917 *bytesWritten = 0;
5918 bufferWalker = buffer;
5920 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5922 ULARGE_INTEGER ulOffset;
5923 DWORD bytesWrittenAt;
5925 * Calculate how many bytes we can copy from this big block.
5927 bytesToWrite =
5928 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5930 TRACE("block %i\n",blockIndex);
5931 ulOffset.u.HighPart = 0;
5932 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
5933 offsetInBlock;
5935 StorageImpl_WriteAt(This->parentStorage,
5936 ulOffset,
5937 bufferWalker,
5938 bytesToWrite,
5939 &bytesWrittenAt);
5942 * Step to the next big block.
5944 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5945 &blockIndex)))
5946 return STG_E_DOCFILECORRUPT;
5948 bufferWalker += bytesWrittenAt;
5949 size -= bytesWrittenAt;
5950 *bytesWritten += bytesWrittenAt;
5951 offsetInBlock = 0; /* There is no offset on the next block */
5953 if (bytesWrittenAt != bytesToWrite)
5954 break;
5957 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5960 /******************************************************************************
5961 * BlockChainStream_Shrink
5963 * Shrinks this chain in the big block depot.
5965 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
5966 ULARGE_INTEGER newSize)
5968 ULONG blockIndex;
5969 ULONG numBlocks;
5972 * Figure out how many blocks are needed to contain the new size
5974 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5976 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5977 numBlocks++;
5979 if (numBlocks)
5982 * Go to the new end of chain
5984 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
5986 /* Mark the new end of chain */
5987 StorageImpl_SetNextBlockInChain(
5988 This->parentStorage,
5989 blockIndex,
5990 BLOCK_END_OF_CHAIN);
5992 This->tailIndex = blockIndex;
5994 else
5996 if (This->headOfStreamPlaceHolder != 0)
5998 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6000 else
6002 DirEntry chainEntry;
6003 assert(This->ownerDirEntry != DIRENTRY_NULL);
6005 StorageImpl_ReadDirEntry(
6006 This->parentStorage,
6007 This->ownerDirEntry,
6008 &chainEntry);
6010 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6012 StorageImpl_WriteDirEntry(
6013 This->parentStorage,
6014 This->ownerDirEntry,
6015 &chainEntry);
6018 This->tailIndex = BLOCK_END_OF_CHAIN;
6021 This->numBlocks = numBlocks;
6024 * Mark the extra blocks as free
6026 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6028 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6029 StorageImpl_FreeBigBlock(This->parentStorage,
6030 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6031 if (last_run->lastOffset == last_run->firstOffset)
6032 This->indexCacheLen--;
6033 else
6034 last_run->lastOffset--;
6037 return TRUE;
6040 /******************************************************************************
6041 * BlockChainStream_Enlarge
6043 * Grows this chain in the big block depot.
6045 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6046 ULARGE_INTEGER newSize)
6048 ULONG blockIndex, currentBlock;
6049 ULONG newNumBlocks;
6050 ULONG oldNumBlocks = 0;
6052 blockIndex = BlockChainStream_GetHeadOfChain(This);
6055 * Empty chain. Create the head.
6057 if (blockIndex == BLOCK_END_OF_CHAIN)
6059 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6060 StorageImpl_SetNextBlockInChain(This->parentStorage,
6061 blockIndex,
6062 BLOCK_END_OF_CHAIN);
6064 if (This->headOfStreamPlaceHolder != 0)
6066 *(This->headOfStreamPlaceHolder) = blockIndex;
6068 else
6070 DirEntry chainEntry;
6071 assert(This->ownerDirEntry != DIRENTRY_NULL);
6073 StorageImpl_ReadDirEntry(
6074 This->parentStorage,
6075 This->ownerDirEntry,
6076 &chainEntry);
6078 chainEntry.startingBlock = blockIndex;
6080 StorageImpl_WriteDirEntry(
6081 This->parentStorage,
6082 This->ownerDirEntry,
6083 &chainEntry);
6086 This->tailIndex = blockIndex;
6087 This->numBlocks = 1;
6091 * Figure out how many blocks are needed to contain this stream
6093 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6095 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6096 newNumBlocks++;
6099 * Go to the current end of chain
6101 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6103 currentBlock = blockIndex;
6105 while (blockIndex != BLOCK_END_OF_CHAIN)
6107 This->numBlocks++;
6108 currentBlock = blockIndex;
6110 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6111 &blockIndex)))
6112 return FALSE;
6115 This->tailIndex = currentBlock;
6118 currentBlock = This->tailIndex;
6119 oldNumBlocks = This->numBlocks;
6122 * Add new blocks to the chain
6124 if (oldNumBlocks < newNumBlocks)
6126 while (oldNumBlocks < newNumBlocks)
6128 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6130 StorageImpl_SetNextBlockInChain(
6131 This->parentStorage,
6132 currentBlock,
6133 blockIndex);
6135 StorageImpl_SetNextBlockInChain(
6136 This->parentStorage,
6137 blockIndex,
6138 BLOCK_END_OF_CHAIN);
6140 currentBlock = blockIndex;
6141 oldNumBlocks++;
6144 This->tailIndex = blockIndex;
6145 This->numBlocks = newNumBlocks;
6148 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6149 return FALSE;
6151 return TRUE;
6154 /******************************************************************************
6155 * BlockChainStream_SetSize
6157 * Sets the size of this stream. The big block depot will be updated.
6158 * The file will grow if we grow the chain.
6160 * TODO: Free the actual blocks in the file when we shrink the chain.
6161 * Currently, the blocks are still in the file. So the file size
6162 * doesn't shrink even if we shrink streams.
6164 BOOL BlockChainStream_SetSize(
6165 BlockChainStream* This,
6166 ULARGE_INTEGER newSize)
6168 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6170 if (newSize.u.LowPart == size.u.LowPart)
6171 return TRUE;
6173 if (newSize.u.LowPart < size.u.LowPart)
6175 BlockChainStream_Shrink(This, newSize);
6177 else
6179 BlockChainStream_Enlarge(This, newSize);
6182 return TRUE;
6185 /******************************************************************************
6186 * BlockChainStream_GetSize
6188 * Returns the size of this chain.
6189 * Will return the block count if this chain doesn't have a directory entry.
6191 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6193 DirEntry chainEntry;
6195 if(This->headOfStreamPlaceHolder == NULL)
6198 * This chain has a directory entry so use the size value from there.
6200 StorageImpl_ReadDirEntry(
6201 This->parentStorage,
6202 This->ownerDirEntry,
6203 &chainEntry);
6205 return chainEntry.size;
6207 else
6210 * this chain is a chain that does not have a directory entry, figure out the
6211 * size by making the product number of used blocks times the
6212 * size of them
6214 ULARGE_INTEGER result;
6215 result.u.HighPart = 0;
6217 result.u.LowPart =
6218 BlockChainStream_GetCount(This) *
6219 This->parentStorage->bigBlockSize;
6221 return result;
6225 /******************************************************************************
6226 ** SmallBlockChainStream implementation
6229 SmallBlockChainStream* SmallBlockChainStream_Construct(
6230 StorageImpl* parentStorage,
6231 ULONG* headOfStreamPlaceHolder,
6232 DirRef dirEntry)
6234 SmallBlockChainStream* newStream;
6236 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6238 newStream->parentStorage = parentStorage;
6239 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6240 newStream->ownerDirEntry = dirEntry;
6242 return newStream;
6245 void SmallBlockChainStream_Destroy(
6246 SmallBlockChainStream* This)
6248 HeapFree(GetProcessHeap(), 0, This);
6251 /******************************************************************************
6252 * SmallBlockChainStream_GetHeadOfChain
6254 * Returns the head of this chain of small blocks.
6256 static ULONG SmallBlockChainStream_GetHeadOfChain(
6257 SmallBlockChainStream* This)
6259 DirEntry chainEntry;
6260 HRESULT hr;
6262 if (This->headOfStreamPlaceHolder != NULL)
6263 return *(This->headOfStreamPlaceHolder);
6265 if (This->ownerDirEntry)
6267 hr = StorageImpl_ReadDirEntry(
6268 This->parentStorage,
6269 This->ownerDirEntry,
6270 &chainEntry);
6272 if (SUCCEEDED(hr))
6274 return chainEntry.startingBlock;
6279 return BLOCK_END_OF_CHAIN;
6282 /******************************************************************************
6283 * SmallBlockChainStream_GetNextBlockInChain
6285 * Returns the index of the next small block in this chain.
6287 * Return Values:
6288 * - BLOCK_END_OF_CHAIN: end of this chain
6289 * - BLOCK_UNUSED: small block 'blockIndex' is free
6291 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6292 SmallBlockChainStream* This,
6293 ULONG blockIndex,
6294 ULONG* nextBlockInChain)
6296 ULARGE_INTEGER offsetOfBlockInDepot;
6297 DWORD buffer;
6298 ULONG bytesRead;
6299 HRESULT res;
6301 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6303 offsetOfBlockInDepot.u.HighPart = 0;
6304 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6307 * Read those bytes in the buffer from the small block file.
6309 res = BlockChainStream_ReadAt(
6310 This->parentStorage->smallBlockDepotChain,
6311 offsetOfBlockInDepot,
6312 sizeof(DWORD),
6313 &buffer,
6314 &bytesRead);
6316 if (SUCCEEDED(res))
6318 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6319 return S_OK;
6322 return res;
6325 /******************************************************************************
6326 * SmallBlockChainStream_SetNextBlockInChain
6328 * Writes the index of the next block of the specified block in the small
6329 * block depot.
6330 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6331 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6333 static void SmallBlockChainStream_SetNextBlockInChain(
6334 SmallBlockChainStream* This,
6335 ULONG blockIndex,
6336 ULONG nextBlock)
6338 ULARGE_INTEGER offsetOfBlockInDepot;
6339 DWORD buffer;
6340 ULONG bytesWritten;
6342 offsetOfBlockInDepot.u.HighPart = 0;
6343 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6345 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6348 * Read those bytes in the buffer from the small block file.
6350 BlockChainStream_WriteAt(
6351 This->parentStorage->smallBlockDepotChain,
6352 offsetOfBlockInDepot,
6353 sizeof(DWORD),
6354 &buffer,
6355 &bytesWritten);
6358 /******************************************************************************
6359 * SmallBlockChainStream_FreeBlock
6361 * Flag small block 'blockIndex' as free in the small block depot.
6363 static void SmallBlockChainStream_FreeBlock(
6364 SmallBlockChainStream* This,
6365 ULONG blockIndex)
6367 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6370 /******************************************************************************
6371 * SmallBlockChainStream_GetNextFreeBlock
6373 * Returns the index of a free small block. The small block depot will be
6374 * enlarged if necessary. The small block chain will also be enlarged if
6375 * necessary.
6377 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6378 SmallBlockChainStream* This)
6380 ULARGE_INTEGER offsetOfBlockInDepot;
6381 DWORD buffer;
6382 ULONG bytesRead;
6383 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6384 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6385 HRESULT res = S_OK;
6386 ULONG smallBlocksPerBigBlock;
6388 offsetOfBlockInDepot.u.HighPart = 0;
6391 * Scan the small block depot for a free block
6393 while (nextBlockIndex != BLOCK_UNUSED)
6395 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6397 res = BlockChainStream_ReadAt(
6398 This->parentStorage->smallBlockDepotChain,
6399 offsetOfBlockInDepot,
6400 sizeof(DWORD),
6401 &buffer,
6402 &bytesRead);
6405 * If we run out of space for the small block depot, enlarge it
6407 if (SUCCEEDED(res))
6409 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6411 if (nextBlockIndex != BLOCK_UNUSED)
6412 blockIndex++;
6414 else
6416 ULONG count =
6417 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6419 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6420 ULARGE_INTEGER newSize, offset;
6421 ULONG bytesWritten;
6423 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6424 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6427 * Initialize all the small blocks to free
6429 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6430 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6431 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6432 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6434 StorageImpl_SaveFileHeader(This->parentStorage);
6438 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6440 smallBlocksPerBigBlock =
6441 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6444 * Verify if we have to allocate big blocks to contain small blocks
6446 if (blockIndex % smallBlocksPerBigBlock == 0)
6448 DirEntry rootEntry;
6449 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6450 ULARGE_INTEGER old_size, size_required;
6452 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6454 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6456 if (size_required.QuadPart > old_size.QuadPart)
6458 BlockChainStream_SetSize(
6459 This->parentStorage->smallBlockRootChain,
6460 size_required);
6462 StorageImpl_ReadDirEntry(
6463 This->parentStorage,
6464 This->parentStorage->base.storageDirEntry,
6465 &rootEntry);
6467 rootEntry.size = size_required;
6469 StorageImpl_WriteDirEntry(
6470 This->parentStorage,
6471 This->parentStorage->base.storageDirEntry,
6472 &rootEntry);
6476 return blockIndex;
6479 /******************************************************************************
6480 * SmallBlockChainStream_ReadAt
6482 * Reads a specified number of bytes from this chain at the specified offset.
6483 * bytesRead may be NULL.
6484 * Failure will be returned if the specified number of bytes has not been read.
6486 HRESULT SmallBlockChainStream_ReadAt(
6487 SmallBlockChainStream* This,
6488 ULARGE_INTEGER offset,
6489 ULONG size,
6490 void* buffer,
6491 ULONG* bytesRead)
6493 HRESULT rc = S_OK;
6494 ULARGE_INTEGER offsetInBigBlockFile;
6495 ULONG blockNoInSequence =
6496 offset.u.LowPart / This->parentStorage->smallBlockSize;
6498 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6499 ULONG bytesToReadInBuffer;
6500 ULONG blockIndex;
6501 ULONG bytesReadFromBigBlockFile;
6502 BYTE* bufferWalker;
6505 * This should never happen on a small block file.
6507 assert(offset.u.HighPart==0);
6510 * Find the first block in the stream that contains part of the buffer.
6512 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6514 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6516 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6517 if(FAILED(rc))
6518 return rc;
6519 blockNoInSequence--;
6523 * Start reading the buffer.
6525 *bytesRead = 0;
6526 bufferWalker = buffer;
6528 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6531 * Calculate how many bytes we can copy from this small block.
6533 bytesToReadInBuffer =
6534 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6537 * Calculate the offset of the small block in the small block file.
6539 offsetInBigBlockFile.u.HighPart = 0;
6540 offsetInBigBlockFile.u.LowPart =
6541 blockIndex * This->parentStorage->smallBlockSize;
6543 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6546 * Read those bytes in the buffer from the small block file.
6547 * The small block has already been identified so it shouldn't fail
6548 * unless the file is corrupt.
6550 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6551 offsetInBigBlockFile,
6552 bytesToReadInBuffer,
6553 bufferWalker,
6554 &bytesReadFromBigBlockFile);
6556 if (FAILED(rc))
6557 return rc;
6560 * Step to the next big block.
6562 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6563 if(FAILED(rc))
6564 return STG_E_DOCFILECORRUPT;
6566 bufferWalker += bytesReadFromBigBlockFile;
6567 size -= bytesReadFromBigBlockFile;
6568 *bytesRead += bytesReadFromBigBlockFile;
6569 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6572 return (size == 0) ? S_OK : STG_E_READFAULT;
6575 /******************************************************************************
6576 * SmallBlockChainStream_WriteAt
6578 * Writes the specified number of bytes to this chain at the specified offset.
6579 * Will fail if not all specified number of bytes have been written.
6581 HRESULT SmallBlockChainStream_WriteAt(
6582 SmallBlockChainStream* This,
6583 ULARGE_INTEGER offset,
6584 ULONG size,
6585 const void* buffer,
6586 ULONG* bytesWritten)
6588 ULARGE_INTEGER offsetInBigBlockFile;
6589 ULONG blockNoInSequence =
6590 offset.u.LowPart / This->parentStorage->smallBlockSize;
6592 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6593 ULONG bytesToWriteInBuffer;
6594 ULONG blockIndex;
6595 ULONG bytesWrittenToBigBlockFile;
6596 const BYTE* bufferWalker;
6597 HRESULT res;
6600 * This should never happen on a small block file.
6602 assert(offset.u.HighPart==0);
6605 * Find the first block in the stream that contains part of the buffer.
6607 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6609 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6611 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6612 return STG_E_DOCFILECORRUPT;
6613 blockNoInSequence--;
6617 * Start writing the buffer.
6619 *bytesWritten = 0;
6620 bufferWalker = buffer;
6621 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6624 * Calculate how many bytes we can copy to this small block.
6626 bytesToWriteInBuffer =
6627 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6630 * Calculate the offset of the small block in the small block file.
6632 offsetInBigBlockFile.u.HighPart = 0;
6633 offsetInBigBlockFile.u.LowPart =
6634 blockIndex * This->parentStorage->smallBlockSize;
6636 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6639 * Write those bytes in the buffer to the small block file.
6641 res = BlockChainStream_WriteAt(
6642 This->parentStorage->smallBlockRootChain,
6643 offsetInBigBlockFile,
6644 bytesToWriteInBuffer,
6645 bufferWalker,
6646 &bytesWrittenToBigBlockFile);
6647 if (FAILED(res))
6648 return res;
6651 * Step to the next big block.
6653 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6654 &blockIndex)))
6655 return FALSE;
6656 bufferWalker += bytesWrittenToBigBlockFile;
6657 size -= bytesWrittenToBigBlockFile;
6658 *bytesWritten += bytesWrittenToBigBlockFile;
6659 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6662 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6665 /******************************************************************************
6666 * SmallBlockChainStream_Shrink
6668 * Shrinks this chain in the small block depot.
6670 static BOOL SmallBlockChainStream_Shrink(
6671 SmallBlockChainStream* This,
6672 ULARGE_INTEGER newSize)
6674 ULONG blockIndex, extraBlock;
6675 ULONG numBlocks;
6676 ULONG count = 0;
6678 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6680 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6681 numBlocks++;
6683 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6686 * Go to the new end of chain
6688 while (count < numBlocks)
6690 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6691 &blockIndex)))
6692 return FALSE;
6693 count++;
6697 * If the count is 0, we have a special case, the head of the chain was
6698 * just freed.
6700 if (count == 0)
6702 DirEntry chainEntry;
6704 StorageImpl_ReadDirEntry(This->parentStorage,
6705 This->ownerDirEntry,
6706 &chainEntry);
6708 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6710 StorageImpl_WriteDirEntry(This->parentStorage,
6711 This->ownerDirEntry,
6712 &chainEntry);
6715 * We start freeing the chain at the head block.
6717 extraBlock = blockIndex;
6719 else
6721 /* Get the next block before marking the new end */
6722 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6723 &extraBlock)))
6724 return FALSE;
6726 /* Mark the new end of chain */
6727 SmallBlockChainStream_SetNextBlockInChain(
6728 This,
6729 blockIndex,
6730 BLOCK_END_OF_CHAIN);
6734 * Mark the extra blocks as free
6736 while (extraBlock != BLOCK_END_OF_CHAIN)
6738 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6739 &blockIndex)))
6740 return FALSE;
6741 SmallBlockChainStream_FreeBlock(This, extraBlock);
6742 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
6743 extraBlock = blockIndex;
6746 return TRUE;
6749 /******************************************************************************
6750 * SmallBlockChainStream_Enlarge
6752 * Grows this chain in the small block depot.
6754 static BOOL SmallBlockChainStream_Enlarge(
6755 SmallBlockChainStream* This,
6756 ULARGE_INTEGER newSize)
6758 ULONG blockIndex, currentBlock;
6759 ULONG newNumBlocks;
6760 ULONG oldNumBlocks = 0;
6762 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6765 * Empty chain. Create the head.
6767 if (blockIndex == BLOCK_END_OF_CHAIN)
6769 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6770 SmallBlockChainStream_SetNextBlockInChain(
6771 This,
6772 blockIndex,
6773 BLOCK_END_OF_CHAIN);
6775 if (This->headOfStreamPlaceHolder != NULL)
6777 *(This->headOfStreamPlaceHolder) = blockIndex;
6779 else
6781 DirEntry chainEntry;
6783 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6784 &chainEntry);
6786 chainEntry.startingBlock = blockIndex;
6788 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6789 &chainEntry);
6793 currentBlock = blockIndex;
6796 * Figure out how many blocks are needed to contain this stream
6798 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6800 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6801 newNumBlocks++;
6804 * Go to the current end of chain
6806 while (blockIndex != BLOCK_END_OF_CHAIN)
6808 oldNumBlocks++;
6809 currentBlock = blockIndex;
6810 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6811 return FALSE;
6815 * Add new blocks to the chain
6817 while (oldNumBlocks < newNumBlocks)
6819 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6820 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6822 SmallBlockChainStream_SetNextBlockInChain(
6823 This,
6824 blockIndex,
6825 BLOCK_END_OF_CHAIN);
6827 currentBlock = blockIndex;
6828 oldNumBlocks++;
6831 return TRUE;
6834 /******************************************************************************
6835 * SmallBlockChainStream_SetSize
6837 * Sets the size of this stream.
6838 * The file will grow if we grow the chain.
6840 * TODO: Free the actual blocks in the file when we shrink the chain.
6841 * Currently, the blocks are still in the file. So the file size
6842 * doesn't shrink even if we shrink streams.
6844 BOOL SmallBlockChainStream_SetSize(
6845 SmallBlockChainStream* This,
6846 ULARGE_INTEGER newSize)
6848 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6850 if (newSize.u.LowPart == size.u.LowPart)
6851 return TRUE;
6853 if (newSize.u.LowPart < size.u.LowPart)
6855 SmallBlockChainStream_Shrink(This, newSize);
6857 else
6859 SmallBlockChainStream_Enlarge(This, newSize);
6862 return TRUE;
6865 /******************************************************************************
6866 * SmallBlockChainStream_GetCount
6868 * Returns the number of small blocks that comprises this chain.
6869 * This is not the size of the stream as the last block may not be full!
6872 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6874 ULONG blockIndex;
6875 ULONG count = 0;
6877 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6879 while(blockIndex != BLOCK_END_OF_CHAIN)
6881 count++;
6883 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6884 blockIndex, &blockIndex)))
6885 return 0;
6888 return count;
6891 /******************************************************************************
6892 * SmallBlockChainStream_GetSize
6894 * Returns the size of this chain.
6896 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6898 DirEntry chainEntry;
6900 if(This->headOfStreamPlaceHolder != NULL)
6902 ULARGE_INTEGER result;
6903 result.u.HighPart = 0;
6905 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
6906 This->parentStorage->smallBlockSize;
6908 return result;
6911 StorageImpl_ReadDirEntry(
6912 This->parentStorage,
6913 This->ownerDirEntry,
6914 &chainEntry);
6916 return chainEntry.size;
6919 static HRESULT create_storagefile(
6920 LPCOLESTR pwcsName,
6921 DWORD grfMode,
6922 DWORD grfAttrs,
6923 STGOPTIONS* pStgOptions,
6924 REFIID riid,
6925 void** ppstgOpen)
6927 StorageBaseImpl* newStorage = 0;
6928 HANDLE hFile = INVALID_HANDLE_VALUE;
6929 HRESULT hr = STG_E_INVALIDFLAG;
6930 DWORD shareMode;
6931 DWORD accessMode;
6932 DWORD creationMode;
6933 DWORD fileAttributes;
6934 WCHAR tempFileName[MAX_PATH];
6936 if (ppstgOpen == 0)
6937 return STG_E_INVALIDPOINTER;
6939 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
6940 return STG_E_INVALIDPARAMETER;
6942 /* if no share mode given then DENY_NONE is the default */
6943 if (STGM_SHARE_MODE(grfMode) == 0)
6944 grfMode |= STGM_SHARE_DENY_NONE;
6946 if ( FAILED( validateSTGM(grfMode) ))
6947 goto end;
6949 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6950 switch(STGM_ACCESS_MODE(grfMode))
6952 case STGM_WRITE:
6953 case STGM_READWRITE:
6954 break;
6955 default:
6956 goto end;
6959 /* in direct mode, can only use SHARE_EXCLUSIVE */
6960 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
6961 goto end;
6963 /* but in transacted mode, any share mode is valid */
6966 * Generate a unique name.
6968 if (pwcsName == 0)
6970 WCHAR tempPath[MAX_PATH];
6971 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
6973 memset(tempPath, 0, sizeof(tempPath));
6974 memset(tempFileName, 0, sizeof(tempFileName));
6976 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
6977 tempPath[0] = '.';
6979 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
6980 pwcsName = tempFileName;
6981 else
6983 hr = STG_E_INSUFFICIENTMEMORY;
6984 goto end;
6987 creationMode = TRUNCATE_EXISTING;
6989 else
6991 creationMode = GetCreationModeFromSTGM(grfMode);
6995 * Interpret the STGM value grfMode
6997 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6998 accessMode = GetAccessModeFromSTGM(grfMode);
7000 if (grfMode & STGM_DELETEONRELEASE)
7001 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7002 else
7003 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7005 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7007 static int fixme;
7008 if (!fixme++)
7009 FIXME("Storage share mode not implemented.\n");
7012 *ppstgOpen = 0;
7014 hFile = CreateFileW(pwcsName,
7015 accessMode,
7016 shareMode,
7017 NULL,
7018 creationMode,
7019 fileAttributes,
7022 if (hFile == INVALID_HANDLE_VALUE)
7024 if(GetLastError() == ERROR_FILE_EXISTS)
7025 hr = STG_E_FILEALREADYEXISTS;
7026 else
7027 hr = E_FAIL;
7028 goto end;
7032 * Allocate and initialize the new IStorage32object.
7034 hr = Storage_Construct(
7035 hFile,
7036 pwcsName,
7037 NULL,
7038 grfMode,
7039 TRUE,
7040 TRUE,
7041 pStgOptions->ulSectorSize,
7042 &newStorage);
7044 if (FAILED(hr))
7046 goto end;
7049 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
7051 IStorage_Release((IStorage*)newStorage);
7053 end:
7054 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7056 return hr;
7059 /******************************************************************************
7060 * StgCreateDocfile [OLE32.@]
7061 * Creates a new compound file storage object
7063 * PARAMS
7064 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7065 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7066 * reserved [ ?] unused?, usually 0
7067 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7069 * RETURNS
7070 * S_OK if the file was successfully created
7071 * some STG_E_ value if error
7072 * NOTES
7073 * if pwcsName is NULL, create file with new unique name
7074 * the function can returns
7075 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7076 * (unrealized now)
7078 HRESULT WINAPI StgCreateDocfile(
7079 LPCOLESTR pwcsName,
7080 DWORD grfMode,
7081 DWORD reserved,
7082 IStorage **ppstgOpen)
7084 STGOPTIONS stgoptions = {1, 0, 512};
7086 TRACE("(%s, %x, %d, %p)\n",
7087 debugstr_w(pwcsName), grfMode,
7088 reserved, ppstgOpen);
7090 if (ppstgOpen == 0)
7091 return STG_E_INVALIDPOINTER;
7092 if (reserved != 0)
7093 return STG_E_INVALIDPARAMETER;
7095 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7098 /******************************************************************************
7099 * StgCreateStorageEx [OLE32.@]
7101 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7103 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7104 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7106 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7108 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7109 return STG_E_INVALIDPARAMETER;
7112 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7114 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7115 return STG_E_INVALIDPARAMETER;
7118 if (stgfmt == STGFMT_FILE)
7120 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7121 return STG_E_INVALIDPARAMETER;
7124 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7126 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7130 ERR("Invalid stgfmt argument\n");
7131 return STG_E_INVALIDPARAMETER;
7134 /******************************************************************************
7135 * StgCreatePropSetStg [OLE32.@]
7137 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7138 IPropertySetStorage **ppPropSetStg)
7140 HRESULT hr;
7142 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
7143 if (reserved)
7144 hr = STG_E_INVALIDPARAMETER;
7145 else
7146 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
7147 (void**)ppPropSetStg);
7148 return hr;
7151 /******************************************************************************
7152 * StgOpenStorageEx [OLE32.@]
7154 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7156 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7157 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7159 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7161 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7162 return STG_E_INVALIDPARAMETER;
7165 switch (stgfmt)
7167 case STGFMT_FILE:
7168 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7169 return STG_E_INVALIDPARAMETER;
7171 case STGFMT_STORAGE:
7172 break;
7174 case STGFMT_DOCFILE:
7175 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7177 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7178 return STG_E_INVALIDPARAMETER;
7180 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7181 break;
7183 case STGFMT_ANY:
7184 WARN("STGFMT_ANY assuming storage\n");
7185 break;
7187 default:
7188 return STG_E_INVALIDPARAMETER;
7191 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7195 /******************************************************************************
7196 * StgOpenStorage [OLE32.@]
7198 HRESULT WINAPI StgOpenStorage(
7199 const OLECHAR *pwcsName,
7200 IStorage *pstgPriority,
7201 DWORD grfMode,
7202 SNB snbExclude,
7203 DWORD reserved,
7204 IStorage **ppstgOpen)
7206 StorageBaseImpl* newStorage = 0;
7207 HRESULT hr = S_OK;
7208 HANDLE hFile = 0;
7209 DWORD shareMode;
7210 DWORD accessMode;
7212 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7213 debugstr_w(pwcsName), pstgPriority, grfMode,
7214 snbExclude, reserved, ppstgOpen);
7216 if (pwcsName == 0)
7218 hr = STG_E_INVALIDNAME;
7219 goto end;
7222 if (ppstgOpen == 0)
7224 hr = STG_E_INVALIDPOINTER;
7225 goto end;
7228 if (reserved)
7230 hr = STG_E_INVALIDPARAMETER;
7231 goto end;
7234 if (grfMode & STGM_PRIORITY)
7236 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7237 return STG_E_INVALIDFLAG;
7238 if (grfMode & STGM_DELETEONRELEASE)
7239 return STG_E_INVALIDFUNCTION;
7240 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7241 return STG_E_INVALIDFLAG;
7242 grfMode &= ~0xf0; /* remove the existing sharing mode */
7243 grfMode |= STGM_SHARE_DENY_NONE;
7245 /* STGM_PRIORITY stops other IStorage objects on the same file from
7246 * committing until the STGM_PRIORITY IStorage is closed. it also
7247 * stops non-transacted mode StgOpenStorage calls with write access from
7248 * succeeding. obviously, both of these cannot be achieved through just
7249 * file share flags */
7250 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7254 * Validate the sharing mode
7256 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7257 switch(STGM_SHARE_MODE(grfMode))
7259 case STGM_SHARE_EXCLUSIVE:
7260 case STGM_SHARE_DENY_WRITE:
7261 break;
7262 default:
7263 hr = STG_E_INVALIDFLAG;
7264 goto end;
7267 if ( FAILED( validateSTGM(grfMode) ) ||
7268 (grfMode&STGM_CREATE))
7270 hr = STG_E_INVALIDFLAG;
7271 goto end;
7274 /* shared reading requires transacted mode */
7275 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7276 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7277 !(grfMode&STGM_TRANSACTED) )
7279 hr = STG_E_INVALIDFLAG;
7280 goto end;
7284 * Interpret the STGM value grfMode
7286 shareMode = GetShareModeFromSTGM(grfMode);
7287 accessMode = GetAccessModeFromSTGM(grfMode);
7289 *ppstgOpen = 0;
7291 hFile = CreateFileW( pwcsName,
7292 accessMode,
7293 shareMode,
7294 NULL,
7295 OPEN_EXISTING,
7296 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7299 if (hFile==INVALID_HANDLE_VALUE)
7301 DWORD last_error = GetLastError();
7303 hr = E_FAIL;
7305 switch (last_error)
7307 case ERROR_FILE_NOT_FOUND:
7308 hr = STG_E_FILENOTFOUND;
7309 break;
7311 case ERROR_PATH_NOT_FOUND:
7312 hr = STG_E_PATHNOTFOUND;
7313 break;
7315 case ERROR_ACCESS_DENIED:
7316 case ERROR_WRITE_PROTECT:
7317 hr = STG_E_ACCESSDENIED;
7318 break;
7320 case ERROR_SHARING_VIOLATION:
7321 hr = STG_E_SHAREVIOLATION;
7322 break;
7324 default:
7325 hr = E_FAIL;
7328 goto end;
7332 * Refuse to open the file if it's too small to be a structured storage file
7333 * FIXME: verify the file when reading instead of here
7335 if (GetFileSize(hFile, NULL) < 0x100)
7337 CloseHandle(hFile);
7338 hr = STG_E_FILEALREADYEXISTS;
7339 goto end;
7343 * Allocate and initialize the new IStorage32object.
7345 hr = Storage_Construct(
7346 hFile,
7347 pwcsName,
7348 NULL,
7349 grfMode,
7350 TRUE,
7351 FALSE,
7352 512,
7353 &newStorage);
7355 if (FAILED(hr))
7358 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7360 if(hr == STG_E_INVALIDHEADER)
7361 hr = STG_E_FILEALREADYEXISTS;
7362 goto end;
7366 * Get an "out" pointer for the caller.
7368 *ppstgOpen = (IStorage*)newStorage;
7370 end:
7371 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7372 return hr;
7375 /******************************************************************************
7376 * StgCreateDocfileOnILockBytes [OLE32.@]
7378 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7379 ILockBytes *plkbyt,
7380 DWORD grfMode,
7381 DWORD reserved,
7382 IStorage** ppstgOpen)
7384 StorageBaseImpl* newStorage = 0;
7385 HRESULT hr = S_OK;
7387 if ((ppstgOpen == 0) || (plkbyt == 0))
7388 return STG_E_INVALIDPOINTER;
7391 * Allocate and initialize the new IStorage object.
7393 hr = Storage_Construct(
7396 plkbyt,
7397 grfMode,
7398 FALSE,
7399 TRUE,
7400 512,
7401 &newStorage);
7403 if (FAILED(hr))
7405 return hr;
7409 * Get an "out" pointer for the caller.
7411 *ppstgOpen = (IStorage*)newStorage;
7413 return hr;
7416 /******************************************************************************
7417 * StgOpenStorageOnILockBytes [OLE32.@]
7419 HRESULT WINAPI StgOpenStorageOnILockBytes(
7420 ILockBytes *plkbyt,
7421 IStorage *pstgPriority,
7422 DWORD grfMode,
7423 SNB snbExclude,
7424 DWORD reserved,
7425 IStorage **ppstgOpen)
7427 StorageBaseImpl* newStorage = 0;
7428 HRESULT hr = S_OK;
7430 if ((plkbyt == 0) || (ppstgOpen == 0))
7431 return STG_E_INVALIDPOINTER;
7433 if ( FAILED( validateSTGM(grfMode) ))
7434 return STG_E_INVALIDFLAG;
7436 *ppstgOpen = 0;
7439 * Allocate and initialize the new IStorage object.
7441 hr = Storage_Construct(
7444 plkbyt,
7445 grfMode,
7446 FALSE,
7447 FALSE,
7448 512,
7449 &newStorage);
7451 if (FAILED(hr))
7453 return hr;
7457 * Get an "out" pointer for the caller.
7459 *ppstgOpen = (IStorage*)newStorage;
7461 return hr;
7464 /******************************************************************************
7465 * StgSetTimes [ole32.@]
7466 * StgSetTimes [OLE32.@]
7470 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7471 FILETIME const *patime, FILETIME const *pmtime)
7473 IStorage *stg = NULL;
7474 HRESULT r;
7476 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7478 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7479 0, 0, &stg);
7480 if( SUCCEEDED(r) )
7482 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7483 IStorage_Release(stg);
7486 return r;
7489 /******************************************************************************
7490 * StgIsStorageILockBytes [OLE32.@]
7492 * Determines if the ILockBytes contains a storage object.
7494 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7496 BYTE sig[8];
7497 ULARGE_INTEGER offset;
7499 offset.u.HighPart = 0;
7500 offset.u.LowPart = 0;
7502 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
7504 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
7505 return S_OK;
7507 return S_FALSE;
7510 /******************************************************************************
7511 * WriteClassStg [OLE32.@]
7513 * This method will store the specified CLSID in the specified storage object
7515 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7517 HRESULT hRes;
7519 if(!pStg)
7520 return E_INVALIDARG;
7522 if(!rclsid)
7523 return STG_E_INVALIDPOINTER;
7525 hRes = IStorage_SetClass(pStg, rclsid);
7527 return hRes;
7530 /***********************************************************************
7531 * ReadClassStg (OLE32.@)
7533 * This method reads the CLSID previously written to a storage object with
7534 * the WriteClassStg.
7536 * PARAMS
7537 * pstg [I] IStorage pointer
7538 * pclsid [O] Pointer to where the CLSID is written
7540 * RETURNS
7541 * Success: S_OK.
7542 * Failure: HRESULT code.
7544 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7546 STATSTG pstatstg;
7547 HRESULT hRes;
7549 TRACE("(%p, %p)\n", pstg, pclsid);
7551 if(!pstg || !pclsid)
7552 return E_INVALIDARG;
7555 * read a STATSTG structure (contains the clsid) from the storage
7557 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7559 if(SUCCEEDED(hRes))
7560 *pclsid=pstatstg.clsid;
7562 return hRes;
7565 /***********************************************************************
7566 * OleLoadFromStream (OLE32.@)
7568 * This function loads an object from stream
7570 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7572 CLSID clsid;
7573 HRESULT res;
7574 LPPERSISTSTREAM xstm;
7576 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7578 res=ReadClassStm(pStm,&clsid);
7579 if (FAILED(res))
7580 return res;
7581 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7582 if (FAILED(res))
7583 return res;
7584 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7585 if (FAILED(res)) {
7586 IUnknown_Release((IUnknown*)*ppvObj);
7587 return res;
7589 res=IPersistStream_Load(xstm,pStm);
7590 IPersistStream_Release(xstm);
7591 /* FIXME: all refcounts ok at this point? I think they should be:
7592 * pStm : unchanged
7593 * ppvObj : 1
7594 * xstm : 0 (released)
7596 return res;
7599 /***********************************************************************
7600 * OleSaveToStream (OLE32.@)
7602 * This function saves an object with the IPersistStream interface on it
7603 * to the specified stream.
7605 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7608 CLSID clsid;
7609 HRESULT res;
7611 TRACE("(%p,%p)\n",pPStm,pStm);
7613 res=IPersistStream_GetClassID(pPStm,&clsid);
7615 if (SUCCEEDED(res)){
7617 res=WriteClassStm(pStm,&clsid);
7619 if (SUCCEEDED(res))
7621 res=IPersistStream_Save(pPStm,pStm,TRUE);
7624 TRACE("Finished Save\n");
7625 return res;
7628 /****************************************************************************
7629 * This method validate a STGM parameter that can contain the values below
7631 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7632 * The stgm values contained in 0xffff0000 are bitmasks.
7634 * STGM_DIRECT 0x00000000
7635 * STGM_TRANSACTED 0x00010000
7636 * STGM_SIMPLE 0x08000000
7638 * STGM_READ 0x00000000
7639 * STGM_WRITE 0x00000001
7640 * STGM_READWRITE 0x00000002
7642 * STGM_SHARE_DENY_NONE 0x00000040
7643 * STGM_SHARE_DENY_READ 0x00000030
7644 * STGM_SHARE_DENY_WRITE 0x00000020
7645 * STGM_SHARE_EXCLUSIVE 0x00000010
7647 * STGM_PRIORITY 0x00040000
7648 * STGM_DELETEONRELEASE 0x04000000
7650 * STGM_CREATE 0x00001000
7651 * STGM_CONVERT 0x00020000
7652 * STGM_FAILIFTHERE 0x00000000
7654 * STGM_NOSCRATCH 0x00100000
7655 * STGM_NOSNAPSHOT 0x00200000
7657 static HRESULT validateSTGM(DWORD stgm)
7659 DWORD access = STGM_ACCESS_MODE(stgm);
7660 DWORD share = STGM_SHARE_MODE(stgm);
7661 DWORD create = STGM_CREATE_MODE(stgm);
7663 if (stgm&~STGM_KNOWN_FLAGS)
7665 ERR("unknown flags %08x\n", stgm);
7666 return E_FAIL;
7669 switch (access)
7671 case STGM_READ:
7672 case STGM_WRITE:
7673 case STGM_READWRITE:
7674 break;
7675 default:
7676 return E_FAIL;
7679 switch (share)
7681 case STGM_SHARE_DENY_NONE:
7682 case STGM_SHARE_DENY_READ:
7683 case STGM_SHARE_DENY_WRITE:
7684 case STGM_SHARE_EXCLUSIVE:
7685 break;
7686 default:
7687 return E_FAIL;
7690 switch (create)
7692 case STGM_CREATE:
7693 case STGM_FAILIFTHERE:
7694 break;
7695 default:
7696 return E_FAIL;
7700 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7702 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7703 return E_FAIL;
7706 * STGM_CREATE | STGM_CONVERT
7707 * if both are false, STGM_FAILIFTHERE is set to TRUE
7709 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7710 return E_FAIL;
7713 * STGM_NOSCRATCH requires STGM_TRANSACTED
7715 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7716 return E_FAIL;
7719 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7720 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7722 if ( (stgm & STGM_NOSNAPSHOT) &&
7723 (!(stgm & STGM_TRANSACTED) ||
7724 share == STGM_SHARE_EXCLUSIVE ||
7725 share == STGM_SHARE_DENY_WRITE) )
7726 return E_FAIL;
7728 return S_OK;
7731 /****************************************************************************
7732 * GetShareModeFromSTGM
7734 * This method will return a share mode flag from a STGM value.
7735 * The STGM value is assumed valid.
7737 static DWORD GetShareModeFromSTGM(DWORD stgm)
7739 switch (STGM_SHARE_MODE(stgm))
7741 case STGM_SHARE_DENY_NONE:
7742 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7743 case STGM_SHARE_DENY_READ:
7744 return FILE_SHARE_WRITE;
7745 case STGM_SHARE_DENY_WRITE:
7746 return FILE_SHARE_READ;
7747 case STGM_SHARE_EXCLUSIVE:
7748 return 0;
7750 ERR("Invalid share mode!\n");
7751 assert(0);
7752 return 0;
7755 /****************************************************************************
7756 * GetAccessModeFromSTGM
7758 * This method will return an access mode flag from a STGM value.
7759 * The STGM value is assumed valid.
7761 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7763 switch (STGM_ACCESS_MODE(stgm))
7765 case STGM_READ:
7766 return GENERIC_READ;
7767 case STGM_WRITE:
7768 case STGM_READWRITE:
7769 return GENERIC_READ | GENERIC_WRITE;
7771 ERR("Invalid access mode!\n");
7772 assert(0);
7773 return 0;
7776 /****************************************************************************
7777 * GetCreationModeFromSTGM
7779 * This method will return a creation mode flag from a STGM value.
7780 * The STGM value is assumed valid.
7782 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7784 switch(STGM_CREATE_MODE(stgm))
7786 case STGM_CREATE:
7787 return CREATE_ALWAYS;
7788 case STGM_CONVERT:
7789 FIXME("STGM_CONVERT not implemented!\n");
7790 return CREATE_NEW;
7791 case STGM_FAILIFTHERE:
7792 return CREATE_NEW;
7794 ERR("Invalid create mode!\n");
7795 assert(0);
7796 return 0;
7800 /*************************************************************************
7801 * OLECONVERT_LoadOLE10 [Internal]
7803 * Loads the OLE10 STREAM to memory
7805 * PARAMS
7806 * pOleStream [I] The OLESTREAM
7807 * pData [I] Data Structure for the OLESTREAM Data
7809 * RETURNS
7810 * Success: S_OK
7811 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7812 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7814 * NOTES
7815 * This function is used by OleConvertOLESTREAMToIStorage only.
7817 * Memory allocated for pData must be freed by the caller
7819 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7821 DWORD dwSize;
7822 HRESULT hRes = S_OK;
7823 int nTryCnt=0;
7824 int max_try = 6;
7826 pData->pData = NULL;
7827 pData->pstrOleObjFileName = NULL;
7829 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7831 /* Get the OleID */
7832 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7833 if(dwSize != sizeof(pData->dwOleID))
7835 hRes = CONVERT10_E_OLESTREAM_GET;
7837 else if(pData->dwOleID != OLESTREAM_ID)
7839 hRes = CONVERT10_E_OLESTREAM_FMT;
7841 else
7843 hRes = S_OK;
7844 break;
7848 if(hRes == S_OK)
7850 /* Get the TypeID... more info needed for this field */
7851 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7852 if(dwSize != sizeof(pData->dwTypeID))
7854 hRes = CONVERT10_E_OLESTREAM_GET;
7857 if(hRes == S_OK)
7859 if(pData->dwTypeID != 0)
7861 /* Get the length of the OleTypeName */
7862 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7863 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7865 hRes = CONVERT10_E_OLESTREAM_GET;
7868 if(hRes == S_OK)
7870 if(pData->dwOleTypeNameLength > 0)
7872 /* Get the OleTypeName */
7873 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7874 if(dwSize != pData->dwOleTypeNameLength)
7876 hRes = CONVERT10_E_OLESTREAM_GET;
7880 if(bStrem1)
7882 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7883 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7885 hRes = CONVERT10_E_OLESTREAM_GET;
7887 if(hRes == S_OK)
7889 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7890 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7891 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7892 if(pData->pstrOleObjFileName)
7894 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7895 if(dwSize != pData->dwOleObjFileNameLength)
7897 hRes = CONVERT10_E_OLESTREAM_GET;
7900 else
7901 hRes = CONVERT10_E_OLESTREAM_GET;
7904 else
7906 /* Get the Width of the Metafile */
7907 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7908 if(dwSize != sizeof(pData->dwMetaFileWidth))
7910 hRes = CONVERT10_E_OLESTREAM_GET;
7912 if(hRes == S_OK)
7914 /* Get the Height of the Metafile */
7915 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7916 if(dwSize != sizeof(pData->dwMetaFileHeight))
7918 hRes = CONVERT10_E_OLESTREAM_GET;
7922 if(hRes == S_OK)
7924 /* Get the Length of the Data */
7925 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7926 if(dwSize != sizeof(pData->dwDataLength))
7928 hRes = CONVERT10_E_OLESTREAM_GET;
7932 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7934 if(!bStrem1) /* if it is a second OLE stream data */
7936 pData->dwDataLength -= 8;
7937 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7938 if(dwSize != sizeof(pData->strUnknown))
7940 hRes = CONVERT10_E_OLESTREAM_GET;
7944 if(hRes == S_OK)
7946 if(pData->dwDataLength > 0)
7948 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
7950 /* Get Data (ex. IStorage, Metafile, or BMP) */
7951 if(pData->pData)
7953 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
7954 if(dwSize != pData->dwDataLength)
7956 hRes = CONVERT10_E_OLESTREAM_GET;
7959 else
7961 hRes = CONVERT10_E_OLESTREAM_GET;
7967 return hRes;
7970 /*************************************************************************
7971 * OLECONVERT_SaveOLE10 [Internal]
7973 * Saves the OLE10 STREAM From memory
7975 * PARAMS
7976 * pData [I] Data Structure for the OLESTREAM Data
7977 * pOleStream [I] The OLESTREAM to save
7979 * RETURNS
7980 * Success: S_OK
7981 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7983 * NOTES
7984 * This function is used by OleConvertIStorageToOLESTREAM only.
7987 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
7989 DWORD dwSize;
7990 HRESULT hRes = S_OK;
7993 /* Set the OleID */
7994 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7995 if(dwSize != sizeof(pData->dwOleID))
7997 hRes = CONVERT10_E_OLESTREAM_PUT;
8000 if(hRes == S_OK)
8002 /* Set the TypeID */
8003 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8004 if(dwSize != sizeof(pData->dwTypeID))
8006 hRes = CONVERT10_E_OLESTREAM_PUT;
8010 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8012 /* Set the Length of the OleTypeName */
8013 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8014 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8016 hRes = CONVERT10_E_OLESTREAM_PUT;
8019 if(hRes == S_OK)
8021 if(pData->dwOleTypeNameLength > 0)
8023 /* Set the OleTypeName */
8024 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8025 if(dwSize != pData->dwOleTypeNameLength)
8027 hRes = CONVERT10_E_OLESTREAM_PUT;
8032 if(hRes == S_OK)
8034 /* Set the width of the Metafile */
8035 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8036 if(dwSize != sizeof(pData->dwMetaFileWidth))
8038 hRes = CONVERT10_E_OLESTREAM_PUT;
8042 if(hRes == S_OK)
8044 /* Set the height of the Metafile */
8045 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8046 if(dwSize != sizeof(pData->dwMetaFileHeight))
8048 hRes = CONVERT10_E_OLESTREAM_PUT;
8052 if(hRes == S_OK)
8054 /* Set the length of the Data */
8055 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8056 if(dwSize != sizeof(pData->dwDataLength))
8058 hRes = CONVERT10_E_OLESTREAM_PUT;
8062 if(hRes == S_OK)
8064 if(pData->dwDataLength > 0)
8066 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8067 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8068 if(dwSize != pData->dwDataLength)
8070 hRes = CONVERT10_E_OLESTREAM_PUT;
8075 return hRes;
8078 /*************************************************************************
8079 * OLECONVERT_GetOLE20FromOLE10[Internal]
8081 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8082 * opens it, and copies the content to the dest IStorage for
8083 * OleConvertOLESTREAMToIStorage
8086 * PARAMS
8087 * pDestStorage [I] The IStorage to copy the data to
8088 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8089 * nBufferLength [I] The size of the buffer
8091 * RETURNS
8092 * Nothing
8094 * NOTES
8098 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8100 HRESULT hRes;
8101 HANDLE hFile;
8102 IStorage *pTempStorage;
8103 DWORD dwNumOfBytesWritten;
8104 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8105 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8107 /* Create a temp File */
8108 GetTempPathW(MAX_PATH, wstrTempDir);
8109 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8110 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8112 if(hFile != INVALID_HANDLE_VALUE)
8114 /* Write IStorage Data to File */
8115 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8116 CloseHandle(hFile);
8118 /* Open and copy temp storage to the Dest Storage */
8119 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8120 if(hRes == S_OK)
8122 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8123 IStorage_Release(pTempStorage);
8125 DeleteFileW(wstrTempFile);
8130 /*************************************************************************
8131 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8133 * Saves the OLE10 STREAM From memory
8135 * PARAMS
8136 * pStorage [I] The Src IStorage to copy
8137 * pData [I] The Dest Memory to write to.
8139 * RETURNS
8140 * The size in bytes allocated for pData
8142 * NOTES
8143 * Memory allocated for pData must be freed by the caller
8145 * Used by OleConvertIStorageToOLESTREAM only.
8148 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8150 HANDLE hFile;
8151 HRESULT hRes;
8152 DWORD nDataLength = 0;
8153 IStorage *pTempStorage;
8154 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8155 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8157 *pData = NULL;
8159 /* Create temp Storage */
8160 GetTempPathW(MAX_PATH, wstrTempDir);
8161 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8162 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8164 if(hRes == S_OK)
8166 /* Copy Src Storage to the Temp Storage */
8167 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8168 IStorage_Release(pTempStorage);
8170 /* Open Temp Storage as a file and copy to memory */
8171 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8172 if(hFile != INVALID_HANDLE_VALUE)
8174 nDataLength = GetFileSize(hFile, NULL);
8175 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8176 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8177 CloseHandle(hFile);
8179 DeleteFileW(wstrTempFile);
8181 return nDataLength;
8184 /*************************************************************************
8185 * OLECONVERT_CreateOleStream [Internal]
8187 * Creates the "\001OLE" stream in the IStorage if necessary.
8189 * PARAMS
8190 * pStorage [I] Dest storage to create the stream in
8192 * RETURNS
8193 * Nothing
8195 * NOTES
8196 * This function is used by OleConvertOLESTREAMToIStorage only.
8198 * This stream is still unknown, MS Word seems to have extra data
8199 * but since the data is stored in the OLESTREAM there should be
8200 * no need to recreate the stream. If the stream is manually
8201 * deleted it will create it with this default data.
8204 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8206 HRESULT hRes;
8207 IStream *pStream;
8208 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8209 BYTE pOleStreamHeader [] =
8211 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8212 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8213 0x00, 0x00, 0x00, 0x00
8216 /* Create stream if not present */
8217 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8218 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8220 if(hRes == S_OK)
8222 /* Write default Data */
8223 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8224 IStream_Release(pStream);
8228 /* write a string to a stream, preceded by its length */
8229 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8231 HRESULT r;
8232 LPSTR str;
8233 DWORD len = 0;
8235 if( string )
8236 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8237 r = IStream_Write( stm, &len, sizeof(len), NULL);
8238 if( FAILED( r ) )
8239 return r;
8240 if(len == 0)
8241 return r;
8242 str = CoTaskMemAlloc( len );
8243 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8244 r = IStream_Write( stm, str, len, NULL);
8245 CoTaskMemFree( str );
8246 return r;
8249 /* read a string preceded by its length from a stream */
8250 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8252 HRESULT r;
8253 DWORD len, count = 0;
8254 LPSTR str;
8255 LPWSTR wstr;
8257 r = IStream_Read( stm, &len, sizeof(len), &count );
8258 if( FAILED( r ) )
8259 return r;
8260 if( count != sizeof(len) )
8261 return E_OUTOFMEMORY;
8263 TRACE("%d bytes\n",len);
8265 str = CoTaskMemAlloc( len );
8266 if( !str )
8267 return E_OUTOFMEMORY;
8268 count = 0;
8269 r = IStream_Read( stm, str, len, &count );
8270 if( FAILED( r ) )
8271 return r;
8272 if( count != len )
8274 CoTaskMemFree( str );
8275 return E_OUTOFMEMORY;
8278 TRACE("Read string %s\n",debugstr_an(str,len));
8280 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8281 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8282 if( wstr )
8283 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8284 CoTaskMemFree( str );
8286 *string = wstr;
8288 return r;
8292 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8293 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8295 IStream *pstm;
8296 HRESULT r = S_OK;
8297 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8299 static const BYTE unknown1[12] =
8300 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8301 0xFF, 0xFF, 0xFF, 0xFF};
8302 static const BYTE unknown2[16] =
8303 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8304 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8306 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8307 debugstr_w(lpszUserType), debugstr_w(szClipName),
8308 debugstr_w(szProgIDName));
8310 /* Create a CompObj stream */
8311 r = IStorage_CreateStream(pstg, szwStreamName,
8312 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8313 if( FAILED (r) )
8314 return r;
8316 /* Write CompObj Structure to stream */
8317 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8319 if( SUCCEEDED( r ) )
8320 r = WriteClassStm( pstm, clsid );
8322 if( SUCCEEDED( r ) )
8323 r = STREAM_WriteString( pstm, lpszUserType );
8324 if( SUCCEEDED( r ) )
8325 r = STREAM_WriteString( pstm, szClipName );
8326 if( SUCCEEDED( r ) )
8327 r = STREAM_WriteString( pstm, szProgIDName );
8328 if( SUCCEEDED( r ) )
8329 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8331 IStream_Release( pstm );
8333 return r;
8336 /***********************************************************************
8337 * WriteFmtUserTypeStg (OLE32.@)
8339 HRESULT WINAPI WriteFmtUserTypeStg(
8340 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8342 HRESULT r;
8343 WCHAR szwClipName[0x40];
8344 CLSID clsid = CLSID_NULL;
8345 LPWSTR wstrProgID = NULL;
8346 DWORD n;
8348 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8350 /* get the clipboard format name */
8351 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8352 szwClipName[n]=0;
8354 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8356 /* FIXME: There's room to save a CLSID and its ProgID, but
8357 the CLSID is not looked up in the registry and in all the
8358 tests I wrote it was CLSID_NULL. Where does it come from?
8361 /* get the real program ID. This may fail, but that's fine */
8362 ProgIDFromCLSID(&clsid, &wstrProgID);
8364 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8366 r = STORAGE_WriteCompObj( pstg, &clsid,
8367 lpszUserType, szwClipName, wstrProgID );
8369 CoTaskMemFree(wstrProgID);
8371 return r;
8375 /******************************************************************************
8376 * ReadFmtUserTypeStg [OLE32.@]
8378 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8380 HRESULT r;
8381 IStream *stm = 0;
8382 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8383 unsigned char unknown1[12];
8384 unsigned char unknown2[16];
8385 DWORD count;
8386 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8387 CLSID clsid;
8389 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8391 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8392 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8393 if( FAILED ( r ) )
8395 WARN("Failed to open stream r = %08x\n", r);
8396 return r;
8399 /* read the various parts of the structure */
8400 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8401 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8402 goto end;
8403 r = ReadClassStm( stm, &clsid );
8404 if( FAILED( r ) )
8405 goto end;
8407 r = STREAM_ReadString( stm, &szCLSIDName );
8408 if( FAILED( r ) )
8409 goto end;
8411 r = STREAM_ReadString( stm, &szOleTypeName );
8412 if( FAILED( r ) )
8413 goto end;
8415 r = STREAM_ReadString( stm, &szProgIDName );
8416 if( FAILED( r ) )
8417 goto end;
8419 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8420 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8421 goto end;
8423 /* ok, success... now we just need to store what we found */
8424 if( pcf )
8425 *pcf = RegisterClipboardFormatW( szOleTypeName );
8426 CoTaskMemFree( szOleTypeName );
8428 if( lplpszUserType )
8429 *lplpszUserType = szCLSIDName;
8430 CoTaskMemFree( szProgIDName );
8432 end:
8433 IStream_Release( stm );
8435 return r;
8439 /*************************************************************************
8440 * OLECONVERT_CreateCompObjStream [Internal]
8442 * Creates a "\001CompObj" is the destination IStorage if necessary.
8444 * PARAMS
8445 * pStorage [I] The dest IStorage to create the CompObj Stream
8446 * if necessary.
8447 * strOleTypeName [I] The ProgID
8449 * RETURNS
8450 * Success: S_OK
8451 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8453 * NOTES
8454 * This function is used by OleConvertOLESTREAMToIStorage only.
8456 * The stream data is stored in the OLESTREAM and there should be
8457 * no need to recreate the stream. If the stream is manually
8458 * deleted it will attempt to create it by querying the registry.
8462 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8464 IStream *pStream;
8465 HRESULT hStorageRes, hRes = S_OK;
8466 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8467 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8468 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8470 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8471 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8473 /* Initialize the CompObj structure */
8474 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8475 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8476 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8479 /* Create a CompObj stream if it doesn't exist */
8480 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8481 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8482 if(hStorageRes == S_OK)
8484 /* copy the OleTypeName to the compobj struct */
8485 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8486 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8488 /* copy the OleTypeName to the compobj struct */
8489 /* Note: in the test made, these were Identical */
8490 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8491 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8493 /* Get the CLSID */
8494 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8495 bufferW, OLESTREAM_MAX_STR_LEN );
8496 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8498 if(hRes == S_OK)
8500 HKEY hKey;
8501 LONG hErr;
8502 /* Get the CLSID Default Name from the Registry */
8503 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8504 if(hErr == ERROR_SUCCESS)
8506 char strTemp[OLESTREAM_MAX_STR_LEN];
8507 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8508 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8509 if(hErr == ERROR_SUCCESS)
8511 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8513 RegCloseKey(hKey);
8517 /* Write CompObj Structure to stream */
8518 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8520 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8522 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8523 if(IStorageCompObj.dwCLSIDNameLength > 0)
8525 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8527 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8528 if(IStorageCompObj.dwOleTypeNameLength > 0)
8530 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8532 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8533 if(IStorageCompObj.dwProgIDNameLength > 0)
8535 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8537 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8538 IStream_Release(pStream);
8540 return hRes;
8544 /*************************************************************************
8545 * OLECONVERT_CreateOlePresStream[Internal]
8547 * Creates the "\002OlePres000" Stream with the Metafile data
8549 * PARAMS
8550 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8551 * dwExtentX [I] Width of the Metafile
8552 * dwExtentY [I] Height of the Metafile
8553 * pData [I] Metafile data
8554 * dwDataLength [I] Size of the Metafile data
8556 * RETURNS
8557 * Success: S_OK
8558 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8560 * NOTES
8561 * This function is used by OleConvertOLESTREAMToIStorage only.
8564 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8566 HRESULT hRes;
8567 IStream *pStream;
8568 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8569 BYTE pOlePresStreamHeader [] =
8571 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8572 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8573 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8574 0x00, 0x00, 0x00, 0x00
8577 BYTE pOlePresStreamHeaderEmpty [] =
8579 0x00, 0x00, 0x00, 0x00,
8580 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8581 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8582 0x00, 0x00, 0x00, 0x00
8585 /* Create the OlePres000 Stream */
8586 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8587 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8589 if(hRes == S_OK)
8591 DWORD nHeaderSize;
8592 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8594 memset(&OlePres, 0, sizeof(OlePres));
8595 /* Do we have any metafile data to save */
8596 if(dwDataLength > 0)
8598 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8599 nHeaderSize = sizeof(pOlePresStreamHeader);
8601 else
8603 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8604 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8606 /* Set width and height of the metafile */
8607 OlePres.dwExtentX = dwExtentX;
8608 OlePres.dwExtentY = -dwExtentY;
8610 /* Set Data and Length */
8611 if(dwDataLength > sizeof(METAFILEPICT16))
8613 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8614 OlePres.pData = &(pData[8]);
8616 /* Save OlePres000 Data to Stream */
8617 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8618 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8619 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8620 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8621 if(OlePres.dwSize > 0)
8623 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8625 IStream_Release(pStream);
8629 /*************************************************************************
8630 * OLECONVERT_CreateOle10NativeStream [Internal]
8632 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8634 * PARAMS
8635 * pStorage [I] Dest storage to create the stream in
8636 * pData [I] Ole10 Native Data (ex. bmp)
8637 * dwDataLength [I] Size of the Ole10 Native Data
8639 * RETURNS
8640 * Nothing
8642 * NOTES
8643 * This function is used by OleConvertOLESTREAMToIStorage only.
8645 * Might need to verify the data and return appropriate error message
8648 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8650 HRESULT hRes;
8651 IStream *pStream;
8652 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8654 /* Create the Ole10Native Stream */
8655 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8656 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8658 if(hRes == S_OK)
8660 /* Write info to stream */
8661 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8662 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8663 IStream_Release(pStream);
8668 /*************************************************************************
8669 * OLECONVERT_GetOLE10ProgID [Internal]
8671 * Finds the ProgID (or OleTypeID) from the IStorage
8673 * PARAMS
8674 * pStorage [I] The Src IStorage to get the ProgID
8675 * strProgID [I] the ProgID string to get
8676 * dwSize [I] the size of the string
8678 * RETURNS
8679 * Success: S_OK
8680 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8682 * NOTES
8683 * This function is used by OleConvertIStorageToOLESTREAM only.
8687 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8689 HRESULT hRes;
8690 IStream *pStream;
8691 LARGE_INTEGER iSeekPos;
8692 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8693 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8695 /* Open the CompObj Stream */
8696 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8697 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8698 if(hRes == S_OK)
8701 /*Get the OleType from the CompObj Stream */
8702 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8703 iSeekPos.u.HighPart = 0;
8705 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8706 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8707 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8708 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8709 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8710 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8711 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8713 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8714 if(*dwSize > 0)
8716 IStream_Read(pStream, strProgID, *dwSize, NULL);
8718 IStream_Release(pStream);
8720 else
8722 STATSTG stat;
8723 LPOLESTR wstrProgID;
8725 /* Get the OleType from the registry */
8726 REFCLSID clsid = &(stat.clsid);
8727 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8728 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8729 if(hRes == S_OK)
8731 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8735 return hRes;
8738 /*************************************************************************
8739 * OLECONVERT_GetOle10PresData [Internal]
8741 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8743 * PARAMS
8744 * pStorage [I] Src IStroage
8745 * pOleStream [I] Dest OleStream Mem Struct
8747 * RETURNS
8748 * Nothing
8750 * NOTES
8751 * This function is used by OleConvertIStorageToOLESTREAM only.
8753 * Memory allocated for pData must be freed by the caller
8757 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8760 HRESULT hRes;
8761 IStream *pStream;
8762 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8764 /* Initialize Default data for OLESTREAM */
8765 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8766 pOleStreamData[0].dwTypeID = 2;
8767 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8768 pOleStreamData[1].dwTypeID = 0;
8769 pOleStreamData[0].dwMetaFileWidth = 0;
8770 pOleStreamData[0].dwMetaFileHeight = 0;
8771 pOleStreamData[0].pData = NULL;
8772 pOleStreamData[1].pData = NULL;
8774 /* Open Ole10Native Stream */
8775 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8776 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8777 if(hRes == S_OK)
8780 /* Read Size and Data */
8781 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8782 if(pOleStreamData->dwDataLength > 0)
8784 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8785 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8787 IStream_Release(pStream);
8793 /*************************************************************************
8794 * OLECONVERT_GetOle20PresData[Internal]
8796 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8798 * PARAMS
8799 * pStorage [I] Src IStroage
8800 * pOleStreamData [I] Dest OleStream Mem Struct
8802 * RETURNS
8803 * Nothing
8805 * NOTES
8806 * This function is used by OleConvertIStorageToOLESTREAM only.
8808 * Memory allocated for pData must be freed by the caller
8810 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8812 HRESULT hRes;
8813 IStream *pStream;
8814 OLECONVERT_ISTORAGE_OLEPRES olePress;
8815 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8817 /* Initialize Default data for OLESTREAM */
8818 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8819 pOleStreamData[0].dwTypeID = 2;
8820 pOleStreamData[0].dwMetaFileWidth = 0;
8821 pOleStreamData[0].dwMetaFileHeight = 0;
8822 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8823 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8824 pOleStreamData[1].dwTypeID = 0;
8825 pOleStreamData[1].dwOleTypeNameLength = 0;
8826 pOleStreamData[1].strOleTypeName[0] = 0;
8827 pOleStreamData[1].dwMetaFileWidth = 0;
8828 pOleStreamData[1].dwMetaFileHeight = 0;
8829 pOleStreamData[1].pData = NULL;
8830 pOleStreamData[1].dwDataLength = 0;
8833 /* Open OlePress000 stream */
8834 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8835 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8836 if(hRes == S_OK)
8838 LARGE_INTEGER iSeekPos;
8839 METAFILEPICT16 MetaFilePict;
8840 static const char strMetafilePictName[] = "METAFILEPICT";
8842 /* Set the TypeID for a Metafile */
8843 pOleStreamData[1].dwTypeID = 5;
8845 /* Set the OleTypeName to Metafile */
8846 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8847 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8849 iSeekPos.u.HighPart = 0;
8850 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8852 /* Get Presentation Data */
8853 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8854 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8855 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8856 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8858 /*Set width and Height */
8859 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8860 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8861 if(olePress.dwSize > 0)
8863 /* Set Length */
8864 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8866 /* Set MetaFilePict struct */
8867 MetaFilePict.mm = 8;
8868 MetaFilePict.xExt = olePress.dwExtentX;
8869 MetaFilePict.yExt = olePress.dwExtentY;
8870 MetaFilePict.hMF = 0;
8872 /* Get Metafile Data */
8873 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8874 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8875 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8877 IStream_Release(pStream);
8881 /*************************************************************************
8882 * OleConvertOLESTREAMToIStorage [OLE32.@]
8884 * Read info on MSDN
8886 * TODO
8887 * DVTARGETDEVICE parameter is not handled
8888 * Still unsure of some mem fields for OLE 10 Stream
8889 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8890 * and "\001OLE" streams
8893 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8894 LPOLESTREAM pOleStream,
8895 LPSTORAGE pstg,
8896 const DVTARGETDEVICE* ptd)
8898 int i;
8899 HRESULT hRes=S_OK;
8900 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8902 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8904 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8906 if(ptd != NULL)
8908 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8911 if(pstg == NULL || pOleStream == NULL)
8913 hRes = E_INVALIDARG;
8916 if(hRes == S_OK)
8918 /* Load the OLESTREAM to Memory */
8919 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8922 if(hRes == S_OK)
8924 /* Load the OLESTREAM to Memory (part 2)*/
8925 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8928 if(hRes == S_OK)
8931 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8933 /* Do we have the IStorage Data in the OLESTREAM */
8934 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8936 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8937 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8939 else
8941 /* It must be an original OLE 1.0 source */
8942 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8945 else
8947 /* It must be an original OLE 1.0 source */
8948 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8951 /* Create CompObj Stream if necessary */
8952 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
8953 if(hRes == S_OK)
8955 /*Create the Ole Stream if necessary */
8956 OLECONVERT_CreateOleStream(pstg);
8961 /* Free allocated memory */
8962 for(i=0; i < 2; i++)
8964 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8965 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
8966 pOleStreamData[i].pstrOleObjFileName = NULL;
8968 return hRes;
8971 /*************************************************************************
8972 * OleConvertIStorageToOLESTREAM [OLE32.@]
8974 * Read info on MSDN
8976 * Read info on MSDN
8978 * TODO
8979 * Still unsure of some mem fields for OLE 10 Stream
8980 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8981 * and "\001OLE" streams.
8984 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
8985 LPSTORAGE pstg,
8986 LPOLESTREAM pOleStream)
8988 int i;
8989 HRESULT hRes = S_OK;
8990 IStream *pStream;
8991 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8992 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8994 TRACE("%p %p\n", pstg, pOleStream);
8996 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8998 if(pstg == NULL || pOleStream == NULL)
9000 hRes = E_INVALIDARG;
9002 if(hRes == S_OK)
9004 /* Get the ProgID */
9005 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9006 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9008 if(hRes == S_OK)
9010 /* Was it originally Ole10 */
9011 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9012 if(hRes == S_OK)
9014 IStream_Release(pStream);
9015 /* Get Presentation Data for Ole10Native */
9016 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9018 else
9020 /* Get Presentation Data (OLE20) */
9021 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9024 /* Save OLESTREAM */
9025 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9026 if(hRes == S_OK)
9028 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9033 /* Free allocated memory */
9034 for(i=0; i < 2; i++)
9036 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9039 return hRes;
9042 /***********************************************************************
9043 * GetConvertStg (OLE32.@)
9045 HRESULT WINAPI GetConvertStg(IStorage *stg) {
9046 FIXME("unimplemented stub!\n");
9047 return E_FAIL;
9050 /******************************************************************************
9051 * StgIsStorageFile [OLE32.@]
9052 * Verify if the file contains a storage object
9054 * PARAMS
9055 * fn [ I] Filename
9057 * RETURNS
9058 * S_OK if file has magic bytes as a storage object
9059 * S_FALSE if file is not storage
9061 HRESULT WINAPI
9062 StgIsStorageFile(LPCOLESTR fn)
9064 HANDLE hf;
9065 BYTE magic[8];
9066 DWORD bytes_read;
9068 TRACE("%s\n", debugstr_w(fn));
9069 hf = CreateFileW(fn, GENERIC_READ,
9070 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9071 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9073 if (hf == INVALID_HANDLE_VALUE)
9074 return STG_E_FILENOTFOUND;
9076 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9078 WARN(" unable to read file\n");
9079 CloseHandle(hf);
9080 return S_FALSE;
9083 CloseHandle(hf);
9085 if (bytes_read != 8) {
9086 TRACE(" too short\n");
9087 return S_FALSE;
9090 if (!memcmp(magic,STORAGE_magic,8)) {
9091 TRACE(" -> YES\n");
9092 return S_OK;
9095 TRACE(" -> Invalid header.\n");
9096 return S_FALSE;
9099 /***********************************************************************
9100 * WriteClassStm (OLE32.@)
9102 * Writes a CLSID to a stream.
9104 * PARAMS
9105 * pStm [I] Stream to write to.
9106 * rclsid [I] CLSID to write.
9108 * RETURNS
9109 * Success: S_OK.
9110 * Failure: HRESULT code.
9112 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9114 TRACE("(%p,%p)\n",pStm,rclsid);
9116 if (!pStm || !rclsid)
9117 return E_INVALIDARG;
9119 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9122 /***********************************************************************
9123 * ReadClassStm (OLE32.@)
9125 * Reads a CLSID from a stream.
9127 * PARAMS
9128 * pStm [I] Stream to read from.
9129 * rclsid [O] CLSID to read.
9131 * RETURNS
9132 * Success: S_OK.
9133 * Failure: HRESULT code.
9135 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9137 ULONG nbByte;
9138 HRESULT res;
9140 TRACE("(%p,%p)\n",pStm,pclsid);
9142 if (!pStm || !pclsid)
9143 return E_INVALIDARG;
9145 /* clear the output args */
9146 *pclsid = CLSID_NULL;
9148 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
9150 if (FAILED(res))
9151 return res;
9153 if (nbByte != sizeof(CLSID))
9154 return STG_E_READFAULT;
9155 else
9156 return S_OK;