ole32: Use symbolic names for range lock offsets.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blobb9e20d81a456a02a8717f74752313d5dcfab2185
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"
55 #include "compobj_private.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(storage);
59 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
60 #define OLESTREAM_ID 0x501
61 #define OLESTREAM_MAX_STR_LEN 255
64 * These are signatures to detect the type of Document file.
66 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
67 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
69 static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
71 return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
74 static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface )
76 return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
79 /****************************************************************************
80 * Storage32InternalImpl definitions.
82 * Definition of the implementation structure for the IStorage32 interface.
83 * This one implements the IStorage32 interface for storage that are
84 * inside another storage.
86 struct StorageInternalImpl
88 struct StorageBaseImpl base;
91 * Entry in the parent's stream tracking list
93 struct list ParentListEntry;
95 StorageBaseImpl *parentStorage;
97 typedef struct StorageInternalImpl StorageInternalImpl;
99 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
100 static const IStorageVtbl Storage32InternalImpl_Vtbl;
102 /* Method definitions for the Storage32InternalImpl class. */
103 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
104 DWORD openFlags, DirRef storageDirEntry);
105 static void StorageImpl_Destroy(StorageBaseImpl* iface);
106 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
107 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
108 static HRESULT StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer, ULONG *read );
109 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
110 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
111 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
112 static void StorageImpl_SaveFileHeader(StorageImpl* This);
114 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex);
115 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
116 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
117 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
118 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
120 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
121 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
122 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
124 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
125 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
126 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
127 ULONG blockIndex, ULONG offset, DWORD value);
128 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
129 ULONG blockIndex, ULONG offset, DWORD* value);
131 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
132 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
134 typedef struct TransactedDirEntry
136 /* If applicable, a reference to the original DirEntry in the transacted
137 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
138 DirRef transactedParentEntry;
140 /* True if this entry is being used. */
141 BOOL inuse;
143 /* True if data is up to date. */
144 BOOL read;
146 /* True if this entry has been modified. */
147 BOOL dirty;
149 /* True if this entry's stream has been modified. */
150 BOOL stream_dirty;
152 /* True if this entry has been deleted in the transacted storage, but the
153 * delete has not yet been committed. */
154 BOOL deleted;
156 /* If this entry's stream has been modified, a reference to where the stream
157 * is stored in the snapshot file. */
158 DirRef stream_entry;
160 /* This directory entry's data, including any changes that have been made. */
161 DirEntry data;
163 /* A reference to the parent of this node. This is only valid while we are
164 * committing changes. */
165 DirRef parent;
167 /* A reference to a newly-created entry in the transacted parent. This is
168 * always equal to transactedParentEntry except when committing changes. */
169 DirRef newTransactedParentEntry;
170 } TransactedDirEntry;
172 /****************************************************************************
173 * Transacted storage object.
175 typedef struct TransactedSnapshotImpl
177 struct StorageBaseImpl base;
180 * Modified streams are temporarily saved to the scratch file.
182 StorageBaseImpl *scratch;
184 /* The directory structure is kept here, so that we can track how these
185 * entries relate to those in the parent storage. */
186 TransactedDirEntry *entries;
187 ULONG entries_size;
188 ULONG firstFreeEntry;
191 * Changes are committed to the transacted parent.
193 StorageBaseImpl *transactedParent;
194 } TransactedSnapshotImpl;
196 /* Generic function to create a transacted wrapper for a direct storage object. */
197 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
199 /* OLESTREAM memory structure to use for Get and Put Routines */
200 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
201 typedef struct
203 DWORD dwOleID;
204 DWORD dwTypeID;
205 DWORD dwOleTypeNameLength;
206 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
207 CHAR *pstrOleObjFileName;
208 DWORD dwOleObjFileNameLength;
209 DWORD dwMetaFileWidth;
210 DWORD dwMetaFileHeight;
211 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
212 DWORD dwDataLength;
213 BYTE *pData;
214 }OLECONVERT_OLESTREAM_DATA;
216 /* CompObj Stream structure */
217 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
218 typedef struct
220 BYTE byUnknown1[12];
221 CLSID clsid;
222 DWORD dwCLSIDNameLength;
223 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
224 DWORD dwOleTypeNameLength;
225 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
226 DWORD dwProgIDNameLength;
227 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
228 BYTE byUnknown2[16];
229 }OLECONVERT_ISTORAGE_COMPOBJ;
232 /* Ole Presentation Stream structure */
233 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
234 typedef struct
236 BYTE byUnknown1[28];
237 DWORD dwExtentX;
238 DWORD dwExtentY;
239 DWORD dwSize;
240 BYTE *pData;
241 }OLECONVERT_ISTORAGE_OLEPRES;
245 /***********************************************************************
246 * Forward declaration of internal functions used by the method DestroyElement
248 static HRESULT deleteStorageContents(
249 StorageBaseImpl *parentStorage,
250 DirRef indexToDelete,
251 DirEntry entryDataToDelete);
253 static HRESULT deleteStreamContents(
254 StorageBaseImpl *parentStorage,
255 DirRef indexToDelete,
256 DirEntry entryDataToDelete);
258 static HRESULT removeFromTree(
259 StorageBaseImpl *This,
260 DirRef parentStorageIndex,
261 DirRef deletedIndex);
263 /***********************************************************************
264 * Declaration of the functions used to manipulate DirEntry
267 static HRESULT insertIntoTree(
268 StorageBaseImpl *This,
269 DirRef parentStorageIndex,
270 DirRef newEntryIndex);
272 static LONG entryNameCmp(
273 const OLECHAR *name1,
274 const OLECHAR *name2);
276 static DirRef findElement(
277 StorageBaseImpl *storage,
278 DirRef storageEntry,
279 const OLECHAR *name,
280 DirEntry *data);
282 static HRESULT findTreeParent(
283 StorageBaseImpl *storage,
284 DirRef storageEntry,
285 const OLECHAR *childName,
286 DirEntry *parentData,
287 DirRef *parentEntry,
288 ULONG *relation);
290 /***********************************************************************
291 * Declaration of miscellaneous functions...
293 static HRESULT validateSTGM(DWORD stgmValue);
295 static DWORD GetShareModeFromSTGM(DWORD stgm);
296 static DWORD GetAccessModeFromSTGM(DWORD stgm);
297 static DWORD GetCreationModeFromSTGM(DWORD stgm);
299 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
302 /****************************************************************************
303 * IEnumSTATSTGImpl definitions.
305 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
306 * This class allows iterating through the content of a storage and to find
307 * specific items inside it.
309 struct IEnumSTATSTGImpl
311 IEnumSTATSTG IEnumSTATSTG_iface;
313 LONG ref; /* Reference count */
314 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
315 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
317 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
320 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
322 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
326 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
327 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
329 /************************************************************************
330 ** Block Functions
333 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
335 return (index+1) * This->bigBlockSize;
338 /************************************************************************
339 ** Storage32BaseImpl implementation
341 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
342 ULARGE_INTEGER offset,
343 void* buffer,
344 ULONG size,
345 ULONG* bytesRead)
347 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
350 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
351 ULARGE_INTEGER offset,
352 const void* buffer,
353 const ULONG size,
354 ULONG* bytesWritten)
356 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
359 /************************************************************************
360 * Storage32BaseImpl_QueryInterface (IUnknown)
362 * This method implements the common QueryInterface for all IStorage32
363 * implementations contained in this file.
365 * See Windows documentation for more details on IUnknown methods.
367 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
368 IStorage* iface,
369 REFIID riid,
370 void** ppvObject)
372 StorageBaseImpl *This = impl_from_IStorage(iface);
374 if (!ppvObject)
375 return E_INVALIDARG;
377 *ppvObject = 0;
379 if (IsEqualGUID(&IID_IUnknown, riid) ||
380 IsEqualGUID(&IID_IStorage, riid))
382 *ppvObject = &This->IStorage_iface;
384 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
386 *ppvObject = &This->IPropertySetStorage_iface;
388 /* locking interface is reported for writer only */
389 else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
391 *ppvObject = &This->IDirectWriterLock_iface;
393 else
394 return E_NOINTERFACE;
396 IStorage_AddRef(iface);
398 return S_OK;
401 /************************************************************************
402 * Storage32BaseImpl_AddRef (IUnknown)
404 * This method implements the common AddRef for all IStorage32
405 * implementations contained in this file.
407 * See Windows documentation for more details on IUnknown methods.
409 static ULONG WINAPI StorageBaseImpl_AddRef(
410 IStorage* iface)
412 StorageBaseImpl *This = impl_from_IStorage(iface);
413 ULONG ref = InterlockedIncrement(&This->ref);
415 TRACE("(%p) AddRef to %d\n", This, ref);
417 return ref;
420 /************************************************************************
421 * Storage32BaseImpl_Release (IUnknown)
423 * This method implements the common Release for all IStorage32
424 * implementations contained in this file.
426 * See Windows documentation for more details on IUnknown methods.
428 static ULONG WINAPI StorageBaseImpl_Release(
429 IStorage* iface)
431 StorageBaseImpl *This = impl_from_IStorage(iface);
433 ULONG ref = InterlockedDecrement(&This->ref);
435 TRACE("(%p) ReleaseRef to %d\n", This, ref);
437 if (ref == 0)
440 * Since we are using a system of base-classes, we want to call the
441 * destructor of the appropriate derived class. To do this, we are
442 * using virtual functions to implement the destructor.
444 StorageBaseImpl_Destroy(This);
447 return ref;
450 /************************************************************************
451 * Storage32BaseImpl_OpenStream (IStorage)
453 * This method will open the specified stream object from the current storage.
455 * See Windows documentation for more details on IStorage methods.
457 static HRESULT WINAPI StorageBaseImpl_OpenStream(
458 IStorage* iface,
459 const OLECHAR* pwcsName, /* [string][in] */
460 void* reserved1, /* [unique][in] */
461 DWORD grfMode, /* [in] */
462 DWORD reserved2, /* [in] */
463 IStream** ppstm) /* [out] */
465 StorageBaseImpl *This = impl_from_IStorage(iface);
466 StgStreamImpl* newStream;
467 DirEntry currentEntry;
468 DirRef streamEntryRef;
469 HRESULT res = STG_E_UNKNOWN;
471 TRACE("(%p, %s, %p, %x, %d, %p)\n",
472 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
474 if ( (pwcsName==NULL) || (ppstm==0) )
476 res = E_INVALIDARG;
477 goto end;
480 *ppstm = NULL;
482 if ( FAILED( validateSTGM(grfMode) ) ||
483 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
485 res = STG_E_INVALIDFLAG;
486 goto end;
490 * As documented.
492 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
494 res = STG_E_INVALIDFUNCTION;
495 goto end;
498 if (This->reverted)
500 res = STG_E_REVERTED;
501 goto end;
505 * Check that we're compatible with the parent's storage mode, but
506 * only if we are not in transacted mode
508 if(!(This->openFlags & STGM_TRANSACTED)) {
509 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
511 res = STG_E_INVALIDFLAG;
512 goto end;
517 * Search for the element with the given name
519 streamEntryRef = findElement(
520 This,
521 This->storageDirEntry,
522 pwcsName,
523 &currentEntry);
526 * If it was found, construct the stream object and return a pointer to it.
528 if ( (streamEntryRef!=DIRENTRY_NULL) &&
529 (currentEntry.stgType==STGTY_STREAM) )
531 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
533 /* A single stream cannot be opened a second time. */
534 res = STG_E_ACCESSDENIED;
535 goto end;
538 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
540 if (newStream)
542 newStream->grfMode = grfMode;
543 *ppstm = &newStream->IStream_iface;
545 IStream_AddRef(*ppstm);
547 res = S_OK;
548 goto end;
551 res = E_OUTOFMEMORY;
552 goto end;
555 res = STG_E_FILENOTFOUND;
557 end:
558 if (res == S_OK)
559 TRACE("<-- IStream %p\n", *ppstm);
560 TRACE("<-- %08x\n", res);
561 return res;
564 /************************************************************************
565 * Storage32BaseImpl_OpenStorage (IStorage)
567 * This method will open a new storage object from the current storage.
569 * See Windows documentation for more details on IStorage methods.
571 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
572 IStorage* iface,
573 const OLECHAR* pwcsName, /* [string][unique][in] */
574 IStorage* pstgPriority, /* [unique][in] */
575 DWORD grfMode, /* [in] */
576 SNB snbExclude, /* [unique][in] */
577 DWORD reserved, /* [in] */
578 IStorage** ppstg) /* [out] */
580 StorageBaseImpl *This = impl_from_IStorage(iface);
581 StorageInternalImpl* newStorage;
582 StorageBaseImpl* newTransactedStorage;
583 DirEntry currentEntry;
584 DirRef storageEntryRef;
585 HRESULT res = STG_E_UNKNOWN;
587 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
588 iface, debugstr_w(pwcsName), pstgPriority,
589 grfMode, snbExclude, reserved, ppstg);
591 if ((pwcsName==NULL) || (ppstg==0) )
593 res = E_INVALIDARG;
594 goto end;
597 if (This->openFlags & STGM_SIMPLE)
599 res = STG_E_INVALIDFUNCTION;
600 goto end;
603 /* as documented */
604 if (snbExclude != NULL)
606 res = STG_E_INVALIDPARAMETER;
607 goto end;
610 if ( FAILED( validateSTGM(grfMode) ))
612 res = STG_E_INVALIDFLAG;
613 goto end;
617 * As documented.
619 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
620 (grfMode & STGM_DELETEONRELEASE) ||
621 (grfMode & STGM_PRIORITY) )
623 res = STG_E_INVALIDFUNCTION;
624 goto end;
627 if (This->reverted)
628 return STG_E_REVERTED;
631 * Check that we're compatible with the parent's storage mode,
632 * but only if we are not transacted
634 if(!(This->openFlags & STGM_TRANSACTED)) {
635 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
637 res = STG_E_ACCESSDENIED;
638 goto end;
642 *ppstg = NULL;
644 storageEntryRef = findElement(
645 This,
646 This->storageDirEntry,
647 pwcsName,
648 &currentEntry);
650 if ( (storageEntryRef!=DIRENTRY_NULL) &&
651 (currentEntry.stgType==STGTY_STORAGE) )
653 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
655 /* A single storage cannot be opened a second time. */
656 res = STG_E_ACCESSDENIED;
657 goto end;
660 newStorage = StorageInternalImpl_Construct(
661 This,
662 grfMode,
663 storageEntryRef);
665 if (newStorage != 0)
667 if (grfMode & STGM_TRANSACTED)
669 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
671 if (FAILED(res))
673 HeapFree(GetProcessHeap(), 0, newStorage);
674 goto end;
677 *ppstg = &newTransactedStorage->IStorage_iface;
679 else
681 *ppstg = &newStorage->base.IStorage_iface;
684 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
686 res = S_OK;
687 goto end;
690 res = STG_E_INSUFFICIENTMEMORY;
691 goto end;
694 res = STG_E_FILENOTFOUND;
696 end:
697 TRACE("<-- %08x\n", res);
698 return res;
701 /************************************************************************
702 * Storage32BaseImpl_EnumElements (IStorage)
704 * This method will create an enumerator object that can be used to
705 * retrieve information about all the elements in the storage object.
707 * See Windows documentation for more details on IStorage methods.
709 static HRESULT WINAPI StorageBaseImpl_EnumElements(
710 IStorage* iface,
711 DWORD reserved1, /* [in] */
712 void* reserved2, /* [size_is][unique][in] */
713 DWORD reserved3, /* [in] */
714 IEnumSTATSTG** ppenum) /* [out] */
716 StorageBaseImpl *This = impl_from_IStorage(iface);
717 IEnumSTATSTGImpl* newEnum;
719 TRACE("(%p, %d, %p, %d, %p)\n",
720 iface, reserved1, reserved2, reserved3, ppenum);
722 if (!ppenum)
723 return E_INVALIDARG;
725 if (This->reverted)
726 return STG_E_REVERTED;
728 newEnum = IEnumSTATSTGImpl_Construct(
729 This,
730 This->storageDirEntry);
732 if (newEnum)
734 *ppenum = &newEnum->IEnumSTATSTG_iface;
735 return S_OK;
738 return E_OUTOFMEMORY;
741 /************************************************************************
742 * Storage32BaseImpl_Stat (IStorage)
744 * This method will retrieve information about this storage object.
746 * See Windows documentation for more details on IStorage methods.
748 static HRESULT WINAPI StorageBaseImpl_Stat(
749 IStorage* iface,
750 STATSTG* pstatstg, /* [out] */
751 DWORD grfStatFlag) /* [in] */
753 StorageBaseImpl *This = impl_from_IStorage(iface);
754 DirEntry currentEntry;
755 HRESULT res = STG_E_UNKNOWN;
757 TRACE("(%p, %p, %x)\n",
758 iface, pstatstg, grfStatFlag);
760 if (!pstatstg)
762 res = E_INVALIDARG;
763 goto end;
766 if (This->reverted)
768 res = STG_E_REVERTED;
769 goto end;
772 res = StorageBaseImpl_ReadDirEntry(
773 This,
774 This->storageDirEntry,
775 &currentEntry);
777 if (SUCCEEDED(res))
779 StorageUtl_CopyDirEntryToSTATSTG(
780 This,
781 pstatstg,
782 &currentEntry,
783 grfStatFlag);
785 pstatstg->grfMode = This->openFlags;
786 pstatstg->grfStateBits = This->stateBits;
789 end:
790 if (res == S_OK)
792 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);
794 TRACE("<-- %08x\n", res);
795 return res;
798 /************************************************************************
799 * Storage32BaseImpl_RenameElement (IStorage)
801 * This method will rename the specified element.
803 * See Windows documentation for more details on IStorage methods.
805 static HRESULT WINAPI StorageBaseImpl_RenameElement(
806 IStorage* iface,
807 const OLECHAR* pwcsOldName, /* [in] */
808 const OLECHAR* pwcsNewName) /* [in] */
810 StorageBaseImpl *This = impl_from_IStorage(iface);
811 DirEntry currentEntry;
812 DirRef currentEntryRef;
814 TRACE("(%p, %s, %s)\n",
815 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
817 if (This->reverted)
818 return STG_E_REVERTED;
820 currentEntryRef = findElement(This,
821 This->storageDirEntry,
822 pwcsNewName,
823 &currentEntry);
825 if (currentEntryRef != DIRENTRY_NULL)
828 * There is already an element with the new name
830 return STG_E_FILEALREADYEXISTS;
834 * Search for the old element name
836 currentEntryRef = findElement(This,
837 This->storageDirEntry,
838 pwcsOldName,
839 &currentEntry);
841 if (currentEntryRef != DIRENTRY_NULL)
843 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
844 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
846 WARN("Element is already open; cannot rename.\n");
847 return STG_E_ACCESSDENIED;
850 /* Remove the element from its current position in the tree */
851 removeFromTree(This, This->storageDirEntry,
852 currentEntryRef);
854 /* Change the name of the element */
855 strcpyW(currentEntry.name, pwcsNewName);
857 /* Delete any sibling links */
858 currentEntry.leftChild = DIRENTRY_NULL;
859 currentEntry.rightChild = DIRENTRY_NULL;
861 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
862 &currentEntry);
864 /* Insert the element in a new position in the tree */
865 insertIntoTree(This, This->storageDirEntry,
866 currentEntryRef);
868 else
871 * There is no element with the old name
873 return STG_E_FILENOTFOUND;
876 return StorageBaseImpl_Flush(This);
879 /************************************************************************
880 * Storage32BaseImpl_CreateStream (IStorage)
882 * This method will create a stream object within this storage
884 * See Windows documentation for more details on IStorage methods.
886 static HRESULT WINAPI StorageBaseImpl_CreateStream(
887 IStorage* iface,
888 const OLECHAR* pwcsName, /* [string][in] */
889 DWORD grfMode, /* [in] */
890 DWORD reserved1, /* [in] */
891 DWORD reserved2, /* [in] */
892 IStream** ppstm) /* [out] */
894 StorageBaseImpl *This = impl_from_IStorage(iface);
895 StgStreamImpl* newStream;
896 DirEntry currentEntry, newStreamEntry;
897 DirRef currentEntryRef, newStreamEntryRef;
898 HRESULT hr;
900 TRACE("(%p, %s, %x, %d, %d, %p)\n",
901 iface, debugstr_w(pwcsName), grfMode,
902 reserved1, reserved2, ppstm);
904 if (ppstm == 0)
905 return STG_E_INVALIDPOINTER;
907 if (pwcsName == 0)
908 return STG_E_INVALIDNAME;
910 if (reserved1 || reserved2)
911 return STG_E_INVALIDPARAMETER;
913 if ( FAILED( validateSTGM(grfMode) ))
914 return STG_E_INVALIDFLAG;
916 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
917 return STG_E_INVALIDFLAG;
919 if (This->reverted)
920 return STG_E_REVERTED;
923 * As documented.
925 if ((grfMode & STGM_DELETEONRELEASE) ||
926 (grfMode & STGM_TRANSACTED))
927 return STG_E_INVALIDFUNCTION;
930 * Don't worry about permissions in transacted mode, as we can always write
931 * changes; we just can't always commit them.
933 if(!(This->openFlags & STGM_TRANSACTED)) {
934 /* Can't create a stream on read-only storage */
935 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
936 return STG_E_ACCESSDENIED;
938 /* Can't create a stream with greater access than the parent. */
939 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
940 return STG_E_ACCESSDENIED;
943 if(This->openFlags & STGM_SIMPLE)
944 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
946 *ppstm = 0;
948 currentEntryRef = findElement(This,
949 This->storageDirEntry,
950 pwcsName,
951 &currentEntry);
953 if (currentEntryRef != DIRENTRY_NULL)
956 * An element with this name already exists
958 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
960 IStorage_DestroyElement(iface, pwcsName);
962 else
963 return STG_E_FILEALREADYEXISTS;
967 * memset the empty entry
969 memset(&newStreamEntry, 0, sizeof(DirEntry));
971 newStreamEntry.sizeOfNameString =
972 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
974 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
975 return STG_E_INVALIDNAME;
977 strcpyW(newStreamEntry.name, pwcsName);
979 newStreamEntry.stgType = STGTY_STREAM;
980 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
981 newStreamEntry.size.u.LowPart = 0;
982 newStreamEntry.size.u.HighPart = 0;
984 newStreamEntry.leftChild = DIRENTRY_NULL;
985 newStreamEntry.rightChild = DIRENTRY_NULL;
986 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
988 /* call CoFileTime to get the current time
989 newStreamEntry.ctime
990 newStreamEntry.mtime
993 /* newStreamEntry.clsid */
996 * Create an entry with the new data
998 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
999 if (FAILED(hr))
1000 return hr;
1003 * Insert the new entry in the parent storage's tree.
1005 hr = insertIntoTree(
1006 This,
1007 This->storageDirEntry,
1008 newStreamEntryRef);
1009 if (FAILED(hr))
1011 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
1012 return hr;
1016 * Open the stream to return it.
1018 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1020 if (newStream)
1022 *ppstm = &newStream->IStream_iface;
1023 IStream_AddRef(*ppstm);
1025 else
1027 return STG_E_INSUFFICIENTMEMORY;
1030 return StorageBaseImpl_Flush(This);
1033 /************************************************************************
1034 * Storage32BaseImpl_SetClass (IStorage)
1036 * This method will write the specified CLSID in the directory entry of this
1037 * storage.
1039 * See Windows documentation for more details on IStorage methods.
1041 static HRESULT WINAPI StorageBaseImpl_SetClass(
1042 IStorage* iface,
1043 REFCLSID clsid) /* [in] */
1045 StorageBaseImpl *This = impl_from_IStorage(iface);
1046 HRESULT hRes;
1047 DirEntry currentEntry;
1049 TRACE("(%p, %p)\n", iface, clsid);
1051 if (This->reverted)
1052 return STG_E_REVERTED;
1054 hRes = StorageBaseImpl_ReadDirEntry(This,
1055 This->storageDirEntry,
1056 &currentEntry);
1057 if (SUCCEEDED(hRes))
1059 currentEntry.clsid = *clsid;
1061 hRes = StorageBaseImpl_WriteDirEntry(This,
1062 This->storageDirEntry,
1063 &currentEntry);
1066 if (SUCCEEDED(hRes))
1067 hRes = StorageBaseImpl_Flush(This);
1069 return hRes;
1072 /************************************************************************
1073 ** Storage32Impl implementation
1076 /************************************************************************
1077 * Storage32BaseImpl_CreateStorage (IStorage)
1079 * This method will create the storage object within the provided storage.
1081 * See Windows documentation for more details on IStorage methods.
1083 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1084 IStorage* iface,
1085 const OLECHAR *pwcsName, /* [string][in] */
1086 DWORD grfMode, /* [in] */
1087 DWORD reserved1, /* [in] */
1088 DWORD reserved2, /* [in] */
1089 IStorage **ppstg) /* [out] */
1091 StorageBaseImpl* This = impl_from_IStorage(iface);
1093 DirEntry currentEntry;
1094 DirEntry newEntry;
1095 DirRef currentEntryRef;
1096 DirRef newEntryRef;
1097 HRESULT hr;
1099 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1100 iface, debugstr_w(pwcsName), grfMode,
1101 reserved1, reserved2, ppstg);
1103 if (ppstg == 0)
1104 return STG_E_INVALIDPOINTER;
1106 if (This->openFlags & STGM_SIMPLE)
1108 return STG_E_INVALIDFUNCTION;
1111 if (pwcsName == 0)
1112 return STG_E_INVALIDNAME;
1114 *ppstg = NULL;
1116 if ( FAILED( validateSTGM(grfMode) ) ||
1117 (grfMode & STGM_DELETEONRELEASE) )
1119 WARN("bad grfMode: 0x%x\n", grfMode);
1120 return STG_E_INVALIDFLAG;
1123 if (This->reverted)
1124 return STG_E_REVERTED;
1127 * Check that we're compatible with the parent's storage mode
1129 if ( !(This->openFlags & STGM_TRANSACTED) &&
1130 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1132 WARN("access denied\n");
1133 return STG_E_ACCESSDENIED;
1136 currentEntryRef = findElement(This,
1137 This->storageDirEntry,
1138 pwcsName,
1139 &currentEntry);
1141 if (currentEntryRef != DIRENTRY_NULL)
1144 * An element with this name already exists
1146 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1147 ((This->openFlags & STGM_TRANSACTED) ||
1148 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1150 hr = IStorage_DestroyElement(iface, pwcsName);
1151 if (FAILED(hr))
1152 return hr;
1154 else
1156 WARN("file already exists\n");
1157 return STG_E_FILEALREADYEXISTS;
1160 else if (!(This->openFlags & STGM_TRANSACTED) &&
1161 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1163 WARN("read-only storage\n");
1164 return STG_E_ACCESSDENIED;
1167 memset(&newEntry, 0, sizeof(DirEntry));
1169 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1171 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1173 FIXME("name too long\n");
1174 return STG_E_INVALIDNAME;
1177 strcpyW(newEntry.name, pwcsName);
1179 newEntry.stgType = STGTY_STORAGE;
1180 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1181 newEntry.size.u.LowPart = 0;
1182 newEntry.size.u.HighPart = 0;
1184 newEntry.leftChild = DIRENTRY_NULL;
1185 newEntry.rightChild = DIRENTRY_NULL;
1186 newEntry.dirRootEntry = DIRENTRY_NULL;
1188 /* call CoFileTime to get the current time
1189 newEntry.ctime
1190 newEntry.mtime
1193 /* newEntry.clsid */
1196 * Create a new directory entry for the storage
1198 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1199 if (FAILED(hr))
1200 return hr;
1203 * Insert the new directory entry into the parent storage's tree
1205 hr = insertIntoTree(
1206 This,
1207 This->storageDirEntry,
1208 newEntryRef);
1209 if (FAILED(hr))
1211 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1212 return hr;
1216 * Open it to get a pointer to return.
1218 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1220 if( (hr != S_OK) || (*ppstg == NULL))
1222 return hr;
1225 if (SUCCEEDED(hr))
1226 hr = StorageBaseImpl_Flush(This);
1228 return S_OK;
1232 /***************************************************************************
1234 * Internal Method
1236 * Reserve a directory entry in the file and initialize it.
1238 static HRESULT StorageImpl_CreateDirEntry(
1239 StorageBaseImpl *base,
1240 const DirEntry *newData,
1241 DirRef *index)
1243 StorageImpl *storage = (StorageImpl*)base;
1244 ULONG currentEntryIndex = 0;
1245 ULONG newEntryIndex = DIRENTRY_NULL;
1246 HRESULT hr = S_OK;
1247 BYTE currentData[RAW_DIRENTRY_SIZE];
1248 WORD sizeOfNameString;
1252 hr = StorageImpl_ReadRawDirEntry(storage,
1253 currentEntryIndex,
1254 currentData);
1256 if (SUCCEEDED(hr))
1258 StorageUtl_ReadWord(
1259 currentData,
1260 OFFSET_PS_NAMELENGTH,
1261 &sizeOfNameString);
1263 if (sizeOfNameString == 0)
1266 * The entry exists and is available, we found it.
1268 newEntryIndex = currentEntryIndex;
1271 else
1274 * We exhausted the directory entries, we will create more space below
1276 newEntryIndex = currentEntryIndex;
1278 currentEntryIndex++;
1280 } while (newEntryIndex == DIRENTRY_NULL);
1283 * grow the directory stream
1285 if (FAILED(hr))
1287 BYTE emptyData[RAW_DIRENTRY_SIZE];
1288 ULARGE_INTEGER newSize;
1289 ULONG entryIndex;
1290 ULONG lastEntry = 0;
1291 ULONG blockCount = 0;
1294 * obtain the new count of blocks in the directory stream
1296 blockCount = BlockChainStream_GetCount(
1297 storage->rootBlockChain)+1;
1300 * initialize the size used by the directory stream
1302 newSize.u.HighPart = 0;
1303 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1306 * add a block to the directory stream
1308 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1311 * memset the empty entry in order to initialize the unused newly
1312 * created entries
1314 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1317 * initialize them
1319 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1321 for(
1322 entryIndex = newEntryIndex + 1;
1323 entryIndex < lastEntry;
1324 entryIndex++)
1326 StorageImpl_WriteRawDirEntry(
1327 storage,
1328 entryIndex,
1329 emptyData);
1332 StorageImpl_SaveFileHeader(storage);
1335 UpdateRawDirEntry(currentData, newData);
1337 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1339 if (SUCCEEDED(hr))
1340 *index = newEntryIndex;
1342 return hr;
1345 /***************************************************************************
1347 * Internal Method
1349 * Mark a directory entry in the file as free.
1351 static HRESULT StorageImpl_DestroyDirEntry(
1352 StorageBaseImpl *base,
1353 DirRef index)
1355 BYTE emptyData[RAW_DIRENTRY_SIZE];
1356 StorageImpl *storage = (StorageImpl*)base;
1358 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1360 return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1364 /****************************************************************************
1366 * Internal Method
1368 * Case insensitive comparison of DirEntry.name by first considering
1369 * their size.
1371 * Returns <0 when name1 < name2
1372 * >0 when name1 > name2
1373 * 0 when name1 == name2
1375 static LONG entryNameCmp(
1376 const OLECHAR *name1,
1377 const OLECHAR *name2)
1379 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1381 while (diff == 0 && *name1 != 0)
1384 * We compare the string themselves only when they are of the same length
1386 diff = toupperW(*name1++) - toupperW(*name2++);
1389 return diff;
1392 /****************************************************************************
1394 * Internal Method
1396 * Add a directory entry to a storage
1398 static HRESULT insertIntoTree(
1399 StorageBaseImpl *This,
1400 DirRef parentStorageIndex,
1401 DirRef newEntryIndex)
1403 DirEntry currentEntry;
1404 DirEntry newEntry;
1407 * Read the inserted entry
1409 StorageBaseImpl_ReadDirEntry(This,
1410 newEntryIndex,
1411 &newEntry);
1414 * Read the storage entry
1416 StorageBaseImpl_ReadDirEntry(This,
1417 parentStorageIndex,
1418 &currentEntry);
1420 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1423 * The root storage contains some element, therefore, start the research
1424 * for the appropriate location.
1426 BOOL found = FALSE;
1427 DirRef current, next, previous, currentEntryId;
1430 * Keep a reference to the root of the storage's element tree
1432 currentEntryId = currentEntry.dirRootEntry;
1435 * Read
1437 StorageBaseImpl_ReadDirEntry(This,
1438 currentEntry.dirRootEntry,
1439 &currentEntry);
1441 previous = currentEntry.leftChild;
1442 next = currentEntry.rightChild;
1443 current = currentEntryId;
1445 while (!found)
1447 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1449 if (diff < 0)
1451 if (previous != DIRENTRY_NULL)
1453 StorageBaseImpl_ReadDirEntry(This,
1454 previous,
1455 &currentEntry);
1456 current = previous;
1458 else
1460 currentEntry.leftChild = newEntryIndex;
1461 StorageBaseImpl_WriteDirEntry(This,
1462 current,
1463 &currentEntry);
1464 found = TRUE;
1467 else if (diff > 0)
1469 if (next != DIRENTRY_NULL)
1471 StorageBaseImpl_ReadDirEntry(This,
1472 next,
1473 &currentEntry);
1474 current = next;
1476 else
1478 currentEntry.rightChild = newEntryIndex;
1479 StorageBaseImpl_WriteDirEntry(This,
1480 current,
1481 &currentEntry);
1482 found = TRUE;
1485 else
1488 * Trying to insert an item with the same name in the
1489 * subtree structure.
1491 return STG_E_FILEALREADYEXISTS;
1494 previous = currentEntry.leftChild;
1495 next = currentEntry.rightChild;
1498 else
1501 * The storage is empty, make the new entry the root of its element tree
1503 currentEntry.dirRootEntry = newEntryIndex;
1504 StorageBaseImpl_WriteDirEntry(This,
1505 parentStorageIndex,
1506 &currentEntry);
1509 return S_OK;
1512 /****************************************************************************
1514 * Internal Method
1516 * Find and read the element of a storage with the given name.
1518 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1519 const OLECHAR *name, DirEntry *data)
1521 DirRef currentEntry;
1523 /* Read the storage entry to find the root of the tree. */
1524 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1526 currentEntry = data->dirRootEntry;
1528 while (currentEntry != DIRENTRY_NULL)
1530 LONG cmp;
1532 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1534 cmp = entryNameCmp(name, data->name);
1536 if (cmp == 0)
1537 /* found it */
1538 break;
1540 else if (cmp < 0)
1541 currentEntry = data->leftChild;
1543 else if (cmp > 0)
1544 currentEntry = data->rightChild;
1547 return currentEntry;
1550 /****************************************************************************
1552 * Internal Method
1554 * Find and read the binary tree parent of the element with the given name.
1556 * If there is no such element, find a place where it could be inserted and
1557 * return STG_E_FILENOTFOUND.
1559 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1560 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1561 ULONG *relation)
1563 DirRef childEntry;
1564 DirEntry childData;
1566 /* Read the storage entry to find the root of the tree. */
1567 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1569 *parentEntry = storageEntry;
1570 *relation = DIRENTRY_RELATION_DIR;
1572 childEntry = parentData->dirRootEntry;
1574 while (childEntry != DIRENTRY_NULL)
1576 LONG cmp;
1578 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1580 cmp = entryNameCmp(childName, childData.name);
1582 if (cmp == 0)
1583 /* found it */
1584 break;
1586 else if (cmp < 0)
1588 *parentData = childData;
1589 *parentEntry = childEntry;
1590 *relation = DIRENTRY_RELATION_PREVIOUS;
1592 childEntry = parentData->leftChild;
1595 else if (cmp > 0)
1597 *parentData = childData;
1598 *parentEntry = childEntry;
1599 *relation = DIRENTRY_RELATION_NEXT;
1601 childEntry = parentData->rightChild;
1605 if (childEntry == DIRENTRY_NULL)
1606 return STG_E_FILENOTFOUND;
1607 else
1608 return S_OK;
1612 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1613 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1614 SNB snbExclude, IStorage *pstgDest);
1616 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
1617 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1618 SNB snbExclude, IStorage *pstgDest)
1620 DirEntry data;
1621 HRESULT hr;
1622 BOOL skip = FALSE;
1623 IStorage *pstgTmp;
1624 IStream *pstrChild, *pstrTmp;
1625 STATSTG strStat;
1627 if (srcEntry == DIRENTRY_NULL)
1628 return S_OK;
1630 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1632 if (FAILED(hr))
1633 return hr;
1635 if ( snbExclude )
1637 WCHAR **snb = snbExclude;
1639 while ( *snb != NULL && !skip )
1641 if ( lstrcmpW(data.name, *snb) == 0 )
1642 skip = TRUE;
1643 ++snb;
1647 if (!skip)
1649 if (data.stgType == STGTY_STORAGE && !skip_storage)
1652 * create a new storage in destination storage
1654 hr = IStorage_CreateStorage( pstgDest, data.name,
1655 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1656 0, 0,
1657 &pstgTmp );
1660 * if it already exist, don't create a new one use this one
1662 if (hr == STG_E_FILEALREADYEXISTS)
1664 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1665 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1666 NULL, 0, &pstgTmp );
1669 if (SUCCEEDED(hr))
1671 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1672 skip_stream, NULL, pstgTmp );
1674 IStorage_Release(pstgTmp);
1677 else if (data.stgType == STGTY_STREAM && !skip_stream)
1680 * create a new stream in destination storage. If the stream already
1681 * exist, it will be deleted and a new one will be created.
1683 hr = IStorage_CreateStream( pstgDest, data.name,
1684 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1685 0, 0, &pstrTmp );
1688 * open child stream storage. This operation must succeed even if the
1689 * stream is already open, so we use internal functions to do it.
1691 if (hr == S_OK)
1693 StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1695 if (streamimpl)
1697 pstrChild = &streamimpl->IStream_iface;
1698 if (pstrChild)
1699 IStream_AddRef(pstrChild);
1701 else
1703 pstrChild = NULL;
1704 hr = E_OUTOFMEMORY;
1708 if (hr == S_OK)
1711 * Get the size of the source stream
1713 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1716 * Set the size of the destination stream.
1718 IStream_SetSize(pstrTmp, strStat.cbSize);
1721 * do the copy
1723 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1724 NULL, NULL );
1726 IStream_Release( pstrChild );
1729 IStream_Release( pstrTmp );
1733 /* copy siblings */
1734 if (SUCCEEDED(hr))
1735 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1736 skip_stream, snbExclude, pstgDest );
1738 if (SUCCEEDED(hr))
1739 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1740 skip_stream, snbExclude, pstgDest );
1742 return hr;
1745 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1746 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1747 SNB snbExclude, IStorage *pstgDest)
1749 DirEntry data;
1750 HRESULT hr;
1752 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1754 if (SUCCEEDED(hr))
1755 hr = IStorage_SetClass( pstgDest, &data.clsid );
1757 if (SUCCEEDED(hr))
1758 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
1759 skip_stream, snbExclude, pstgDest );
1761 return hr;
1764 /*************************************************************************
1765 * CopyTo (IStorage)
1767 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1768 IStorage* iface,
1769 DWORD ciidExclude, /* [in] */
1770 const IID* rgiidExclude, /* [size_is][unique][in] */
1771 SNB snbExclude, /* [unique][in] */
1772 IStorage* pstgDest) /* [unique][in] */
1774 StorageBaseImpl *This = impl_from_IStorage(iface);
1776 BOOL skip_storage = FALSE, skip_stream = FALSE;
1777 DWORD i;
1779 TRACE("(%p, %d, %p, %p, %p)\n",
1780 iface, ciidExclude, rgiidExclude,
1781 snbExclude, pstgDest);
1783 if ( pstgDest == 0 )
1784 return STG_E_INVALIDPOINTER;
1786 for(i = 0; i < ciidExclude; ++i)
1788 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1789 skip_storage = TRUE;
1790 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1791 skip_stream = TRUE;
1792 else
1793 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1796 if (!skip_storage)
1798 /* Give up early if it looks like this would be infinitely recursive.
1799 * Oddly enough, this includes some cases that aren't really recursive, like
1800 * copying to a transacted child. */
1801 IStorage *pstgDestAncestor = pstgDest;
1802 IStorage *pstgDestAncestorChild = NULL;
1804 /* Go up the chain from the destination until we find the source storage. */
1805 while (pstgDestAncestor != iface) {
1806 pstgDestAncestorChild = pstgDest;
1808 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
1810 TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
1812 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
1814 else if (pstgDestAncestor->lpVtbl == &Storage32InternalImpl_Vtbl)
1816 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
1818 pstgDestAncestor = &internal->parentStorage->IStorage_iface;
1820 else
1821 break;
1824 if (pstgDestAncestor == iface)
1826 BOOL fail = TRUE;
1828 if (pstgDestAncestorChild && snbExclude)
1830 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
1831 DirEntry data;
1832 WCHAR **snb = snbExclude;
1834 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
1836 while ( *snb != NULL && fail )
1838 if ( lstrcmpW(data.name, *snb) == 0 )
1839 fail = FALSE;
1840 ++snb;
1844 if (fail)
1845 return STG_E_ACCESSDENIED;
1849 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
1850 skip_storage, skip_stream, snbExclude, pstgDest );
1853 /*************************************************************************
1854 * MoveElementTo (IStorage)
1856 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1857 IStorage* iface,
1858 const OLECHAR *pwcsName, /* [string][in] */
1859 IStorage *pstgDest, /* [unique][in] */
1860 const OLECHAR *pwcsNewName,/* [string][in] */
1861 DWORD grfFlags) /* [in] */
1863 FIXME("(%p %s %p %s %u): stub\n", iface,
1864 debugstr_w(pwcsName), pstgDest,
1865 debugstr_w(pwcsNewName), grfFlags);
1866 return E_NOTIMPL;
1869 /*************************************************************************
1870 * Commit (IStorage)
1872 * Ensures that any changes made to a storage object open in transacted mode
1873 * are reflected in the parent storage
1875 * In a non-transacted mode, this ensures all cached writes are completed.
1877 static HRESULT WINAPI StorageImpl_Commit(
1878 IStorage* iface,
1879 DWORD grfCommitFlags)/* [in] */
1881 StorageBaseImpl* This = impl_from_IStorage(iface);
1882 TRACE("(%p %d)\n", iface, grfCommitFlags);
1883 return StorageBaseImpl_Flush(This);
1886 /*************************************************************************
1887 * Revert (IStorage)
1889 * Discard all changes that have been made since the last commit operation
1891 static HRESULT WINAPI StorageImpl_Revert(
1892 IStorage* iface)
1894 TRACE("(%p)\n", iface);
1895 return S_OK;
1898 /*************************************************************************
1899 * DestroyElement (IStorage)
1901 * Strategy: This implementation is built this way for simplicity not for speed.
1902 * I always delete the topmost element of the enumeration and adjust
1903 * the deleted element pointer all the time. This takes longer to
1904 * do but allow to reinvoke DestroyElement whenever we encounter a
1905 * storage object. The optimisation resides in the usage of another
1906 * enumeration strategy that would give all the leaves of a storage
1907 * first. (postfix order)
1909 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1910 IStorage* iface,
1911 const OLECHAR *pwcsName)/* [string][in] */
1913 StorageBaseImpl *This = impl_from_IStorage(iface);
1915 HRESULT hr = S_OK;
1916 DirEntry entryToDelete;
1917 DirRef entryToDeleteRef;
1919 TRACE("(%p, %s)\n",
1920 iface, debugstr_w(pwcsName));
1922 if (pwcsName==NULL)
1923 return STG_E_INVALIDPOINTER;
1925 if (This->reverted)
1926 return STG_E_REVERTED;
1928 if ( !(This->openFlags & STGM_TRANSACTED) &&
1929 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1930 return STG_E_ACCESSDENIED;
1932 entryToDeleteRef = findElement(
1933 This,
1934 This->storageDirEntry,
1935 pwcsName,
1936 &entryToDelete);
1938 if ( entryToDeleteRef == DIRENTRY_NULL )
1940 return STG_E_FILENOTFOUND;
1943 if ( entryToDelete.stgType == STGTY_STORAGE )
1945 hr = deleteStorageContents(
1946 This,
1947 entryToDeleteRef,
1948 entryToDelete);
1950 else if ( entryToDelete.stgType == STGTY_STREAM )
1952 hr = deleteStreamContents(
1953 This,
1954 entryToDeleteRef,
1955 entryToDelete);
1958 if (hr!=S_OK)
1959 return hr;
1962 * Remove the entry from its parent storage
1964 hr = removeFromTree(
1965 This,
1966 This->storageDirEntry,
1967 entryToDeleteRef);
1970 * Invalidate the entry
1972 if (SUCCEEDED(hr))
1973 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1975 if (SUCCEEDED(hr))
1976 hr = StorageBaseImpl_Flush(This);
1978 return hr;
1982 /******************************************************************************
1983 * Internal stream list handlers
1986 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1988 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1989 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1992 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1994 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1995 list_remove(&(strm->StrmListEntry));
1998 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
2000 StgStreamImpl *strm;
2002 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
2004 if (strm->dirEntry == streamEntry)
2006 return TRUE;
2010 return FALSE;
2013 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
2015 StorageInternalImpl *childstg;
2017 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
2019 if (childstg->base.storageDirEntry == storageEntry)
2021 return TRUE;
2025 return FALSE;
2028 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2030 struct list *cur, *cur2;
2031 StgStreamImpl *strm=NULL;
2032 StorageInternalImpl *childstg=NULL;
2034 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2035 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2036 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2037 strm->parentStorage = NULL;
2038 list_remove(cur);
2041 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2042 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2043 StorageBaseImpl_Invalidate( &childstg->base );
2046 if (stg->transactedChild)
2048 StorageBaseImpl_Invalidate(stg->transactedChild);
2050 stg->transactedChild = NULL;
2055 /*********************************************************************
2057 * Internal Method
2059 * Delete the contents of a storage entry.
2062 static HRESULT deleteStorageContents(
2063 StorageBaseImpl *parentStorage,
2064 DirRef indexToDelete,
2065 DirEntry entryDataToDelete)
2067 IEnumSTATSTG *elements = 0;
2068 IStorage *childStorage = 0;
2069 STATSTG currentElement;
2070 HRESULT hr;
2071 HRESULT destroyHr = S_OK;
2072 StorageInternalImpl *stg, *stg2;
2074 /* Invalidate any open storage objects. */
2075 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2077 if (stg->base.storageDirEntry == indexToDelete)
2079 StorageBaseImpl_Invalidate(&stg->base);
2084 * Open the storage and enumerate it
2086 hr = IStorage_OpenStorage(
2087 &parentStorage->IStorage_iface,
2088 entryDataToDelete.name,
2090 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2093 &childStorage);
2095 if (hr != S_OK)
2097 return hr;
2101 * Enumerate the elements
2103 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2108 * Obtain the next element
2110 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2111 if (hr==S_OK)
2113 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2115 CoTaskMemFree(currentElement.pwcsName);
2119 * We need to Reset the enumeration every time because we delete elements
2120 * and the enumeration could be invalid
2122 IEnumSTATSTG_Reset(elements);
2124 } while ((hr == S_OK) && (destroyHr == S_OK));
2126 IStorage_Release(childStorage);
2127 IEnumSTATSTG_Release(elements);
2129 return destroyHr;
2132 /*********************************************************************
2134 * Internal Method
2136 * Perform the deletion of a stream's data
2139 static HRESULT deleteStreamContents(
2140 StorageBaseImpl *parentStorage,
2141 DirRef indexToDelete,
2142 DirEntry entryDataToDelete)
2144 IStream *pis;
2145 HRESULT hr;
2146 ULARGE_INTEGER size;
2147 StgStreamImpl *strm, *strm2;
2149 /* Invalidate any open stream objects. */
2150 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2152 if (strm->dirEntry == indexToDelete)
2154 TRACE("Stream deleted %p\n", strm);
2155 strm->parentStorage = NULL;
2156 list_remove(&strm->StrmListEntry);
2160 size.u.HighPart = 0;
2161 size.u.LowPart = 0;
2163 hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2164 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2166 if (hr!=S_OK)
2168 return(hr);
2172 * Zap the stream
2174 hr = IStream_SetSize(pis, size);
2176 if(hr != S_OK)
2178 return hr;
2182 * Release the stream object.
2184 IStream_Release(pis);
2186 return S_OK;
2189 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2191 switch (relation)
2193 case DIRENTRY_RELATION_PREVIOUS:
2194 entry->leftChild = new_target;
2195 break;
2196 case DIRENTRY_RELATION_NEXT:
2197 entry->rightChild = new_target;
2198 break;
2199 case DIRENTRY_RELATION_DIR:
2200 entry->dirRootEntry = new_target;
2201 break;
2202 default:
2203 assert(0);
2207 /*************************************************************************
2209 * Internal Method
2211 * This method removes a directory entry from its parent storage tree without
2212 * freeing any resources attached to it.
2214 static HRESULT removeFromTree(
2215 StorageBaseImpl *This,
2216 DirRef parentStorageIndex,
2217 DirRef deletedIndex)
2219 DirEntry entryToDelete;
2220 DirEntry parentEntry;
2221 DirRef parentEntryRef;
2222 ULONG typeOfRelation;
2223 HRESULT hr;
2225 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2227 if (hr != S_OK)
2228 return hr;
2231 * Find the element that links to the one we want to delete.
2233 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2234 &parentEntry, &parentEntryRef, &typeOfRelation);
2236 if (hr != S_OK)
2237 return hr;
2239 if (entryToDelete.leftChild != DIRENTRY_NULL)
2242 * Replace the deleted entry with its left child
2244 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2246 hr = StorageBaseImpl_WriteDirEntry(
2247 This,
2248 parentEntryRef,
2249 &parentEntry);
2250 if(FAILED(hr))
2252 return hr;
2255 if (entryToDelete.rightChild != DIRENTRY_NULL)
2258 * We need to reinsert the right child somewhere. We already know it and
2259 * its children are greater than everything in the left tree, so we
2260 * insert it at the rightmost point in the left tree.
2262 DirRef newRightChildParent = entryToDelete.leftChild;
2263 DirEntry newRightChildParentEntry;
2267 hr = StorageBaseImpl_ReadDirEntry(
2268 This,
2269 newRightChildParent,
2270 &newRightChildParentEntry);
2271 if (FAILED(hr))
2273 return hr;
2276 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2277 newRightChildParent = newRightChildParentEntry.rightChild;
2278 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2280 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2282 hr = StorageBaseImpl_WriteDirEntry(
2283 This,
2284 newRightChildParent,
2285 &newRightChildParentEntry);
2286 if (FAILED(hr))
2288 return hr;
2292 else
2295 * Replace the deleted entry with its right child
2297 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2299 hr = StorageBaseImpl_WriteDirEntry(
2300 This,
2301 parentEntryRef,
2302 &parentEntry);
2303 if(FAILED(hr))
2305 return hr;
2309 return hr;
2313 /******************************************************************************
2314 * SetElementTimes (IStorage)
2316 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2317 IStorage* iface,
2318 const OLECHAR *pwcsName,/* [string][in] */
2319 const FILETIME *pctime, /* [in] */
2320 const FILETIME *patime, /* [in] */
2321 const FILETIME *pmtime) /* [in] */
2323 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2324 return S_OK;
2327 /******************************************************************************
2328 * SetStateBits (IStorage)
2330 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2331 IStorage* iface,
2332 DWORD grfStateBits,/* [in] */
2333 DWORD grfMask) /* [in] */
2335 StorageBaseImpl *This = impl_from_IStorage(iface);
2337 if (This->reverted)
2338 return STG_E_REVERTED;
2340 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2341 return S_OK;
2344 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2345 DirRef index, const DirEntry *data)
2347 StorageImpl *This = (StorageImpl*)base;
2348 return StorageImpl_WriteDirEntry(This, index, data);
2351 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2352 DirRef index, DirEntry *data)
2354 StorageImpl *This = (StorageImpl*)base;
2355 return StorageImpl_ReadDirEntry(This, index, data);
2358 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2360 int i;
2362 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2364 if (!This->blockChainCache[i])
2366 return &This->blockChainCache[i];
2370 i = This->blockChainToEvict;
2372 BlockChainStream_Destroy(This->blockChainCache[i]);
2373 This->blockChainCache[i] = NULL;
2375 This->blockChainToEvict++;
2376 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2377 This->blockChainToEvict = 0;
2379 return &This->blockChainCache[i];
2382 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2383 DirRef index)
2385 int i, free_index=-1;
2387 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2389 if (!This->blockChainCache[i])
2391 if (free_index == -1) free_index = i;
2393 else if (This->blockChainCache[i]->ownerDirEntry == index)
2395 return &This->blockChainCache[i];
2399 if (free_index == -1)
2401 free_index = This->blockChainToEvict;
2403 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2404 This->blockChainCache[free_index] = NULL;
2406 This->blockChainToEvict++;
2407 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2408 This->blockChainToEvict = 0;
2411 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2412 return &This->blockChainCache[free_index];
2415 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2417 int i;
2419 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2421 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2423 BlockChainStream_Destroy(This->blockChainCache[i]);
2424 This->blockChainCache[i] = NULL;
2425 return;
2430 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2431 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2433 StorageImpl *This = (StorageImpl*)base;
2434 DirEntry data;
2435 HRESULT hr;
2436 ULONG bytesToRead;
2438 hr = StorageImpl_ReadDirEntry(This, index, &data);
2439 if (FAILED(hr)) return hr;
2441 if (data.size.QuadPart == 0)
2443 *bytesRead = 0;
2444 return S_OK;
2447 if (offset.QuadPart + size > data.size.QuadPart)
2449 bytesToRead = data.size.QuadPart - offset.QuadPart;
2451 else
2453 bytesToRead = size;
2456 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2458 SmallBlockChainStream *stream;
2460 stream = SmallBlockChainStream_Construct(This, NULL, index);
2461 if (!stream) return E_OUTOFMEMORY;
2463 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2465 SmallBlockChainStream_Destroy(stream);
2467 return hr;
2469 else
2471 BlockChainStream *stream = NULL;
2473 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2474 if (!stream) return E_OUTOFMEMORY;
2476 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2478 return hr;
2482 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2483 ULARGE_INTEGER newsize)
2485 StorageImpl *This = (StorageImpl*)base;
2486 DirEntry data;
2487 HRESULT hr;
2488 SmallBlockChainStream *smallblock=NULL;
2489 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2491 hr = StorageImpl_ReadDirEntry(This, index, &data);
2492 if (FAILED(hr)) return hr;
2494 /* In simple mode keep the stream size above the small block limit */
2495 if (This->base.openFlags & STGM_SIMPLE)
2496 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2498 if (data.size.QuadPart == newsize.QuadPart)
2499 return S_OK;
2501 /* Create a block chain object of the appropriate type */
2502 if (data.size.QuadPart == 0)
2504 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2506 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2507 if (!smallblock) return E_OUTOFMEMORY;
2509 else
2511 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2512 bigblock = *pbigblock;
2513 if (!bigblock) return E_OUTOFMEMORY;
2516 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2518 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2519 if (!smallblock) return E_OUTOFMEMORY;
2521 else
2523 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2524 bigblock = *pbigblock;
2525 if (!bigblock) return E_OUTOFMEMORY;
2528 /* Change the block chain type if necessary. */
2529 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2531 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2532 if (!bigblock)
2534 SmallBlockChainStream_Destroy(smallblock);
2535 return E_FAIL;
2538 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2539 *pbigblock = bigblock;
2541 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2543 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
2544 if (!smallblock)
2545 return E_FAIL;
2548 /* Set the size of the block chain. */
2549 if (smallblock)
2551 SmallBlockChainStream_SetSize(smallblock, newsize);
2552 SmallBlockChainStream_Destroy(smallblock);
2554 else
2556 BlockChainStream_SetSize(bigblock, newsize);
2559 /* Set the size in the directory entry. */
2560 hr = StorageImpl_ReadDirEntry(This, index, &data);
2561 if (SUCCEEDED(hr))
2563 data.size = newsize;
2565 hr = StorageImpl_WriteDirEntry(This, index, &data);
2567 return hr;
2570 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2571 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2573 StorageImpl *This = (StorageImpl*)base;
2574 DirEntry data;
2575 HRESULT hr;
2576 ULARGE_INTEGER newSize;
2578 hr = StorageImpl_ReadDirEntry(This, index, &data);
2579 if (FAILED(hr)) return hr;
2581 /* Grow the stream if necessary */
2582 newSize.QuadPart = 0;
2583 newSize.QuadPart = offset.QuadPart + size;
2585 if (newSize.QuadPart > data.size.QuadPart)
2587 hr = StorageImpl_StreamSetSize(base, index, newSize);
2588 if (FAILED(hr))
2589 return hr;
2591 hr = StorageImpl_ReadDirEntry(This, index, &data);
2592 if (FAILED(hr)) return hr;
2595 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2597 SmallBlockChainStream *stream;
2599 stream = SmallBlockChainStream_Construct(This, NULL, index);
2600 if (!stream) return E_OUTOFMEMORY;
2602 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2604 SmallBlockChainStream_Destroy(stream);
2606 return hr;
2608 else
2610 BlockChainStream *stream;
2612 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2613 if (!stream) return E_OUTOFMEMORY;
2615 return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2619 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2620 DirRef src)
2622 StorageImpl *This = (StorageImpl*)base;
2623 DirEntry dst_data, src_data;
2624 HRESULT hr;
2626 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2628 if (SUCCEEDED(hr))
2629 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2631 if (SUCCEEDED(hr))
2633 StorageImpl_DeleteCachedBlockChainStream(This, src);
2634 dst_data.startingBlock = src_data.startingBlock;
2635 dst_data.size = src_data.size;
2637 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2640 return hr;
2643 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2645 StorageImpl *This = (StorageImpl*) iface;
2646 STATSTG statstg;
2647 HRESULT hr;
2649 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2651 *result = statstg.pwcsName;
2653 return hr;
2656 static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
2658 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2659 return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
2662 static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
2664 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2665 return IStorage_AddRef(&This->IStorage_iface);
2668 static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
2670 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2671 return IStorage_Release(&This->IStorage_iface);
2674 static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
2676 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2677 FIXME("(%p)->(%d): stub\n", This, timeout);
2678 return E_NOTIMPL;
2681 static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
2683 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2684 FIXME("(%p): stub\n", This);
2685 return E_NOTIMPL;
2688 static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
2690 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2691 FIXME("(%p): stub\n", This);
2692 return E_NOTIMPL;
2695 static const IDirectWriterLockVtbl DirectWriterLockVtbl =
2697 directwriterlock_QueryInterface,
2698 directwriterlock_AddRef,
2699 directwriterlock_Release,
2700 directwriterlock_WaitForWriteAccess,
2701 directwriterlock_ReleaseWriteAccess,
2702 directwriterlock_HaveWriteAccess
2706 * Virtual function table for the IStorage32Impl class.
2708 static const IStorageVtbl Storage32Impl_Vtbl =
2710 StorageBaseImpl_QueryInterface,
2711 StorageBaseImpl_AddRef,
2712 StorageBaseImpl_Release,
2713 StorageBaseImpl_CreateStream,
2714 StorageBaseImpl_OpenStream,
2715 StorageBaseImpl_CreateStorage,
2716 StorageBaseImpl_OpenStorage,
2717 StorageBaseImpl_CopyTo,
2718 StorageBaseImpl_MoveElementTo,
2719 StorageImpl_Commit,
2720 StorageImpl_Revert,
2721 StorageBaseImpl_EnumElements,
2722 StorageBaseImpl_DestroyElement,
2723 StorageBaseImpl_RenameElement,
2724 StorageBaseImpl_SetElementTimes,
2725 StorageBaseImpl_SetClass,
2726 StorageBaseImpl_SetStateBits,
2727 StorageBaseImpl_Stat
2730 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2732 StorageImpl_Destroy,
2733 StorageImpl_Invalidate,
2734 StorageImpl_Flush,
2735 StorageImpl_GetFilename,
2736 StorageImpl_CreateDirEntry,
2737 StorageImpl_BaseWriteDirEntry,
2738 StorageImpl_BaseReadDirEntry,
2739 StorageImpl_DestroyDirEntry,
2740 StorageImpl_StreamReadAt,
2741 StorageImpl_StreamWriteAt,
2742 StorageImpl_StreamSetSize,
2743 StorageImpl_StreamLink
2746 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
2747 ULARGE_INTEGER cb, DWORD dwLockType)
2749 HRESULT hr;
2751 /* potential optimization: if we have an HFILE use LockFileEx in blocking mode directly */
2755 int delay=0;
2757 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
2759 if (hr == STG_E_ACCESSDENIED)
2761 Sleep(delay);
2762 if (delay < 150) delay++;
2764 } while (hr == STG_E_ACCESSDENIED);
2766 return hr;
2769 static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
2770 ULONG end, HRESULT fail_hr)
2772 HRESULT hr;
2773 ULARGE_INTEGER offset, cb;
2775 offset.QuadPart = start;
2776 cb.QuadPart = 1 + end - start;
2778 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2779 if (SUCCEEDED(hr)) ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2781 if (hr == STG_E_ACCESSDENIED)
2782 return fail_hr;
2783 else
2784 return S_OK;
2787 static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
2789 HRESULT hr=S_OK;
2790 int i, j;
2791 ULARGE_INTEGER offset, cb;
2793 cb.QuadPart = 1;
2795 for (i=start; i<=end; i++)
2797 offset.QuadPart = i;
2798 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2799 if (hr != STG_E_ACCESSDENIED)
2800 break;
2803 if (SUCCEEDED(hr))
2805 for (j=0; j<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); j++)
2807 if (This->locked_bytes[j] == 0)
2809 This->locked_bytes[j] = i;
2810 break;
2815 return hr;
2818 static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
2820 HRESULT hr;
2821 ULARGE_INTEGER offset;
2822 ULARGE_INTEGER cb;
2823 DWORD share_mode = STGM_SHARE_MODE(openFlags);
2825 /* Wrap all other locking inside a single lock so we can check ranges safely */
2826 offset.QuadPart = RANGELOCK_CHECKLOCKS;
2827 cb.QuadPart = 1;
2828 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
2830 /* If the ILockBytes doesn't support locking that's ok. */
2831 if (FAILED(hr)) return S_OK;
2833 hr = S_OK;
2835 /* First check for any conflicting locks. */
2836 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
2837 hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
2839 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
2840 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
2842 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
2843 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
2845 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
2846 hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
2848 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
2849 hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
2851 /* Then grab our locks. */
2852 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
2854 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
2855 if (SUCCEEDED(hr))
2856 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
2859 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
2860 hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
2862 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
2863 hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
2865 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
2866 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
2868 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
2869 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
2871 offset.QuadPart = RANGELOCK_CHECKLOCKS;
2872 cb.QuadPart = 1;
2873 ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2875 return hr;
2878 static HRESULT StorageImpl_Construct(
2879 HANDLE hFile,
2880 LPCOLESTR pwcsName,
2881 ILockBytes* pLkbyt,
2882 DWORD openFlags,
2883 BOOL fileBased,
2884 BOOL create,
2885 ULONG sector_size,
2886 StorageImpl** result)
2888 StorageImpl* This;
2889 HRESULT hr = S_OK;
2890 DirEntry currentEntry;
2891 DirRef currentEntryRef;
2893 if ( FAILED( validateSTGM(openFlags) ))
2894 return STG_E_INVALIDFLAG;
2896 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2897 if (!This)
2898 return E_OUTOFMEMORY;
2900 memset(This, 0, sizeof(StorageImpl));
2902 list_init(&This->base.strmHead);
2904 list_init(&This->base.storageHead);
2906 This->base.IStorage_iface.lpVtbl = &Storage32Impl_Vtbl;
2907 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
2908 This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
2909 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2910 This->base.openFlags = (openFlags & ~STGM_CREATE);
2911 This->base.ref = 1;
2912 This->base.create = create;
2914 if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
2915 This->base.lockingrole = SWMR_Writer;
2916 else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
2917 This->base.lockingrole = SWMR_Reader;
2918 else
2919 This->base.lockingrole = SWMR_None;
2921 This->base.reverted = FALSE;
2924 * Initialize the big block cache.
2926 This->bigBlockSize = sector_size;
2927 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2928 if (hFile)
2929 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2930 else
2932 This->lockBytes = pLkbyt;
2933 ILockBytes_AddRef(pLkbyt);
2936 if (FAILED(hr))
2937 goto end;
2939 hr = StorageImpl_GrabLocks(This, openFlags);
2941 if (FAILED(hr))
2942 goto end;
2944 if (create)
2946 ULARGE_INTEGER size;
2947 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2949 /* Discard any existing data. */
2950 size.QuadPart = 0;
2951 ILockBytes_SetSize(This->lockBytes, size);
2954 * Initialize all header variables:
2955 * - The big block depot consists of one block and it is at block 0
2956 * - The directory table starts at block 1
2957 * - There is no small block depot
2959 memset( This->bigBlockDepotStart,
2960 BLOCK_UNUSED,
2961 sizeof(This->bigBlockDepotStart));
2963 This->bigBlockDepotCount = 1;
2964 This->bigBlockDepotStart[0] = 0;
2965 This->rootStartBlock = 1;
2966 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2967 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2968 if (sector_size == 4096)
2969 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2970 else
2971 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2972 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2973 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2974 This->extBigBlockDepotCount = 0;
2976 StorageImpl_SaveFileHeader(This);
2979 * Add one block for the big block depot and one block for the directory table
2981 size.u.HighPart = 0;
2982 size.u.LowPart = This->bigBlockSize * 3;
2983 ILockBytes_SetSize(This->lockBytes, size);
2986 * Initialize the big block depot
2988 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2989 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2990 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2991 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2993 else
2996 * Load the header for the file.
2998 hr = StorageImpl_LoadFileHeader(This);
3000 if (FAILED(hr))
3002 goto end;
3007 * There is no block depot cached yet.
3009 This->indexBlockDepotCached = 0xFFFFFFFF;
3010 This->indexExtBlockDepotCached = 0xFFFFFFFF;
3013 * Start searching for free blocks with block 0.
3015 This->prevFreeBlock = 0;
3017 This->firstFreeSmallBlock = 0;
3019 /* Read the extended big block depot locations. */
3020 if (This->extBigBlockDepotCount != 0)
3022 ULONG current_block = This->extBigBlockDepotStart;
3023 ULONG cache_size = This->extBigBlockDepotCount * 2;
3024 ULONG i;
3026 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
3027 if (!This->extBigBlockDepotLocations)
3029 hr = E_OUTOFMEMORY;
3030 goto end;
3033 This->extBigBlockDepotLocationsSize = cache_size;
3035 for (i=0; i<This->extBigBlockDepotCount; i++)
3037 if (current_block == BLOCK_END_OF_CHAIN)
3039 WARN("File has too few extended big block depot blocks.\n");
3040 hr = STG_E_DOCFILECORRUPT;
3041 goto end;
3043 This->extBigBlockDepotLocations[i] = current_block;
3044 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
3047 else
3049 This->extBigBlockDepotLocations = NULL;
3050 This->extBigBlockDepotLocationsSize = 0;
3054 * Create the block chain abstractions.
3056 if(!(This->rootBlockChain =
3057 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
3059 hr = STG_E_READFAULT;
3060 goto end;
3063 if(!(This->smallBlockDepotChain =
3064 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
3065 DIRENTRY_NULL)))
3067 hr = STG_E_READFAULT;
3068 goto end;
3072 * Write the root storage entry (memory only)
3074 if (create)
3076 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
3077 DirEntry rootEntry;
3079 * Initialize the directory table
3081 memset(&rootEntry, 0, sizeof(rootEntry));
3082 strcpyW(rootEntry.name, rootentryW);
3083 rootEntry.sizeOfNameString = sizeof(rootentryW);
3084 rootEntry.stgType = STGTY_ROOT;
3085 rootEntry.leftChild = DIRENTRY_NULL;
3086 rootEntry.rightChild = DIRENTRY_NULL;
3087 rootEntry.dirRootEntry = DIRENTRY_NULL;
3088 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
3089 rootEntry.size.u.HighPart = 0;
3090 rootEntry.size.u.LowPart = 0;
3092 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
3096 * Find the ID of the root storage.
3098 currentEntryRef = 0;
3102 hr = StorageImpl_ReadDirEntry(
3103 This,
3104 currentEntryRef,
3105 &currentEntry);
3107 if (SUCCEEDED(hr))
3109 if ( (currentEntry.sizeOfNameString != 0 ) &&
3110 (currentEntry.stgType == STGTY_ROOT) )
3112 This->base.storageDirEntry = currentEntryRef;
3116 currentEntryRef++;
3118 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
3120 if (FAILED(hr))
3122 hr = STG_E_READFAULT;
3123 goto end;
3127 * Create the block chain abstraction for the small block root chain.
3129 if(!(This->smallBlockRootChain =
3130 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
3132 hr = STG_E_READFAULT;
3135 end:
3136 if (FAILED(hr))
3138 IStorage_Release(&This->base.IStorage_iface);
3139 *result = NULL;
3141 else
3143 StorageImpl_Flush(&This->base);
3144 *result = This;
3147 return hr;
3150 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
3152 StorageImpl *This = (StorageImpl*) iface;
3154 StorageBaseImpl_DeleteAll(&This->base);
3156 This->base.reverted = TRUE;
3159 static void StorageImpl_Destroy(StorageBaseImpl* iface)
3161 StorageImpl *This = (StorageImpl*) iface;
3162 int i;
3163 TRACE("(%p)\n", This);
3165 StorageImpl_Flush(iface);
3167 StorageImpl_Invalidate(iface);
3169 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3171 BlockChainStream_Destroy(This->smallBlockRootChain);
3172 BlockChainStream_Destroy(This->rootBlockChain);
3173 BlockChainStream_Destroy(This->smallBlockDepotChain);
3175 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
3176 BlockChainStream_Destroy(This->blockChainCache[i]);
3178 for (i=0; i<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); i++)
3180 ULARGE_INTEGER offset, cb;
3181 cb.QuadPart = 1;
3182 if (This->locked_bytes[i] != 0)
3184 offset.QuadPart = This->locked_bytes[i];
3185 ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
3189 if (This->lockBytes)
3190 ILockBytes_Release(This->lockBytes);
3191 HeapFree(GetProcessHeap(), 0, This);
3194 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
3196 StorageImpl *This = (StorageImpl*)storage;
3197 int i;
3198 HRESULT hr;
3199 TRACE("(%p)\n", This);
3201 hr = BlockChainStream_Flush(This->smallBlockRootChain);
3203 if (SUCCEEDED(hr))
3204 hr = BlockChainStream_Flush(This->rootBlockChain);
3206 if (SUCCEEDED(hr))
3207 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
3209 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
3210 if (This->blockChainCache[i])
3211 hr = BlockChainStream_Flush(This->blockChainCache[i]);
3213 if (SUCCEEDED(hr))
3214 hr = ILockBytes_Flush(This->lockBytes);
3216 return hr;
3219 /******************************************************************************
3220 * Storage32Impl_GetNextFreeBigBlock
3222 * Returns the index of the next free big block.
3223 * If the big block depot is filled, this method will enlarge it.
3226 static ULONG StorageImpl_GetNextFreeBigBlock(
3227 StorageImpl* This)
3229 ULONG depotBlockIndexPos;
3230 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3231 ULONG depotBlockOffset;
3232 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3233 ULONG nextBlockIndex = BLOCK_SPECIAL;
3234 int depotIndex = 0;
3235 ULONG freeBlock = BLOCK_UNUSED;
3236 ULONG read;
3237 ULARGE_INTEGER neededSize;
3238 STATSTG statstg;
3240 depotIndex = This->prevFreeBlock / blocksPerDepot;
3241 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
3244 * Scan the entire big block depot until we find a block marked free
3246 while (nextBlockIndex != BLOCK_UNUSED)
3248 if (depotIndex < COUNT_BBDEPOTINHEADER)
3250 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
3253 * Grow the primary depot.
3255 if (depotBlockIndexPos == BLOCK_UNUSED)
3257 depotBlockIndexPos = depotIndex*blocksPerDepot;
3260 * Add a block depot.
3262 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
3263 This->bigBlockDepotCount++;
3264 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3267 * Flag it as a block depot.
3269 StorageImpl_SetNextBlockInChain(This,
3270 depotBlockIndexPos,
3271 BLOCK_SPECIAL);
3273 /* Save new header information.
3275 StorageImpl_SaveFileHeader(This);
3278 else
3280 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3282 if (depotBlockIndexPos == BLOCK_UNUSED)
3285 * Grow the extended depot.
3287 ULONG extIndex = BLOCK_UNUSED;
3288 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3289 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3291 if (extBlockOffset == 0)
3293 /* We need an extended block.
3295 extIndex = Storage32Impl_AddExtBlockDepot(This);
3296 This->extBigBlockDepotCount++;
3297 depotBlockIndexPos = extIndex + 1;
3299 else
3300 depotBlockIndexPos = depotIndex * blocksPerDepot;
3303 * Add a block depot and mark it in the extended block.
3305 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
3306 This->bigBlockDepotCount++;
3307 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3309 /* Flag the block depot.
3311 StorageImpl_SetNextBlockInChain(This,
3312 depotBlockIndexPos,
3313 BLOCK_SPECIAL);
3315 /* If necessary, flag the extended depot block.
3317 if (extIndex != BLOCK_UNUSED)
3318 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3320 /* Save header information.
3322 StorageImpl_SaveFileHeader(This);
3326 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
3328 if (read)
3330 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3331 ( nextBlockIndex != BLOCK_UNUSED))
3333 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3335 if (nextBlockIndex == BLOCK_UNUSED)
3337 freeBlock = (depotIndex * blocksPerDepot) +
3338 (depotBlockOffset/sizeof(ULONG));
3341 depotBlockOffset += sizeof(ULONG);
3345 depotIndex++;
3346 depotBlockOffset = 0;
3350 * make sure that the block physically exists before using it
3352 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3354 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3356 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3357 ILockBytes_SetSize(This->lockBytes, neededSize);
3359 This->prevFreeBlock = freeBlock;
3361 return freeBlock;
3364 /******************************************************************************
3365 * Storage32Impl_AddBlockDepot
3367 * This will create a depot block, essentially it is a block initialized
3368 * to BLOCK_UNUSEDs.
3370 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
3372 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3373 ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
3374 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3375 ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
3378 * Initialize blocks as free
3380 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3382 /* Reserve the range lock sector */
3383 if (depotIndex == rangeLockDepot)
3385 ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
3388 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3391 /******************************************************************************
3392 * Storage32Impl_GetExtDepotBlock
3394 * Returns the index of the block that corresponds to the specified depot
3395 * index. This method is only for depot indexes equal or greater than
3396 * COUNT_BBDEPOTINHEADER.
3398 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3400 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3401 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3402 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3403 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3404 ULONG blockIndex = BLOCK_UNUSED;
3405 ULONG extBlockIndex;
3406 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3407 int index, num_blocks;
3409 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3411 if (extBlockCount >= This->extBigBlockDepotCount)
3412 return BLOCK_UNUSED;
3414 if (This->indexExtBlockDepotCached != extBlockCount)
3416 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3418 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
3420 num_blocks = This->bigBlockSize / 4;
3422 for (index = 0; index < num_blocks; index++)
3424 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3425 This->extBlockDepotCached[index] = blockIndex;
3428 This->indexExtBlockDepotCached = extBlockCount;
3431 blockIndex = This->extBlockDepotCached[extBlockOffset];
3433 return blockIndex;
3436 /******************************************************************************
3437 * Storage32Impl_SetExtDepotBlock
3439 * Associates the specified block index to the specified depot index.
3440 * This method is only for depot indexes equal or greater than
3441 * COUNT_BBDEPOTINHEADER.
3443 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3445 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3446 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3447 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3448 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3449 ULONG extBlockIndex;
3451 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3453 assert(extBlockCount < This->extBigBlockDepotCount);
3455 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3457 if (extBlockIndex != BLOCK_UNUSED)
3459 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3460 extBlockOffset * sizeof(ULONG),
3461 blockIndex);
3464 if (This->indexExtBlockDepotCached == extBlockCount)
3466 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3470 /******************************************************************************
3471 * Storage32Impl_AddExtBlockDepot
3473 * Creates an extended depot block.
3475 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3477 ULONG numExtBlocks = This->extBigBlockDepotCount;
3478 ULONG nextExtBlock = This->extBigBlockDepotStart;
3479 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3480 ULONG index = BLOCK_UNUSED;
3481 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3482 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3483 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3485 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3486 blocksPerDepotBlock;
3488 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3491 * The first extended block.
3493 This->extBigBlockDepotStart = index;
3495 else
3498 * Find the last existing extended block.
3500 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3503 * Add the new extended block to the chain.
3505 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3506 index);
3510 * Initialize this block.
3512 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3513 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3515 /* Add the block to our cache. */
3516 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3518 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3519 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3521 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3522 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3524 This->extBigBlockDepotLocations = new_cache;
3525 This->extBigBlockDepotLocationsSize = new_cache_size;
3527 This->extBigBlockDepotLocations[numExtBlocks] = index;
3529 return index;
3532 /******************************************************************************
3533 * Storage32Impl_FreeBigBlock
3535 * This method will flag the specified block as free in the big block depot.
3537 static void StorageImpl_FreeBigBlock(
3538 StorageImpl* This,
3539 ULONG blockIndex)
3541 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3543 if (blockIndex < This->prevFreeBlock)
3544 This->prevFreeBlock = blockIndex;
3547 /************************************************************************
3548 * Storage32Impl_GetNextBlockInChain
3550 * This method will retrieve the block index of the next big block in
3551 * in the chain.
3553 * Params: This - Pointer to the Storage object.
3554 * blockIndex - Index of the block to retrieve the chain
3555 * for.
3556 * nextBlockIndex - receives the return value.
3558 * Returns: This method returns the index of the next block in the chain.
3559 * It will return the constants:
3560 * BLOCK_SPECIAL - If the block given was not part of a
3561 * chain.
3562 * BLOCK_END_OF_CHAIN - If the block given was the last in
3563 * a chain.
3564 * BLOCK_UNUSED - If the block given was not past of a chain
3565 * and is available.
3566 * BLOCK_EXTBBDEPOT - This block is part of the extended
3567 * big block depot.
3569 * See Windows documentation for more details on IStorage methods.
3571 static HRESULT StorageImpl_GetNextBlockInChain(
3572 StorageImpl* This,
3573 ULONG blockIndex,
3574 ULONG* nextBlockIndex)
3576 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3577 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3578 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3579 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3580 ULONG read;
3581 ULONG depotBlockIndexPos;
3582 int index, num_blocks;
3584 *nextBlockIndex = BLOCK_SPECIAL;
3586 if(depotBlockCount >= This->bigBlockDepotCount)
3588 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3589 This->bigBlockDepotCount);
3590 return STG_E_READFAULT;
3594 * Cache the currently accessed depot block.
3596 if (depotBlockCount != This->indexBlockDepotCached)
3598 This->indexBlockDepotCached = depotBlockCount;
3600 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3602 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3604 else
3607 * We have to look in the extended depot.
3609 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3612 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
3614 if (!read)
3615 return STG_E_READFAULT;
3617 num_blocks = This->bigBlockSize / 4;
3619 for (index = 0; index < num_blocks; index++)
3621 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3622 This->blockDepotCached[index] = *nextBlockIndex;
3626 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3628 return S_OK;
3631 /******************************************************************************
3632 * Storage32Impl_GetNextExtendedBlock
3634 * Given an extended block this method will return the next extended block.
3636 * NOTES:
3637 * The last ULONG of an extended block is the block index of the next
3638 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3639 * depot.
3641 * Return values:
3642 * - The index of the next extended block
3643 * - BLOCK_UNUSED: there is no next extended block.
3644 * - Any other return values denotes failure.
3646 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3648 ULONG nextBlockIndex = BLOCK_SPECIAL;
3649 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3651 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3652 &nextBlockIndex);
3654 return nextBlockIndex;
3657 /******************************************************************************
3658 * Storage32Impl_SetNextBlockInChain
3660 * This method will write the index of the specified block's next block
3661 * in the big block depot.
3663 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3664 * do the following
3666 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3667 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3668 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3671 static void StorageImpl_SetNextBlockInChain(
3672 StorageImpl* This,
3673 ULONG blockIndex,
3674 ULONG nextBlock)
3676 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3677 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3678 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3679 ULONG depotBlockIndexPos;
3681 assert(depotBlockCount < This->bigBlockDepotCount);
3682 assert(blockIndex != nextBlock);
3684 if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
3685 /* This should never happen (storage file format spec forbids it), but
3686 * older versions of Wine may have generated broken files. We don't want to
3687 * assert and potentially lose data, but we do want to know if this ever
3688 * happens in a newly-created file. */
3689 ERR("Using range lock page\n");
3691 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3693 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3695 else
3698 * We have to look in the extended depot.
3700 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3703 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3704 nextBlock);
3706 * Update the cached block depot, if necessary.
3708 if (depotBlockCount == This->indexBlockDepotCached)
3710 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3714 /******************************************************************************
3715 * Storage32Impl_LoadFileHeader
3717 * This method will read in the file header
3719 static HRESULT StorageImpl_LoadFileHeader(
3720 StorageImpl* This)
3722 HRESULT hr;
3723 BYTE headerBigBlock[HEADER_SIZE];
3724 int index;
3725 ULARGE_INTEGER offset;
3726 DWORD bytes_read;
3728 TRACE("\n");
3730 * Get a pointer to the big block of data containing the header.
3732 offset.u.HighPart = 0;
3733 offset.u.LowPart = 0;
3734 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3735 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3736 hr = STG_E_FILENOTFOUND;
3739 * Extract the information from the header.
3741 if (SUCCEEDED(hr))
3744 * Check for the "magic number" signature and return an error if it is not
3745 * found.
3747 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3749 return STG_E_OLDFORMAT;
3752 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3754 return STG_E_INVALIDHEADER;
3757 StorageUtl_ReadWord(
3758 headerBigBlock,
3759 OFFSET_BIGBLOCKSIZEBITS,
3760 &This->bigBlockSizeBits);
3762 StorageUtl_ReadWord(
3763 headerBigBlock,
3764 OFFSET_SMALLBLOCKSIZEBITS,
3765 &This->smallBlockSizeBits);
3767 StorageUtl_ReadDWord(
3768 headerBigBlock,
3769 OFFSET_BBDEPOTCOUNT,
3770 &This->bigBlockDepotCount);
3772 StorageUtl_ReadDWord(
3773 headerBigBlock,
3774 OFFSET_ROOTSTARTBLOCK,
3775 &This->rootStartBlock);
3777 StorageUtl_ReadDWord(
3778 headerBigBlock,
3779 OFFSET_SMALLBLOCKLIMIT,
3780 &This->smallBlockLimit);
3782 StorageUtl_ReadDWord(
3783 headerBigBlock,
3784 OFFSET_SBDEPOTSTART,
3785 &This->smallBlockDepotStart);
3787 StorageUtl_ReadDWord(
3788 headerBigBlock,
3789 OFFSET_EXTBBDEPOTSTART,
3790 &This->extBigBlockDepotStart);
3792 StorageUtl_ReadDWord(
3793 headerBigBlock,
3794 OFFSET_EXTBBDEPOTCOUNT,
3795 &This->extBigBlockDepotCount);
3797 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3799 StorageUtl_ReadDWord(
3800 headerBigBlock,
3801 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3802 &(This->bigBlockDepotStart[index]));
3806 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3808 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3809 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3812 * Right now, the code is making some assumptions about the size of the
3813 * blocks, just make sure they are what we're expecting.
3815 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3816 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3817 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3819 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3820 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3821 hr = STG_E_INVALIDHEADER;
3823 else
3824 hr = S_OK;
3827 return hr;
3830 /******************************************************************************
3831 * Storage32Impl_SaveFileHeader
3833 * This method will save to the file the header
3835 static void StorageImpl_SaveFileHeader(
3836 StorageImpl* This)
3838 BYTE headerBigBlock[HEADER_SIZE];
3839 int index;
3840 HRESULT hr;
3841 ULARGE_INTEGER offset;
3842 DWORD bytes_read, bytes_written;
3843 DWORD major_version, dirsectorcount;
3846 * Get a pointer to the big block of data containing the header.
3848 offset.u.HighPart = 0;
3849 offset.u.LowPart = 0;
3850 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3851 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3852 hr = STG_E_FILENOTFOUND;
3854 if (This->bigBlockSizeBits == 0x9)
3855 major_version = 3;
3856 else if (This->bigBlockSizeBits == 0xc)
3857 major_version = 4;
3858 else
3860 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3861 major_version = 4;
3865 * If the block read failed, the file is probably new.
3867 if (FAILED(hr))
3870 * Initialize for all unknown fields.
3872 memset(headerBigBlock, 0, HEADER_SIZE);
3875 * Initialize the magic number.
3877 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3881 * Write the information to the header.
3883 StorageUtl_WriteWord(
3884 headerBigBlock,
3885 OFFSET_MINORVERSION,
3886 0x3e);
3888 StorageUtl_WriteWord(
3889 headerBigBlock,
3890 OFFSET_MAJORVERSION,
3891 major_version);
3893 StorageUtl_WriteWord(
3894 headerBigBlock,
3895 OFFSET_BYTEORDERMARKER,
3896 (WORD)-2);
3898 StorageUtl_WriteWord(
3899 headerBigBlock,
3900 OFFSET_BIGBLOCKSIZEBITS,
3901 This->bigBlockSizeBits);
3903 StorageUtl_WriteWord(
3904 headerBigBlock,
3905 OFFSET_SMALLBLOCKSIZEBITS,
3906 This->smallBlockSizeBits);
3908 if (major_version >= 4)
3910 if (This->rootBlockChain)
3911 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3912 else
3913 /* This file is being created, and it will start out with one block. */
3914 dirsectorcount = 1;
3916 else
3917 /* This field must be 0 in versions older than 4 */
3918 dirsectorcount = 0;
3920 StorageUtl_WriteDWord(
3921 headerBigBlock,
3922 OFFSET_DIRSECTORCOUNT,
3923 dirsectorcount);
3925 StorageUtl_WriteDWord(
3926 headerBigBlock,
3927 OFFSET_BBDEPOTCOUNT,
3928 This->bigBlockDepotCount);
3930 StorageUtl_WriteDWord(
3931 headerBigBlock,
3932 OFFSET_ROOTSTARTBLOCK,
3933 This->rootStartBlock);
3935 StorageUtl_WriteDWord(
3936 headerBigBlock,
3937 OFFSET_SMALLBLOCKLIMIT,
3938 This->smallBlockLimit);
3940 StorageUtl_WriteDWord(
3941 headerBigBlock,
3942 OFFSET_SBDEPOTSTART,
3943 This->smallBlockDepotStart);
3945 StorageUtl_WriteDWord(
3946 headerBigBlock,
3947 OFFSET_SBDEPOTCOUNT,
3948 This->smallBlockDepotChain ?
3949 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3951 StorageUtl_WriteDWord(
3952 headerBigBlock,
3953 OFFSET_EXTBBDEPOTSTART,
3954 This->extBigBlockDepotStart);
3956 StorageUtl_WriteDWord(
3957 headerBigBlock,
3958 OFFSET_EXTBBDEPOTCOUNT,
3959 This->extBigBlockDepotCount);
3961 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3963 StorageUtl_WriteDWord(
3964 headerBigBlock,
3965 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3966 (This->bigBlockDepotStart[index]));
3970 * Write the big block back to the file.
3972 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3975 /******************************************************************************
3976 * StorageImpl_ReadRawDirEntry
3978 * This method will read the raw data from a directory entry in the file.
3980 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3982 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3984 ULARGE_INTEGER offset;
3985 HRESULT hr;
3986 ULONG bytesRead;
3988 offset.u.HighPart = 0;
3989 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3991 hr = BlockChainStream_ReadAt(
3992 This->rootBlockChain,
3993 offset,
3994 RAW_DIRENTRY_SIZE,
3995 buffer,
3996 &bytesRead);
3998 if (bytesRead != RAW_DIRENTRY_SIZE)
3999 return STG_E_READFAULT;
4001 return hr;
4004 /******************************************************************************
4005 * StorageImpl_WriteRawDirEntry
4007 * This method will write the raw data from a directory entry in the file.
4009 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4011 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
4013 ULARGE_INTEGER offset;
4014 ULONG bytesRead;
4016 offset.u.HighPart = 0;
4017 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
4019 return BlockChainStream_WriteAt(
4020 This->rootBlockChain,
4021 offset,
4022 RAW_DIRENTRY_SIZE,
4023 buffer,
4024 &bytesRead);
4027 /******************************************************************************
4028 * UpdateRawDirEntry
4030 * Update raw directory entry data from the fields in newData.
4032 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4034 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
4036 memset(buffer, 0, RAW_DIRENTRY_SIZE);
4038 memcpy(
4039 buffer + OFFSET_PS_NAME,
4040 newData->name,
4041 DIRENTRY_NAME_BUFFER_LEN );
4043 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
4045 StorageUtl_WriteWord(
4046 buffer,
4047 OFFSET_PS_NAMELENGTH,
4048 newData->sizeOfNameString);
4050 StorageUtl_WriteDWord(
4051 buffer,
4052 OFFSET_PS_LEFTCHILD,
4053 newData->leftChild);
4055 StorageUtl_WriteDWord(
4056 buffer,
4057 OFFSET_PS_RIGHTCHILD,
4058 newData->rightChild);
4060 StorageUtl_WriteDWord(
4061 buffer,
4062 OFFSET_PS_DIRROOT,
4063 newData->dirRootEntry);
4065 StorageUtl_WriteGUID(
4066 buffer,
4067 OFFSET_PS_GUID,
4068 &newData->clsid);
4070 StorageUtl_WriteDWord(
4071 buffer,
4072 OFFSET_PS_CTIMELOW,
4073 newData->ctime.dwLowDateTime);
4075 StorageUtl_WriteDWord(
4076 buffer,
4077 OFFSET_PS_CTIMEHIGH,
4078 newData->ctime.dwHighDateTime);
4080 StorageUtl_WriteDWord(
4081 buffer,
4082 OFFSET_PS_MTIMELOW,
4083 newData->mtime.dwLowDateTime);
4085 StorageUtl_WriteDWord(
4086 buffer,
4087 OFFSET_PS_MTIMEHIGH,
4088 newData->ctime.dwHighDateTime);
4090 StorageUtl_WriteDWord(
4091 buffer,
4092 OFFSET_PS_STARTBLOCK,
4093 newData->startingBlock);
4095 StorageUtl_WriteDWord(
4096 buffer,
4097 OFFSET_PS_SIZE,
4098 newData->size.u.LowPart);
4101 /******************************************************************************
4102 * Storage32Impl_ReadDirEntry
4104 * This method will read the specified directory entry.
4106 HRESULT StorageImpl_ReadDirEntry(
4107 StorageImpl* This,
4108 DirRef index,
4109 DirEntry* buffer)
4111 BYTE currentEntry[RAW_DIRENTRY_SIZE];
4112 HRESULT readRes;
4114 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
4116 if (SUCCEEDED(readRes))
4118 memset(buffer->name, 0, sizeof(buffer->name));
4119 memcpy(
4120 buffer->name,
4121 (WCHAR *)currentEntry+OFFSET_PS_NAME,
4122 DIRENTRY_NAME_BUFFER_LEN );
4123 TRACE("storage name: %s\n", debugstr_w(buffer->name));
4125 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
4127 StorageUtl_ReadWord(
4128 currentEntry,
4129 OFFSET_PS_NAMELENGTH,
4130 &buffer->sizeOfNameString);
4132 StorageUtl_ReadDWord(
4133 currentEntry,
4134 OFFSET_PS_LEFTCHILD,
4135 &buffer->leftChild);
4137 StorageUtl_ReadDWord(
4138 currentEntry,
4139 OFFSET_PS_RIGHTCHILD,
4140 &buffer->rightChild);
4142 StorageUtl_ReadDWord(
4143 currentEntry,
4144 OFFSET_PS_DIRROOT,
4145 &buffer->dirRootEntry);
4147 StorageUtl_ReadGUID(
4148 currentEntry,
4149 OFFSET_PS_GUID,
4150 &buffer->clsid);
4152 StorageUtl_ReadDWord(
4153 currentEntry,
4154 OFFSET_PS_CTIMELOW,
4155 &buffer->ctime.dwLowDateTime);
4157 StorageUtl_ReadDWord(
4158 currentEntry,
4159 OFFSET_PS_CTIMEHIGH,
4160 &buffer->ctime.dwHighDateTime);
4162 StorageUtl_ReadDWord(
4163 currentEntry,
4164 OFFSET_PS_MTIMELOW,
4165 &buffer->mtime.dwLowDateTime);
4167 StorageUtl_ReadDWord(
4168 currentEntry,
4169 OFFSET_PS_MTIMEHIGH,
4170 &buffer->mtime.dwHighDateTime);
4172 StorageUtl_ReadDWord(
4173 currentEntry,
4174 OFFSET_PS_STARTBLOCK,
4175 &buffer->startingBlock);
4177 StorageUtl_ReadDWord(
4178 currentEntry,
4179 OFFSET_PS_SIZE,
4180 &buffer->size.u.LowPart);
4182 buffer->size.u.HighPart = 0;
4185 return readRes;
4188 /*********************************************************************
4189 * Write the specified directory entry to the file
4191 HRESULT StorageImpl_WriteDirEntry(
4192 StorageImpl* This,
4193 DirRef index,
4194 const DirEntry* buffer)
4196 BYTE currentEntry[RAW_DIRENTRY_SIZE];
4198 UpdateRawDirEntry(currentEntry, buffer);
4200 return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
4203 static HRESULT StorageImpl_ReadBigBlock(
4204 StorageImpl* This,
4205 ULONG blockIndex,
4206 void* buffer,
4207 ULONG* out_read)
4209 ULARGE_INTEGER ulOffset;
4210 DWORD read=0;
4211 HRESULT hr;
4213 ulOffset.u.HighPart = 0;
4214 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4216 hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
4218 if (SUCCEEDED(hr) && read < This->bigBlockSize)
4220 /* File ends during this block; fill the rest with 0's. */
4221 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
4224 if (out_read) *out_read = read;
4226 return hr;
4229 static BOOL StorageImpl_ReadDWordFromBigBlock(
4230 StorageImpl* This,
4231 ULONG blockIndex,
4232 ULONG offset,
4233 DWORD* value)
4235 ULARGE_INTEGER ulOffset;
4236 DWORD read;
4237 DWORD tmp;
4239 ulOffset.u.HighPart = 0;
4240 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4241 ulOffset.u.LowPart += offset;
4243 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
4244 *value = lendian32toh(tmp);
4245 return (read == sizeof(DWORD));
4248 static BOOL StorageImpl_WriteBigBlock(
4249 StorageImpl* This,
4250 ULONG blockIndex,
4251 const void* buffer)
4253 ULARGE_INTEGER ulOffset;
4254 DWORD wrote;
4256 ulOffset.u.HighPart = 0;
4257 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4259 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
4260 return (wrote == This->bigBlockSize);
4263 static BOOL StorageImpl_WriteDWordToBigBlock(
4264 StorageImpl* This,
4265 ULONG blockIndex,
4266 ULONG offset,
4267 DWORD value)
4269 ULARGE_INTEGER ulOffset;
4270 DWORD wrote;
4272 ulOffset.u.HighPart = 0;
4273 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4274 ulOffset.u.LowPart += offset;
4276 value = htole32(value);
4277 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4278 return (wrote == sizeof(DWORD));
4281 /******************************************************************************
4282 * Storage32Impl_SmallBlocksToBigBlocks
4284 * This method will convert a small block chain to a big block chain.
4285 * The small block chain will be destroyed.
4287 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4288 StorageImpl* This,
4289 SmallBlockChainStream** ppsbChain)
4291 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4292 ULARGE_INTEGER size, offset;
4293 ULONG cbRead, cbWritten;
4294 ULARGE_INTEGER cbTotalRead;
4295 DirRef streamEntryRef;
4296 HRESULT resWrite = S_OK;
4297 HRESULT resRead;
4298 DirEntry streamEntry;
4299 BYTE *buffer;
4300 BlockChainStream *bbTempChain = NULL;
4301 BlockChainStream *bigBlockChain = NULL;
4304 * Create a temporary big block chain that doesn't have
4305 * an associated directory entry. This temporary chain will be
4306 * used to copy data from small blocks to big blocks.
4308 bbTempChain = BlockChainStream_Construct(This,
4309 &bbHeadOfChain,
4310 DIRENTRY_NULL);
4311 if(!bbTempChain) return NULL;
4313 * Grow the big block chain.
4315 size = SmallBlockChainStream_GetSize(*ppsbChain);
4316 BlockChainStream_SetSize(bbTempChain, size);
4319 * Copy the contents of the small block chain to the big block chain
4320 * by small block size increments.
4322 offset.u.LowPart = 0;
4323 offset.u.HighPart = 0;
4324 cbTotalRead.QuadPart = 0;
4326 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4329 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4330 offset,
4331 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4332 buffer,
4333 &cbRead);
4334 if (FAILED(resRead))
4335 break;
4337 if (cbRead > 0)
4339 cbTotalRead.QuadPart += cbRead;
4341 resWrite = BlockChainStream_WriteAt(bbTempChain,
4342 offset,
4343 cbRead,
4344 buffer,
4345 &cbWritten);
4347 if (FAILED(resWrite))
4348 break;
4350 offset.u.LowPart += cbRead;
4352 else
4354 resRead = STG_E_READFAULT;
4355 break;
4357 } while (cbTotalRead.QuadPart < size.QuadPart);
4358 HeapFree(GetProcessHeap(),0,buffer);
4360 size.u.HighPart = 0;
4361 size.u.LowPart = 0;
4363 if (FAILED(resRead) || FAILED(resWrite))
4365 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4366 BlockChainStream_SetSize(bbTempChain, size);
4367 BlockChainStream_Destroy(bbTempChain);
4368 return NULL;
4372 * Destroy the small block chain.
4374 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4375 SmallBlockChainStream_SetSize(*ppsbChain, size);
4376 SmallBlockChainStream_Destroy(*ppsbChain);
4377 *ppsbChain = 0;
4380 * Change the directory entry. This chain is now a big block chain
4381 * and it doesn't reside in the small blocks chain anymore.
4383 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4385 streamEntry.startingBlock = bbHeadOfChain;
4387 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4390 * Destroy the temporary entryless big block chain.
4391 * Create a new big block chain associated with this entry.
4393 BlockChainStream_Destroy(bbTempChain);
4394 bigBlockChain = BlockChainStream_Construct(This,
4395 NULL,
4396 streamEntryRef);
4398 return bigBlockChain;
4401 /******************************************************************************
4402 * Storage32Impl_BigBlocksToSmallBlocks
4404 * This method will convert a big block chain to a small block chain.
4405 * The big block chain will be destroyed on success.
4407 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4408 StorageImpl* This,
4409 BlockChainStream** ppbbChain,
4410 ULARGE_INTEGER newSize)
4412 ULARGE_INTEGER size, offset, cbTotalRead;
4413 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4414 DirRef streamEntryRef;
4415 HRESULT resWrite = S_OK, resRead = S_OK;
4416 DirEntry streamEntry;
4417 BYTE* buffer;
4418 SmallBlockChainStream* sbTempChain;
4420 TRACE("%p %p\n", This, ppbbChain);
4422 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4423 DIRENTRY_NULL);
4425 if(!sbTempChain)
4426 return NULL;
4428 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4429 size = BlockChainStream_GetSize(*ppbbChain);
4430 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4432 offset.u.HighPart = 0;
4433 offset.u.LowPart = 0;
4434 cbTotalRead.QuadPart = 0;
4435 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4436 while(cbTotalRead.QuadPart < size.QuadPart)
4438 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4439 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4440 buffer, &cbRead);
4442 if(FAILED(resRead))
4443 break;
4445 if(cbRead > 0)
4447 cbTotalRead.QuadPart += cbRead;
4449 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4450 cbRead, buffer, &cbWritten);
4452 if(FAILED(resWrite))
4453 break;
4455 offset.u.LowPart += cbRead;
4457 else
4459 resRead = STG_E_READFAULT;
4460 break;
4463 HeapFree(GetProcessHeap(), 0, buffer);
4465 size.u.HighPart = 0;
4466 size.u.LowPart = 0;
4468 if(FAILED(resRead) || FAILED(resWrite))
4470 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4471 SmallBlockChainStream_SetSize(sbTempChain, size);
4472 SmallBlockChainStream_Destroy(sbTempChain);
4473 return NULL;
4476 /* destroy the original big block chain */
4477 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4478 BlockChainStream_SetSize(*ppbbChain, size);
4479 BlockChainStream_Destroy(*ppbbChain);
4480 *ppbbChain = NULL;
4482 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4483 streamEntry.startingBlock = sbHeadOfChain;
4484 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4486 SmallBlockChainStream_Destroy(sbTempChain);
4487 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4490 static HRESULT StorageBaseImpl_CopyStream(
4491 StorageBaseImpl *dst, DirRef dst_entry,
4492 StorageBaseImpl *src, DirRef src_entry)
4494 HRESULT hr;
4495 BYTE data[4096];
4496 DirEntry srcdata;
4497 ULARGE_INTEGER bytes_copied;
4498 ULONG bytestocopy, bytesread, byteswritten;
4500 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4502 if (SUCCEEDED(hr))
4504 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4506 bytes_copied.QuadPart = 0;
4507 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4509 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4511 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4512 data, &bytesread);
4513 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4515 if (SUCCEEDED(hr))
4516 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4517 data, &byteswritten);
4518 if (SUCCEEDED(hr))
4520 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4521 bytes_copied.QuadPart += byteswritten;
4526 return hr;
4529 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4531 DirRef result=This->firstFreeEntry;
4533 while (result < This->entries_size && This->entries[result].inuse)
4534 result++;
4536 if (result == This->entries_size)
4538 ULONG new_size = This->entries_size * 2;
4539 TransactedDirEntry *new_entries;
4541 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4542 if (!new_entries) return DIRENTRY_NULL;
4544 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4545 HeapFree(GetProcessHeap(), 0, This->entries);
4547 This->entries = new_entries;
4548 This->entries_size = new_size;
4551 This->entries[result].inuse = TRUE;
4553 This->firstFreeEntry = result+1;
4555 return result;
4558 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4559 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4561 DirRef stubEntryRef;
4562 TransactedDirEntry *entry;
4564 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4566 if (stubEntryRef != DIRENTRY_NULL)
4568 entry = &This->entries[stubEntryRef];
4570 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4572 entry->read = FALSE;
4575 return stubEntryRef;
4578 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4579 TransactedSnapshotImpl *This, DirRef entry)
4581 HRESULT hr=S_OK;
4582 DirEntry data;
4584 if (!This->entries[entry].read)
4586 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4587 This->entries[entry].transactedParentEntry,
4588 &data);
4590 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4592 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4594 if (data.leftChild == DIRENTRY_NULL)
4595 hr = E_OUTOFMEMORY;
4598 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4600 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4602 if (data.rightChild == DIRENTRY_NULL)
4603 hr = E_OUTOFMEMORY;
4606 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4608 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4610 if (data.dirRootEntry == DIRENTRY_NULL)
4611 hr = E_OUTOFMEMORY;
4614 if (SUCCEEDED(hr))
4616 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4617 This->entries[entry].read = TRUE;
4621 return hr;
4624 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4625 TransactedSnapshotImpl *This, DirRef entry)
4627 HRESULT hr = S_OK;
4629 if (!This->entries[entry].stream_dirty)
4631 DirEntry new_entrydata;
4633 memset(&new_entrydata, 0, sizeof(DirEntry));
4634 new_entrydata.name[0] = 'S';
4635 new_entrydata.sizeOfNameString = 1;
4636 new_entrydata.stgType = STGTY_STREAM;
4637 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4638 new_entrydata.leftChild = DIRENTRY_NULL;
4639 new_entrydata.rightChild = DIRENTRY_NULL;
4640 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4642 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4643 &This->entries[entry].stream_entry);
4645 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4647 hr = StorageBaseImpl_CopyStream(
4648 This->scratch, This->entries[entry].stream_entry,
4649 This->transactedParent, This->entries[entry].transactedParentEntry);
4651 if (FAILED(hr))
4652 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4655 if (SUCCEEDED(hr))
4656 This->entries[entry].stream_dirty = TRUE;
4658 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4660 /* Since this entry is modified, and we aren't using its stream data, we
4661 * no longer care about the original entry. */
4662 DirRef delete_ref;
4663 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4665 if (delete_ref != DIRENTRY_NULL)
4666 This->entries[delete_ref].deleted = TRUE;
4668 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4672 return hr;
4675 /* Find the first entry in a depth-first traversal. */
4676 static DirRef TransactedSnapshotImpl_FindFirstChild(
4677 TransactedSnapshotImpl* This, DirRef parent)
4679 DirRef cursor, prev;
4680 TransactedDirEntry *entry;
4682 cursor = parent;
4683 entry = &This->entries[cursor];
4684 while (entry->read)
4686 if (entry->data.leftChild != DIRENTRY_NULL)
4688 prev = cursor;
4689 cursor = entry->data.leftChild;
4690 entry = &This->entries[cursor];
4691 entry->parent = prev;
4693 else if (entry->data.rightChild != DIRENTRY_NULL)
4695 prev = cursor;
4696 cursor = entry->data.rightChild;
4697 entry = &This->entries[cursor];
4698 entry->parent = prev;
4700 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4702 prev = cursor;
4703 cursor = entry->data.dirRootEntry;
4704 entry = &This->entries[cursor];
4705 entry->parent = prev;
4707 else
4708 break;
4711 return cursor;
4714 /* Find the next entry in a depth-first traversal. */
4715 static DirRef TransactedSnapshotImpl_FindNextChild(
4716 TransactedSnapshotImpl* This, DirRef current)
4718 DirRef parent;
4719 TransactedDirEntry *parent_entry;
4721 parent = This->entries[current].parent;
4722 parent_entry = &This->entries[parent];
4724 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4726 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4728 This->entries[parent_entry->data.rightChild].parent = parent;
4729 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4732 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4734 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4735 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4739 return parent;
4742 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4743 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4744 TransactedSnapshotImpl* This, DirRef entry)
4746 return entry != DIRENTRY_NULL &&
4747 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4750 /* Destroy the entries created by CopyTree. */
4751 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4752 TransactedSnapshotImpl* This, DirRef stop)
4754 DirRef cursor;
4755 TransactedDirEntry *entry;
4756 ULARGE_INTEGER zero;
4758 zero.QuadPart = 0;
4760 if (!This->entries[This->base.storageDirEntry].read)
4761 return;
4763 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4765 if (cursor == DIRENTRY_NULL)
4766 return;
4768 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4770 while (cursor != DIRENTRY_NULL && cursor != stop)
4772 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4774 entry = &This->entries[cursor];
4776 if (entry->stream_dirty)
4777 StorageBaseImpl_StreamSetSize(This->transactedParent,
4778 entry->newTransactedParentEntry, zero);
4780 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4781 entry->newTransactedParentEntry);
4783 entry->newTransactedParentEntry = entry->transactedParentEntry;
4786 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4790 /* Make a copy of our edited tree that we can use in the parent. */
4791 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4793 DirRef cursor;
4794 TransactedDirEntry *entry;
4795 HRESULT hr = S_OK;
4797 cursor = This->base.storageDirEntry;
4798 entry = &This->entries[cursor];
4799 entry->parent = DIRENTRY_NULL;
4800 entry->newTransactedParentEntry = entry->transactedParentEntry;
4802 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4803 return S_OK;
4805 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4807 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4808 entry = &This->entries[cursor];
4810 while (cursor != DIRENTRY_NULL)
4812 /* Make a copy of this entry in the transacted parent. */
4813 if (!entry->read ||
4814 (!entry->dirty && !entry->stream_dirty &&
4815 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4816 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4817 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4818 entry->newTransactedParentEntry = entry->transactedParentEntry;
4819 else
4821 DirEntry newData;
4823 memcpy(&newData, &entry->data, sizeof(DirEntry));
4825 newData.size.QuadPart = 0;
4826 newData.startingBlock = BLOCK_END_OF_CHAIN;
4828 if (newData.leftChild != DIRENTRY_NULL)
4829 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4831 if (newData.rightChild != DIRENTRY_NULL)
4832 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4834 if (newData.dirRootEntry != DIRENTRY_NULL)
4835 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4837 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4838 &entry->newTransactedParentEntry);
4839 if (FAILED(hr))
4841 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4842 return hr;
4845 if (entry->stream_dirty)
4847 hr = StorageBaseImpl_CopyStream(
4848 This->transactedParent, entry->newTransactedParentEntry,
4849 This->scratch, entry->stream_entry);
4851 else if (entry->data.size.QuadPart)
4853 hr = StorageBaseImpl_StreamLink(
4854 This->transactedParent, entry->newTransactedParentEntry,
4855 entry->transactedParentEntry);
4858 if (FAILED(hr))
4860 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4861 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4862 return hr;
4866 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4867 entry = &This->entries[cursor];
4870 return hr;
4873 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4874 IStorage* iface,
4875 DWORD grfCommitFlags) /* [in] */
4877 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
4878 TransactedDirEntry *root_entry;
4879 DirRef i, dir_root_ref;
4880 DirEntry data;
4881 ULARGE_INTEGER zero;
4882 HRESULT hr;
4884 zero.QuadPart = 0;
4886 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4888 /* Cannot commit a read-only transacted storage */
4889 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4890 return STG_E_ACCESSDENIED;
4892 /* To prevent data loss, we create the new structure in the file before we
4893 * delete the old one, so that in case of errors the old data is intact. We
4894 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4895 * needed in the rare situation where we have just enough free disk space to
4896 * overwrite the existing data. */
4898 root_entry = &This->entries[This->base.storageDirEntry];
4900 if (!root_entry->read)
4901 return S_OK;
4903 hr = TransactedSnapshotImpl_CopyTree(This);
4904 if (FAILED(hr)) return hr;
4906 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4907 dir_root_ref = DIRENTRY_NULL;
4908 else
4909 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4911 hr = StorageBaseImpl_Flush(This->transactedParent);
4913 /* Update the storage to use the new data in one step. */
4914 if (SUCCEEDED(hr))
4915 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4916 root_entry->transactedParentEntry, &data);
4918 if (SUCCEEDED(hr))
4920 data.dirRootEntry = dir_root_ref;
4921 data.clsid = root_entry->data.clsid;
4922 data.ctime = root_entry->data.ctime;
4923 data.mtime = root_entry->data.mtime;
4925 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4926 root_entry->transactedParentEntry, &data);
4929 /* Try to flush after updating the root storage, but if the flush fails, keep
4930 * going, on the theory that it'll either succeed later or the subsequent
4931 * writes will fail. */
4932 StorageBaseImpl_Flush(This->transactedParent);
4934 if (SUCCEEDED(hr))
4936 /* Destroy the old now-orphaned data. */
4937 for (i=0; i<This->entries_size; i++)
4939 TransactedDirEntry *entry = &This->entries[i];
4940 if (entry->inuse)
4942 if (entry->deleted)
4944 StorageBaseImpl_StreamSetSize(This->transactedParent,
4945 entry->transactedParentEntry, zero);
4946 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4947 entry->transactedParentEntry);
4948 memset(entry, 0, sizeof(TransactedDirEntry));
4949 This->firstFreeEntry = min(i, This->firstFreeEntry);
4951 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4953 if (entry->transactedParentEntry != DIRENTRY_NULL)
4954 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4955 entry->transactedParentEntry);
4956 if (entry->stream_dirty)
4958 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4959 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4960 entry->stream_dirty = FALSE;
4962 entry->dirty = FALSE;
4963 entry->transactedParentEntry = entry->newTransactedParentEntry;
4968 else
4970 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4973 if (SUCCEEDED(hr))
4974 hr = StorageBaseImpl_Flush(This->transactedParent);
4976 return hr;
4979 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4980 IStorage* iface)
4982 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
4983 ULARGE_INTEGER zero;
4984 ULONG i;
4986 TRACE("(%p)\n", iface);
4988 /* Destroy the open objects. */
4989 StorageBaseImpl_DeleteAll(&This->base);
4991 /* Clear out the scratch file. */
4992 zero.QuadPart = 0;
4993 for (i=0; i<This->entries_size; i++)
4995 if (This->entries[i].stream_dirty)
4997 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4998 zero);
5000 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
5004 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
5006 This->firstFreeEntry = 0;
5007 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
5009 return S_OK;
5012 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
5014 if (!This->reverted)
5016 TRACE("Storage invalidated (stg=%p)\n", This);
5018 This->reverted = TRUE;
5020 StorageBaseImpl_DeleteAll(This);
5024 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
5026 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
5028 IStorage_Revert(&This->base.IStorage_iface);
5029 IStorage_Release(&This->transactedParent->IStorage_iface);
5030 IStorage_Release(&This->scratch->IStorage_iface);
5031 HeapFree(GetProcessHeap(), 0, This->entries);
5032 HeapFree(GetProcessHeap(), 0, This);
5035 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
5037 /* We only need to flush when committing. */
5038 return S_OK;
5041 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5043 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
5045 return StorageBaseImpl_GetFilename(This->transactedParent, result);
5048 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
5049 const DirEntry *newData, DirRef *index)
5051 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5052 DirRef new_ref;
5053 TransactedDirEntry *new_entry;
5055 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
5056 if (new_ref == DIRENTRY_NULL)
5057 return E_OUTOFMEMORY;
5059 new_entry = &This->entries[new_ref];
5061 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
5062 new_entry->read = TRUE;
5063 new_entry->dirty = TRUE;
5064 memcpy(&new_entry->data, newData, sizeof(DirEntry));
5066 *index = new_ref;
5068 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
5070 return S_OK;
5073 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
5074 DirRef index, const DirEntry *data)
5076 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5077 HRESULT hr;
5079 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
5081 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5082 if (FAILED(hr)) return hr;
5084 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
5086 if (index != This->base.storageDirEntry)
5088 This->entries[index].dirty = TRUE;
5090 if (data->size.QuadPart == 0 &&
5091 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
5093 /* Since this entry is modified, and we aren't using its stream data, we
5094 * no longer care about the original entry. */
5095 DirRef delete_ref;
5096 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
5098 if (delete_ref != DIRENTRY_NULL)
5099 This->entries[delete_ref].deleted = TRUE;
5101 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5105 return S_OK;
5108 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
5109 DirRef index, DirEntry *data)
5111 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5112 HRESULT hr;
5114 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5115 if (FAILED(hr)) return hr;
5117 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
5119 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
5121 return S_OK;
5124 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
5125 DirRef index)
5127 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5129 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
5130 This->entries[index].data.size.QuadPart != 0)
5132 /* If we deleted this entry while it has stream data. We must have left the
5133 * data because some other entry is using it, and we need to leave the
5134 * original entry alone. */
5135 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
5136 This->firstFreeEntry = min(index, This->firstFreeEntry);
5138 else
5140 This->entries[index].deleted = TRUE;
5143 return S_OK;
5146 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
5147 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5149 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5151 if (This->entries[index].stream_dirty)
5153 return StorageBaseImpl_StreamReadAt(This->scratch,
5154 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
5156 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
5158 /* This stream doesn't live in the parent, and we haven't allocated storage
5159 * for it yet */
5160 *bytesRead = 0;
5161 return S_OK;
5163 else
5165 return StorageBaseImpl_StreamReadAt(This->transactedParent,
5166 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
5170 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
5171 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5173 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5174 HRESULT hr;
5176 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5177 if (FAILED(hr)) return hr;
5179 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5180 if (FAILED(hr)) return hr;
5182 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
5183 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
5185 if (SUCCEEDED(hr) && size != 0)
5186 This->entries[index].data.size.QuadPart = max(
5187 This->entries[index].data.size.QuadPart,
5188 offset.QuadPart + size);
5190 return hr;
5193 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
5194 DirRef index, ULARGE_INTEGER newsize)
5196 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5197 HRESULT hr;
5199 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5200 if (FAILED(hr)) return hr;
5202 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
5203 return S_OK;
5205 if (newsize.QuadPart == 0)
5207 /* Destroy any parent references or entries in the scratch file. */
5208 if (This->entries[index].stream_dirty)
5210 ULARGE_INTEGER zero;
5211 zero.QuadPart = 0;
5212 StorageBaseImpl_StreamSetSize(This->scratch,
5213 This->entries[index].stream_entry, zero);
5214 StorageBaseImpl_DestroyDirEntry(This->scratch,
5215 This->entries[index].stream_entry);
5216 This->entries[index].stream_dirty = FALSE;
5218 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
5220 DirRef delete_ref;
5221 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
5223 if (delete_ref != DIRENTRY_NULL)
5224 This->entries[delete_ref].deleted = TRUE;
5226 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5229 else
5231 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5232 if (FAILED(hr)) return hr;
5234 hr = StorageBaseImpl_StreamSetSize(This->scratch,
5235 This->entries[index].stream_entry, newsize);
5238 if (SUCCEEDED(hr))
5239 This->entries[index].data.size = newsize;
5241 return hr;
5244 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
5245 DirRef dst, DirRef src)
5247 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5248 HRESULT hr;
5249 TransactedDirEntry *dst_entry, *src_entry;
5251 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
5252 if (FAILED(hr)) return hr;
5254 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
5255 if (FAILED(hr)) return hr;
5257 dst_entry = &This->entries[dst];
5258 src_entry = &This->entries[src];
5260 dst_entry->stream_dirty = src_entry->stream_dirty;
5261 dst_entry->stream_entry = src_entry->stream_entry;
5262 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
5263 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
5264 dst_entry->data.size = src_entry->data.size;
5266 return S_OK;
5269 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5271 StorageBaseImpl_QueryInterface,
5272 StorageBaseImpl_AddRef,
5273 StorageBaseImpl_Release,
5274 StorageBaseImpl_CreateStream,
5275 StorageBaseImpl_OpenStream,
5276 StorageBaseImpl_CreateStorage,
5277 StorageBaseImpl_OpenStorage,
5278 StorageBaseImpl_CopyTo,
5279 StorageBaseImpl_MoveElementTo,
5280 TransactedSnapshotImpl_Commit,
5281 TransactedSnapshotImpl_Revert,
5282 StorageBaseImpl_EnumElements,
5283 StorageBaseImpl_DestroyElement,
5284 StorageBaseImpl_RenameElement,
5285 StorageBaseImpl_SetElementTimes,
5286 StorageBaseImpl_SetClass,
5287 StorageBaseImpl_SetStateBits,
5288 StorageBaseImpl_Stat
5291 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5293 TransactedSnapshotImpl_Destroy,
5294 TransactedSnapshotImpl_Invalidate,
5295 TransactedSnapshotImpl_Flush,
5296 TransactedSnapshotImpl_GetFilename,
5297 TransactedSnapshotImpl_CreateDirEntry,
5298 TransactedSnapshotImpl_WriteDirEntry,
5299 TransactedSnapshotImpl_ReadDirEntry,
5300 TransactedSnapshotImpl_DestroyDirEntry,
5301 TransactedSnapshotImpl_StreamReadAt,
5302 TransactedSnapshotImpl_StreamWriteAt,
5303 TransactedSnapshotImpl_StreamSetSize,
5304 TransactedSnapshotImpl_StreamLink
5307 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5308 TransactedSnapshotImpl** result)
5310 HRESULT hr;
5312 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5313 if (*result)
5315 IStorage *scratch;
5317 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5319 /* This is OK because the property set storage functions use the IStorage functions. */
5320 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
5321 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5323 list_init(&(*result)->base.strmHead);
5325 list_init(&(*result)->base.storageHead);
5327 (*result)->base.ref = 1;
5329 (*result)->base.openFlags = parentStorage->openFlags;
5331 /* Create a new temporary storage to act as the scratch file. */
5332 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
5333 0, &scratch);
5334 (*result)->scratch = impl_from_IStorage(scratch);
5336 if (SUCCEEDED(hr))
5338 ULONG num_entries = 20;
5340 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5341 (*result)->entries_size = num_entries;
5342 (*result)->firstFreeEntry = 0;
5344 if ((*result)->entries)
5346 /* parentStorage already has 1 reference, which we take over here. */
5347 (*result)->transactedParent = parentStorage;
5349 parentStorage->transactedChild = &(*result)->base;
5351 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5353 else
5355 IStorage_Release(scratch);
5357 hr = E_OUTOFMEMORY;
5361 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
5363 return hr;
5365 else
5366 return E_OUTOFMEMORY;
5369 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5370 StorageBaseImpl** result)
5372 static int fixme=0;
5374 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5376 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5379 return TransactedSnapshotImpl_Construct(parentStorage,
5380 (TransactedSnapshotImpl**)result);
5383 static HRESULT Storage_Construct(
5384 HANDLE hFile,
5385 LPCOLESTR pwcsName,
5386 ILockBytes* pLkbyt,
5387 DWORD openFlags,
5388 BOOL fileBased,
5389 BOOL create,
5390 ULONG sector_size,
5391 StorageBaseImpl** result)
5393 StorageImpl *newStorage;
5394 StorageBaseImpl *newTransactedStorage;
5395 HRESULT hr;
5397 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5398 if (FAILED(hr)) goto end;
5400 if (openFlags & STGM_TRANSACTED)
5402 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5403 if (FAILED(hr))
5404 IStorage_Release(&newStorage->base.IStorage_iface);
5405 else
5406 *result = newTransactedStorage;
5408 else
5409 *result = &newStorage->base;
5411 end:
5412 return hr;
5415 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5417 StorageInternalImpl* This = (StorageInternalImpl*) base;
5419 if (!This->base.reverted)
5421 TRACE("Storage invalidated (stg=%p)\n", This);
5423 This->base.reverted = TRUE;
5425 This->parentStorage = NULL;
5427 StorageBaseImpl_DeleteAll(&This->base);
5429 list_remove(&This->ParentListEntry);
5433 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5435 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5437 StorageInternalImpl_Invalidate(&This->base);
5439 HeapFree(GetProcessHeap(), 0, This);
5442 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5444 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5446 return StorageBaseImpl_Flush(This->parentStorage);
5449 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5451 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5453 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5456 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5457 const DirEntry *newData, DirRef *index)
5459 StorageInternalImpl* This = (StorageInternalImpl*) base;
5461 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5462 newData, index);
5465 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5466 DirRef index, const DirEntry *data)
5468 StorageInternalImpl* This = (StorageInternalImpl*) base;
5470 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5471 index, data);
5474 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5475 DirRef index, DirEntry *data)
5477 StorageInternalImpl* This = (StorageInternalImpl*) base;
5479 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5480 index, data);
5483 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5484 DirRef index)
5486 StorageInternalImpl* This = (StorageInternalImpl*) base;
5488 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5489 index);
5492 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5493 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5495 StorageInternalImpl* This = (StorageInternalImpl*) base;
5497 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5498 index, offset, size, buffer, bytesRead);
5501 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5502 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5504 StorageInternalImpl* This = (StorageInternalImpl*) base;
5506 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5507 index, offset, size, buffer, bytesWritten);
5510 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5511 DirRef index, ULARGE_INTEGER newsize)
5513 StorageInternalImpl* This = (StorageInternalImpl*) base;
5515 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5516 index, newsize);
5519 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5520 DirRef dst, DirRef src)
5522 StorageInternalImpl* This = (StorageInternalImpl*) base;
5524 return StorageBaseImpl_StreamLink(This->parentStorage,
5525 dst, src);
5528 /******************************************************************************
5530 ** Storage32InternalImpl_Commit
5533 static HRESULT WINAPI StorageInternalImpl_Commit(
5534 IStorage* iface,
5535 DWORD grfCommitFlags) /* [in] */
5537 StorageBaseImpl* This = impl_from_IStorage(iface);
5538 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5539 return StorageBaseImpl_Flush(This);
5542 /******************************************************************************
5544 ** Storage32InternalImpl_Revert
5547 static HRESULT WINAPI StorageInternalImpl_Revert(
5548 IStorage* iface)
5550 FIXME("(%p): stub\n", iface);
5551 return S_OK;
5554 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5556 IStorage_Release(&This->parentStorage->IStorage_iface);
5557 HeapFree(GetProcessHeap(), 0, This);
5560 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5561 IEnumSTATSTG* iface,
5562 REFIID riid,
5563 void** ppvObject)
5565 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5567 if (ppvObject==0)
5568 return E_INVALIDARG;
5570 *ppvObject = 0;
5572 if (IsEqualGUID(&IID_IUnknown, riid) ||
5573 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5575 *ppvObject = This;
5576 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
5577 return S_OK;
5580 return E_NOINTERFACE;
5583 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5584 IEnumSTATSTG* iface)
5586 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5587 return InterlockedIncrement(&This->ref);
5590 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5591 IEnumSTATSTG* iface)
5593 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5595 ULONG newRef;
5597 newRef = InterlockedDecrement(&This->ref);
5599 if (newRef==0)
5601 IEnumSTATSTGImpl_Destroy(This);
5604 return newRef;
5607 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5608 IEnumSTATSTGImpl* This,
5609 DirRef *ref)
5611 DirRef result = DIRENTRY_NULL;
5612 DirRef searchNode;
5613 DirEntry entry;
5614 HRESULT hr;
5615 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5617 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5618 This->parentStorage->storageDirEntry, &entry);
5619 searchNode = entry.dirRootEntry;
5621 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5623 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5625 if (SUCCEEDED(hr))
5627 LONG diff = entryNameCmp( entry.name, This->name);
5629 if (diff <= 0)
5631 searchNode = entry.rightChild;
5633 else
5635 result = searchNode;
5636 memcpy(result_name, entry.name, sizeof(result_name));
5637 searchNode = entry.leftChild;
5642 if (SUCCEEDED(hr))
5644 *ref = result;
5645 if (result != DIRENTRY_NULL)
5646 memcpy(This->name, result_name, sizeof(result_name));
5649 return hr;
5652 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5653 IEnumSTATSTG* iface,
5654 ULONG celt,
5655 STATSTG* rgelt,
5656 ULONG* pceltFetched)
5658 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5660 DirEntry currentEntry;
5661 STATSTG* currentReturnStruct = rgelt;
5662 ULONG objectFetched = 0;
5663 DirRef currentSearchNode;
5664 HRESULT hr=S_OK;
5666 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5667 return E_INVALIDARG;
5669 if (This->parentStorage->reverted)
5670 return STG_E_REVERTED;
5673 * To avoid the special case, get another pointer to a ULONG value if
5674 * the caller didn't supply one.
5676 if (pceltFetched==0)
5677 pceltFetched = &objectFetched;
5680 * Start the iteration, we will iterate until we hit the end of the
5681 * linked list or until we hit the number of items to iterate through
5683 *pceltFetched = 0;
5685 while ( *pceltFetched < celt )
5687 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5689 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5690 break;
5693 * Read the entry from the storage.
5695 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5696 currentSearchNode,
5697 &currentEntry);
5700 * Copy the information to the return buffer.
5702 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5703 currentReturnStruct,
5704 &currentEntry,
5705 STATFLAG_DEFAULT);
5708 * Step to the next item in the iteration
5710 (*pceltFetched)++;
5711 currentReturnStruct++;
5714 if (SUCCEEDED(hr) && *pceltFetched != celt)
5715 hr = S_FALSE;
5717 return hr;
5721 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5722 IEnumSTATSTG* iface,
5723 ULONG celt)
5725 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5727 ULONG objectFetched = 0;
5728 DirRef currentSearchNode;
5729 HRESULT hr=S_OK;
5731 if (This->parentStorage->reverted)
5732 return STG_E_REVERTED;
5734 while ( (objectFetched < celt) )
5736 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5738 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5739 break;
5741 objectFetched++;
5744 if (SUCCEEDED(hr) && objectFetched != celt)
5745 return S_FALSE;
5747 return hr;
5750 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5751 IEnumSTATSTG* iface)
5753 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5755 if (This->parentStorage->reverted)
5756 return STG_E_REVERTED;
5758 This->name[0] = 0;
5760 return S_OK;
5763 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5764 IEnumSTATSTG* iface,
5765 IEnumSTATSTG** ppenum)
5767 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5768 IEnumSTATSTGImpl* newClone;
5770 if (This->parentStorage->reverted)
5771 return STG_E_REVERTED;
5773 if (ppenum==0)
5774 return E_INVALIDARG;
5776 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5777 This->storageDirEntry);
5778 if (!newClone)
5780 *ppenum = NULL;
5781 return E_OUTOFMEMORY;
5785 * The new clone enumeration must point to the same current node as
5786 * the old one.
5788 memcpy(newClone->name, This->name, sizeof(newClone->name));
5790 *ppenum = &newClone->IEnumSTATSTG_iface;
5792 return S_OK;
5796 * Virtual function table for the IEnumSTATSTGImpl class.
5798 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5800 IEnumSTATSTGImpl_QueryInterface,
5801 IEnumSTATSTGImpl_AddRef,
5802 IEnumSTATSTGImpl_Release,
5803 IEnumSTATSTGImpl_Next,
5804 IEnumSTATSTGImpl_Skip,
5805 IEnumSTATSTGImpl_Reset,
5806 IEnumSTATSTGImpl_Clone
5809 /******************************************************************************
5810 ** IEnumSTATSTGImpl implementation
5813 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5814 StorageBaseImpl* parentStorage,
5815 DirRef storageDirEntry)
5817 IEnumSTATSTGImpl* newEnumeration;
5819 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5821 if (newEnumeration)
5823 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5824 newEnumeration->ref = 1;
5825 newEnumeration->name[0] = 0;
5828 * We want to nail-down the reference to the storage in case the
5829 * enumeration out-lives the storage in the client application.
5831 newEnumeration->parentStorage = parentStorage;
5832 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
5834 newEnumeration->storageDirEntry = storageDirEntry;
5837 return newEnumeration;
5841 * Virtual function table for the Storage32InternalImpl class.
5843 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5845 StorageBaseImpl_QueryInterface,
5846 StorageBaseImpl_AddRef,
5847 StorageBaseImpl_Release,
5848 StorageBaseImpl_CreateStream,
5849 StorageBaseImpl_OpenStream,
5850 StorageBaseImpl_CreateStorage,
5851 StorageBaseImpl_OpenStorage,
5852 StorageBaseImpl_CopyTo,
5853 StorageBaseImpl_MoveElementTo,
5854 StorageInternalImpl_Commit,
5855 StorageInternalImpl_Revert,
5856 StorageBaseImpl_EnumElements,
5857 StorageBaseImpl_DestroyElement,
5858 StorageBaseImpl_RenameElement,
5859 StorageBaseImpl_SetElementTimes,
5860 StorageBaseImpl_SetClass,
5861 StorageBaseImpl_SetStateBits,
5862 StorageBaseImpl_Stat
5865 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5867 StorageInternalImpl_Destroy,
5868 StorageInternalImpl_Invalidate,
5869 StorageInternalImpl_Flush,
5870 StorageInternalImpl_GetFilename,
5871 StorageInternalImpl_CreateDirEntry,
5872 StorageInternalImpl_WriteDirEntry,
5873 StorageInternalImpl_ReadDirEntry,
5874 StorageInternalImpl_DestroyDirEntry,
5875 StorageInternalImpl_StreamReadAt,
5876 StorageInternalImpl_StreamWriteAt,
5877 StorageInternalImpl_StreamSetSize,
5878 StorageInternalImpl_StreamLink
5881 /******************************************************************************
5882 ** Storage32InternalImpl implementation
5885 static StorageInternalImpl* StorageInternalImpl_Construct(
5886 StorageBaseImpl* parentStorage,
5887 DWORD openFlags,
5888 DirRef storageDirEntry)
5890 StorageInternalImpl* newStorage;
5892 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5894 if (newStorage!=0)
5896 list_init(&newStorage->base.strmHead);
5898 list_init(&newStorage->base.storageHead);
5901 * Initialize the virtual function table.
5903 newStorage->base.IStorage_iface.lpVtbl = &Storage32InternalImpl_Vtbl;
5904 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5905 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5906 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5908 newStorage->base.reverted = FALSE;
5910 newStorage->base.ref = 1;
5912 newStorage->parentStorage = parentStorage;
5915 * Keep a reference to the directory entry of this storage
5917 newStorage->base.storageDirEntry = storageDirEntry;
5919 newStorage->base.create = FALSE;
5921 return newStorage;
5924 return 0;
5927 /******************************************************************************
5928 ** StorageUtl implementation
5931 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5933 WORD tmp;
5935 memcpy(&tmp, buffer+offset, sizeof(WORD));
5936 *value = lendian16toh(tmp);
5939 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5941 value = htole16(value);
5942 memcpy(buffer+offset, &value, sizeof(WORD));
5945 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5947 DWORD tmp;
5949 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5950 *value = lendian32toh(tmp);
5953 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5955 value = htole32(value);
5956 memcpy(buffer+offset, &value, sizeof(DWORD));
5959 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5960 ULARGE_INTEGER* value)
5962 #ifdef WORDS_BIGENDIAN
5963 ULARGE_INTEGER tmp;
5965 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5966 value->u.LowPart = htole32(tmp.u.HighPart);
5967 value->u.HighPart = htole32(tmp.u.LowPart);
5968 #else
5969 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5970 #endif
5973 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5974 const ULARGE_INTEGER *value)
5976 #ifdef WORDS_BIGENDIAN
5977 ULARGE_INTEGER tmp;
5979 tmp.u.LowPart = htole32(value->u.HighPart);
5980 tmp.u.HighPart = htole32(value->u.LowPart);
5981 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5982 #else
5983 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5984 #endif
5987 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5989 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5990 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5991 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5993 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5996 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5998 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5999 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
6000 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
6002 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
6005 void StorageUtl_CopyDirEntryToSTATSTG(
6006 StorageBaseImpl* storage,
6007 STATSTG* destination,
6008 const DirEntry* source,
6009 int statFlags)
6012 * The copy of the string occurs only when the flag is not set
6014 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
6016 /* Use the filename for the root storage. */
6017 destination->pwcsName = 0;
6018 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
6020 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
6021 (source->name[0] == 0) )
6023 destination->pwcsName = 0;
6025 else
6027 destination->pwcsName =
6028 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
6030 strcpyW(destination->pwcsName, source->name);
6033 switch (source->stgType)
6035 case STGTY_STORAGE:
6036 case STGTY_ROOT:
6037 destination->type = STGTY_STORAGE;
6038 break;
6039 case STGTY_STREAM:
6040 destination->type = STGTY_STREAM;
6041 break;
6042 default:
6043 destination->type = STGTY_STREAM;
6044 break;
6047 destination->cbSize = source->size;
6049 currentReturnStruct->mtime = {0}; TODO
6050 currentReturnStruct->ctime = {0};
6051 currentReturnStruct->atime = {0};
6053 destination->grfMode = 0;
6054 destination->grfLocksSupported = 0;
6055 destination->clsid = source->clsid;
6056 destination->grfStateBits = 0;
6057 destination->reserved = 0;
6060 /******************************************************************************
6061 ** BlockChainStream implementation
6064 /* Read and save the index of all blocks in this stream. */
6065 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
6067 ULONG next_sector, next_offset;
6068 HRESULT hr;
6069 struct BlockChainRun *last_run;
6071 if (This->indexCacheLen == 0)
6073 last_run = NULL;
6074 next_offset = 0;
6075 next_sector = BlockChainStream_GetHeadOfChain(This);
6077 else
6079 last_run = &This->indexCache[This->indexCacheLen-1];
6080 next_offset = last_run->lastOffset+1;
6081 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
6082 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
6083 &next_sector);
6084 if (FAILED(hr)) return hr;
6087 while (next_sector != BLOCK_END_OF_CHAIN)
6089 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
6091 /* Add the current block to the cache. */
6092 if (This->indexCacheSize == 0)
6094 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
6095 if (!This->indexCache) return E_OUTOFMEMORY;
6096 This->indexCacheSize = 16;
6098 else if (This->indexCacheSize == This->indexCacheLen)
6100 struct BlockChainRun *new_cache;
6101 ULONG new_size;
6103 new_size = This->indexCacheSize * 2;
6104 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
6105 if (!new_cache) return E_OUTOFMEMORY;
6106 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
6108 HeapFree(GetProcessHeap(), 0, This->indexCache);
6109 This->indexCache = new_cache;
6110 This->indexCacheSize = new_size;
6113 This->indexCacheLen++;
6114 last_run = &This->indexCache[This->indexCacheLen-1];
6115 last_run->firstSector = next_sector;
6116 last_run->firstOffset = next_offset;
6119 last_run->lastOffset = next_offset;
6121 /* Find the next block. */
6122 next_offset++;
6123 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
6124 if (FAILED(hr)) return hr;
6127 if (This->indexCacheLen)
6129 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
6130 This->numBlocks = last_run->lastOffset+1;
6132 else
6134 This->tailIndex = BLOCK_END_OF_CHAIN;
6135 This->numBlocks = 0;
6138 return S_OK;
6141 /* Locate the nth block in this stream. */
6142 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
6144 ULONG min_offset = 0, max_offset = This->numBlocks-1;
6145 ULONG min_run = 0, max_run = This->indexCacheLen-1;
6147 if (offset >= This->numBlocks)
6148 return BLOCK_END_OF_CHAIN;
6150 while (min_run < max_run)
6152 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
6153 if (offset < This->indexCache[run_to_check].firstOffset)
6155 max_offset = This->indexCache[run_to_check].firstOffset-1;
6156 max_run = run_to_check-1;
6158 else if (offset > This->indexCache[run_to_check].lastOffset)
6160 min_offset = This->indexCache[run_to_check].lastOffset+1;
6161 min_run = run_to_check+1;
6163 else
6164 /* Block is in this run. */
6165 min_run = max_run = run_to_check;
6168 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
6171 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
6172 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
6174 BlockChainBlock *result=NULL;
6175 int i;
6177 for (i=0; i<2; i++)
6178 if (This->cachedBlocks[i].index == index)
6180 *sector = This->cachedBlocks[i].sector;
6181 *block = &This->cachedBlocks[i];
6182 return S_OK;
6185 *sector = BlockChainStream_GetSectorOfOffset(This, index);
6186 if (*sector == BLOCK_END_OF_CHAIN)
6187 return STG_E_DOCFILECORRUPT;
6189 if (create)
6191 if (This->cachedBlocks[0].index == 0xffffffff)
6192 result = &This->cachedBlocks[0];
6193 else if (This->cachedBlocks[1].index == 0xffffffff)
6194 result = &This->cachedBlocks[1];
6195 else
6197 result = &This->cachedBlocks[This->blockToEvict++];
6198 if (This->blockToEvict == 2)
6199 This->blockToEvict = 0;
6202 if (result->dirty)
6204 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
6205 return STG_E_WRITEFAULT;
6206 result->dirty = FALSE;
6209 result->read = FALSE;
6210 result->index = index;
6211 result->sector = *sector;
6214 *block = result;
6215 return S_OK;
6218 BlockChainStream* BlockChainStream_Construct(
6219 StorageImpl* parentStorage,
6220 ULONG* headOfStreamPlaceHolder,
6221 DirRef dirEntry)
6223 BlockChainStream* newStream;
6225 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
6227 newStream->parentStorage = parentStorage;
6228 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6229 newStream->ownerDirEntry = dirEntry;
6230 newStream->indexCache = NULL;
6231 newStream->indexCacheLen = 0;
6232 newStream->indexCacheSize = 0;
6233 newStream->cachedBlocks[0].index = 0xffffffff;
6234 newStream->cachedBlocks[0].dirty = FALSE;
6235 newStream->cachedBlocks[1].index = 0xffffffff;
6236 newStream->cachedBlocks[1].dirty = FALSE;
6237 newStream->blockToEvict = 0;
6239 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
6241 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
6242 HeapFree(GetProcessHeap(), 0, newStream);
6243 return NULL;
6246 return newStream;
6249 HRESULT BlockChainStream_Flush(BlockChainStream* This)
6251 int i;
6252 if (!This) return S_OK;
6253 for (i=0; i<2; i++)
6255 if (This->cachedBlocks[i].dirty)
6257 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6258 This->cachedBlocks[i].dirty = FALSE;
6259 else
6260 return STG_E_WRITEFAULT;
6263 return S_OK;
6266 void BlockChainStream_Destroy(BlockChainStream* This)
6268 if (This)
6270 BlockChainStream_Flush(This);
6271 HeapFree(GetProcessHeap(), 0, This->indexCache);
6273 HeapFree(GetProcessHeap(), 0, This);
6276 /******************************************************************************
6277 * BlockChainStream_GetHeadOfChain
6279 * Returns the head of this stream chain.
6280 * Some special chains don't have directory entries, their heads are kept in
6281 * This->headOfStreamPlaceHolder.
6284 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6286 DirEntry chainEntry;
6287 HRESULT hr;
6289 if (This->headOfStreamPlaceHolder != 0)
6290 return *(This->headOfStreamPlaceHolder);
6292 if (This->ownerDirEntry != DIRENTRY_NULL)
6294 hr = StorageImpl_ReadDirEntry(
6295 This->parentStorage,
6296 This->ownerDirEntry,
6297 &chainEntry);
6299 if (SUCCEEDED(hr))
6301 return chainEntry.startingBlock;
6305 return BLOCK_END_OF_CHAIN;
6308 /******************************************************************************
6309 * BlockChainStream_GetCount
6311 * Returns the number of blocks that comprises this chain.
6312 * This is not the size of the stream as the last block may not be full!
6314 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6316 return This->numBlocks;
6319 /******************************************************************************
6320 * BlockChainStream_ReadAt
6322 * Reads a specified number of bytes from this chain at the specified offset.
6323 * bytesRead may be NULL.
6324 * Failure will be returned if the specified number of bytes has not been read.
6326 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6327 ULARGE_INTEGER offset,
6328 ULONG size,
6329 void* buffer,
6330 ULONG* bytesRead)
6332 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6333 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6334 ULONG bytesToReadInBuffer;
6335 ULONG blockIndex;
6336 BYTE* bufferWalker;
6337 ULARGE_INTEGER stream_size;
6338 HRESULT hr;
6339 BlockChainBlock *cachedBlock;
6341 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6344 * Find the first block in the stream that contains part of the buffer.
6346 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6348 *bytesRead = 0;
6350 stream_size = BlockChainStream_GetSize(This);
6351 if (stream_size.QuadPart > offset.QuadPart)
6352 size = min(stream_size.QuadPart - offset.QuadPart, size);
6353 else
6354 return S_OK;
6357 * Start reading the buffer.
6359 bufferWalker = buffer;
6361 while (size > 0)
6363 ULARGE_INTEGER ulOffset;
6364 DWORD bytesReadAt;
6367 * Calculate how many bytes we can copy from this big block.
6369 bytesToReadInBuffer =
6370 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6372 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6374 if (FAILED(hr))
6375 return hr;
6377 if (!cachedBlock)
6379 /* Not in cache, and we're going to read past the end of the block. */
6380 ulOffset.u.HighPart = 0;
6381 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6382 offsetInBlock;
6384 StorageImpl_ReadAt(This->parentStorage,
6385 ulOffset,
6386 bufferWalker,
6387 bytesToReadInBuffer,
6388 &bytesReadAt);
6390 else
6392 if (!cachedBlock->read)
6394 ULONG read;
6395 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
6396 return STG_E_READFAULT;
6398 cachedBlock->read = TRUE;
6401 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6402 bytesReadAt = bytesToReadInBuffer;
6405 blockNoInSequence++;
6406 bufferWalker += bytesReadAt;
6407 size -= bytesReadAt;
6408 *bytesRead += bytesReadAt;
6409 offsetInBlock = 0; /* There is no offset on the next block */
6411 if (bytesToReadInBuffer != bytesReadAt)
6412 break;
6415 return S_OK;
6418 /******************************************************************************
6419 * BlockChainStream_WriteAt
6421 * Writes the specified number of bytes to this chain at the specified offset.
6422 * Will fail if not all specified number of bytes have been written.
6424 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6425 ULARGE_INTEGER offset,
6426 ULONG size,
6427 const void* buffer,
6428 ULONG* bytesWritten)
6430 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6431 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6432 ULONG bytesToWrite;
6433 ULONG blockIndex;
6434 const BYTE* bufferWalker;
6435 HRESULT hr;
6436 BlockChainBlock *cachedBlock;
6438 *bytesWritten = 0;
6439 bufferWalker = buffer;
6441 while (size > 0)
6443 ULARGE_INTEGER ulOffset;
6444 DWORD bytesWrittenAt;
6447 * Calculate how many bytes we can copy to this big block.
6449 bytesToWrite =
6450 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6452 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6454 /* BlockChainStream_SetSize should have already been called to ensure we have
6455 * enough blocks in the chain to write into */
6456 if (FAILED(hr))
6458 ERR("not enough blocks in chain to write data\n");
6459 return hr;
6462 if (!cachedBlock)
6464 /* Not in cache, and we're going to write past the end of the block. */
6465 ulOffset.u.HighPart = 0;
6466 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6467 offsetInBlock;
6469 StorageImpl_WriteAt(This->parentStorage,
6470 ulOffset,
6471 bufferWalker,
6472 bytesToWrite,
6473 &bytesWrittenAt);
6475 else
6477 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6479 ULONG read;
6480 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
6481 return STG_E_READFAULT;
6484 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6485 bytesWrittenAt = bytesToWrite;
6486 cachedBlock->read = TRUE;
6487 cachedBlock->dirty = TRUE;
6490 blockNoInSequence++;
6491 bufferWalker += bytesWrittenAt;
6492 size -= bytesWrittenAt;
6493 *bytesWritten += bytesWrittenAt;
6494 offsetInBlock = 0; /* There is no offset on the next block */
6496 if (bytesWrittenAt != bytesToWrite)
6497 break;
6500 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6503 /******************************************************************************
6504 * BlockChainStream_Shrink
6506 * Shrinks this chain in the big block depot.
6508 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6509 ULARGE_INTEGER newSize)
6511 ULONG blockIndex;
6512 ULONG numBlocks;
6513 int i;
6516 * Figure out how many blocks are needed to contain the new size
6518 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6520 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6521 numBlocks++;
6523 if (numBlocks)
6526 * Go to the new end of chain
6528 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6530 /* Mark the new end of chain */
6531 StorageImpl_SetNextBlockInChain(
6532 This->parentStorage,
6533 blockIndex,
6534 BLOCK_END_OF_CHAIN);
6536 This->tailIndex = blockIndex;
6538 else
6540 if (This->headOfStreamPlaceHolder != 0)
6542 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6544 else
6546 DirEntry chainEntry;
6547 assert(This->ownerDirEntry != DIRENTRY_NULL);
6549 StorageImpl_ReadDirEntry(
6550 This->parentStorage,
6551 This->ownerDirEntry,
6552 &chainEntry);
6554 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6556 StorageImpl_WriteDirEntry(
6557 This->parentStorage,
6558 This->ownerDirEntry,
6559 &chainEntry);
6562 This->tailIndex = BLOCK_END_OF_CHAIN;
6565 This->numBlocks = numBlocks;
6568 * Mark the extra blocks as free
6570 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6572 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6573 StorageImpl_FreeBigBlock(This->parentStorage,
6574 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6575 if (last_run->lastOffset == last_run->firstOffset)
6576 This->indexCacheLen--;
6577 else
6578 last_run->lastOffset--;
6582 * Reset the last accessed block cache.
6584 for (i=0; i<2; i++)
6586 if (This->cachedBlocks[i].index >= numBlocks)
6588 This->cachedBlocks[i].index = 0xffffffff;
6589 This->cachedBlocks[i].dirty = FALSE;
6593 return TRUE;
6596 /******************************************************************************
6597 * BlockChainStream_Enlarge
6599 * Grows this chain in the big block depot.
6601 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6602 ULARGE_INTEGER newSize)
6604 ULONG blockIndex, currentBlock;
6605 ULONG newNumBlocks;
6606 ULONG oldNumBlocks = 0;
6608 blockIndex = BlockChainStream_GetHeadOfChain(This);
6611 * Empty chain. Create the head.
6613 if (blockIndex == BLOCK_END_OF_CHAIN)
6615 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6616 StorageImpl_SetNextBlockInChain(This->parentStorage,
6617 blockIndex,
6618 BLOCK_END_OF_CHAIN);
6620 if (This->headOfStreamPlaceHolder != 0)
6622 *(This->headOfStreamPlaceHolder) = blockIndex;
6624 else
6626 DirEntry chainEntry;
6627 assert(This->ownerDirEntry != DIRENTRY_NULL);
6629 StorageImpl_ReadDirEntry(
6630 This->parentStorage,
6631 This->ownerDirEntry,
6632 &chainEntry);
6634 chainEntry.startingBlock = blockIndex;
6636 StorageImpl_WriteDirEntry(
6637 This->parentStorage,
6638 This->ownerDirEntry,
6639 &chainEntry);
6642 This->tailIndex = blockIndex;
6643 This->numBlocks = 1;
6647 * Figure out how many blocks are needed to contain this stream
6649 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6651 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6652 newNumBlocks++;
6655 * Go to the current end of chain
6657 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6659 currentBlock = blockIndex;
6661 while (blockIndex != BLOCK_END_OF_CHAIN)
6663 This->numBlocks++;
6664 currentBlock = blockIndex;
6666 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6667 &blockIndex)))
6668 return FALSE;
6671 This->tailIndex = currentBlock;
6674 currentBlock = This->tailIndex;
6675 oldNumBlocks = This->numBlocks;
6678 * Add new blocks to the chain
6680 if (oldNumBlocks < newNumBlocks)
6682 while (oldNumBlocks < newNumBlocks)
6684 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6686 StorageImpl_SetNextBlockInChain(
6687 This->parentStorage,
6688 currentBlock,
6689 blockIndex);
6691 StorageImpl_SetNextBlockInChain(
6692 This->parentStorage,
6693 blockIndex,
6694 BLOCK_END_OF_CHAIN);
6696 currentBlock = blockIndex;
6697 oldNumBlocks++;
6700 This->tailIndex = blockIndex;
6701 This->numBlocks = newNumBlocks;
6704 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6705 return FALSE;
6707 return TRUE;
6710 /******************************************************************************
6711 * BlockChainStream_SetSize
6713 * Sets the size of this stream. The big block depot will be updated.
6714 * The file will grow if we grow the chain.
6716 * TODO: Free the actual blocks in the file when we shrink the chain.
6717 * Currently, the blocks are still in the file. So the file size
6718 * doesn't shrink even if we shrink streams.
6720 BOOL BlockChainStream_SetSize(
6721 BlockChainStream* This,
6722 ULARGE_INTEGER newSize)
6724 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6726 if (newSize.u.LowPart == size.u.LowPart)
6727 return TRUE;
6729 if (newSize.u.LowPart < size.u.LowPart)
6731 BlockChainStream_Shrink(This, newSize);
6733 else
6735 BlockChainStream_Enlarge(This, newSize);
6738 return TRUE;
6741 /******************************************************************************
6742 * BlockChainStream_GetSize
6744 * Returns the size of this chain.
6745 * Will return the block count if this chain doesn't have a directory entry.
6747 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6749 DirEntry chainEntry;
6751 if(This->headOfStreamPlaceHolder == NULL)
6754 * This chain has a directory entry so use the size value from there.
6756 StorageImpl_ReadDirEntry(
6757 This->parentStorage,
6758 This->ownerDirEntry,
6759 &chainEntry);
6761 return chainEntry.size;
6763 else
6766 * this chain is a chain that does not have a directory entry, figure out the
6767 * size by making the product number of used blocks times the
6768 * size of them
6770 ULARGE_INTEGER result;
6771 result.u.HighPart = 0;
6773 result.u.LowPart =
6774 BlockChainStream_GetCount(This) *
6775 This->parentStorage->bigBlockSize;
6777 return result;
6781 /******************************************************************************
6782 ** SmallBlockChainStream implementation
6785 SmallBlockChainStream* SmallBlockChainStream_Construct(
6786 StorageImpl* parentStorage,
6787 ULONG* headOfStreamPlaceHolder,
6788 DirRef dirEntry)
6790 SmallBlockChainStream* newStream;
6792 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6794 newStream->parentStorage = parentStorage;
6795 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6796 newStream->ownerDirEntry = dirEntry;
6798 return newStream;
6801 void SmallBlockChainStream_Destroy(
6802 SmallBlockChainStream* This)
6804 HeapFree(GetProcessHeap(), 0, This);
6807 /******************************************************************************
6808 * SmallBlockChainStream_GetHeadOfChain
6810 * Returns the head of this chain of small blocks.
6812 static ULONG SmallBlockChainStream_GetHeadOfChain(
6813 SmallBlockChainStream* This)
6815 DirEntry chainEntry;
6816 HRESULT hr;
6818 if (This->headOfStreamPlaceHolder != NULL)
6819 return *(This->headOfStreamPlaceHolder);
6821 if (This->ownerDirEntry)
6823 hr = StorageImpl_ReadDirEntry(
6824 This->parentStorage,
6825 This->ownerDirEntry,
6826 &chainEntry);
6828 if (SUCCEEDED(hr))
6830 return chainEntry.startingBlock;
6835 return BLOCK_END_OF_CHAIN;
6838 /******************************************************************************
6839 * SmallBlockChainStream_GetNextBlockInChain
6841 * Returns the index of the next small block in this chain.
6843 * Return Values:
6844 * - BLOCK_END_OF_CHAIN: end of this chain
6845 * - BLOCK_UNUSED: small block 'blockIndex' is free
6847 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6848 SmallBlockChainStream* This,
6849 ULONG blockIndex,
6850 ULONG* nextBlockInChain)
6852 ULARGE_INTEGER offsetOfBlockInDepot;
6853 DWORD buffer;
6854 ULONG bytesRead;
6855 HRESULT res;
6857 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6859 offsetOfBlockInDepot.u.HighPart = 0;
6860 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6863 * Read those bytes in the buffer from the small block file.
6865 res = BlockChainStream_ReadAt(
6866 This->parentStorage->smallBlockDepotChain,
6867 offsetOfBlockInDepot,
6868 sizeof(DWORD),
6869 &buffer,
6870 &bytesRead);
6872 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6873 res = STG_E_READFAULT;
6875 if (SUCCEEDED(res))
6877 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6878 return S_OK;
6881 return res;
6884 /******************************************************************************
6885 * SmallBlockChainStream_SetNextBlockInChain
6887 * Writes the index of the next block of the specified block in the small
6888 * block depot.
6889 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6890 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6892 static void SmallBlockChainStream_SetNextBlockInChain(
6893 SmallBlockChainStream* This,
6894 ULONG blockIndex,
6895 ULONG nextBlock)
6897 ULARGE_INTEGER offsetOfBlockInDepot;
6898 DWORD buffer;
6899 ULONG bytesWritten;
6901 offsetOfBlockInDepot.u.HighPart = 0;
6902 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6904 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6907 * Read those bytes in the buffer from the small block file.
6909 BlockChainStream_WriteAt(
6910 This->parentStorage->smallBlockDepotChain,
6911 offsetOfBlockInDepot,
6912 sizeof(DWORD),
6913 &buffer,
6914 &bytesWritten);
6917 /******************************************************************************
6918 * SmallBlockChainStream_FreeBlock
6920 * Flag small block 'blockIndex' as free in the small block depot.
6922 static void SmallBlockChainStream_FreeBlock(
6923 SmallBlockChainStream* This,
6924 ULONG blockIndex)
6926 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6929 /******************************************************************************
6930 * SmallBlockChainStream_GetNextFreeBlock
6932 * Returns the index of a free small block. The small block depot will be
6933 * enlarged if necessary. The small block chain will also be enlarged if
6934 * necessary.
6936 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6937 SmallBlockChainStream* This)
6939 ULARGE_INTEGER offsetOfBlockInDepot;
6940 DWORD buffer;
6941 ULONG bytesRead;
6942 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6943 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6944 HRESULT res = S_OK;
6945 ULONG smallBlocksPerBigBlock;
6946 DirEntry rootEntry;
6947 ULONG blocksRequired;
6948 ULARGE_INTEGER old_size, size_required;
6950 offsetOfBlockInDepot.u.HighPart = 0;
6953 * Scan the small block depot for a free block
6955 while (nextBlockIndex != BLOCK_UNUSED)
6957 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6959 res = BlockChainStream_ReadAt(
6960 This->parentStorage->smallBlockDepotChain,
6961 offsetOfBlockInDepot,
6962 sizeof(DWORD),
6963 &buffer,
6964 &bytesRead);
6967 * If we run out of space for the small block depot, enlarge it
6969 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6971 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6973 if (nextBlockIndex != BLOCK_UNUSED)
6974 blockIndex++;
6976 else
6978 ULONG count =
6979 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6981 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6982 ULARGE_INTEGER newSize, offset;
6983 ULONG bytesWritten;
6985 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6986 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6989 * Initialize all the small blocks to free
6991 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6992 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6993 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6994 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6996 StorageImpl_SaveFileHeader(This->parentStorage);
7000 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
7002 smallBlocksPerBigBlock =
7003 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
7006 * Verify if we have to allocate big blocks to contain small blocks
7008 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
7010 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
7012 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
7014 if (size_required.QuadPart > old_size.QuadPart)
7016 BlockChainStream_SetSize(
7017 This->parentStorage->smallBlockRootChain,
7018 size_required);
7020 StorageImpl_ReadDirEntry(
7021 This->parentStorage,
7022 This->parentStorage->base.storageDirEntry,
7023 &rootEntry);
7025 rootEntry.size = size_required;
7027 StorageImpl_WriteDirEntry(
7028 This->parentStorage,
7029 This->parentStorage->base.storageDirEntry,
7030 &rootEntry);
7033 return blockIndex;
7036 /******************************************************************************
7037 * SmallBlockChainStream_ReadAt
7039 * Reads a specified number of bytes from this chain at the specified offset.
7040 * bytesRead may be NULL.
7041 * Failure will be returned if the specified number of bytes has not been read.
7043 HRESULT SmallBlockChainStream_ReadAt(
7044 SmallBlockChainStream* This,
7045 ULARGE_INTEGER offset,
7046 ULONG size,
7047 void* buffer,
7048 ULONG* bytesRead)
7050 HRESULT rc = S_OK;
7051 ULARGE_INTEGER offsetInBigBlockFile;
7052 ULONG blockNoInSequence =
7053 offset.u.LowPart / This->parentStorage->smallBlockSize;
7055 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7056 ULONG bytesToReadInBuffer;
7057 ULONG blockIndex;
7058 ULONG bytesReadFromBigBlockFile;
7059 BYTE* bufferWalker;
7060 ULARGE_INTEGER stream_size;
7063 * This should never happen on a small block file.
7065 assert(offset.u.HighPart==0);
7067 *bytesRead = 0;
7069 stream_size = SmallBlockChainStream_GetSize(This);
7070 if (stream_size.QuadPart > offset.QuadPart)
7071 size = min(stream_size.QuadPart - offset.QuadPart, size);
7072 else
7073 return S_OK;
7076 * Find the first block in the stream that contains part of the buffer.
7078 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7080 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7082 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7083 if(FAILED(rc))
7084 return rc;
7085 blockNoInSequence--;
7089 * Start reading the buffer.
7091 bufferWalker = buffer;
7093 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7096 * Calculate how many bytes we can copy from this small block.
7098 bytesToReadInBuffer =
7099 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7102 * Calculate the offset of the small block in the small block file.
7104 offsetInBigBlockFile.u.HighPart = 0;
7105 offsetInBigBlockFile.u.LowPart =
7106 blockIndex * This->parentStorage->smallBlockSize;
7108 offsetInBigBlockFile.u.LowPart += offsetInBlock;
7111 * Read those bytes in the buffer from the small block file.
7112 * The small block has already been identified so it shouldn't fail
7113 * unless the file is corrupt.
7115 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
7116 offsetInBigBlockFile,
7117 bytesToReadInBuffer,
7118 bufferWalker,
7119 &bytesReadFromBigBlockFile);
7121 if (FAILED(rc))
7122 return rc;
7124 if (!bytesReadFromBigBlockFile)
7125 return STG_E_DOCFILECORRUPT;
7128 * Step to the next big block.
7130 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7131 if(FAILED(rc))
7132 return STG_E_DOCFILECORRUPT;
7134 bufferWalker += bytesReadFromBigBlockFile;
7135 size -= bytesReadFromBigBlockFile;
7136 *bytesRead += bytesReadFromBigBlockFile;
7137 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
7140 return S_OK;
7143 /******************************************************************************
7144 * SmallBlockChainStream_WriteAt
7146 * Writes the specified number of bytes to this chain at the specified offset.
7147 * Will fail if not all specified number of bytes have been written.
7149 HRESULT SmallBlockChainStream_WriteAt(
7150 SmallBlockChainStream* This,
7151 ULARGE_INTEGER offset,
7152 ULONG size,
7153 const void* buffer,
7154 ULONG* bytesWritten)
7156 ULARGE_INTEGER offsetInBigBlockFile;
7157 ULONG blockNoInSequence =
7158 offset.u.LowPart / This->parentStorage->smallBlockSize;
7160 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7161 ULONG bytesToWriteInBuffer;
7162 ULONG blockIndex;
7163 ULONG bytesWrittenToBigBlockFile;
7164 const BYTE* bufferWalker;
7165 HRESULT res;
7168 * This should never happen on a small block file.
7170 assert(offset.u.HighPart==0);
7173 * Find the first block in the stream that contains part of the buffer.
7175 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7177 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7179 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
7180 return STG_E_DOCFILECORRUPT;
7181 blockNoInSequence--;
7185 * Start writing the buffer.
7187 *bytesWritten = 0;
7188 bufferWalker = buffer;
7189 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7192 * Calculate how many bytes we can copy to this small block.
7194 bytesToWriteInBuffer =
7195 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7198 * Calculate the offset of the small block in the small block file.
7200 offsetInBigBlockFile.u.HighPart = 0;
7201 offsetInBigBlockFile.u.LowPart =
7202 blockIndex * This->parentStorage->smallBlockSize;
7204 offsetInBigBlockFile.u.LowPart += offsetInBlock;
7207 * Write those bytes in the buffer to the small block file.
7209 res = BlockChainStream_WriteAt(
7210 This->parentStorage->smallBlockRootChain,
7211 offsetInBigBlockFile,
7212 bytesToWriteInBuffer,
7213 bufferWalker,
7214 &bytesWrittenToBigBlockFile);
7215 if (FAILED(res))
7216 return res;
7219 * Step to the next big block.
7221 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7222 &blockIndex)))
7223 return FALSE;
7224 bufferWalker += bytesWrittenToBigBlockFile;
7225 size -= bytesWrittenToBigBlockFile;
7226 *bytesWritten += bytesWrittenToBigBlockFile;
7227 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7230 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7233 /******************************************************************************
7234 * SmallBlockChainStream_Shrink
7236 * Shrinks this chain in the small block depot.
7238 static BOOL SmallBlockChainStream_Shrink(
7239 SmallBlockChainStream* This,
7240 ULARGE_INTEGER newSize)
7242 ULONG blockIndex, extraBlock;
7243 ULONG numBlocks;
7244 ULONG count = 0;
7246 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7248 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7249 numBlocks++;
7251 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7254 * Go to the new end of chain
7256 while (count < numBlocks)
7258 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7259 &blockIndex)))
7260 return FALSE;
7261 count++;
7265 * If the count is 0, we have a special case, the head of the chain was
7266 * just freed.
7268 if (count == 0)
7270 DirEntry chainEntry;
7272 StorageImpl_ReadDirEntry(This->parentStorage,
7273 This->ownerDirEntry,
7274 &chainEntry);
7276 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7278 StorageImpl_WriteDirEntry(This->parentStorage,
7279 This->ownerDirEntry,
7280 &chainEntry);
7283 * We start freeing the chain at the head block.
7285 extraBlock = blockIndex;
7287 else
7289 /* Get the next block before marking the new end */
7290 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7291 &extraBlock)))
7292 return FALSE;
7294 /* Mark the new end of chain */
7295 SmallBlockChainStream_SetNextBlockInChain(
7296 This,
7297 blockIndex,
7298 BLOCK_END_OF_CHAIN);
7302 * Mark the extra blocks as free
7304 while (extraBlock != BLOCK_END_OF_CHAIN)
7306 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7307 &blockIndex)))
7308 return FALSE;
7309 SmallBlockChainStream_FreeBlock(This, extraBlock);
7310 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7311 extraBlock = blockIndex;
7314 return TRUE;
7317 /******************************************************************************
7318 * SmallBlockChainStream_Enlarge
7320 * Grows this chain in the small block depot.
7322 static BOOL SmallBlockChainStream_Enlarge(
7323 SmallBlockChainStream* This,
7324 ULARGE_INTEGER newSize)
7326 ULONG blockIndex, currentBlock;
7327 ULONG newNumBlocks;
7328 ULONG oldNumBlocks = 0;
7330 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7333 * Empty chain. Create the head.
7335 if (blockIndex == BLOCK_END_OF_CHAIN)
7337 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7338 SmallBlockChainStream_SetNextBlockInChain(
7339 This,
7340 blockIndex,
7341 BLOCK_END_OF_CHAIN);
7343 if (This->headOfStreamPlaceHolder != NULL)
7345 *(This->headOfStreamPlaceHolder) = blockIndex;
7347 else
7349 DirEntry chainEntry;
7351 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7352 &chainEntry);
7354 chainEntry.startingBlock = blockIndex;
7356 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7357 &chainEntry);
7361 currentBlock = blockIndex;
7364 * Figure out how many blocks are needed to contain this stream
7366 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7368 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7369 newNumBlocks++;
7372 * Go to the current end of chain
7374 while (blockIndex != BLOCK_END_OF_CHAIN)
7376 oldNumBlocks++;
7377 currentBlock = blockIndex;
7378 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7379 return FALSE;
7383 * Add new blocks to the chain
7385 while (oldNumBlocks < newNumBlocks)
7387 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7388 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7390 SmallBlockChainStream_SetNextBlockInChain(
7391 This,
7392 blockIndex,
7393 BLOCK_END_OF_CHAIN);
7395 currentBlock = blockIndex;
7396 oldNumBlocks++;
7399 return TRUE;
7402 /******************************************************************************
7403 * SmallBlockChainStream_SetSize
7405 * Sets the size of this stream.
7406 * The file will grow if we grow the chain.
7408 * TODO: Free the actual blocks in the file when we shrink the chain.
7409 * Currently, the blocks are still in the file. So the file size
7410 * doesn't shrink even if we shrink streams.
7412 BOOL SmallBlockChainStream_SetSize(
7413 SmallBlockChainStream* This,
7414 ULARGE_INTEGER newSize)
7416 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7418 if (newSize.u.LowPart == size.u.LowPart)
7419 return TRUE;
7421 if (newSize.u.LowPart < size.u.LowPart)
7423 SmallBlockChainStream_Shrink(This, newSize);
7425 else
7427 SmallBlockChainStream_Enlarge(This, newSize);
7430 return TRUE;
7433 /******************************************************************************
7434 * SmallBlockChainStream_GetCount
7436 * Returns the number of small blocks that comprises this chain.
7437 * This is not the size of the stream as the last block may not be full!
7440 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7442 ULONG blockIndex;
7443 ULONG count = 0;
7445 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7447 while(blockIndex != BLOCK_END_OF_CHAIN)
7449 count++;
7451 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7452 blockIndex, &blockIndex)))
7453 return 0;
7456 return count;
7459 /******************************************************************************
7460 * SmallBlockChainStream_GetSize
7462 * Returns the size of this chain.
7464 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7466 DirEntry chainEntry;
7468 if(This->headOfStreamPlaceHolder != NULL)
7470 ULARGE_INTEGER result;
7471 result.u.HighPart = 0;
7473 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7474 This->parentStorage->smallBlockSize;
7476 return result;
7479 StorageImpl_ReadDirEntry(
7480 This->parentStorage,
7481 This->ownerDirEntry,
7482 &chainEntry);
7484 return chainEntry.size;
7487 static HRESULT create_storagefile(
7488 LPCOLESTR pwcsName,
7489 DWORD grfMode,
7490 DWORD grfAttrs,
7491 STGOPTIONS* pStgOptions,
7492 REFIID riid,
7493 void** ppstgOpen)
7495 StorageBaseImpl* newStorage = 0;
7496 HANDLE hFile = INVALID_HANDLE_VALUE;
7497 HRESULT hr = STG_E_INVALIDFLAG;
7498 DWORD shareMode;
7499 DWORD accessMode;
7500 DWORD creationMode;
7501 DWORD fileAttributes;
7502 WCHAR tempFileName[MAX_PATH];
7504 if (ppstgOpen == 0)
7505 return STG_E_INVALIDPOINTER;
7507 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7508 return STG_E_INVALIDPARAMETER;
7510 /* if no share mode given then DENY_NONE is the default */
7511 if (STGM_SHARE_MODE(grfMode) == 0)
7512 grfMode |= STGM_SHARE_DENY_NONE;
7514 if ( FAILED( validateSTGM(grfMode) ))
7515 goto end;
7517 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7518 switch(STGM_ACCESS_MODE(grfMode))
7520 case STGM_WRITE:
7521 case STGM_READWRITE:
7522 break;
7523 default:
7524 goto end;
7527 /* in direct mode, can only use SHARE_EXCLUSIVE */
7528 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7529 goto end;
7531 /* but in transacted mode, any share mode is valid */
7534 * Generate a unique name.
7536 if (pwcsName == 0)
7538 WCHAR tempPath[MAX_PATH];
7539 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7541 memset(tempPath, 0, sizeof(tempPath));
7542 memset(tempFileName, 0, sizeof(tempFileName));
7544 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7545 tempPath[0] = '.';
7547 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7548 pwcsName = tempFileName;
7549 else
7551 hr = STG_E_INSUFFICIENTMEMORY;
7552 goto end;
7555 creationMode = TRUNCATE_EXISTING;
7557 else
7559 creationMode = GetCreationModeFromSTGM(grfMode);
7563 * Interpret the STGM value grfMode
7565 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7566 accessMode = GetAccessModeFromSTGM(grfMode);
7568 if (grfMode & STGM_DELETEONRELEASE)
7569 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7570 else
7571 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7573 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7575 static int fixme;
7576 if (!fixme++)
7577 FIXME("Storage share mode not implemented.\n");
7580 *ppstgOpen = 0;
7582 hFile = CreateFileW(pwcsName,
7583 accessMode,
7584 shareMode,
7585 NULL,
7586 creationMode,
7587 fileAttributes,
7590 if (hFile == INVALID_HANDLE_VALUE)
7592 if(GetLastError() == ERROR_FILE_EXISTS)
7593 hr = STG_E_FILEALREADYEXISTS;
7594 else
7595 hr = E_FAIL;
7596 goto end;
7600 * Allocate and initialize the new IStorage32object.
7602 hr = Storage_Construct(
7603 hFile,
7604 pwcsName,
7605 NULL,
7606 grfMode,
7607 TRUE,
7608 TRUE,
7609 pStgOptions->ulSectorSize,
7610 &newStorage);
7612 if (FAILED(hr))
7614 goto end;
7617 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
7618 IStorage_Release(&newStorage->IStorage_iface);
7620 end:
7621 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7623 return hr;
7626 /******************************************************************************
7627 * StgCreateDocfile [OLE32.@]
7628 * Creates a new compound file storage object
7630 * PARAMS
7631 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7632 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7633 * reserved [ ?] unused?, usually 0
7634 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7636 * RETURNS
7637 * S_OK if the file was successfully created
7638 * some STG_E_ value if error
7639 * NOTES
7640 * if pwcsName is NULL, create file with new unique name
7641 * the function can returns
7642 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7643 * (unrealized now)
7645 HRESULT WINAPI StgCreateDocfile(
7646 LPCOLESTR pwcsName,
7647 DWORD grfMode,
7648 DWORD reserved,
7649 IStorage **ppstgOpen)
7651 STGOPTIONS stgoptions = {1, 0, 512};
7653 TRACE("(%s, %x, %d, %p)\n",
7654 debugstr_w(pwcsName), grfMode,
7655 reserved, ppstgOpen);
7657 if (ppstgOpen == 0)
7658 return STG_E_INVALIDPOINTER;
7659 if (reserved != 0)
7660 return STG_E_INVALIDPARAMETER;
7662 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7665 /******************************************************************************
7666 * StgCreateStorageEx [OLE32.@]
7668 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7670 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7671 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7673 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7675 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7676 return STG_E_INVALIDPARAMETER;
7679 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7681 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7682 return STG_E_INVALIDPARAMETER;
7685 if (stgfmt == STGFMT_FILE)
7687 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7688 return STG_E_INVALIDPARAMETER;
7691 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7693 STGOPTIONS defaultOptions = {1, 0, 512};
7695 if (!pStgOptions) pStgOptions = &defaultOptions;
7696 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7700 ERR("Invalid stgfmt argument\n");
7701 return STG_E_INVALIDPARAMETER;
7704 /******************************************************************************
7705 * StgCreatePropSetStg [OLE32.@]
7707 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7708 IPropertySetStorage **propset)
7710 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
7711 if (reserved)
7712 return STG_E_INVALIDPARAMETER;
7714 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
7717 /******************************************************************************
7718 * StgOpenStorageEx [OLE32.@]
7720 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7722 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7723 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7725 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7727 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7728 return STG_E_INVALIDPARAMETER;
7731 switch (stgfmt)
7733 case STGFMT_FILE:
7734 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7735 return STG_E_INVALIDPARAMETER;
7737 case STGFMT_STORAGE:
7738 break;
7740 case STGFMT_DOCFILE:
7741 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7743 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7744 return STG_E_INVALIDPARAMETER;
7746 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7747 break;
7749 case STGFMT_ANY:
7750 WARN("STGFMT_ANY assuming storage\n");
7751 break;
7753 default:
7754 return STG_E_INVALIDPARAMETER;
7757 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7761 /******************************************************************************
7762 * StgOpenStorage [OLE32.@]
7764 HRESULT WINAPI StgOpenStorage(
7765 const OLECHAR *pwcsName,
7766 IStorage *pstgPriority,
7767 DWORD grfMode,
7768 SNB snbExclude,
7769 DWORD reserved,
7770 IStorage **ppstgOpen)
7772 StorageBaseImpl* newStorage = 0;
7773 HRESULT hr = S_OK;
7774 HANDLE hFile = 0;
7775 DWORD shareMode;
7776 DWORD accessMode;
7777 LPWSTR temp_name = NULL;
7779 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7780 debugstr_w(pwcsName), pstgPriority, grfMode,
7781 snbExclude, reserved, ppstgOpen);
7783 if (pstgPriority)
7785 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
7786 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
7787 if (FAILED(hr)) goto end;
7788 pwcsName = temp_name;
7789 TRACE("using filename %s\n", debugstr_w(temp_name));
7792 if (pwcsName == 0)
7794 hr = STG_E_INVALIDNAME;
7795 goto end;
7798 if (ppstgOpen == 0)
7800 hr = STG_E_INVALIDPOINTER;
7801 goto end;
7804 if (reserved)
7806 hr = STG_E_INVALIDPARAMETER;
7807 goto end;
7810 if (grfMode & STGM_PRIORITY)
7812 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7813 return STG_E_INVALIDFLAG;
7814 if (grfMode & STGM_DELETEONRELEASE)
7815 return STG_E_INVALIDFUNCTION;
7816 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7817 return STG_E_INVALIDFLAG;
7818 grfMode &= ~0xf0; /* remove the existing sharing mode */
7819 grfMode |= STGM_SHARE_DENY_NONE;
7821 /* STGM_PRIORITY stops other IStorage objects on the same file from
7822 * committing until the STGM_PRIORITY IStorage is closed. it also
7823 * stops non-transacted mode StgOpenStorage calls with write access from
7824 * succeeding. obviously, both of these cannot be achieved through just
7825 * file share flags */
7826 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7830 * Validate the sharing mode
7832 if (grfMode & STGM_DIRECT_SWMR)
7834 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
7835 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
7837 hr = STG_E_INVALIDFLAG;
7838 goto end;
7841 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7842 switch(STGM_SHARE_MODE(grfMode))
7844 case STGM_SHARE_EXCLUSIVE:
7845 case STGM_SHARE_DENY_WRITE:
7846 break;
7847 default:
7848 hr = STG_E_INVALIDFLAG;
7849 goto end;
7852 if ( FAILED( validateSTGM(grfMode) ) ||
7853 (grfMode&STGM_CREATE))
7855 hr = STG_E_INVALIDFLAG;
7856 goto end;
7859 /* shared reading requires transacted or single writer mode */
7860 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7861 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7862 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
7864 hr = STG_E_INVALIDFLAG;
7865 goto end;
7869 * Interpret the STGM value grfMode
7871 shareMode = GetShareModeFromSTGM(grfMode);
7872 accessMode = GetAccessModeFromSTGM(grfMode);
7874 *ppstgOpen = 0;
7876 hFile = CreateFileW( pwcsName,
7877 accessMode,
7878 shareMode,
7879 NULL,
7880 OPEN_EXISTING,
7881 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7884 if (hFile==INVALID_HANDLE_VALUE)
7886 DWORD last_error = GetLastError();
7888 hr = E_FAIL;
7890 switch (last_error)
7892 case ERROR_FILE_NOT_FOUND:
7893 hr = STG_E_FILENOTFOUND;
7894 break;
7896 case ERROR_PATH_NOT_FOUND:
7897 hr = STG_E_PATHNOTFOUND;
7898 break;
7900 case ERROR_ACCESS_DENIED:
7901 case ERROR_WRITE_PROTECT:
7902 hr = STG_E_ACCESSDENIED;
7903 break;
7905 case ERROR_SHARING_VIOLATION:
7906 hr = STG_E_SHAREVIOLATION;
7907 break;
7909 default:
7910 hr = E_FAIL;
7913 goto end;
7917 * Refuse to open the file if it's too small to be a structured storage file
7918 * FIXME: verify the file when reading instead of here
7920 if (GetFileSize(hFile, NULL) < 0x100)
7922 CloseHandle(hFile);
7923 hr = STG_E_FILEALREADYEXISTS;
7924 goto end;
7928 * Allocate and initialize the new IStorage32object.
7930 hr = Storage_Construct(
7931 hFile,
7932 pwcsName,
7933 NULL,
7934 grfMode,
7935 TRUE,
7936 FALSE,
7937 512,
7938 &newStorage);
7940 if (FAILED(hr))
7943 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7945 if(hr == STG_E_INVALIDHEADER)
7946 hr = STG_E_FILEALREADYEXISTS;
7947 goto end;
7950 *ppstgOpen = &newStorage->IStorage_iface;
7952 end:
7953 CoTaskMemFree(temp_name);
7954 if (pstgPriority) IStorage_Release(pstgPriority);
7955 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7956 return hr;
7959 /******************************************************************************
7960 * StgCreateDocfileOnILockBytes [OLE32.@]
7962 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7963 ILockBytes *plkbyt,
7964 DWORD grfMode,
7965 DWORD reserved,
7966 IStorage** ppstgOpen)
7968 StorageBaseImpl* newStorage = 0;
7969 HRESULT hr = S_OK;
7971 if ((ppstgOpen == 0) || (plkbyt == 0))
7972 return STG_E_INVALIDPOINTER;
7975 * Allocate and initialize the new IStorage object.
7977 hr = Storage_Construct(
7980 plkbyt,
7981 grfMode,
7982 FALSE,
7983 TRUE,
7984 512,
7985 &newStorage);
7987 if (FAILED(hr))
7989 return hr;
7992 *ppstgOpen = &newStorage->IStorage_iface;
7994 return hr;
7997 /******************************************************************************
7998 * StgOpenStorageOnILockBytes [OLE32.@]
8000 HRESULT WINAPI StgOpenStorageOnILockBytes(
8001 ILockBytes *plkbyt,
8002 IStorage *pstgPriority,
8003 DWORD grfMode,
8004 SNB snbExclude,
8005 DWORD reserved,
8006 IStorage **ppstgOpen)
8008 StorageBaseImpl* newStorage = 0;
8009 HRESULT hr = S_OK;
8011 if ((plkbyt == 0) || (ppstgOpen == 0))
8012 return STG_E_INVALIDPOINTER;
8014 if ( FAILED( validateSTGM(grfMode) ))
8015 return STG_E_INVALIDFLAG;
8017 *ppstgOpen = 0;
8020 * Allocate and initialize the new IStorage object.
8022 hr = Storage_Construct(
8025 plkbyt,
8026 grfMode,
8027 FALSE,
8028 FALSE,
8029 512,
8030 &newStorage);
8032 if (FAILED(hr))
8034 return hr;
8037 *ppstgOpen = &newStorage->IStorage_iface;
8039 return hr;
8042 /******************************************************************************
8043 * StgSetTimes [ole32.@]
8044 * StgSetTimes [OLE32.@]
8048 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
8049 FILETIME const *patime, FILETIME const *pmtime)
8051 IStorage *stg = NULL;
8052 HRESULT r;
8054 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
8056 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
8057 0, 0, &stg);
8058 if( SUCCEEDED(r) )
8060 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
8061 IStorage_Release(stg);
8064 return r;
8067 /******************************************************************************
8068 * StgIsStorageILockBytes [OLE32.@]
8070 * Determines if the ILockBytes contains a storage object.
8072 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
8074 BYTE sig[sizeof(STORAGE_magic)];
8075 ULARGE_INTEGER offset;
8076 ULONG read = 0;
8078 offset.u.HighPart = 0;
8079 offset.u.LowPart = 0;
8081 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
8083 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
8084 return S_OK;
8086 return S_FALSE;
8089 /******************************************************************************
8090 * WriteClassStg [OLE32.@]
8092 * This method will store the specified CLSID in the specified storage object
8094 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
8096 if(!pStg)
8097 return E_INVALIDARG;
8099 if(!rclsid)
8100 return STG_E_INVALIDPOINTER;
8102 return IStorage_SetClass(pStg, rclsid);
8105 /***********************************************************************
8106 * ReadClassStg (OLE32.@)
8108 * This method reads the CLSID previously written to a storage object with
8109 * the WriteClassStg.
8111 * PARAMS
8112 * pstg [I] IStorage pointer
8113 * pclsid [O] Pointer to where the CLSID is written
8115 * RETURNS
8116 * Success: S_OK.
8117 * Failure: HRESULT code.
8119 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
8121 STATSTG pstatstg;
8122 HRESULT hRes;
8124 TRACE("(%p, %p)\n", pstg, pclsid);
8126 if(!pstg || !pclsid)
8127 return E_INVALIDARG;
8130 * read a STATSTG structure (contains the clsid) from the storage
8132 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
8134 if(SUCCEEDED(hRes))
8135 *pclsid=pstatstg.clsid;
8137 return hRes;
8140 /***********************************************************************
8141 * OleLoadFromStream (OLE32.@)
8143 * This function loads an object from stream
8145 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
8147 CLSID clsid;
8148 HRESULT res;
8149 LPPERSISTSTREAM xstm;
8151 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
8153 res=ReadClassStm(pStm,&clsid);
8154 if (FAILED(res))
8155 return res;
8156 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
8157 if (FAILED(res))
8158 return res;
8159 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
8160 if (FAILED(res)) {
8161 IUnknown_Release((IUnknown*)*ppvObj);
8162 return res;
8164 res=IPersistStream_Load(xstm,pStm);
8165 IPersistStream_Release(xstm);
8166 /* FIXME: all refcounts ok at this point? I think they should be:
8167 * pStm : unchanged
8168 * ppvObj : 1
8169 * xstm : 0 (released)
8171 return res;
8174 /***********************************************************************
8175 * OleSaveToStream (OLE32.@)
8177 * This function saves an object with the IPersistStream interface on it
8178 * to the specified stream.
8180 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
8183 CLSID clsid;
8184 HRESULT res;
8186 TRACE("(%p,%p)\n",pPStm,pStm);
8188 res=IPersistStream_GetClassID(pPStm,&clsid);
8190 if (SUCCEEDED(res)){
8192 res=WriteClassStm(pStm,&clsid);
8194 if (SUCCEEDED(res))
8196 res=IPersistStream_Save(pPStm,pStm,TRUE);
8199 TRACE("Finished Save\n");
8200 return res;
8203 /****************************************************************************
8204 * This method validate a STGM parameter that can contain the values below
8206 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8207 * The stgm values contained in 0xffff0000 are bitmasks.
8209 * STGM_DIRECT 0x00000000
8210 * STGM_TRANSACTED 0x00010000
8211 * STGM_SIMPLE 0x08000000
8213 * STGM_READ 0x00000000
8214 * STGM_WRITE 0x00000001
8215 * STGM_READWRITE 0x00000002
8217 * STGM_SHARE_DENY_NONE 0x00000040
8218 * STGM_SHARE_DENY_READ 0x00000030
8219 * STGM_SHARE_DENY_WRITE 0x00000020
8220 * STGM_SHARE_EXCLUSIVE 0x00000010
8222 * STGM_PRIORITY 0x00040000
8223 * STGM_DELETEONRELEASE 0x04000000
8225 * STGM_CREATE 0x00001000
8226 * STGM_CONVERT 0x00020000
8227 * STGM_FAILIFTHERE 0x00000000
8229 * STGM_NOSCRATCH 0x00100000
8230 * STGM_NOSNAPSHOT 0x00200000
8232 static HRESULT validateSTGM(DWORD stgm)
8234 DWORD access = STGM_ACCESS_MODE(stgm);
8235 DWORD share = STGM_SHARE_MODE(stgm);
8236 DWORD create = STGM_CREATE_MODE(stgm);
8238 if (stgm&~STGM_KNOWN_FLAGS)
8240 ERR("unknown flags %08x\n", stgm);
8241 return E_FAIL;
8244 switch (access)
8246 case STGM_READ:
8247 case STGM_WRITE:
8248 case STGM_READWRITE:
8249 break;
8250 default:
8251 return E_FAIL;
8254 switch (share)
8256 case STGM_SHARE_DENY_NONE:
8257 case STGM_SHARE_DENY_READ:
8258 case STGM_SHARE_DENY_WRITE:
8259 case STGM_SHARE_EXCLUSIVE:
8260 break;
8261 default:
8262 return E_FAIL;
8265 switch (create)
8267 case STGM_CREATE:
8268 case STGM_FAILIFTHERE:
8269 break;
8270 default:
8271 return E_FAIL;
8275 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8277 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8278 return E_FAIL;
8281 * STGM_CREATE | STGM_CONVERT
8282 * if both are false, STGM_FAILIFTHERE is set to TRUE
8284 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8285 return E_FAIL;
8288 * STGM_NOSCRATCH requires STGM_TRANSACTED
8290 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8291 return E_FAIL;
8294 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8295 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8297 if ( (stgm & STGM_NOSNAPSHOT) &&
8298 (!(stgm & STGM_TRANSACTED) ||
8299 share == STGM_SHARE_EXCLUSIVE ||
8300 share == STGM_SHARE_DENY_WRITE) )
8301 return E_FAIL;
8303 return S_OK;
8306 /****************************************************************************
8307 * GetShareModeFromSTGM
8309 * This method will return a share mode flag from a STGM value.
8310 * The STGM value is assumed valid.
8312 static DWORD GetShareModeFromSTGM(DWORD stgm)
8314 switch (STGM_SHARE_MODE(stgm))
8316 case STGM_SHARE_DENY_NONE:
8317 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8318 case STGM_SHARE_DENY_READ:
8319 return FILE_SHARE_WRITE;
8320 case STGM_SHARE_DENY_WRITE:
8321 return FILE_SHARE_READ;
8322 case STGM_SHARE_EXCLUSIVE:
8323 return 0;
8325 ERR("Invalid share mode!\n");
8326 assert(0);
8327 return 0;
8330 /****************************************************************************
8331 * GetAccessModeFromSTGM
8333 * This method will return an access mode flag from a STGM value.
8334 * The STGM value is assumed valid.
8336 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8338 switch (STGM_ACCESS_MODE(stgm))
8340 case STGM_READ:
8341 return GENERIC_READ;
8342 case STGM_WRITE:
8343 case STGM_READWRITE:
8344 return GENERIC_READ | GENERIC_WRITE;
8346 ERR("Invalid access mode!\n");
8347 assert(0);
8348 return 0;
8351 /****************************************************************************
8352 * GetCreationModeFromSTGM
8354 * This method will return a creation mode flag from a STGM value.
8355 * The STGM value is assumed valid.
8357 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8359 switch(STGM_CREATE_MODE(stgm))
8361 case STGM_CREATE:
8362 return CREATE_ALWAYS;
8363 case STGM_CONVERT:
8364 FIXME("STGM_CONVERT not implemented!\n");
8365 return CREATE_NEW;
8366 case STGM_FAILIFTHERE:
8367 return CREATE_NEW;
8369 ERR("Invalid create mode!\n");
8370 assert(0);
8371 return 0;
8375 /*************************************************************************
8376 * OLECONVERT_LoadOLE10 [Internal]
8378 * Loads the OLE10 STREAM to memory
8380 * PARAMS
8381 * pOleStream [I] The OLESTREAM
8382 * pData [I] Data Structure for the OLESTREAM Data
8384 * RETURNS
8385 * Success: S_OK
8386 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8387 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8389 * NOTES
8390 * This function is used by OleConvertOLESTREAMToIStorage only.
8392 * Memory allocated for pData must be freed by the caller
8394 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8396 DWORD dwSize;
8397 HRESULT hRes = S_OK;
8398 int nTryCnt=0;
8399 int max_try = 6;
8401 pData->pData = NULL;
8402 pData->pstrOleObjFileName = NULL;
8404 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8406 /* Get the OleID */
8407 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8408 if(dwSize != sizeof(pData->dwOleID))
8410 hRes = CONVERT10_E_OLESTREAM_GET;
8412 else if(pData->dwOleID != OLESTREAM_ID)
8414 hRes = CONVERT10_E_OLESTREAM_FMT;
8416 else
8418 hRes = S_OK;
8419 break;
8423 if(hRes == S_OK)
8425 /* Get the TypeID... more info needed for this field */
8426 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8427 if(dwSize != sizeof(pData->dwTypeID))
8429 hRes = CONVERT10_E_OLESTREAM_GET;
8432 if(hRes == S_OK)
8434 if(pData->dwTypeID != 0)
8436 /* Get the length of the OleTypeName */
8437 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8438 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8440 hRes = CONVERT10_E_OLESTREAM_GET;
8443 if(hRes == S_OK)
8445 if(pData->dwOleTypeNameLength > 0)
8447 /* Get the OleTypeName */
8448 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8449 if(dwSize != pData->dwOleTypeNameLength)
8451 hRes = CONVERT10_E_OLESTREAM_GET;
8455 if(bStrem1)
8457 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8458 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8460 hRes = CONVERT10_E_OLESTREAM_GET;
8462 if(hRes == S_OK)
8464 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8465 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8466 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8467 if(pData->pstrOleObjFileName)
8469 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8470 if(dwSize != pData->dwOleObjFileNameLength)
8472 hRes = CONVERT10_E_OLESTREAM_GET;
8475 else
8476 hRes = CONVERT10_E_OLESTREAM_GET;
8479 else
8481 /* Get the Width of the Metafile */
8482 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8483 if(dwSize != sizeof(pData->dwMetaFileWidth))
8485 hRes = CONVERT10_E_OLESTREAM_GET;
8487 if(hRes == S_OK)
8489 /* Get the Height of the Metafile */
8490 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8491 if(dwSize != sizeof(pData->dwMetaFileHeight))
8493 hRes = CONVERT10_E_OLESTREAM_GET;
8497 if(hRes == S_OK)
8499 /* Get the Length of the Data */
8500 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8501 if(dwSize != sizeof(pData->dwDataLength))
8503 hRes = CONVERT10_E_OLESTREAM_GET;
8507 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8509 if(!bStrem1) /* if it is a second OLE stream data */
8511 pData->dwDataLength -= 8;
8512 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8513 if(dwSize != sizeof(pData->strUnknown))
8515 hRes = CONVERT10_E_OLESTREAM_GET;
8519 if(hRes == S_OK)
8521 if(pData->dwDataLength > 0)
8523 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8525 /* Get Data (ex. IStorage, Metafile, or BMP) */
8526 if(pData->pData)
8528 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8529 if(dwSize != pData->dwDataLength)
8531 hRes = CONVERT10_E_OLESTREAM_GET;
8534 else
8536 hRes = CONVERT10_E_OLESTREAM_GET;
8542 return hRes;
8545 /*************************************************************************
8546 * OLECONVERT_SaveOLE10 [Internal]
8548 * Saves the OLE10 STREAM From memory
8550 * PARAMS
8551 * pData [I] Data Structure for the OLESTREAM Data
8552 * pOleStream [I] The OLESTREAM to save
8554 * RETURNS
8555 * Success: S_OK
8556 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8558 * NOTES
8559 * This function is used by OleConvertIStorageToOLESTREAM only.
8562 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8564 DWORD dwSize;
8565 HRESULT hRes = S_OK;
8568 /* Set the OleID */
8569 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8570 if(dwSize != sizeof(pData->dwOleID))
8572 hRes = CONVERT10_E_OLESTREAM_PUT;
8575 if(hRes == S_OK)
8577 /* Set the TypeID */
8578 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8579 if(dwSize != sizeof(pData->dwTypeID))
8581 hRes = CONVERT10_E_OLESTREAM_PUT;
8585 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8587 /* Set the Length of the OleTypeName */
8588 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8589 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8591 hRes = CONVERT10_E_OLESTREAM_PUT;
8594 if(hRes == S_OK)
8596 if(pData->dwOleTypeNameLength > 0)
8598 /* Set the OleTypeName */
8599 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8600 if(dwSize != pData->dwOleTypeNameLength)
8602 hRes = CONVERT10_E_OLESTREAM_PUT;
8607 if(hRes == S_OK)
8609 /* Set the width of the Metafile */
8610 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8611 if(dwSize != sizeof(pData->dwMetaFileWidth))
8613 hRes = CONVERT10_E_OLESTREAM_PUT;
8617 if(hRes == S_OK)
8619 /* Set the height of the Metafile */
8620 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8621 if(dwSize != sizeof(pData->dwMetaFileHeight))
8623 hRes = CONVERT10_E_OLESTREAM_PUT;
8627 if(hRes == S_OK)
8629 /* Set the length of the Data */
8630 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8631 if(dwSize != sizeof(pData->dwDataLength))
8633 hRes = CONVERT10_E_OLESTREAM_PUT;
8637 if(hRes == S_OK)
8639 if(pData->dwDataLength > 0)
8641 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8642 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8643 if(dwSize != pData->dwDataLength)
8645 hRes = CONVERT10_E_OLESTREAM_PUT;
8650 return hRes;
8653 /*************************************************************************
8654 * OLECONVERT_GetOLE20FromOLE10[Internal]
8656 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8657 * opens it, and copies the content to the dest IStorage for
8658 * OleConvertOLESTREAMToIStorage
8661 * PARAMS
8662 * pDestStorage [I] The IStorage to copy the data to
8663 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8664 * nBufferLength [I] The size of the buffer
8666 * RETURNS
8667 * Nothing
8669 * NOTES
8673 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8675 HRESULT hRes;
8676 HANDLE hFile;
8677 IStorage *pTempStorage;
8678 DWORD dwNumOfBytesWritten;
8679 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8680 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8682 /* Create a temp File */
8683 GetTempPathW(MAX_PATH, wstrTempDir);
8684 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8685 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8687 if(hFile != INVALID_HANDLE_VALUE)
8689 /* Write IStorage Data to File */
8690 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8691 CloseHandle(hFile);
8693 /* Open and copy temp storage to the Dest Storage */
8694 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8695 if(hRes == S_OK)
8697 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8698 IStorage_Release(pTempStorage);
8700 DeleteFileW(wstrTempFile);
8705 /*************************************************************************
8706 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8708 * Saves the OLE10 STREAM From memory
8710 * PARAMS
8711 * pStorage [I] The Src IStorage to copy
8712 * pData [I] The Dest Memory to write to.
8714 * RETURNS
8715 * The size in bytes allocated for pData
8717 * NOTES
8718 * Memory allocated for pData must be freed by the caller
8720 * Used by OleConvertIStorageToOLESTREAM only.
8723 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8725 HANDLE hFile;
8726 HRESULT hRes;
8727 DWORD nDataLength = 0;
8728 IStorage *pTempStorage;
8729 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8730 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8732 *pData = NULL;
8734 /* Create temp Storage */
8735 GetTempPathW(MAX_PATH, wstrTempDir);
8736 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8737 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8739 if(hRes == S_OK)
8741 /* Copy Src Storage to the Temp Storage */
8742 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8743 IStorage_Release(pTempStorage);
8745 /* Open Temp Storage as a file and copy to memory */
8746 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8747 if(hFile != INVALID_HANDLE_VALUE)
8749 nDataLength = GetFileSize(hFile, NULL);
8750 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8751 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8752 CloseHandle(hFile);
8754 DeleteFileW(wstrTempFile);
8756 return nDataLength;
8759 /*************************************************************************
8760 * STORAGE_CreateOleStream [Internal]
8762 * Creates the "\001OLE" stream in the IStorage if necessary.
8764 * PARAMS
8765 * storage [I] Dest storage to create the stream in
8766 * flags [I] flags to be set for newly created stream
8768 * RETURNS
8769 * HRESULT return value
8771 * NOTES
8773 * This stream is still unknown, MS Word seems to have extra data
8774 * but since the data is stored in the OLESTREAM there should be
8775 * no need to recreate the stream. If the stream is manually
8776 * deleted it will create it with this default data.
8779 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
8781 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
8782 static const DWORD version_magic = 0x02000001;
8783 IStream *stream;
8784 HRESULT hr;
8786 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
8787 if (hr == S_OK)
8789 struct empty_1ole_stream {
8790 DWORD version_magic;
8791 DWORD flags;
8792 DWORD update_options;
8793 DWORD reserved;
8794 DWORD mon_stream_size;
8796 struct empty_1ole_stream stream_data;
8798 stream_data.version_magic = version_magic;
8799 stream_data.flags = flags;
8800 stream_data.update_options = 0;
8801 stream_data.reserved = 0;
8802 stream_data.mon_stream_size = 0;
8804 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
8805 IStream_Release(stream);
8808 return hr;
8811 /* write a string to a stream, preceded by its length */
8812 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8814 HRESULT r;
8815 LPSTR str;
8816 DWORD len = 0;
8818 if( string )
8819 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8820 r = IStream_Write( stm, &len, sizeof(len), NULL);
8821 if( FAILED( r ) )
8822 return r;
8823 if(len == 0)
8824 return r;
8825 str = CoTaskMemAlloc( len );
8826 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8827 r = IStream_Write( stm, str, len, NULL);
8828 CoTaskMemFree( str );
8829 return r;
8832 /* read a string preceded by its length from a stream */
8833 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8835 HRESULT r;
8836 DWORD len, count = 0;
8837 LPSTR str;
8838 LPWSTR wstr;
8840 r = IStream_Read( stm, &len, sizeof(len), &count );
8841 if( FAILED( r ) )
8842 return r;
8843 if( count != sizeof(len) )
8844 return E_OUTOFMEMORY;
8846 TRACE("%d bytes\n",len);
8848 str = CoTaskMemAlloc( len );
8849 if( !str )
8850 return E_OUTOFMEMORY;
8851 count = 0;
8852 r = IStream_Read( stm, str, len, &count );
8853 if( FAILED( r ) )
8854 return r;
8855 if( count != len )
8857 CoTaskMemFree( str );
8858 return E_OUTOFMEMORY;
8861 TRACE("Read string %s\n",debugstr_an(str,len));
8863 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8864 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8865 if( wstr )
8867 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8868 wstr[len] = 0;
8870 CoTaskMemFree( str );
8872 *string = wstr;
8874 return r;
8878 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8879 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8881 IStream *pstm;
8882 HRESULT r = S_OK;
8883 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8885 static const BYTE unknown1[12] =
8886 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8887 0xFF, 0xFF, 0xFF, 0xFF};
8888 static const BYTE unknown2[16] =
8889 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8890 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8892 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8893 debugstr_w(lpszUserType), debugstr_w(szClipName),
8894 debugstr_w(szProgIDName));
8896 /* Create a CompObj stream */
8897 r = IStorage_CreateStream(pstg, szwStreamName,
8898 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8899 if( FAILED (r) )
8900 return r;
8902 /* Write CompObj Structure to stream */
8903 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8905 if( SUCCEEDED( r ) )
8906 r = WriteClassStm( pstm, clsid );
8908 if( SUCCEEDED( r ) )
8909 r = STREAM_WriteString( pstm, lpszUserType );
8910 if( SUCCEEDED( r ) )
8911 r = STREAM_WriteString( pstm, szClipName );
8912 if( SUCCEEDED( r ) )
8913 r = STREAM_WriteString( pstm, szProgIDName );
8914 if( SUCCEEDED( r ) )
8915 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8917 IStream_Release( pstm );
8919 return r;
8922 /***********************************************************************
8923 * WriteFmtUserTypeStg (OLE32.@)
8925 HRESULT WINAPI WriteFmtUserTypeStg(
8926 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8928 STATSTG stat;
8929 HRESULT r;
8930 WCHAR szwClipName[0x40];
8931 CLSID clsid;
8932 LPWSTR wstrProgID = NULL;
8933 DWORD n;
8935 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8937 /* get the clipboard format name */
8938 if( cf )
8940 n = GetClipboardFormatNameW( cf, szwClipName,
8941 sizeof(szwClipName)/sizeof(szwClipName[0]) );
8942 szwClipName[n]=0;
8945 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8947 r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
8948 if(SUCCEEDED(r))
8949 clsid = stat.clsid;
8950 else
8951 clsid = CLSID_NULL;
8953 ProgIDFromCLSID(&clsid, &wstrProgID);
8955 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8957 r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
8958 cf ? szwClipName : NULL, wstrProgID );
8960 CoTaskMemFree(wstrProgID);
8962 return r;
8966 /******************************************************************************
8967 * ReadFmtUserTypeStg [OLE32.@]
8969 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8971 HRESULT r;
8972 IStream *stm = 0;
8973 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8974 unsigned char unknown1[12];
8975 unsigned char unknown2[16];
8976 DWORD count;
8977 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8978 CLSID clsid;
8980 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8982 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8983 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8984 if( FAILED ( r ) )
8986 WARN("Failed to open stream r = %08x\n", r);
8987 return r;
8990 /* read the various parts of the structure */
8991 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8992 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8993 goto end;
8994 r = ReadClassStm( stm, &clsid );
8995 if( FAILED( r ) )
8996 goto end;
8998 r = STREAM_ReadString( stm, &szCLSIDName );
8999 if( FAILED( r ) )
9000 goto end;
9002 r = STREAM_ReadString( stm, &szOleTypeName );
9003 if( FAILED( r ) )
9004 goto end;
9006 r = STREAM_ReadString( stm, &szProgIDName );
9007 if( FAILED( r ) )
9008 goto end;
9010 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
9011 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
9012 goto end;
9014 /* ok, success... now we just need to store what we found */
9015 if( pcf )
9016 *pcf = RegisterClipboardFormatW( szOleTypeName );
9018 if( lplpszUserType )
9020 *lplpszUserType = szCLSIDName;
9021 szCLSIDName = NULL;
9024 end:
9025 CoTaskMemFree( szCLSIDName );
9026 CoTaskMemFree( szOleTypeName );
9027 CoTaskMemFree( szProgIDName );
9028 IStream_Release( stm );
9030 return r;
9034 /*************************************************************************
9035 * OLECONVERT_CreateCompObjStream [Internal]
9037 * Creates a "\001CompObj" is the destination IStorage if necessary.
9039 * PARAMS
9040 * pStorage [I] The dest IStorage to create the CompObj Stream
9041 * if necessary.
9042 * strOleTypeName [I] The ProgID
9044 * RETURNS
9045 * Success: S_OK
9046 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9048 * NOTES
9049 * This function is used by OleConvertOLESTREAMToIStorage only.
9051 * The stream data is stored in the OLESTREAM and there should be
9052 * no need to recreate the stream. If the stream is manually
9053 * deleted it will attempt to create it by querying the registry.
9057 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
9059 IStream *pStream;
9060 HRESULT hStorageRes, hRes = S_OK;
9061 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
9062 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9063 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
9065 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9066 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
9068 /* Initialize the CompObj structure */
9069 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
9070 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
9071 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
9074 /* Create a CompObj stream if it doesn't exist */
9075 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
9076 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9077 if(hStorageRes == S_OK)
9079 /* copy the OleTypeName to the compobj struct */
9080 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
9081 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
9083 /* copy the OleTypeName to the compobj struct */
9084 /* Note: in the test made, these were Identical */
9085 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
9086 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
9088 /* Get the CLSID */
9089 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
9090 bufferW, OLESTREAM_MAX_STR_LEN );
9091 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
9093 if(hRes == S_OK)
9095 HKEY hKey;
9096 LONG hErr;
9097 /* Get the CLSID Default Name from the Registry */
9098 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
9099 if(hErr == ERROR_SUCCESS)
9101 char strTemp[OLESTREAM_MAX_STR_LEN];
9102 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
9103 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
9104 if(hErr == ERROR_SUCCESS)
9106 strcpy(IStorageCompObj.strCLSIDName, strTemp);
9108 RegCloseKey(hKey);
9112 /* Write CompObj Structure to stream */
9113 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
9115 WriteClassStm(pStream,&(IStorageCompObj.clsid));
9117 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
9118 if(IStorageCompObj.dwCLSIDNameLength > 0)
9120 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
9122 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
9123 if(IStorageCompObj.dwOleTypeNameLength > 0)
9125 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
9127 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
9128 if(IStorageCompObj.dwProgIDNameLength > 0)
9130 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
9132 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
9133 IStream_Release(pStream);
9135 return hRes;
9139 /*************************************************************************
9140 * OLECONVERT_CreateOlePresStream[Internal]
9142 * Creates the "\002OlePres000" Stream with the Metafile data
9144 * PARAMS
9145 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
9146 * dwExtentX [I] Width of the Metafile
9147 * dwExtentY [I] Height of the Metafile
9148 * pData [I] Metafile data
9149 * dwDataLength [I] Size of the Metafile data
9151 * RETURNS
9152 * Success: S_OK
9153 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9155 * NOTES
9156 * This function is used by OleConvertOLESTREAMToIStorage only.
9159 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
9161 HRESULT hRes;
9162 IStream *pStream;
9163 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9164 BYTE pOlePresStreamHeader [] =
9166 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
9167 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9168 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9169 0x00, 0x00, 0x00, 0x00
9172 BYTE pOlePresStreamHeaderEmpty [] =
9174 0x00, 0x00, 0x00, 0x00,
9175 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9176 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9177 0x00, 0x00, 0x00, 0x00
9180 /* Create the OlePres000 Stream */
9181 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9182 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9184 if(hRes == S_OK)
9186 DWORD nHeaderSize;
9187 OLECONVERT_ISTORAGE_OLEPRES OlePres;
9189 memset(&OlePres, 0, sizeof(OlePres));
9190 /* Do we have any metafile data to save */
9191 if(dwDataLength > 0)
9193 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
9194 nHeaderSize = sizeof(pOlePresStreamHeader);
9196 else
9198 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
9199 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
9201 /* Set width and height of the metafile */
9202 OlePres.dwExtentX = dwExtentX;
9203 OlePres.dwExtentY = -dwExtentY;
9205 /* Set Data and Length */
9206 if(dwDataLength > sizeof(METAFILEPICT16))
9208 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
9209 OlePres.pData = &(pData[8]);
9211 /* Save OlePres000 Data to Stream */
9212 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
9213 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
9214 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
9215 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
9216 if(OlePres.dwSize > 0)
9218 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
9220 IStream_Release(pStream);
9224 /*************************************************************************
9225 * OLECONVERT_CreateOle10NativeStream [Internal]
9227 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9229 * PARAMS
9230 * pStorage [I] Dest storage to create the stream in
9231 * pData [I] Ole10 Native Data (ex. bmp)
9232 * dwDataLength [I] Size of the Ole10 Native Data
9234 * RETURNS
9235 * Nothing
9237 * NOTES
9238 * This function is used by OleConvertOLESTREAMToIStorage only.
9240 * Might need to verify the data and return appropriate error message
9243 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
9245 HRESULT hRes;
9246 IStream *pStream;
9247 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9249 /* Create the Ole10Native Stream */
9250 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9251 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9253 if(hRes == S_OK)
9255 /* Write info to stream */
9256 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9257 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9258 IStream_Release(pStream);
9263 /*************************************************************************
9264 * OLECONVERT_GetOLE10ProgID [Internal]
9266 * Finds the ProgID (or OleTypeID) from the IStorage
9268 * PARAMS
9269 * pStorage [I] The Src IStorage to get the ProgID
9270 * strProgID [I] the ProgID string to get
9271 * dwSize [I] the size of the string
9273 * RETURNS
9274 * Success: S_OK
9275 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9277 * NOTES
9278 * This function is used by OleConvertIStorageToOLESTREAM only.
9282 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9284 HRESULT hRes;
9285 IStream *pStream;
9286 LARGE_INTEGER iSeekPos;
9287 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9288 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9290 /* Open the CompObj Stream */
9291 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9292 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9293 if(hRes == S_OK)
9296 /*Get the OleType from the CompObj Stream */
9297 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9298 iSeekPos.u.HighPart = 0;
9300 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9301 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9302 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9303 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9304 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9305 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9306 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9308 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9309 if(*dwSize > 0)
9311 IStream_Read(pStream, strProgID, *dwSize, NULL);
9313 IStream_Release(pStream);
9315 else
9317 STATSTG stat;
9318 LPOLESTR wstrProgID;
9320 /* Get the OleType from the registry */
9321 REFCLSID clsid = &(stat.clsid);
9322 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9323 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9324 if(hRes == S_OK)
9326 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9327 CoTaskMemFree(wstrProgID);
9331 return hRes;
9334 /*************************************************************************
9335 * OLECONVERT_GetOle10PresData [Internal]
9337 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9339 * PARAMS
9340 * pStorage [I] Src IStroage
9341 * pOleStream [I] Dest OleStream Mem Struct
9343 * RETURNS
9344 * Nothing
9346 * NOTES
9347 * This function is used by OleConvertIStorageToOLESTREAM only.
9349 * Memory allocated for pData must be freed by the caller
9353 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9356 HRESULT hRes;
9357 IStream *pStream;
9358 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9360 /* Initialize Default data for OLESTREAM */
9361 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9362 pOleStreamData[0].dwTypeID = 2;
9363 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9364 pOleStreamData[1].dwTypeID = 0;
9365 pOleStreamData[0].dwMetaFileWidth = 0;
9366 pOleStreamData[0].dwMetaFileHeight = 0;
9367 pOleStreamData[0].pData = NULL;
9368 pOleStreamData[1].pData = NULL;
9370 /* Open Ole10Native Stream */
9371 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9372 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9373 if(hRes == S_OK)
9376 /* Read Size and Data */
9377 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9378 if(pOleStreamData->dwDataLength > 0)
9380 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9381 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9383 IStream_Release(pStream);
9389 /*************************************************************************
9390 * OLECONVERT_GetOle20PresData[Internal]
9392 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9394 * PARAMS
9395 * pStorage [I] Src IStroage
9396 * pOleStreamData [I] Dest OleStream Mem Struct
9398 * RETURNS
9399 * Nothing
9401 * NOTES
9402 * This function is used by OleConvertIStorageToOLESTREAM only.
9404 * Memory allocated for pData must be freed by the caller
9406 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9408 HRESULT hRes;
9409 IStream *pStream;
9410 OLECONVERT_ISTORAGE_OLEPRES olePress;
9411 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9413 /* Initialize Default data for OLESTREAM */
9414 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9415 pOleStreamData[0].dwTypeID = 2;
9416 pOleStreamData[0].dwMetaFileWidth = 0;
9417 pOleStreamData[0].dwMetaFileHeight = 0;
9418 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9419 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9420 pOleStreamData[1].dwTypeID = 0;
9421 pOleStreamData[1].dwOleTypeNameLength = 0;
9422 pOleStreamData[1].strOleTypeName[0] = 0;
9423 pOleStreamData[1].dwMetaFileWidth = 0;
9424 pOleStreamData[1].dwMetaFileHeight = 0;
9425 pOleStreamData[1].pData = NULL;
9426 pOleStreamData[1].dwDataLength = 0;
9429 /* Open OlePress000 stream */
9430 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9431 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9432 if(hRes == S_OK)
9434 LARGE_INTEGER iSeekPos;
9435 METAFILEPICT16 MetaFilePict;
9436 static const char strMetafilePictName[] = "METAFILEPICT";
9438 /* Set the TypeID for a Metafile */
9439 pOleStreamData[1].dwTypeID = 5;
9441 /* Set the OleTypeName to Metafile */
9442 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9443 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9445 iSeekPos.u.HighPart = 0;
9446 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9448 /* Get Presentation Data */
9449 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9450 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9451 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9452 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9454 /*Set width and Height */
9455 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9456 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9457 if(olePress.dwSize > 0)
9459 /* Set Length */
9460 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9462 /* Set MetaFilePict struct */
9463 MetaFilePict.mm = 8;
9464 MetaFilePict.xExt = olePress.dwExtentX;
9465 MetaFilePict.yExt = olePress.dwExtentY;
9466 MetaFilePict.hMF = 0;
9468 /* Get Metafile Data */
9469 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9470 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9471 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9473 IStream_Release(pStream);
9477 /*************************************************************************
9478 * OleConvertOLESTREAMToIStorage [OLE32.@]
9480 * Read info on MSDN
9482 * TODO
9483 * DVTARGETDEVICE parameter is not handled
9484 * Still unsure of some mem fields for OLE 10 Stream
9485 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9486 * and "\001OLE" streams
9489 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9490 LPOLESTREAM pOleStream,
9491 LPSTORAGE pstg,
9492 const DVTARGETDEVICE* ptd)
9494 int i;
9495 HRESULT hRes=S_OK;
9496 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9498 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9500 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9502 if(ptd != NULL)
9504 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9507 if(pstg == NULL || pOleStream == NULL)
9509 hRes = E_INVALIDARG;
9512 if(hRes == S_OK)
9514 /* Load the OLESTREAM to Memory */
9515 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9518 if(hRes == S_OK)
9520 /* Load the OLESTREAM to Memory (part 2)*/
9521 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9524 if(hRes == S_OK)
9527 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9529 /* Do we have the IStorage Data in the OLESTREAM */
9530 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9532 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9533 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9535 else
9537 /* It must be an original OLE 1.0 source */
9538 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9541 else
9543 /* It must be an original OLE 1.0 source */
9544 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9547 /* Create CompObj Stream if necessary */
9548 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9549 if(hRes == S_OK)
9551 /*Create the Ole Stream if necessary */
9552 STORAGE_CreateOleStream(pstg, 0);
9557 /* Free allocated memory */
9558 for(i=0; i < 2; i++)
9560 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9561 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9562 pOleStreamData[i].pstrOleObjFileName = NULL;
9564 return hRes;
9567 /*************************************************************************
9568 * OleConvertIStorageToOLESTREAM [OLE32.@]
9570 * Read info on MSDN
9572 * Read info on MSDN
9574 * TODO
9575 * Still unsure of some mem fields for OLE 10 Stream
9576 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9577 * and "\001OLE" streams.
9580 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9581 LPSTORAGE pstg,
9582 LPOLESTREAM pOleStream)
9584 int i;
9585 HRESULT hRes = S_OK;
9586 IStream *pStream;
9587 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9588 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9590 TRACE("%p %p\n", pstg, pOleStream);
9592 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9594 if(pstg == NULL || pOleStream == NULL)
9596 hRes = E_INVALIDARG;
9598 if(hRes == S_OK)
9600 /* Get the ProgID */
9601 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9602 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9604 if(hRes == S_OK)
9606 /* Was it originally Ole10 */
9607 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9608 if(hRes == S_OK)
9610 IStream_Release(pStream);
9611 /* Get Presentation Data for Ole10Native */
9612 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9614 else
9616 /* Get Presentation Data (OLE20) */
9617 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9620 /* Save OLESTREAM */
9621 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9622 if(hRes == S_OK)
9624 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9629 /* Free allocated memory */
9630 for(i=0; i < 2; i++)
9632 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9635 return hRes;
9638 enum stream_1ole_flags {
9639 OleStream_LinkedObject = 0x00000001,
9640 OleStream_Convert = 0x00000004
9643 /***********************************************************************
9644 * GetConvertStg (OLE32.@)
9646 HRESULT WINAPI GetConvertStg(IStorage *stg)
9648 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9649 static const DWORD version_magic = 0x02000001;
9650 DWORD header[2];
9651 IStream *stream;
9652 HRESULT hr;
9654 TRACE("%p\n", stg);
9656 if (!stg) return E_INVALIDARG;
9658 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
9659 if (FAILED(hr)) return hr;
9661 hr = IStream_Read(stream, header, sizeof(header), NULL);
9662 IStream_Release(stream);
9663 if (FAILED(hr)) return hr;
9665 if (header[0] != version_magic)
9667 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
9668 return E_FAIL;
9671 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
9674 /***********************************************************************
9675 * SetConvertStg (OLE32.@)
9677 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
9679 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9680 DWORD flags = convert ? OleStream_Convert : 0;
9681 IStream *stream;
9682 DWORD header[2];
9683 HRESULT hr;
9685 TRACE("(%p, %d)\n", storage, convert);
9687 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
9688 if (FAILED(hr))
9690 if (hr != STG_E_FILENOTFOUND)
9691 return hr;
9693 return STORAGE_CreateOleStream(storage, flags);
9696 hr = IStream_Read(stream, header, sizeof(header), NULL);
9697 if (FAILED(hr))
9699 IStream_Release(stream);
9700 return hr;
9703 /* update flag if differs */
9704 if ((header[1] ^ flags) & OleStream_Convert)
9706 LARGE_INTEGER pos = {{0}};
9708 if (header[1] & OleStream_Convert)
9709 flags = header[1] & ~OleStream_Convert;
9710 else
9711 flags = header[1] | OleStream_Convert;
9713 pos.QuadPart = sizeof(DWORD);
9714 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
9715 if (FAILED(hr))
9717 IStream_Release(stream);
9718 return hr;
9721 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
9724 IStream_Release(stream);
9725 return hr;
9728 /******************************************************************************
9729 * StgIsStorageFile [OLE32.@]
9730 * Verify if the file contains a storage object
9732 * PARAMS
9733 * fn [ I] Filename
9735 * RETURNS
9736 * S_OK if file has magic bytes as a storage object
9737 * S_FALSE if file is not storage
9739 HRESULT WINAPI
9740 StgIsStorageFile(LPCOLESTR fn)
9742 HANDLE hf;
9743 BYTE magic[8];
9744 DWORD bytes_read;
9746 TRACE("%s\n", debugstr_w(fn));
9747 hf = CreateFileW(fn, GENERIC_READ,
9748 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9749 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9751 if (hf == INVALID_HANDLE_VALUE)
9752 return STG_E_FILENOTFOUND;
9754 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9756 WARN(" unable to read file\n");
9757 CloseHandle(hf);
9758 return S_FALSE;
9761 CloseHandle(hf);
9763 if (bytes_read != 8) {
9764 TRACE(" too short\n");
9765 return S_FALSE;
9768 if (!memcmp(magic,STORAGE_magic,8)) {
9769 TRACE(" -> YES\n");
9770 return S_OK;
9773 TRACE(" -> Invalid header.\n");
9774 return S_FALSE;
9777 /***********************************************************************
9778 * WriteClassStm (OLE32.@)
9780 * Writes a CLSID to a stream.
9782 * PARAMS
9783 * pStm [I] Stream to write to.
9784 * rclsid [I] CLSID to write.
9786 * RETURNS
9787 * Success: S_OK.
9788 * Failure: HRESULT code.
9790 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9792 TRACE("(%p,%p)\n",pStm,rclsid);
9794 if (!pStm || !rclsid)
9795 return E_INVALIDARG;
9797 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9800 /***********************************************************************
9801 * ReadClassStm (OLE32.@)
9803 * Reads a CLSID from a stream.
9805 * PARAMS
9806 * pStm [I] Stream to read from.
9807 * rclsid [O] CLSID to read.
9809 * RETURNS
9810 * Success: S_OK.
9811 * Failure: HRESULT code.
9813 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9815 ULONG nbByte;
9816 HRESULT res;
9818 TRACE("(%p,%p)\n",pStm,pclsid);
9820 if (!pStm || !pclsid)
9821 return E_INVALIDARG;
9823 /* clear the output args */
9824 *pclsid = CLSID_NULL;
9826 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9828 if (FAILED(res))
9829 return res;
9831 if (nbByte != sizeof(CLSID))
9832 return STG_E_READFAULT;
9833 else
9834 return S_OK;