ole32: Use a snapshot file when sharing storages for writing.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob39fa68fc8ca0e347c73d10e78a5664c3e60b8680
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);
113 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset, ULARGE_INTEGER cb, DWORD dwLockType);
115 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex);
116 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
117 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
118 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
119 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
121 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
122 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
123 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
125 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
126 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
127 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
128 ULONG blockIndex, ULONG offset, DWORD value);
129 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
130 ULONG blockIndex, ULONG offset, DWORD* value);
132 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
133 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
135 typedef struct TransactedDirEntry
137 /* If applicable, a reference to the original DirEntry in the transacted
138 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
139 DirRef transactedParentEntry;
141 /* True if this entry is being used. */
142 BOOL inuse;
144 /* True if data is up to date. */
145 BOOL read;
147 /* True if this entry has been modified. */
148 BOOL dirty;
150 /* True if this entry's stream has been modified. */
151 BOOL stream_dirty;
153 /* True if this entry has been deleted in the transacted storage, but the
154 * delete has not yet been committed. */
155 BOOL deleted;
157 /* If this entry's stream has been modified, a reference to where the stream
158 * is stored in the snapshot file. */
159 DirRef stream_entry;
161 /* This directory entry's data, including any changes that have been made. */
162 DirEntry data;
164 /* A reference to the parent of this node. This is only valid while we are
165 * committing changes. */
166 DirRef parent;
168 /* A reference to a newly-created entry in the transacted parent. This is
169 * always equal to transactedParentEntry except when committing changes. */
170 DirRef newTransactedParentEntry;
171 } TransactedDirEntry;
173 /****************************************************************************
174 * Transacted storage object.
176 typedef struct TransactedSnapshotImpl
178 struct StorageBaseImpl base;
181 * Modified streams are temporarily saved to the scratch file.
183 StorageBaseImpl *scratch;
185 /* The directory structure is kept here, so that we can track how these
186 * entries relate to those in the parent storage. */
187 TransactedDirEntry *entries;
188 ULONG entries_size;
189 ULONG firstFreeEntry;
192 * Changes are committed to the transacted parent.
194 StorageBaseImpl *transactedParent;
196 /* The transaction signature from when we last committed */
197 ULONG lastTransactionSig;
198 } TransactedSnapshotImpl;
200 typedef struct TransactedSharedImpl
202 struct StorageBaseImpl base;
205 * Snapshot and uncommitted changes go here.
207 TransactedSnapshotImpl *scratch;
210 * Changes are committed to the transacted parent.
212 StorageBaseImpl *transactedParent;
214 /* The transaction signature from when we last committed */
215 ULONG lastTransactionSig;
216 } TransactedSharedImpl;
218 /* Generic function to create a transacted wrapper for a direct storage object. */
219 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, BOOL toplevel, StorageBaseImpl** result);
221 /* OLESTREAM memory structure to use for Get and Put Routines */
222 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
223 typedef struct
225 DWORD dwOleID;
226 DWORD dwTypeID;
227 DWORD dwOleTypeNameLength;
228 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
229 CHAR *pstrOleObjFileName;
230 DWORD dwOleObjFileNameLength;
231 DWORD dwMetaFileWidth;
232 DWORD dwMetaFileHeight;
233 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
234 DWORD dwDataLength;
235 BYTE *pData;
236 }OLECONVERT_OLESTREAM_DATA;
238 /* CompObj Stream structure */
239 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
240 typedef struct
242 BYTE byUnknown1[12];
243 CLSID clsid;
244 DWORD dwCLSIDNameLength;
245 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
246 DWORD dwOleTypeNameLength;
247 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
248 DWORD dwProgIDNameLength;
249 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
250 BYTE byUnknown2[16];
251 }OLECONVERT_ISTORAGE_COMPOBJ;
254 /* Ole Presentation Stream structure */
255 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
256 typedef struct
258 BYTE byUnknown1[28];
259 DWORD dwExtentX;
260 DWORD dwExtentY;
261 DWORD dwSize;
262 BYTE *pData;
263 }OLECONVERT_ISTORAGE_OLEPRES;
267 /***********************************************************************
268 * Forward declaration of internal functions used by the method DestroyElement
270 static HRESULT deleteStorageContents(
271 StorageBaseImpl *parentStorage,
272 DirRef indexToDelete,
273 DirEntry entryDataToDelete);
275 static HRESULT deleteStreamContents(
276 StorageBaseImpl *parentStorage,
277 DirRef indexToDelete,
278 DirEntry entryDataToDelete);
280 static HRESULT removeFromTree(
281 StorageBaseImpl *This,
282 DirRef parentStorageIndex,
283 DirRef deletedIndex);
285 /***********************************************************************
286 * Declaration of the functions used to manipulate DirEntry
289 static HRESULT insertIntoTree(
290 StorageBaseImpl *This,
291 DirRef parentStorageIndex,
292 DirRef newEntryIndex);
294 static LONG entryNameCmp(
295 const OLECHAR *name1,
296 const OLECHAR *name2);
298 static DirRef findElement(
299 StorageBaseImpl *storage,
300 DirRef storageEntry,
301 const OLECHAR *name,
302 DirEntry *data);
304 static HRESULT findTreeParent(
305 StorageBaseImpl *storage,
306 DirRef storageEntry,
307 const OLECHAR *childName,
308 DirEntry *parentData,
309 DirRef *parentEntry,
310 ULONG *relation);
312 /***********************************************************************
313 * Declaration of miscellaneous functions...
315 static HRESULT validateSTGM(DWORD stgmValue);
317 static DWORD GetShareModeFromSTGM(DWORD stgm);
318 static DWORD GetAccessModeFromSTGM(DWORD stgm);
319 static DWORD GetCreationModeFromSTGM(DWORD stgm);
321 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
324 /****************************************************************************
325 * IEnumSTATSTGImpl definitions.
327 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
328 * This class allows iterating through the content of a storage and to find
329 * specific items inside it.
331 struct IEnumSTATSTGImpl
333 IEnumSTATSTG IEnumSTATSTG_iface;
335 LONG ref; /* Reference count */
336 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
337 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
339 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
342 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
344 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
348 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
349 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
351 /************************************************************************
352 ** Block Functions
355 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
357 return (index+1) * This->bigBlockSize;
360 /************************************************************************
361 ** Storage32BaseImpl implementation
363 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
364 ULARGE_INTEGER offset,
365 void* buffer,
366 ULONG size,
367 ULONG* bytesRead)
369 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
372 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
373 ULARGE_INTEGER offset,
374 const void* buffer,
375 const ULONG size,
376 ULONG* bytesWritten)
378 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
381 /************************************************************************
382 * Storage32BaseImpl_QueryInterface (IUnknown)
384 * This method implements the common QueryInterface for all IStorage32
385 * implementations contained in this file.
387 * See Windows documentation for more details on IUnknown methods.
389 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
390 IStorage* iface,
391 REFIID riid,
392 void** ppvObject)
394 StorageBaseImpl *This = impl_from_IStorage(iface);
396 if (!ppvObject)
397 return E_INVALIDARG;
399 *ppvObject = 0;
401 if (IsEqualGUID(&IID_IUnknown, riid) ||
402 IsEqualGUID(&IID_IStorage, riid))
404 *ppvObject = &This->IStorage_iface;
406 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
408 *ppvObject = &This->IPropertySetStorage_iface;
410 /* locking interface is reported for writer only */
411 else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
413 *ppvObject = &This->IDirectWriterLock_iface;
415 else
416 return E_NOINTERFACE;
418 IStorage_AddRef(iface);
420 return S_OK;
423 /************************************************************************
424 * Storage32BaseImpl_AddRef (IUnknown)
426 * This method implements the common AddRef for all IStorage32
427 * implementations contained in this file.
429 * See Windows documentation for more details on IUnknown methods.
431 static ULONG WINAPI StorageBaseImpl_AddRef(
432 IStorage* iface)
434 StorageBaseImpl *This = impl_from_IStorage(iface);
435 ULONG ref = InterlockedIncrement(&This->ref);
437 TRACE("(%p) AddRef to %d\n", This, ref);
439 return ref;
442 /************************************************************************
443 * Storage32BaseImpl_Release (IUnknown)
445 * This method implements the common Release for all IStorage32
446 * implementations contained in this file.
448 * See Windows documentation for more details on IUnknown methods.
450 static ULONG WINAPI StorageBaseImpl_Release(
451 IStorage* iface)
453 StorageBaseImpl *This = impl_from_IStorage(iface);
455 ULONG ref = InterlockedDecrement(&This->ref);
457 TRACE("(%p) ReleaseRef to %d\n", This, ref);
459 if (ref == 0)
462 * Since we are using a system of base-classes, we want to call the
463 * destructor of the appropriate derived class. To do this, we are
464 * using virtual functions to implement the destructor.
466 StorageBaseImpl_Destroy(This);
469 return ref;
472 /************************************************************************
473 * Storage32BaseImpl_OpenStream (IStorage)
475 * This method will open the specified stream object from the current storage.
477 * See Windows documentation for more details on IStorage methods.
479 static HRESULT WINAPI StorageBaseImpl_OpenStream(
480 IStorage* iface,
481 const OLECHAR* pwcsName, /* [string][in] */
482 void* reserved1, /* [unique][in] */
483 DWORD grfMode, /* [in] */
484 DWORD reserved2, /* [in] */
485 IStream** ppstm) /* [out] */
487 StorageBaseImpl *This = impl_from_IStorage(iface);
488 StgStreamImpl* newStream;
489 DirEntry currentEntry;
490 DirRef streamEntryRef;
491 HRESULT res = STG_E_UNKNOWN;
493 TRACE("(%p, %s, %p, %x, %d, %p)\n",
494 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
496 if ( (pwcsName==NULL) || (ppstm==0) )
498 res = E_INVALIDARG;
499 goto end;
502 *ppstm = NULL;
504 if ( FAILED( validateSTGM(grfMode) ) ||
505 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
507 res = STG_E_INVALIDFLAG;
508 goto end;
512 * As documented.
514 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
516 res = STG_E_INVALIDFUNCTION;
517 goto end;
520 if (This->reverted)
522 res = STG_E_REVERTED;
523 goto end;
527 * Check that we're compatible with the parent's storage mode, but
528 * only if we are not in transacted mode
530 if(!(This->openFlags & STGM_TRANSACTED)) {
531 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
533 res = STG_E_INVALIDFLAG;
534 goto end;
539 * Search for the element with the given name
541 streamEntryRef = findElement(
542 This,
543 This->storageDirEntry,
544 pwcsName,
545 &currentEntry);
548 * If it was found, construct the stream object and return a pointer to it.
550 if ( (streamEntryRef!=DIRENTRY_NULL) &&
551 (currentEntry.stgType==STGTY_STREAM) )
553 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
555 /* A single stream cannot be opened a second time. */
556 res = STG_E_ACCESSDENIED;
557 goto end;
560 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
562 if (newStream)
564 newStream->grfMode = grfMode;
565 *ppstm = &newStream->IStream_iface;
567 IStream_AddRef(*ppstm);
569 res = S_OK;
570 goto end;
573 res = E_OUTOFMEMORY;
574 goto end;
577 res = STG_E_FILENOTFOUND;
579 end:
580 if (res == S_OK)
581 TRACE("<-- IStream %p\n", *ppstm);
582 TRACE("<-- %08x\n", res);
583 return res;
586 /************************************************************************
587 * Storage32BaseImpl_OpenStorage (IStorage)
589 * This method will open a new storage object from the current storage.
591 * See Windows documentation for more details on IStorage methods.
593 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
594 IStorage* iface,
595 const OLECHAR* pwcsName, /* [string][unique][in] */
596 IStorage* pstgPriority, /* [unique][in] */
597 DWORD grfMode, /* [in] */
598 SNB snbExclude, /* [unique][in] */
599 DWORD reserved, /* [in] */
600 IStorage** ppstg) /* [out] */
602 StorageBaseImpl *This = impl_from_IStorage(iface);
603 StorageInternalImpl* newStorage;
604 StorageBaseImpl* newTransactedStorage;
605 DirEntry currentEntry;
606 DirRef storageEntryRef;
607 HRESULT res = STG_E_UNKNOWN;
609 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
610 iface, debugstr_w(pwcsName), pstgPriority,
611 grfMode, snbExclude, reserved, ppstg);
613 if ((pwcsName==NULL) || (ppstg==0) )
615 res = E_INVALIDARG;
616 goto end;
619 if (This->openFlags & STGM_SIMPLE)
621 res = STG_E_INVALIDFUNCTION;
622 goto end;
625 /* as documented */
626 if (snbExclude != NULL)
628 res = STG_E_INVALIDPARAMETER;
629 goto end;
632 if ( FAILED( validateSTGM(grfMode) ))
634 res = STG_E_INVALIDFLAG;
635 goto end;
639 * As documented.
641 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
642 (grfMode & STGM_DELETEONRELEASE) ||
643 (grfMode & STGM_PRIORITY) )
645 res = STG_E_INVALIDFUNCTION;
646 goto end;
649 if (This->reverted)
650 return STG_E_REVERTED;
653 * Check that we're compatible with the parent's storage mode,
654 * but only if we are not transacted
656 if(!(This->openFlags & STGM_TRANSACTED)) {
657 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
659 res = STG_E_ACCESSDENIED;
660 goto end;
664 *ppstg = NULL;
666 storageEntryRef = findElement(
667 This,
668 This->storageDirEntry,
669 pwcsName,
670 &currentEntry);
672 if ( (storageEntryRef!=DIRENTRY_NULL) &&
673 (currentEntry.stgType==STGTY_STORAGE) )
675 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
677 /* A single storage cannot be opened a second time. */
678 res = STG_E_ACCESSDENIED;
679 goto end;
682 newStorage = StorageInternalImpl_Construct(
683 This,
684 grfMode,
685 storageEntryRef);
687 if (newStorage != 0)
689 if (grfMode & STGM_TRANSACTED)
691 res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
693 if (FAILED(res))
695 HeapFree(GetProcessHeap(), 0, newStorage);
696 goto end;
699 *ppstg = &newTransactedStorage->IStorage_iface;
701 else
703 *ppstg = &newStorage->base.IStorage_iface;
706 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
708 res = S_OK;
709 goto end;
712 res = STG_E_INSUFFICIENTMEMORY;
713 goto end;
716 res = STG_E_FILENOTFOUND;
718 end:
719 TRACE("<-- %08x\n", res);
720 return res;
723 /************************************************************************
724 * Storage32BaseImpl_EnumElements (IStorage)
726 * This method will create an enumerator object that can be used to
727 * retrieve information about all the elements in the storage object.
729 * See Windows documentation for more details on IStorage methods.
731 static HRESULT WINAPI StorageBaseImpl_EnumElements(
732 IStorage* iface,
733 DWORD reserved1, /* [in] */
734 void* reserved2, /* [size_is][unique][in] */
735 DWORD reserved3, /* [in] */
736 IEnumSTATSTG** ppenum) /* [out] */
738 StorageBaseImpl *This = impl_from_IStorage(iface);
739 IEnumSTATSTGImpl* newEnum;
741 TRACE("(%p, %d, %p, %d, %p)\n",
742 iface, reserved1, reserved2, reserved3, ppenum);
744 if (!ppenum)
745 return E_INVALIDARG;
747 if (This->reverted)
748 return STG_E_REVERTED;
750 newEnum = IEnumSTATSTGImpl_Construct(
751 This,
752 This->storageDirEntry);
754 if (newEnum)
756 *ppenum = &newEnum->IEnumSTATSTG_iface;
757 return S_OK;
760 return E_OUTOFMEMORY;
763 /************************************************************************
764 * Storage32BaseImpl_Stat (IStorage)
766 * This method will retrieve information about this storage object.
768 * See Windows documentation for more details on IStorage methods.
770 static HRESULT WINAPI StorageBaseImpl_Stat(
771 IStorage* iface,
772 STATSTG* pstatstg, /* [out] */
773 DWORD grfStatFlag) /* [in] */
775 StorageBaseImpl *This = impl_from_IStorage(iface);
776 DirEntry currentEntry;
777 HRESULT res = STG_E_UNKNOWN;
779 TRACE("(%p, %p, %x)\n",
780 iface, pstatstg, grfStatFlag);
782 if (!pstatstg)
784 res = E_INVALIDARG;
785 goto end;
788 if (This->reverted)
790 res = STG_E_REVERTED;
791 goto end;
794 res = StorageBaseImpl_ReadDirEntry(
795 This,
796 This->storageDirEntry,
797 &currentEntry);
799 if (SUCCEEDED(res))
801 StorageUtl_CopyDirEntryToSTATSTG(
802 This,
803 pstatstg,
804 &currentEntry,
805 grfStatFlag);
807 pstatstg->grfMode = This->openFlags;
808 pstatstg->grfStateBits = This->stateBits;
811 end:
812 if (res == S_OK)
814 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);
816 TRACE("<-- %08x\n", res);
817 return res;
820 /************************************************************************
821 * Storage32BaseImpl_RenameElement (IStorage)
823 * This method will rename the specified element.
825 * See Windows documentation for more details on IStorage methods.
827 static HRESULT WINAPI StorageBaseImpl_RenameElement(
828 IStorage* iface,
829 const OLECHAR* pwcsOldName, /* [in] */
830 const OLECHAR* pwcsNewName) /* [in] */
832 StorageBaseImpl *This = impl_from_IStorage(iface);
833 DirEntry currentEntry;
834 DirRef currentEntryRef;
836 TRACE("(%p, %s, %s)\n",
837 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
839 if (This->reverted)
840 return STG_E_REVERTED;
842 currentEntryRef = findElement(This,
843 This->storageDirEntry,
844 pwcsNewName,
845 &currentEntry);
847 if (currentEntryRef != DIRENTRY_NULL)
850 * There is already an element with the new name
852 return STG_E_FILEALREADYEXISTS;
856 * Search for the old element name
858 currentEntryRef = findElement(This,
859 This->storageDirEntry,
860 pwcsOldName,
861 &currentEntry);
863 if (currentEntryRef != DIRENTRY_NULL)
865 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
866 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
868 WARN("Element is already open; cannot rename.\n");
869 return STG_E_ACCESSDENIED;
872 /* Remove the element from its current position in the tree */
873 removeFromTree(This, This->storageDirEntry,
874 currentEntryRef);
876 /* Change the name of the element */
877 strcpyW(currentEntry.name, pwcsNewName);
879 /* Delete any sibling links */
880 currentEntry.leftChild = DIRENTRY_NULL;
881 currentEntry.rightChild = DIRENTRY_NULL;
883 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
884 &currentEntry);
886 /* Insert the element in a new position in the tree */
887 insertIntoTree(This, This->storageDirEntry,
888 currentEntryRef);
890 else
893 * There is no element with the old name
895 return STG_E_FILENOTFOUND;
898 return StorageBaseImpl_Flush(This);
901 /************************************************************************
902 * Storage32BaseImpl_CreateStream (IStorage)
904 * This method will create a stream object within this storage
906 * See Windows documentation for more details on IStorage methods.
908 static HRESULT WINAPI StorageBaseImpl_CreateStream(
909 IStorage* iface,
910 const OLECHAR* pwcsName, /* [string][in] */
911 DWORD grfMode, /* [in] */
912 DWORD reserved1, /* [in] */
913 DWORD reserved2, /* [in] */
914 IStream** ppstm) /* [out] */
916 StorageBaseImpl *This = impl_from_IStorage(iface);
917 StgStreamImpl* newStream;
918 DirEntry currentEntry, newStreamEntry;
919 DirRef currentEntryRef, newStreamEntryRef;
920 HRESULT hr;
922 TRACE("(%p, %s, %x, %d, %d, %p)\n",
923 iface, debugstr_w(pwcsName), grfMode,
924 reserved1, reserved2, ppstm);
926 if (ppstm == 0)
927 return STG_E_INVALIDPOINTER;
929 if (pwcsName == 0)
930 return STG_E_INVALIDNAME;
932 if (reserved1 || reserved2)
933 return STG_E_INVALIDPARAMETER;
935 if ( FAILED( validateSTGM(grfMode) ))
936 return STG_E_INVALIDFLAG;
938 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
939 return STG_E_INVALIDFLAG;
941 if (This->reverted)
942 return STG_E_REVERTED;
945 * As documented.
947 if ((grfMode & STGM_DELETEONRELEASE) ||
948 (grfMode & STGM_TRANSACTED))
949 return STG_E_INVALIDFUNCTION;
952 * Don't worry about permissions in transacted mode, as we can always write
953 * changes; we just can't always commit them.
955 if(!(This->openFlags & STGM_TRANSACTED)) {
956 /* Can't create a stream on read-only storage */
957 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
958 return STG_E_ACCESSDENIED;
960 /* Can't create a stream with greater access than the parent. */
961 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
962 return STG_E_ACCESSDENIED;
965 if(This->openFlags & STGM_SIMPLE)
966 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
968 *ppstm = 0;
970 currentEntryRef = findElement(This,
971 This->storageDirEntry,
972 pwcsName,
973 &currentEntry);
975 if (currentEntryRef != DIRENTRY_NULL)
978 * An element with this name already exists
980 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
982 IStorage_DestroyElement(iface, pwcsName);
984 else
985 return STG_E_FILEALREADYEXISTS;
989 * memset the empty entry
991 memset(&newStreamEntry, 0, sizeof(DirEntry));
993 newStreamEntry.sizeOfNameString =
994 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
996 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
997 return STG_E_INVALIDNAME;
999 strcpyW(newStreamEntry.name, pwcsName);
1001 newStreamEntry.stgType = STGTY_STREAM;
1002 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
1003 newStreamEntry.size.u.LowPart = 0;
1004 newStreamEntry.size.u.HighPart = 0;
1006 newStreamEntry.leftChild = DIRENTRY_NULL;
1007 newStreamEntry.rightChild = DIRENTRY_NULL;
1008 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
1010 /* call CoFileTime to get the current time
1011 newStreamEntry.ctime
1012 newStreamEntry.mtime
1015 /* newStreamEntry.clsid */
1018 * Create an entry with the new data
1020 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
1021 if (FAILED(hr))
1022 return hr;
1025 * Insert the new entry in the parent storage's tree.
1027 hr = insertIntoTree(
1028 This,
1029 This->storageDirEntry,
1030 newStreamEntryRef);
1031 if (FAILED(hr))
1033 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
1034 return hr;
1038 * Open the stream to return it.
1040 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1042 if (newStream)
1044 *ppstm = &newStream->IStream_iface;
1045 IStream_AddRef(*ppstm);
1047 else
1049 return STG_E_INSUFFICIENTMEMORY;
1052 return StorageBaseImpl_Flush(This);
1055 /************************************************************************
1056 * Storage32BaseImpl_SetClass (IStorage)
1058 * This method will write the specified CLSID in the directory entry of this
1059 * storage.
1061 * See Windows documentation for more details on IStorage methods.
1063 static HRESULT WINAPI StorageBaseImpl_SetClass(
1064 IStorage* iface,
1065 REFCLSID clsid) /* [in] */
1067 StorageBaseImpl *This = impl_from_IStorage(iface);
1068 HRESULT hRes;
1069 DirEntry currentEntry;
1071 TRACE("(%p, %p)\n", iface, clsid);
1073 if (This->reverted)
1074 return STG_E_REVERTED;
1076 hRes = StorageBaseImpl_ReadDirEntry(This,
1077 This->storageDirEntry,
1078 &currentEntry);
1079 if (SUCCEEDED(hRes))
1081 currentEntry.clsid = *clsid;
1083 hRes = StorageBaseImpl_WriteDirEntry(This,
1084 This->storageDirEntry,
1085 &currentEntry);
1088 if (SUCCEEDED(hRes))
1089 hRes = StorageBaseImpl_Flush(This);
1091 return hRes;
1094 /************************************************************************
1095 ** Storage32Impl implementation
1098 /************************************************************************
1099 * Storage32BaseImpl_CreateStorage (IStorage)
1101 * This method will create the storage object within the provided storage.
1103 * See Windows documentation for more details on IStorage methods.
1105 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1106 IStorage* iface,
1107 const OLECHAR *pwcsName, /* [string][in] */
1108 DWORD grfMode, /* [in] */
1109 DWORD reserved1, /* [in] */
1110 DWORD reserved2, /* [in] */
1111 IStorage **ppstg) /* [out] */
1113 StorageBaseImpl* This = impl_from_IStorage(iface);
1115 DirEntry currentEntry;
1116 DirEntry newEntry;
1117 DirRef currentEntryRef;
1118 DirRef newEntryRef;
1119 HRESULT hr;
1121 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1122 iface, debugstr_w(pwcsName), grfMode,
1123 reserved1, reserved2, ppstg);
1125 if (ppstg == 0)
1126 return STG_E_INVALIDPOINTER;
1128 if (This->openFlags & STGM_SIMPLE)
1130 return STG_E_INVALIDFUNCTION;
1133 if (pwcsName == 0)
1134 return STG_E_INVALIDNAME;
1136 *ppstg = NULL;
1138 if ( FAILED( validateSTGM(grfMode) ) ||
1139 (grfMode & STGM_DELETEONRELEASE) )
1141 WARN("bad grfMode: 0x%x\n", grfMode);
1142 return STG_E_INVALIDFLAG;
1145 if (This->reverted)
1146 return STG_E_REVERTED;
1149 * Check that we're compatible with the parent's storage mode
1151 if ( !(This->openFlags & STGM_TRANSACTED) &&
1152 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1154 WARN("access denied\n");
1155 return STG_E_ACCESSDENIED;
1158 currentEntryRef = findElement(This,
1159 This->storageDirEntry,
1160 pwcsName,
1161 &currentEntry);
1163 if (currentEntryRef != DIRENTRY_NULL)
1166 * An element with this name already exists
1168 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1169 ((This->openFlags & STGM_TRANSACTED) ||
1170 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1172 hr = IStorage_DestroyElement(iface, pwcsName);
1173 if (FAILED(hr))
1174 return hr;
1176 else
1178 WARN("file already exists\n");
1179 return STG_E_FILEALREADYEXISTS;
1182 else if (!(This->openFlags & STGM_TRANSACTED) &&
1183 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1185 WARN("read-only storage\n");
1186 return STG_E_ACCESSDENIED;
1189 memset(&newEntry, 0, sizeof(DirEntry));
1191 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1193 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1195 FIXME("name too long\n");
1196 return STG_E_INVALIDNAME;
1199 strcpyW(newEntry.name, pwcsName);
1201 newEntry.stgType = STGTY_STORAGE;
1202 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1203 newEntry.size.u.LowPart = 0;
1204 newEntry.size.u.HighPart = 0;
1206 newEntry.leftChild = DIRENTRY_NULL;
1207 newEntry.rightChild = DIRENTRY_NULL;
1208 newEntry.dirRootEntry = DIRENTRY_NULL;
1210 /* call CoFileTime to get the current time
1211 newEntry.ctime
1212 newEntry.mtime
1215 /* newEntry.clsid */
1218 * Create a new directory entry for the storage
1220 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1221 if (FAILED(hr))
1222 return hr;
1225 * Insert the new directory entry into the parent storage's tree
1227 hr = insertIntoTree(
1228 This,
1229 This->storageDirEntry,
1230 newEntryRef);
1231 if (FAILED(hr))
1233 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1234 return hr;
1238 * Open it to get a pointer to return.
1240 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1242 if( (hr != S_OK) || (*ppstg == NULL))
1244 return hr;
1247 if (SUCCEEDED(hr))
1248 hr = StorageBaseImpl_Flush(This);
1250 return S_OK;
1254 /***************************************************************************
1256 * Internal Method
1258 * Reserve a directory entry in the file and initialize it.
1260 static HRESULT StorageImpl_CreateDirEntry(
1261 StorageBaseImpl *base,
1262 const DirEntry *newData,
1263 DirRef *index)
1265 StorageImpl *storage = (StorageImpl*)base;
1266 ULONG currentEntryIndex = 0;
1267 ULONG newEntryIndex = DIRENTRY_NULL;
1268 HRESULT hr = S_OK;
1269 BYTE currentData[RAW_DIRENTRY_SIZE];
1270 WORD sizeOfNameString;
1274 hr = StorageImpl_ReadRawDirEntry(storage,
1275 currentEntryIndex,
1276 currentData);
1278 if (SUCCEEDED(hr))
1280 StorageUtl_ReadWord(
1281 currentData,
1282 OFFSET_PS_NAMELENGTH,
1283 &sizeOfNameString);
1285 if (sizeOfNameString == 0)
1288 * The entry exists and is available, we found it.
1290 newEntryIndex = currentEntryIndex;
1293 else
1296 * We exhausted the directory entries, we will create more space below
1298 newEntryIndex = currentEntryIndex;
1300 currentEntryIndex++;
1302 } while (newEntryIndex == DIRENTRY_NULL);
1305 * grow the directory stream
1307 if (FAILED(hr))
1309 BYTE emptyData[RAW_DIRENTRY_SIZE];
1310 ULARGE_INTEGER newSize;
1311 ULONG entryIndex;
1312 ULONG lastEntry = 0;
1313 ULONG blockCount = 0;
1316 * obtain the new count of blocks in the directory stream
1318 blockCount = BlockChainStream_GetCount(
1319 storage->rootBlockChain)+1;
1322 * initialize the size used by the directory stream
1324 newSize.u.HighPart = 0;
1325 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1328 * add a block to the directory stream
1330 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1333 * memset the empty entry in order to initialize the unused newly
1334 * created entries
1336 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1339 * initialize them
1341 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1343 for(
1344 entryIndex = newEntryIndex + 1;
1345 entryIndex < lastEntry;
1346 entryIndex++)
1348 StorageImpl_WriteRawDirEntry(
1349 storage,
1350 entryIndex,
1351 emptyData);
1354 StorageImpl_SaveFileHeader(storage);
1357 UpdateRawDirEntry(currentData, newData);
1359 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1361 if (SUCCEEDED(hr))
1362 *index = newEntryIndex;
1364 return hr;
1367 /***************************************************************************
1369 * Internal Method
1371 * Mark a directory entry in the file as free.
1373 static HRESULT StorageImpl_DestroyDirEntry(
1374 StorageBaseImpl *base,
1375 DirRef index)
1377 BYTE emptyData[RAW_DIRENTRY_SIZE];
1378 StorageImpl *storage = (StorageImpl*)base;
1380 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1382 return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1386 /****************************************************************************
1388 * Internal Method
1390 * Case insensitive comparison of DirEntry.name by first considering
1391 * their size.
1393 * Returns <0 when name1 < name2
1394 * >0 when name1 > name2
1395 * 0 when name1 == name2
1397 static LONG entryNameCmp(
1398 const OLECHAR *name1,
1399 const OLECHAR *name2)
1401 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1403 while (diff == 0 && *name1 != 0)
1406 * We compare the string themselves only when they are of the same length
1408 diff = toupperW(*name1++) - toupperW(*name2++);
1411 return diff;
1414 /****************************************************************************
1416 * Internal Method
1418 * Add a directory entry to a storage
1420 static HRESULT insertIntoTree(
1421 StorageBaseImpl *This,
1422 DirRef parentStorageIndex,
1423 DirRef newEntryIndex)
1425 DirEntry currentEntry;
1426 DirEntry newEntry;
1429 * Read the inserted entry
1431 StorageBaseImpl_ReadDirEntry(This,
1432 newEntryIndex,
1433 &newEntry);
1436 * Read the storage entry
1438 StorageBaseImpl_ReadDirEntry(This,
1439 parentStorageIndex,
1440 &currentEntry);
1442 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1445 * The root storage contains some element, therefore, start the research
1446 * for the appropriate location.
1448 BOOL found = FALSE;
1449 DirRef current, next, previous, currentEntryId;
1452 * Keep a reference to the root of the storage's element tree
1454 currentEntryId = currentEntry.dirRootEntry;
1457 * Read
1459 StorageBaseImpl_ReadDirEntry(This,
1460 currentEntry.dirRootEntry,
1461 &currentEntry);
1463 previous = currentEntry.leftChild;
1464 next = currentEntry.rightChild;
1465 current = currentEntryId;
1467 while (!found)
1469 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1471 if (diff < 0)
1473 if (previous != DIRENTRY_NULL)
1475 StorageBaseImpl_ReadDirEntry(This,
1476 previous,
1477 &currentEntry);
1478 current = previous;
1480 else
1482 currentEntry.leftChild = newEntryIndex;
1483 StorageBaseImpl_WriteDirEntry(This,
1484 current,
1485 &currentEntry);
1486 found = TRUE;
1489 else if (diff > 0)
1491 if (next != DIRENTRY_NULL)
1493 StorageBaseImpl_ReadDirEntry(This,
1494 next,
1495 &currentEntry);
1496 current = next;
1498 else
1500 currentEntry.rightChild = newEntryIndex;
1501 StorageBaseImpl_WriteDirEntry(This,
1502 current,
1503 &currentEntry);
1504 found = TRUE;
1507 else
1510 * Trying to insert an item with the same name in the
1511 * subtree structure.
1513 return STG_E_FILEALREADYEXISTS;
1516 previous = currentEntry.leftChild;
1517 next = currentEntry.rightChild;
1520 else
1523 * The storage is empty, make the new entry the root of its element tree
1525 currentEntry.dirRootEntry = newEntryIndex;
1526 StorageBaseImpl_WriteDirEntry(This,
1527 parentStorageIndex,
1528 &currentEntry);
1531 return S_OK;
1534 /****************************************************************************
1536 * Internal Method
1538 * Find and read the element of a storage with the given name.
1540 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1541 const OLECHAR *name, DirEntry *data)
1543 DirRef currentEntry;
1545 /* Read the storage entry to find the root of the tree. */
1546 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1548 currentEntry = data->dirRootEntry;
1550 while (currentEntry != DIRENTRY_NULL)
1552 LONG cmp;
1554 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1556 cmp = entryNameCmp(name, data->name);
1558 if (cmp == 0)
1559 /* found it */
1560 break;
1562 else if (cmp < 0)
1563 currentEntry = data->leftChild;
1565 else if (cmp > 0)
1566 currentEntry = data->rightChild;
1569 return currentEntry;
1572 /****************************************************************************
1574 * Internal Method
1576 * Find and read the binary tree parent of the element with the given name.
1578 * If there is no such element, find a place where it could be inserted and
1579 * return STG_E_FILENOTFOUND.
1581 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1582 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1583 ULONG *relation)
1585 DirRef childEntry;
1586 DirEntry childData;
1588 /* Read the storage entry to find the root of the tree. */
1589 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1591 *parentEntry = storageEntry;
1592 *relation = DIRENTRY_RELATION_DIR;
1594 childEntry = parentData->dirRootEntry;
1596 while (childEntry != DIRENTRY_NULL)
1598 LONG cmp;
1600 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1602 cmp = entryNameCmp(childName, childData.name);
1604 if (cmp == 0)
1605 /* found it */
1606 break;
1608 else if (cmp < 0)
1610 *parentData = childData;
1611 *parentEntry = childEntry;
1612 *relation = DIRENTRY_RELATION_PREVIOUS;
1614 childEntry = parentData->leftChild;
1617 else if (cmp > 0)
1619 *parentData = childData;
1620 *parentEntry = childEntry;
1621 *relation = DIRENTRY_RELATION_NEXT;
1623 childEntry = parentData->rightChild;
1627 if (childEntry == DIRENTRY_NULL)
1628 return STG_E_FILENOTFOUND;
1629 else
1630 return S_OK;
1634 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1635 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1636 SNB snbExclude, IStorage *pstgDest);
1638 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
1639 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1640 SNB snbExclude, IStorage *pstgDest)
1642 DirEntry data;
1643 HRESULT hr;
1644 BOOL skip = FALSE;
1645 IStorage *pstgTmp;
1646 IStream *pstrChild, *pstrTmp;
1647 STATSTG strStat;
1649 if (srcEntry == DIRENTRY_NULL)
1650 return S_OK;
1652 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1654 if (FAILED(hr))
1655 return hr;
1657 if ( snbExclude )
1659 WCHAR **snb = snbExclude;
1661 while ( *snb != NULL && !skip )
1663 if ( lstrcmpW(data.name, *snb) == 0 )
1664 skip = TRUE;
1665 ++snb;
1669 if (!skip)
1671 if (data.stgType == STGTY_STORAGE && !skip_storage)
1674 * create a new storage in destination storage
1676 hr = IStorage_CreateStorage( pstgDest, data.name,
1677 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1678 0, 0,
1679 &pstgTmp );
1682 * if it already exist, don't create a new one use this one
1684 if (hr == STG_E_FILEALREADYEXISTS)
1686 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1687 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1688 NULL, 0, &pstgTmp );
1691 if (SUCCEEDED(hr))
1693 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1694 skip_stream, NULL, pstgTmp );
1696 IStorage_Release(pstgTmp);
1699 else if (data.stgType == STGTY_STREAM && !skip_stream)
1702 * create a new stream in destination storage. If the stream already
1703 * exist, it will be deleted and a new one will be created.
1705 hr = IStorage_CreateStream( pstgDest, data.name,
1706 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1707 0, 0, &pstrTmp );
1710 * open child stream storage. This operation must succeed even if the
1711 * stream is already open, so we use internal functions to do it.
1713 if (hr == S_OK)
1715 StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1717 if (streamimpl)
1719 pstrChild = &streamimpl->IStream_iface;
1720 if (pstrChild)
1721 IStream_AddRef(pstrChild);
1723 else
1725 pstrChild = NULL;
1726 hr = E_OUTOFMEMORY;
1730 if (hr == S_OK)
1733 * Get the size of the source stream
1735 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1738 * Set the size of the destination stream.
1740 IStream_SetSize(pstrTmp, strStat.cbSize);
1743 * do the copy
1745 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1746 NULL, NULL );
1748 IStream_Release( pstrChild );
1751 IStream_Release( pstrTmp );
1755 /* copy siblings */
1756 if (SUCCEEDED(hr))
1757 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1758 skip_stream, snbExclude, pstgDest );
1760 if (SUCCEEDED(hr))
1761 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1762 skip_stream, snbExclude, pstgDest );
1764 return hr;
1767 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1768 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1769 SNB snbExclude, IStorage *pstgDest)
1771 DirEntry data;
1772 HRESULT hr;
1774 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1776 if (SUCCEEDED(hr))
1777 hr = IStorage_SetClass( pstgDest, &data.clsid );
1779 if (SUCCEEDED(hr))
1780 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
1781 skip_stream, snbExclude, pstgDest );
1783 return hr;
1786 /*************************************************************************
1787 * CopyTo (IStorage)
1789 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1790 IStorage* iface,
1791 DWORD ciidExclude, /* [in] */
1792 const IID* rgiidExclude, /* [size_is][unique][in] */
1793 SNB snbExclude, /* [unique][in] */
1794 IStorage* pstgDest) /* [unique][in] */
1796 StorageBaseImpl *This = impl_from_IStorage(iface);
1798 BOOL skip_storage = FALSE, skip_stream = FALSE;
1799 DWORD i;
1801 TRACE("(%p, %d, %p, %p, %p)\n",
1802 iface, ciidExclude, rgiidExclude,
1803 snbExclude, pstgDest);
1805 if ( pstgDest == 0 )
1806 return STG_E_INVALIDPOINTER;
1808 for(i = 0; i < ciidExclude; ++i)
1810 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1811 skip_storage = TRUE;
1812 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1813 skip_stream = TRUE;
1814 else
1815 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1818 if (!skip_storage)
1820 /* Give up early if it looks like this would be infinitely recursive.
1821 * Oddly enough, this includes some cases that aren't really recursive, like
1822 * copying to a transacted child. */
1823 IStorage *pstgDestAncestor = pstgDest;
1824 IStorage *pstgDestAncestorChild = NULL;
1826 /* Go up the chain from the destination until we find the source storage. */
1827 while (pstgDestAncestor != iface) {
1828 pstgDestAncestorChild = pstgDest;
1830 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
1832 TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
1834 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
1836 else if (pstgDestAncestor->lpVtbl == &Storage32InternalImpl_Vtbl)
1838 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
1840 pstgDestAncestor = &internal->parentStorage->IStorage_iface;
1842 else
1843 break;
1846 if (pstgDestAncestor == iface)
1848 BOOL fail = TRUE;
1850 if (pstgDestAncestorChild && snbExclude)
1852 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
1853 DirEntry data;
1854 WCHAR **snb = snbExclude;
1856 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
1858 while ( *snb != NULL && fail )
1860 if ( lstrcmpW(data.name, *snb) == 0 )
1861 fail = FALSE;
1862 ++snb;
1866 if (fail)
1867 return STG_E_ACCESSDENIED;
1871 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
1872 skip_storage, skip_stream, snbExclude, pstgDest );
1875 /*************************************************************************
1876 * MoveElementTo (IStorage)
1878 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1879 IStorage* iface,
1880 const OLECHAR *pwcsName, /* [string][in] */
1881 IStorage *pstgDest, /* [unique][in] */
1882 const OLECHAR *pwcsNewName,/* [string][in] */
1883 DWORD grfFlags) /* [in] */
1885 FIXME("(%p %s %p %s %u): stub\n", iface,
1886 debugstr_w(pwcsName), pstgDest,
1887 debugstr_w(pwcsNewName), grfFlags);
1888 return E_NOTIMPL;
1891 /*************************************************************************
1892 * Commit (IStorage)
1894 * Ensures that any changes made to a storage object open in transacted mode
1895 * are reflected in the parent storage
1897 * In a non-transacted mode, this ensures all cached writes are completed.
1899 static HRESULT WINAPI StorageImpl_Commit(
1900 IStorage* iface,
1901 DWORD grfCommitFlags)/* [in] */
1903 StorageBaseImpl* This = impl_from_IStorage(iface);
1904 TRACE("(%p %d)\n", iface, grfCommitFlags);
1905 return StorageBaseImpl_Flush(This);
1908 /*************************************************************************
1909 * Revert (IStorage)
1911 * Discard all changes that have been made since the last commit operation
1913 static HRESULT WINAPI StorageImpl_Revert(
1914 IStorage* iface)
1916 TRACE("(%p)\n", iface);
1917 return S_OK;
1920 /*************************************************************************
1921 * DestroyElement (IStorage)
1923 * Strategy: This implementation is built this way for simplicity not for speed.
1924 * I always delete the topmost element of the enumeration and adjust
1925 * the deleted element pointer all the time. This takes longer to
1926 * do but allow to reinvoke DestroyElement whenever we encounter a
1927 * storage object. The optimisation resides in the usage of another
1928 * enumeration strategy that would give all the leaves of a storage
1929 * first. (postfix order)
1931 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1932 IStorage* iface,
1933 const OLECHAR *pwcsName)/* [string][in] */
1935 StorageBaseImpl *This = impl_from_IStorage(iface);
1937 HRESULT hr = S_OK;
1938 DirEntry entryToDelete;
1939 DirRef entryToDeleteRef;
1941 TRACE("(%p, %s)\n",
1942 iface, debugstr_w(pwcsName));
1944 if (pwcsName==NULL)
1945 return STG_E_INVALIDPOINTER;
1947 if (This->reverted)
1948 return STG_E_REVERTED;
1950 if ( !(This->openFlags & STGM_TRANSACTED) &&
1951 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1952 return STG_E_ACCESSDENIED;
1954 entryToDeleteRef = findElement(
1955 This,
1956 This->storageDirEntry,
1957 pwcsName,
1958 &entryToDelete);
1960 if ( entryToDeleteRef == DIRENTRY_NULL )
1962 return STG_E_FILENOTFOUND;
1965 if ( entryToDelete.stgType == STGTY_STORAGE )
1967 hr = deleteStorageContents(
1968 This,
1969 entryToDeleteRef,
1970 entryToDelete);
1972 else if ( entryToDelete.stgType == STGTY_STREAM )
1974 hr = deleteStreamContents(
1975 This,
1976 entryToDeleteRef,
1977 entryToDelete);
1980 if (hr!=S_OK)
1981 return hr;
1984 * Remove the entry from its parent storage
1986 hr = removeFromTree(
1987 This,
1988 This->storageDirEntry,
1989 entryToDeleteRef);
1992 * Invalidate the entry
1994 if (SUCCEEDED(hr))
1995 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1997 if (SUCCEEDED(hr))
1998 hr = StorageBaseImpl_Flush(This);
2000 return hr;
2004 /******************************************************************************
2005 * Internal stream list handlers
2008 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2010 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
2011 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
2014 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2016 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
2017 list_remove(&(strm->StrmListEntry));
2020 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
2022 StgStreamImpl *strm;
2024 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
2026 if (strm->dirEntry == streamEntry)
2028 return TRUE;
2032 return FALSE;
2035 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
2037 StorageInternalImpl *childstg;
2039 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
2041 if (childstg->base.storageDirEntry == storageEntry)
2043 return TRUE;
2047 return FALSE;
2050 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2052 struct list *cur, *cur2;
2053 StgStreamImpl *strm=NULL;
2054 StorageInternalImpl *childstg=NULL;
2056 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2057 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2058 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2059 strm->parentStorage = NULL;
2060 list_remove(cur);
2063 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2064 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2065 StorageBaseImpl_Invalidate( &childstg->base );
2068 if (stg->transactedChild)
2070 StorageBaseImpl_Invalidate(stg->transactedChild);
2072 stg->transactedChild = NULL;
2077 /*********************************************************************
2079 * Internal Method
2081 * Delete the contents of a storage entry.
2084 static HRESULT deleteStorageContents(
2085 StorageBaseImpl *parentStorage,
2086 DirRef indexToDelete,
2087 DirEntry entryDataToDelete)
2089 IEnumSTATSTG *elements = 0;
2090 IStorage *childStorage = 0;
2091 STATSTG currentElement;
2092 HRESULT hr;
2093 HRESULT destroyHr = S_OK;
2094 StorageInternalImpl *stg, *stg2;
2096 /* Invalidate any open storage objects. */
2097 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2099 if (stg->base.storageDirEntry == indexToDelete)
2101 StorageBaseImpl_Invalidate(&stg->base);
2106 * Open the storage and enumerate it
2108 hr = IStorage_OpenStorage(
2109 &parentStorage->IStorage_iface,
2110 entryDataToDelete.name,
2112 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2115 &childStorage);
2117 if (hr != S_OK)
2119 return hr;
2123 * Enumerate the elements
2125 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2130 * Obtain the next element
2132 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2133 if (hr==S_OK)
2135 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2137 CoTaskMemFree(currentElement.pwcsName);
2141 * We need to Reset the enumeration every time because we delete elements
2142 * and the enumeration could be invalid
2144 IEnumSTATSTG_Reset(elements);
2146 } while ((hr == S_OK) && (destroyHr == S_OK));
2148 IStorage_Release(childStorage);
2149 IEnumSTATSTG_Release(elements);
2151 return destroyHr;
2154 /*********************************************************************
2156 * Internal Method
2158 * Perform the deletion of a stream's data
2161 static HRESULT deleteStreamContents(
2162 StorageBaseImpl *parentStorage,
2163 DirRef indexToDelete,
2164 DirEntry entryDataToDelete)
2166 IStream *pis;
2167 HRESULT hr;
2168 ULARGE_INTEGER size;
2169 StgStreamImpl *strm, *strm2;
2171 /* Invalidate any open stream objects. */
2172 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2174 if (strm->dirEntry == indexToDelete)
2176 TRACE("Stream deleted %p\n", strm);
2177 strm->parentStorage = NULL;
2178 list_remove(&strm->StrmListEntry);
2182 size.u.HighPart = 0;
2183 size.u.LowPart = 0;
2185 hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2186 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2188 if (hr!=S_OK)
2190 return(hr);
2194 * Zap the stream
2196 hr = IStream_SetSize(pis, size);
2198 if(hr != S_OK)
2200 return hr;
2204 * Release the stream object.
2206 IStream_Release(pis);
2208 return S_OK;
2211 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2213 switch (relation)
2215 case DIRENTRY_RELATION_PREVIOUS:
2216 entry->leftChild = new_target;
2217 break;
2218 case DIRENTRY_RELATION_NEXT:
2219 entry->rightChild = new_target;
2220 break;
2221 case DIRENTRY_RELATION_DIR:
2222 entry->dirRootEntry = new_target;
2223 break;
2224 default:
2225 assert(0);
2229 /*************************************************************************
2231 * Internal Method
2233 * This method removes a directory entry from its parent storage tree without
2234 * freeing any resources attached to it.
2236 static HRESULT removeFromTree(
2237 StorageBaseImpl *This,
2238 DirRef parentStorageIndex,
2239 DirRef deletedIndex)
2241 DirEntry entryToDelete;
2242 DirEntry parentEntry;
2243 DirRef parentEntryRef;
2244 ULONG typeOfRelation;
2245 HRESULT hr;
2247 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2249 if (hr != S_OK)
2250 return hr;
2253 * Find the element that links to the one we want to delete.
2255 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2256 &parentEntry, &parentEntryRef, &typeOfRelation);
2258 if (hr != S_OK)
2259 return hr;
2261 if (entryToDelete.leftChild != DIRENTRY_NULL)
2264 * Replace the deleted entry with its left child
2266 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2268 hr = StorageBaseImpl_WriteDirEntry(
2269 This,
2270 parentEntryRef,
2271 &parentEntry);
2272 if(FAILED(hr))
2274 return hr;
2277 if (entryToDelete.rightChild != DIRENTRY_NULL)
2280 * We need to reinsert the right child somewhere. We already know it and
2281 * its children are greater than everything in the left tree, so we
2282 * insert it at the rightmost point in the left tree.
2284 DirRef newRightChildParent = entryToDelete.leftChild;
2285 DirEntry newRightChildParentEntry;
2289 hr = StorageBaseImpl_ReadDirEntry(
2290 This,
2291 newRightChildParent,
2292 &newRightChildParentEntry);
2293 if (FAILED(hr))
2295 return hr;
2298 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2299 newRightChildParent = newRightChildParentEntry.rightChild;
2300 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2302 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2304 hr = StorageBaseImpl_WriteDirEntry(
2305 This,
2306 newRightChildParent,
2307 &newRightChildParentEntry);
2308 if (FAILED(hr))
2310 return hr;
2314 else
2317 * Replace the deleted entry with its right child
2319 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2321 hr = StorageBaseImpl_WriteDirEntry(
2322 This,
2323 parentEntryRef,
2324 &parentEntry);
2325 if(FAILED(hr))
2327 return hr;
2331 return hr;
2335 /******************************************************************************
2336 * SetElementTimes (IStorage)
2338 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2339 IStorage* iface,
2340 const OLECHAR *pwcsName,/* [string][in] */
2341 const FILETIME *pctime, /* [in] */
2342 const FILETIME *patime, /* [in] */
2343 const FILETIME *pmtime) /* [in] */
2345 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2346 return S_OK;
2349 /******************************************************************************
2350 * SetStateBits (IStorage)
2352 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2353 IStorage* iface,
2354 DWORD grfStateBits,/* [in] */
2355 DWORD grfMask) /* [in] */
2357 StorageBaseImpl *This = impl_from_IStorage(iface);
2359 if (This->reverted)
2360 return STG_E_REVERTED;
2362 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2363 return S_OK;
2366 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2367 DirRef index, const DirEntry *data)
2369 StorageImpl *This = (StorageImpl*)base;
2370 return StorageImpl_WriteDirEntry(This, index, data);
2373 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2374 DirRef index, DirEntry *data)
2376 StorageImpl *This = (StorageImpl*)base;
2377 return StorageImpl_ReadDirEntry(This, index, data);
2380 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2382 int i;
2384 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2386 if (!This->blockChainCache[i])
2388 return &This->blockChainCache[i];
2392 i = This->blockChainToEvict;
2394 BlockChainStream_Destroy(This->blockChainCache[i]);
2395 This->blockChainCache[i] = NULL;
2397 This->blockChainToEvict++;
2398 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2399 This->blockChainToEvict = 0;
2401 return &This->blockChainCache[i];
2404 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2405 DirRef index)
2407 int i, free_index=-1;
2409 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2411 if (!This->blockChainCache[i])
2413 if (free_index == -1) free_index = i;
2415 else if (This->blockChainCache[i]->ownerDirEntry == index)
2417 return &This->blockChainCache[i];
2421 if (free_index == -1)
2423 free_index = This->blockChainToEvict;
2425 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2426 This->blockChainCache[free_index] = NULL;
2428 This->blockChainToEvict++;
2429 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2430 This->blockChainToEvict = 0;
2433 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2434 return &This->blockChainCache[free_index];
2437 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2439 int i;
2441 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2443 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2445 BlockChainStream_Destroy(This->blockChainCache[i]);
2446 This->blockChainCache[i] = NULL;
2447 return;
2452 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2453 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2455 StorageImpl *This = (StorageImpl*)base;
2456 DirEntry data;
2457 HRESULT hr;
2458 ULONG bytesToRead;
2460 hr = StorageImpl_ReadDirEntry(This, index, &data);
2461 if (FAILED(hr)) return hr;
2463 if (data.size.QuadPart == 0)
2465 *bytesRead = 0;
2466 return S_OK;
2469 if (offset.QuadPart + size > data.size.QuadPart)
2471 bytesToRead = data.size.QuadPart - offset.QuadPart;
2473 else
2475 bytesToRead = size;
2478 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2480 SmallBlockChainStream *stream;
2482 stream = SmallBlockChainStream_Construct(This, NULL, index);
2483 if (!stream) return E_OUTOFMEMORY;
2485 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2487 SmallBlockChainStream_Destroy(stream);
2489 return hr;
2491 else
2493 BlockChainStream *stream = NULL;
2495 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2496 if (!stream) return E_OUTOFMEMORY;
2498 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2500 return hr;
2504 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2505 ULARGE_INTEGER newsize)
2507 StorageImpl *This = (StorageImpl*)base;
2508 DirEntry data;
2509 HRESULT hr;
2510 SmallBlockChainStream *smallblock=NULL;
2511 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2513 hr = StorageImpl_ReadDirEntry(This, index, &data);
2514 if (FAILED(hr)) return hr;
2516 /* In simple mode keep the stream size above the small block limit */
2517 if (This->base.openFlags & STGM_SIMPLE)
2518 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2520 if (data.size.QuadPart == newsize.QuadPart)
2521 return S_OK;
2523 /* Create a block chain object of the appropriate type */
2524 if (data.size.QuadPart == 0)
2526 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2528 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2529 if (!smallblock) return E_OUTOFMEMORY;
2531 else
2533 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2534 bigblock = *pbigblock;
2535 if (!bigblock) return E_OUTOFMEMORY;
2538 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2540 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2541 if (!smallblock) return E_OUTOFMEMORY;
2543 else
2545 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2546 bigblock = *pbigblock;
2547 if (!bigblock) return E_OUTOFMEMORY;
2550 /* Change the block chain type if necessary. */
2551 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2553 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2554 if (!bigblock)
2556 SmallBlockChainStream_Destroy(smallblock);
2557 return E_FAIL;
2560 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2561 *pbigblock = bigblock;
2563 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2565 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
2566 if (!smallblock)
2567 return E_FAIL;
2570 /* Set the size of the block chain. */
2571 if (smallblock)
2573 SmallBlockChainStream_SetSize(smallblock, newsize);
2574 SmallBlockChainStream_Destroy(smallblock);
2576 else
2578 BlockChainStream_SetSize(bigblock, newsize);
2581 /* Set the size in the directory entry. */
2582 hr = StorageImpl_ReadDirEntry(This, index, &data);
2583 if (SUCCEEDED(hr))
2585 data.size = newsize;
2587 hr = StorageImpl_WriteDirEntry(This, index, &data);
2589 return hr;
2592 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2593 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2595 StorageImpl *This = (StorageImpl*)base;
2596 DirEntry data;
2597 HRESULT hr;
2598 ULARGE_INTEGER newSize;
2600 hr = StorageImpl_ReadDirEntry(This, index, &data);
2601 if (FAILED(hr)) return hr;
2603 /* Grow the stream if necessary */
2604 newSize.QuadPart = 0;
2605 newSize.QuadPart = offset.QuadPart + size;
2607 if (newSize.QuadPart > data.size.QuadPart)
2609 hr = StorageImpl_StreamSetSize(base, index, newSize);
2610 if (FAILED(hr))
2611 return hr;
2613 hr = StorageImpl_ReadDirEntry(This, index, &data);
2614 if (FAILED(hr)) return hr;
2617 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2619 SmallBlockChainStream *stream;
2621 stream = SmallBlockChainStream_Construct(This, NULL, index);
2622 if (!stream) return E_OUTOFMEMORY;
2624 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2626 SmallBlockChainStream_Destroy(stream);
2628 return hr;
2630 else
2632 BlockChainStream *stream;
2634 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2635 if (!stream) return E_OUTOFMEMORY;
2637 return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2641 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2642 DirRef src)
2644 StorageImpl *This = (StorageImpl*)base;
2645 DirEntry dst_data, src_data;
2646 HRESULT hr;
2648 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2650 if (SUCCEEDED(hr))
2651 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2653 if (SUCCEEDED(hr))
2655 StorageImpl_DeleteCachedBlockChainStream(This, src);
2656 dst_data.startingBlock = src_data.startingBlock;
2657 dst_data.size = src_data.size;
2659 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2662 return hr;
2665 static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
2666 ULONG* result, BOOL refresh)
2668 StorageImpl *This = (StorageImpl*)base;
2669 HRESULT hr=S_OK;
2671 if (refresh)
2673 ULARGE_INTEGER offset;
2674 ULONG bytes_read;
2675 BYTE data[4];
2677 offset.u.HighPart = 0;
2678 offset.u.LowPart = OFFSET_TRANSACTIONSIG;
2679 hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);
2681 if (SUCCEEDED(hr))
2683 /* FIXME: Throw out everything and reload the file if this changed. */
2684 StorageUtl_ReadDWord(data, 0, &This->transactionSig);
2688 *result = This->transactionSig;
2690 return hr;
2693 static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
2694 ULONG value)
2696 StorageImpl *This = (StorageImpl*)base;
2698 This->transactionSig = value;
2699 StorageImpl_SaveFileHeader(This);
2701 return S_OK;
2704 static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
2706 StorageImpl *This = (StorageImpl*)base;
2707 HRESULT hr;
2708 ULARGE_INTEGER offset, cb;
2710 if (write)
2712 /* Synchronous grab of second priority range, the commit lock, and the
2713 * lock-checking lock. */
2714 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
2715 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
2717 else
2719 offset.QuadPart = RANGELOCK_COMMIT;
2720 cb.QuadPart = 1;
2723 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
2725 if (hr == STG_E_INVALIDFUNCTION)
2726 hr = S_OK;
2728 return hr;
2731 static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
2733 StorageImpl *This = (StorageImpl*)base;
2734 HRESULT hr;
2735 ULARGE_INTEGER offset, cb;
2737 if (write)
2739 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
2740 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
2742 else
2744 offset.QuadPart = RANGELOCK_COMMIT;
2745 cb.QuadPart = 1;
2748 hr = ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2750 if (hr == STG_E_INVALIDFUNCTION)
2751 hr = S_OK;
2753 return hr;
2756 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2758 StorageImpl *This = (StorageImpl*) iface;
2759 STATSTG statstg;
2760 HRESULT hr;
2762 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2764 *result = statstg.pwcsName;
2766 return hr;
2769 static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
2771 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2772 return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
2775 static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
2777 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2778 return IStorage_AddRef(&This->IStorage_iface);
2781 static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
2783 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2784 return IStorage_Release(&This->IStorage_iface);
2787 static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
2789 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2790 FIXME("(%p)->(%d): stub\n", This, timeout);
2791 return E_NOTIMPL;
2794 static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
2796 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2797 FIXME("(%p): stub\n", This);
2798 return E_NOTIMPL;
2801 static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
2803 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2804 FIXME("(%p): stub\n", This);
2805 return E_NOTIMPL;
2808 static const IDirectWriterLockVtbl DirectWriterLockVtbl =
2810 directwriterlock_QueryInterface,
2811 directwriterlock_AddRef,
2812 directwriterlock_Release,
2813 directwriterlock_WaitForWriteAccess,
2814 directwriterlock_ReleaseWriteAccess,
2815 directwriterlock_HaveWriteAccess
2819 * Virtual function table for the IStorage32Impl class.
2821 static const IStorageVtbl Storage32Impl_Vtbl =
2823 StorageBaseImpl_QueryInterface,
2824 StorageBaseImpl_AddRef,
2825 StorageBaseImpl_Release,
2826 StorageBaseImpl_CreateStream,
2827 StorageBaseImpl_OpenStream,
2828 StorageBaseImpl_CreateStorage,
2829 StorageBaseImpl_OpenStorage,
2830 StorageBaseImpl_CopyTo,
2831 StorageBaseImpl_MoveElementTo,
2832 StorageImpl_Commit,
2833 StorageImpl_Revert,
2834 StorageBaseImpl_EnumElements,
2835 StorageBaseImpl_DestroyElement,
2836 StorageBaseImpl_RenameElement,
2837 StorageBaseImpl_SetElementTimes,
2838 StorageBaseImpl_SetClass,
2839 StorageBaseImpl_SetStateBits,
2840 StorageBaseImpl_Stat
2843 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2845 StorageImpl_Destroy,
2846 StorageImpl_Invalidate,
2847 StorageImpl_Flush,
2848 StorageImpl_GetFilename,
2849 StorageImpl_CreateDirEntry,
2850 StorageImpl_BaseWriteDirEntry,
2851 StorageImpl_BaseReadDirEntry,
2852 StorageImpl_DestroyDirEntry,
2853 StorageImpl_StreamReadAt,
2854 StorageImpl_StreamWriteAt,
2855 StorageImpl_StreamSetSize,
2856 StorageImpl_StreamLink,
2857 StorageImpl_GetTransactionSig,
2858 StorageImpl_SetTransactionSig,
2859 StorageImpl_LockTransaction,
2860 StorageImpl_UnlockTransaction
2863 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
2864 ULARGE_INTEGER cb, DWORD dwLockType)
2866 HRESULT hr;
2868 /* if it's a FileLockBytesImpl use LockFileEx in blocking mode */
2869 if (SUCCEEDED(FileLockBytesImpl_LockRegionSync(This->lockBytes, offset, cb)))
2870 return S_OK;
2872 /* otherwise we have to fake it based on an async lock */
2875 int delay=0;
2877 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
2879 if (hr == STG_E_ACCESSDENIED)
2881 Sleep(delay);
2882 if (delay < 150) delay++;
2884 } while (hr == STG_E_ACCESSDENIED);
2886 return hr;
2889 static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
2890 ULONG end, HRESULT fail_hr)
2892 HRESULT hr;
2893 ULARGE_INTEGER offset, cb;
2895 offset.QuadPart = start;
2896 cb.QuadPart = 1 + end - start;
2898 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2899 if (SUCCEEDED(hr)) ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2901 if (hr == STG_E_ACCESSDENIED)
2902 return fail_hr;
2903 else
2904 return S_OK;
2907 static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
2909 HRESULT hr=S_OK;
2910 int i, j;
2911 ULARGE_INTEGER offset, cb;
2913 cb.QuadPart = 1;
2915 for (i=start; i<=end; i++)
2917 offset.QuadPart = i;
2918 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2919 if (hr != STG_E_ACCESSDENIED)
2920 break;
2923 if (SUCCEEDED(hr))
2925 for (j=0; j<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); j++)
2927 if (This->locked_bytes[j] == 0)
2929 This->locked_bytes[j] = i;
2930 break;
2935 return hr;
2938 static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
2940 HRESULT hr;
2941 ULARGE_INTEGER offset;
2942 ULARGE_INTEGER cb;
2943 DWORD share_mode = STGM_SHARE_MODE(openFlags);
2945 if (openFlags & STGM_NOSNAPSHOT)
2947 /* STGM_NOSNAPSHOT implies deny write */
2948 if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
2949 else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
2952 /* Wrap all other locking inside a single lock so we can check ranges safely */
2953 offset.QuadPart = RANGELOCK_CHECKLOCKS;
2954 cb.QuadPart = 1;
2955 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
2957 /* If the ILockBytes doesn't support locking that's ok. */
2958 if (FAILED(hr)) return S_OK;
2960 hr = S_OK;
2962 /* First check for any conflicting locks. */
2963 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
2964 hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
2966 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
2967 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
2969 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
2970 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
2972 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
2973 hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
2975 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
2976 hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
2978 /* Then grab our locks. */
2979 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
2981 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
2982 if (SUCCEEDED(hr))
2983 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
2986 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
2987 hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
2989 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
2990 hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
2992 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
2993 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
2995 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
2996 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
2998 if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
2999 hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
3001 offset.QuadPart = RANGELOCK_CHECKLOCKS;
3002 cb.QuadPart = 1;
3003 ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
3005 return hr;
3008 static HRESULT StorageImpl_Construct(
3009 HANDLE hFile,
3010 LPCOLESTR pwcsName,
3011 ILockBytes* pLkbyt,
3012 DWORD openFlags,
3013 BOOL fileBased,
3014 BOOL create,
3015 ULONG sector_size,
3016 StorageImpl** result)
3018 StorageImpl* This;
3019 HRESULT hr = S_OK;
3020 DirEntry currentEntry;
3021 DirRef currentEntryRef;
3023 if ( FAILED( validateSTGM(openFlags) ))
3024 return STG_E_INVALIDFLAG;
3026 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
3027 if (!This)
3028 return E_OUTOFMEMORY;
3030 memset(This, 0, sizeof(StorageImpl));
3032 list_init(&This->base.strmHead);
3034 list_init(&This->base.storageHead);
3036 This->base.IStorage_iface.lpVtbl = &Storage32Impl_Vtbl;
3037 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
3038 This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
3039 This->base.baseVtbl = &StorageImpl_BaseVtbl;
3040 This->base.openFlags = (openFlags & ~STGM_CREATE);
3041 This->base.ref = 1;
3042 This->base.create = create;
3044 if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
3045 This->base.lockingrole = SWMR_Writer;
3046 else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
3047 This->base.lockingrole = SWMR_Reader;
3048 else
3049 This->base.lockingrole = SWMR_None;
3051 This->base.reverted = FALSE;
3054 * Initialize the big block cache.
3056 This->bigBlockSize = sector_size;
3057 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
3058 if (hFile)
3059 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
3060 else
3062 This->lockBytes = pLkbyt;
3063 ILockBytes_AddRef(pLkbyt);
3066 if (FAILED(hr))
3067 goto end;
3069 hr = StorageImpl_GrabLocks(This, openFlags);
3071 if (FAILED(hr))
3072 goto end;
3074 if (create)
3076 ULARGE_INTEGER size;
3077 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
3079 /* Discard any existing data. */
3080 size.QuadPart = 0;
3081 ILockBytes_SetSize(This->lockBytes, size);
3084 * Initialize all header variables:
3085 * - The big block depot consists of one block and it is at block 0
3086 * - The directory table starts at block 1
3087 * - There is no small block depot
3089 memset( This->bigBlockDepotStart,
3090 BLOCK_UNUSED,
3091 sizeof(This->bigBlockDepotStart));
3093 This->bigBlockDepotCount = 1;
3094 This->bigBlockDepotStart[0] = 0;
3095 This->rootStartBlock = 1;
3096 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
3097 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
3098 if (sector_size == 4096)
3099 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
3100 else
3101 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
3102 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
3103 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
3104 This->extBigBlockDepotCount = 0;
3106 StorageImpl_SaveFileHeader(This);
3109 * Add one block for the big block depot and one block for the directory table
3111 size.u.HighPart = 0;
3112 size.u.LowPart = This->bigBlockSize * 3;
3113 ILockBytes_SetSize(This->lockBytes, size);
3116 * Initialize the big block depot
3118 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3119 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
3120 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
3121 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
3123 else
3126 * Load the header for the file.
3128 hr = StorageImpl_LoadFileHeader(This);
3130 if (FAILED(hr))
3132 goto end;
3137 * There is no block depot cached yet.
3139 This->indexBlockDepotCached = 0xFFFFFFFF;
3140 This->indexExtBlockDepotCached = 0xFFFFFFFF;
3143 * Start searching for free blocks with block 0.
3145 This->prevFreeBlock = 0;
3147 This->firstFreeSmallBlock = 0;
3149 /* Read the extended big block depot locations. */
3150 if (This->extBigBlockDepotCount != 0)
3152 ULONG current_block = This->extBigBlockDepotStart;
3153 ULONG cache_size = This->extBigBlockDepotCount * 2;
3154 ULONG i;
3156 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
3157 if (!This->extBigBlockDepotLocations)
3159 hr = E_OUTOFMEMORY;
3160 goto end;
3163 This->extBigBlockDepotLocationsSize = cache_size;
3165 for (i=0; i<This->extBigBlockDepotCount; i++)
3167 if (current_block == BLOCK_END_OF_CHAIN)
3169 WARN("File has too few extended big block depot blocks.\n");
3170 hr = STG_E_DOCFILECORRUPT;
3171 goto end;
3173 This->extBigBlockDepotLocations[i] = current_block;
3174 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
3177 else
3179 This->extBigBlockDepotLocations = NULL;
3180 This->extBigBlockDepotLocationsSize = 0;
3184 * Create the block chain abstractions.
3186 if(!(This->rootBlockChain =
3187 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
3189 hr = STG_E_READFAULT;
3190 goto end;
3193 if(!(This->smallBlockDepotChain =
3194 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
3195 DIRENTRY_NULL)))
3197 hr = STG_E_READFAULT;
3198 goto end;
3202 * Write the root storage entry (memory only)
3204 if (create)
3206 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
3207 DirEntry rootEntry;
3209 * Initialize the directory table
3211 memset(&rootEntry, 0, sizeof(rootEntry));
3212 strcpyW(rootEntry.name, rootentryW);
3213 rootEntry.sizeOfNameString = sizeof(rootentryW);
3214 rootEntry.stgType = STGTY_ROOT;
3215 rootEntry.leftChild = DIRENTRY_NULL;
3216 rootEntry.rightChild = DIRENTRY_NULL;
3217 rootEntry.dirRootEntry = DIRENTRY_NULL;
3218 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
3219 rootEntry.size.u.HighPart = 0;
3220 rootEntry.size.u.LowPart = 0;
3222 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
3226 * Find the ID of the root storage.
3228 currentEntryRef = 0;
3232 hr = StorageImpl_ReadDirEntry(
3233 This,
3234 currentEntryRef,
3235 &currentEntry);
3237 if (SUCCEEDED(hr))
3239 if ( (currentEntry.sizeOfNameString != 0 ) &&
3240 (currentEntry.stgType == STGTY_ROOT) )
3242 This->base.storageDirEntry = currentEntryRef;
3246 currentEntryRef++;
3248 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
3250 if (FAILED(hr))
3252 hr = STG_E_READFAULT;
3253 goto end;
3257 * Create the block chain abstraction for the small block root chain.
3259 if(!(This->smallBlockRootChain =
3260 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
3262 hr = STG_E_READFAULT;
3265 end:
3266 if (FAILED(hr))
3268 IStorage_Release(&This->base.IStorage_iface);
3269 *result = NULL;
3271 else
3273 StorageImpl_Flush(&This->base);
3274 *result = This;
3277 return hr;
3280 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
3282 StorageImpl *This = (StorageImpl*) iface;
3284 StorageBaseImpl_DeleteAll(&This->base);
3286 This->base.reverted = TRUE;
3289 static void StorageImpl_Destroy(StorageBaseImpl* iface)
3291 StorageImpl *This = (StorageImpl*) iface;
3292 int i;
3293 TRACE("(%p)\n", This);
3295 StorageImpl_Flush(iface);
3297 StorageImpl_Invalidate(iface);
3299 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3301 BlockChainStream_Destroy(This->smallBlockRootChain);
3302 BlockChainStream_Destroy(This->rootBlockChain);
3303 BlockChainStream_Destroy(This->smallBlockDepotChain);
3305 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
3306 BlockChainStream_Destroy(This->blockChainCache[i]);
3308 for (i=0; i<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); i++)
3310 ULARGE_INTEGER offset, cb;
3311 cb.QuadPart = 1;
3312 if (This->locked_bytes[i] != 0)
3314 offset.QuadPart = This->locked_bytes[i];
3315 ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
3319 if (This->lockBytes)
3320 ILockBytes_Release(This->lockBytes);
3321 HeapFree(GetProcessHeap(), 0, This);
3324 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
3326 StorageImpl *This = (StorageImpl*)storage;
3327 int i;
3328 HRESULT hr;
3329 TRACE("(%p)\n", This);
3331 hr = BlockChainStream_Flush(This->smallBlockRootChain);
3333 if (SUCCEEDED(hr))
3334 hr = BlockChainStream_Flush(This->rootBlockChain);
3336 if (SUCCEEDED(hr))
3337 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
3339 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
3340 if (This->blockChainCache[i])
3341 hr = BlockChainStream_Flush(This->blockChainCache[i]);
3343 if (SUCCEEDED(hr))
3344 hr = ILockBytes_Flush(This->lockBytes);
3346 return hr;
3349 /******************************************************************************
3350 * Storage32Impl_GetNextFreeBigBlock
3352 * Returns the index of the next free big block.
3353 * If the big block depot is filled, this method will enlarge it.
3356 static ULONG StorageImpl_GetNextFreeBigBlock(
3357 StorageImpl* This)
3359 ULONG depotBlockIndexPos;
3360 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3361 ULONG depotBlockOffset;
3362 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3363 ULONG nextBlockIndex = BLOCK_SPECIAL;
3364 int depotIndex = 0;
3365 ULONG freeBlock = BLOCK_UNUSED;
3366 ULONG read;
3367 ULARGE_INTEGER neededSize;
3368 STATSTG statstg;
3370 depotIndex = This->prevFreeBlock / blocksPerDepot;
3371 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
3374 * Scan the entire big block depot until we find a block marked free
3376 while (nextBlockIndex != BLOCK_UNUSED)
3378 if (depotIndex < COUNT_BBDEPOTINHEADER)
3380 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
3383 * Grow the primary depot.
3385 if (depotBlockIndexPos == BLOCK_UNUSED)
3387 depotBlockIndexPos = depotIndex*blocksPerDepot;
3390 * Add a block depot.
3392 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
3393 This->bigBlockDepotCount++;
3394 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3397 * Flag it as a block depot.
3399 StorageImpl_SetNextBlockInChain(This,
3400 depotBlockIndexPos,
3401 BLOCK_SPECIAL);
3403 /* Save new header information.
3405 StorageImpl_SaveFileHeader(This);
3408 else
3410 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3412 if (depotBlockIndexPos == BLOCK_UNUSED)
3415 * Grow the extended depot.
3417 ULONG extIndex = BLOCK_UNUSED;
3418 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3419 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3421 if (extBlockOffset == 0)
3423 /* We need an extended block.
3425 extIndex = Storage32Impl_AddExtBlockDepot(This);
3426 This->extBigBlockDepotCount++;
3427 depotBlockIndexPos = extIndex + 1;
3429 else
3430 depotBlockIndexPos = depotIndex * blocksPerDepot;
3433 * Add a block depot and mark it in the extended block.
3435 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
3436 This->bigBlockDepotCount++;
3437 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3439 /* Flag the block depot.
3441 StorageImpl_SetNextBlockInChain(This,
3442 depotBlockIndexPos,
3443 BLOCK_SPECIAL);
3445 /* If necessary, flag the extended depot block.
3447 if (extIndex != BLOCK_UNUSED)
3448 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3450 /* Save header information.
3452 StorageImpl_SaveFileHeader(This);
3456 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
3458 if (read)
3460 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3461 ( nextBlockIndex != BLOCK_UNUSED))
3463 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3465 if (nextBlockIndex == BLOCK_UNUSED)
3467 freeBlock = (depotIndex * blocksPerDepot) +
3468 (depotBlockOffset/sizeof(ULONG));
3471 depotBlockOffset += sizeof(ULONG);
3475 depotIndex++;
3476 depotBlockOffset = 0;
3480 * make sure that the block physically exists before using it
3482 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3484 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3486 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3487 ILockBytes_SetSize(This->lockBytes, neededSize);
3489 This->prevFreeBlock = freeBlock;
3491 return freeBlock;
3494 /******************************************************************************
3495 * Storage32Impl_AddBlockDepot
3497 * This will create a depot block, essentially it is a block initialized
3498 * to BLOCK_UNUSEDs.
3500 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
3502 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3503 ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
3504 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3505 ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
3508 * Initialize blocks as free
3510 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3512 /* Reserve the range lock sector */
3513 if (depotIndex == rangeLockDepot)
3515 ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
3518 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3521 /******************************************************************************
3522 * Storage32Impl_GetExtDepotBlock
3524 * Returns the index of the block that corresponds to the specified depot
3525 * index. This method is only for depot indexes equal or greater than
3526 * COUNT_BBDEPOTINHEADER.
3528 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3530 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3531 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3532 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3533 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3534 ULONG blockIndex = BLOCK_UNUSED;
3535 ULONG extBlockIndex;
3536 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3537 int index, num_blocks;
3539 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3541 if (extBlockCount >= This->extBigBlockDepotCount)
3542 return BLOCK_UNUSED;
3544 if (This->indexExtBlockDepotCached != extBlockCount)
3546 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3548 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
3550 num_blocks = This->bigBlockSize / 4;
3552 for (index = 0; index < num_blocks; index++)
3554 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3555 This->extBlockDepotCached[index] = blockIndex;
3558 This->indexExtBlockDepotCached = extBlockCount;
3561 blockIndex = This->extBlockDepotCached[extBlockOffset];
3563 return blockIndex;
3566 /******************************************************************************
3567 * Storage32Impl_SetExtDepotBlock
3569 * Associates the specified block index to the specified depot index.
3570 * This method is only for depot indexes equal or greater than
3571 * COUNT_BBDEPOTINHEADER.
3573 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3575 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3576 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3577 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3578 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3579 ULONG extBlockIndex;
3581 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3583 assert(extBlockCount < This->extBigBlockDepotCount);
3585 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3587 if (extBlockIndex != BLOCK_UNUSED)
3589 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3590 extBlockOffset * sizeof(ULONG),
3591 blockIndex);
3594 if (This->indexExtBlockDepotCached == extBlockCount)
3596 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3600 /******************************************************************************
3601 * Storage32Impl_AddExtBlockDepot
3603 * Creates an extended depot block.
3605 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3607 ULONG numExtBlocks = This->extBigBlockDepotCount;
3608 ULONG nextExtBlock = This->extBigBlockDepotStart;
3609 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3610 ULONG index = BLOCK_UNUSED;
3611 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3612 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3613 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3615 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3616 blocksPerDepotBlock;
3618 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3621 * The first extended block.
3623 This->extBigBlockDepotStart = index;
3625 else
3628 * Find the last existing extended block.
3630 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3633 * Add the new extended block to the chain.
3635 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3636 index);
3640 * Initialize this block.
3642 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3643 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3645 /* Add the block to our cache. */
3646 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3648 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3649 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3651 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3652 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3654 This->extBigBlockDepotLocations = new_cache;
3655 This->extBigBlockDepotLocationsSize = new_cache_size;
3657 This->extBigBlockDepotLocations[numExtBlocks] = index;
3659 return index;
3662 /******************************************************************************
3663 * Storage32Impl_FreeBigBlock
3665 * This method will flag the specified block as free in the big block depot.
3667 static void StorageImpl_FreeBigBlock(
3668 StorageImpl* This,
3669 ULONG blockIndex)
3671 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3673 if (blockIndex < This->prevFreeBlock)
3674 This->prevFreeBlock = blockIndex;
3677 /************************************************************************
3678 * Storage32Impl_GetNextBlockInChain
3680 * This method will retrieve the block index of the next big block in
3681 * in the chain.
3683 * Params: This - Pointer to the Storage object.
3684 * blockIndex - Index of the block to retrieve the chain
3685 * for.
3686 * nextBlockIndex - receives the return value.
3688 * Returns: This method returns the index of the next block in the chain.
3689 * It will return the constants:
3690 * BLOCK_SPECIAL - If the block given was not part of a
3691 * chain.
3692 * BLOCK_END_OF_CHAIN - If the block given was the last in
3693 * a chain.
3694 * BLOCK_UNUSED - If the block given was not past of a chain
3695 * and is available.
3696 * BLOCK_EXTBBDEPOT - This block is part of the extended
3697 * big block depot.
3699 * See Windows documentation for more details on IStorage methods.
3701 static HRESULT StorageImpl_GetNextBlockInChain(
3702 StorageImpl* This,
3703 ULONG blockIndex,
3704 ULONG* nextBlockIndex)
3706 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3707 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3708 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3709 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3710 ULONG read;
3711 ULONG depotBlockIndexPos;
3712 int index, num_blocks;
3714 *nextBlockIndex = BLOCK_SPECIAL;
3716 if(depotBlockCount >= This->bigBlockDepotCount)
3718 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3719 This->bigBlockDepotCount);
3720 return STG_E_READFAULT;
3724 * Cache the currently accessed depot block.
3726 if (depotBlockCount != This->indexBlockDepotCached)
3728 This->indexBlockDepotCached = depotBlockCount;
3730 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3732 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3734 else
3737 * We have to look in the extended depot.
3739 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3742 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
3744 if (!read)
3745 return STG_E_READFAULT;
3747 num_blocks = This->bigBlockSize / 4;
3749 for (index = 0; index < num_blocks; index++)
3751 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3752 This->blockDepotCached[index] = *nextBlockIndex;
3756 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3758 return S_OK;
3761 /******************************************************************************
3762 * Storage32Impl_GetNextExtendedBlock
3764 * Given an extended block this method will return the next extended block.
3766 * NOTES:
3767 * The last ULONG of an extended block is the block index of the next
3768 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3769 * depot.
3771 * Return values:
3772 * - The index of the next extended block
3773 * - BLOCK_UNUSED: there is no next extended block.
3774 * - Any other return values denotes failure.
3776 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3778 ULONG nextBlockIndex = BLOCK_SPECIAL;
3779 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3781 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3782 &nextBlockIndex);
3784 return nextBlockIndex;
3787 /******************************************************************************
3788 * Storage32Impl_SetNextBlockInChain
3790 * This method will write the index of the specified block's next block
3791 * in the big block depot.
3793 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3794 * do the following
3796 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3797 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3798 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3801 static void StorageImpl_SetNextBlockInChain(
3802 StorageImpl* This,
3803 ULONG blockIndex,
3804 ULONG nextBlock)
3806 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3807 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3808 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3809 ULONG depotBlockIndexPos;
3811 assert(depotBlockCount < This->bigBlockDepotCount);
3812 assert(blockIndex != nextBlock);
3814 if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
3815 /* This should never happen (storage file format spec forbids it), but
3816 * older versions of Wine may have generated broken files. We don't want to
3817 * assert and potentially lose data, but we do want to know if this ever
3818 * happens in a newly-created file. */
3819 ERR("Using range lock page\n");
3821 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3823 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3825 else
3828 * We have to look in the extended depot.
3830 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3833 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3834 nextBlock);
3836 * Update the cached block depot, if necessary.
3838 if (depotBlockCount == This->indexBlockDepotCached)
3840 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3844 /******************************************************************************
3845 * Storage32Impl_LoadFileHeader
3847 * This method will read in the file header
3849 static HRESULT StorageImpl_LoadFileHeader(
3850 StorageImpl* This)
3852 HRESULT hr;
3853 BYTE headerBigBlock[HEADER_SIZE];
3854 int index;
3855 ULARGE_INTEGER offset;
3856 DWORD bytes_read;
3858 TRACE("\n");
3860 * Get a pointer to the big block of data containing the header.
3862 offset.u.HighPart = 0;
3863 offset.u.LowPart = 0;
3864 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3865 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3866 hr = STG_E_FILENOTFOUND;
3869 * Extract the information from the header.
3871 if (SUCCEEDED(hr))
3874 * Check for the "magic number" signature and return an error if it is not
3875 * found.
3877 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3879 return STG_E_OLDFORMAT;
3882 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3884 return STG_E_INVALIDHEADER;
3887 StorageUtl_ReadWord(
3888 headerBigBlock,
3889 OFFSET_BIGBLOCKSIZEBITS,
3890 &This->bigBlockSizeBits);
3892 StorageUtl_ReadWord(
3893 headerBigBlock,
3894 OFFSET_SMALLBLOCKSIZEBITS,
3895 &This->smallBlockSizeBits);
3897 StorageUtl_ReadDWord(
3898 headerBigBlock,
3899 OFFSET_BBDEPOTCOUNT,
3900 &This->bigBlockDepotCount);
3902 StorageUtl_ReadDWord(
3903 headerBigBlock,
3904 OFFSET_ROOTSTARTBLOCK,
3905 &This->rootStartBlock);
3907 StorageUtl_ReadDWord(
3908 headerBigBlock,
3909 OFFSET_TRANSACTIONSIG,
3910 &This->transactionSig);
3912 StorageUtl_ReadDWord(
3913 headerBigBlock,
3914 OFFSET_SMALLBLOCKLIMIT,
3915 &This->smallBlockLimit);
3917 StorageUtl_ReadDWord(
3918 headerBigBlock,
3919 OFFSET_SBDEPOTSTART,
3920 &This->smallBlockDepotStart);
3922 StorageUtl_ReadDWord(
3923 headerBigBlock,
3924 OFFSET_EXTBBDEPOTSTART,
3925 &This->extBigBlockDepotStart);
3927 StorageUtl_ReadDWord(
3928 headerBigBlock,
3929 OFFSET_EXTBBDEPOTCOUNT,
3930 &This->extBigBlockDepotCount);
3932 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3934 StorageUtl_ReadDWord(
3935 headerBigBlock,
3936 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3937 &(This->bigBlockDepotStart[index]));
3941 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3943 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3944 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3947 * Right now, the code is making some assumptions about the size of the
3948 * blocks, just make sure they are what we're expecting.
3950 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3951 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3952 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3954 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3955 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3956 hr = STG_E_INVALIDHEADER;
3958 else
3959 hr = S_OK;
3962 return hr;
3965 /******************************************************************************
3966 * Storage32Impl_SaveFileHeader
3968 * This method will save to the file the header
3970 static void StorageImpl_SaveFileHeader(
3971 StorageImpl* This)
3973 BYTE headerBigBlock[HEADER_SIZE];
3974 int index;
3975 HRESULT hr;
3976 ULARGE_INTEGER offset;
3977 DWORD bytes_read, bytes_written;
3978 DWORD major_version, dirsectorcount;
3981 * Get a pointer to the big block of data containing the header.
3983 offset.u.HighPart = 0;
3984 offset.u.LowPart = 0;
3985 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3986 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3987 hr = STG_E_FILENOTFOUND;
3989 if (This->bigBlockSizeBits == 0x9)
3990 major_version = 3;
3991 else if (This->bigBlockSizeBits == 0xc)
3992 major_version = 4;
3993 else
3995 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3996 major_version = 4;
4000 * If the block read failed, the file is probably new.
4002 if (FAILED(hr))
4005 * Initialize for all unknown fields.
4007 memset(headerBigBlock, 0, HEADER_SIZE);
4010 * Initialize the magic number.
4012 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
4016 * Write the information to the header.
4018 StorageUtl_WriteWord(
4019 headerBigBlock,
4020 OFFSET_MINORVERSION,
4021 0x3e);
4023 StorageUtl_WriteWord(
4024 headerBigBlock,
4025 OFFSET_MAJORVERSION,
4026 major_version);
4028 StorageUtl_WriteWord(
4029 headerBigBlock,
4030 OFFSET_BYTEORDERMARKER,
4031 (WORD)-2);
4033 StorageUtl_WriteWord(
4034 headerBigBlock,
4035 OFFSET_BIGBLOCKSIZEBITS,
4036 This->bigBlockSizeBits);
4038 StorageUtl_WriteWord(
4039 headerBigBlock,
4040 OFFSET_SMALLBLOCKSIZEBITS,
4041 This->smallBlockSizeBits);
4043 if (major_version >= 4)
4045 if (This->rootBlockChain)
4046 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
4047 else
4048 /* This file is being created, and it will start out with one block. */
4049 dirsectorcount = 1;
4051 else
4052 /* This field must be 0 in versions older than 4 */
4053 dirsectorcount = 0;
4055 StorageUtl_WriteDWord(
4056 headerBigBlock,
4057 OFFSET_DIRSECTORCOUNT,
4058 dirsectorcount);
4060 StorageUtl_WriteDWord(
4061 headerBigBlock,
4062 OFFSET_BBDEPOTCOUNT,
4063 This->bigBlockDepotCount);
4065 StorageUtl_WriteDWord(
4066 headerBigBlock,
4067 OFFSET_ROOTSTARTBLOCK,
4068 This->rootStartBlock);
4070 StorageUtl_WriteDWord(
4071 headerBigBlock,
4072 OFFSET_TRANSACTIONSIG,
4073 This->transactionSig);
4075 StorageUtl_WriteDWord(
4076 headerBigBlock,
4077 OFFSET_SMALLBLOCKLIMIT,
4078 This->smallBlockLimit);
4080 StorageUtl_WriteDWord(
4081 headerBigBlock,
4082 OFFSET_SBDEPOTSTART,
4083 This->smallBlockDepotStart);
4085 StorageUtl_WriteDWord(
4086 headerBigBlock,
4087 OFFSET_SBDEPOTCOUNT,
4088 This->smallBlockDepotChain ?
4089 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
4091 StorageUtl_WriteDWord(
4092 headerBigBlock,
4093 OFFSET_EXTBBDEPOTSTART,
4094 This->extBigBlockDepotStart);
4096 StorageUtl_WriteDWord(
4097 headerBigBlock,
4098 OFFSET_EXTBBDEPOTCOUNT,
4099 This->extBigBlockDepotCount);
4101 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
4103 StorageUtl_WriteDWord(
4104 headerBigBlock,
4105 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
4106 (This->bigBlockDepotStart[index]));
4110 * Write the big block back to the file.
4112 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
4115 /******************************************************************************
4116 * StorageImpl_ReadRawDirEntry
4118 * This method will read the raw data from a directory entry in the file.
4120 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4122 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
4124 ULARGE_INTEGER offset;
4125 HRESULT hr;
4126 ULONG bytesRead;
4128 offset.u.HighPart = 0;
4129 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
4131 hr = BlockChainStream_ReadAt(
4132 This->rootBlockChain,
4133 offset,
4134 RAW_DIRENTRY_SIZE,
4135 buffer,
4136 &bytesRead);
4138 if (bytesRead != RAW_DIRENTRY_SIZE)
4139 return STG_E_READFAULT;
4141 return hr;
4144 /******************************************************************************
4145 * StorageImpl_WriteRawDirEntry
4147 * This method will write the raw data from a directory entry in the file.
4149 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4151 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
4153 ULARGE_INTEGER offset;
4154 ULONG bytesRead;
4156 offset.u.HighPart = 0;
4157 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
4159 return BlockChainStream_WriteAt(
4160 This->rootBlockChain,
4161 offset,
4162 RAW_DIRENTRY_SIZE,
4163 buffer,
4164 &bytesRead);
4167 /******************************************************************************
4168 * UpdateRawDirEntry
4170 * Update raw directory entry data from the fields in newData.
4172 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4174 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
4176 memset(buffer, 0, RAW_DIRENTRY_SIZE);
4178 memcpy(
4179 buffer + OFFSET_PS_NAME,
4180 newData->name,
4181 DIRENTRY_NAME_BUFFER_LEN );
4183 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
4185 StorageUtl_WriteWord(
4186 buffer,
4187 OFFSET_PS_NAMELENGTH,
4188 newData->sizeOfNameString);
4190 StorageUtl_WriteDWord(
4191 buffer,
4192 OFFSET_PS_LEFTCHILD,
4193 newData->leftChild);
4195 StorageUtl_WriteDWord(
4196 buffer,
4197 OFFSET_PS_RIGHTCHILD,
4198 newData->rightChild);
4200 StorageUtl_WriteDWord(
4201 buffer,
4202 OFFSET_PS_DIRROOT,
4203 newData->dirRootEntry);
4205 StorageUtl_WriteGUID(
4206 buffer,
4207 OFFSET_PS_GUID,
4208 &newData->clsid);
4210 StorageUtl_WriteDWord(
4211 buffer,
4212 OFFSET_PS_CTIMELOW,
4213 newData->ctime.dwLowDateTime);
4215 StorageUtl_WriteDWord(
4216 buffer,
4217 OFFSET_PS_CTIMEHIGH,
4218 newData->ctime.dwHighDateTime);
4220 StorageUtl_WriteDWord(
4221 buffer,
4222 OFFSET_PS_MTIMELOW,
4223 newData->mtime.dwLowDateTime);
4225 StorageUtl_WriteDWord(
4226 buffer,
4227 OFFSET_PS_MTIMEHIGH,
4228 newData->ctime.dwHighDateTime);
4230 StorageUtl_WriteDWord(
4231 buffer,
4232 OFFSET_PS_STARTBLOCK,
4233 newData->startingBlock);
4235 StorageUtl_WriteDWord(
4236 buffer,
4237 OFFSET_PS_SIZE,
4238 newData->size.u.LowPart);
4241 /******************************************************************************
4242 * Storage32Impl_ReadDirEntry
4244 * This method will read the specified directory entry.
4246 HRESULT StorageImpl_ReadDirEntry(
4247 StorageImpl* This,
4248 DirRef index,
4249 DirEntry* buffer)
4251 BYTE currentEntry[RAW_DIRENTRY_SIZE];
4252 HRESULT readRes;
4254 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
4256 if (SUCCEEDED(readRes))
4258 memset(buffer->name, 0, sizeof(buffer->name));
4259 memcpy(
4260 buffer->name,
4261 (WCHAR *)currentEntry+OFFSET_PS_NAME,
4262 DIRENTRY_NAME_BUFFER_LEN );
4263 TRACE("storage name: %s\n", debugstr_w(buffer->name));
4265 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
4267 StorageUtl_ReadWord(
4268 currentEntry,
4269 OFFSET_PS_NAMELENGTH,
4270 &buffer->sizeOfNameString);
4272 StorageUtl_ReadDWord(
4273 currentEntry,
4274 OFFSET_PS_LEFTCHILD,
4275 &buffer->leftChild);
4277 StorageUtl_ReadDWord(
4278 currentEntry,
4279 OFFSET_PS_RIGHTCHILD,
4280 &buffer->rightChild);
4282 StorageUtl_ReadDWord(
4283 currentEntry,
4284 OFFSET_PS_DIRROOT,
4285 &buffer->dirRootEntry);
4287 StorageUtl_ReadGUID(
4288 currentEntry,
4289 OFFSET_PS_GUID,
4290 &buffer->clsid);
4292 StorageUtl_ReadDWord(
4293 currentEntry,
4294 OFFSET_PS_CTIMELOW,
4295 &buffer->ctime.dwLowDateTime);
4297 StorageUtl_ReadDWord(
4298 currentEntry,
4299 OFFSET_PS_CTIMEHIGH,
4300 &buffer->ctime.dwHighDateTime);
4302 StorageUtl_ReadDWord(
4303 currentEntry,
4304 OFFSET_PS_MTIMELOW,
4305 &buffer->mtime.dwLowDateTime);
4307 StorageUtl_ReadDWord(
4308 currentEntry,
4309 OFFSET_PS_MTIMEHIGH,
4310 &buffer->mtime.dwHighDateTime);
4312 StorageUtl_ReadDWord(
4313 currentEntry,
4314 OFFSET_PS_STARTBLOCK,
4315 &buffer->startingBlock);
4317 StorageUtl_ReadDWord(
4318 currentEntry,
4319 OFFSET_PS_SIZE,
4320 &buffer->size.u.LowPart);
4322 buffer->size.u.HighPart = 0;
4325 return readRes;
4328 /*********************************************************************
4329 * Write the specified directory entry to the file
4331 HRESULT StorageImpl_WriteDirEntry(
4332 StorageImpl* This,
4333 DirRef index,
4334 const DirEntry* buffer)
4336 BYTE currentEntry[RAW_DIRENTRY_SIZE];
4338 UpdateRawDirEntry(currentEntry, buffer);
4340 return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
4343 static HRESULT StorageImpl_ReadBigBlock(
4344 StorageImpl* This,
4345 ULONG blockIndex,
4346 void* buffer,
4347 ULONG* out_read)
4349 ULARGE_INTEGER ulOffset;
4350 DWORD read=0;
4351 HRESULT hr;
4353 ulOffset.u.HighPart = 0;
4354 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4356 hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
4358 if (SUCCEEDED(hr) && read < This->bigBlockSize)
4360 /* File ends during this block; fill the rest with 0's. */
4361 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
4364 if (out_read) *out_read = read;
4366 return hr;
4369 static BOOL StorageImpl_ReadDWordFromBigBlock(
4370 StorageImpl* This,
4371 ULONG blockIndex,
4372 ULONG offset,
4373 DWORD* value)
4375 ULARGE_INTEGER ulOffset;
4376 DWORD read;
4377 DWORD tmp;
4379 ulOffset.u.HighPart = 0;
4380 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4381 ulOffset.u.LowPart += offset;
4383 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
4384 *value = lendian32toh(tmp);
4385 return (read == sizeof(DWORD));
4388 static BOOL StorageImpl_WriteBigBlock(
4389 StorageImpl* This,
4390 ULONG blockIndex,
4391 const void* buffer)
4393 ULARGE_INTEGER ulOffset;
4394 DWORD wrote;
4396 ulOffset.u.HighPart = 0;
4397 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4399 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
4400 return (wrote == This->bigBlockSize);
4403 static BOOL StorageImpl_WriteDWordToBigBlock(
4404 StorageImpl* This,
4405 ULONG blockIndex,
4406 ULONG offset,
4407 DWORD value)
4409 ULARGE_INTEGER ulOffset;
4410 DWORD wrote;
4412 ulOffset.u.HighPart = 0;
4413 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4414 ulOffset.u.LowPart += offset;
4416 value = htole32(value);
4417 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4418 return (wrote == sizeof(DWORD));
4421 /******************************************************************************
4422 * Storage32Impl_SmallBlocksToBigBlocks
4424 * This method will convert a small block chain to a big block chain.
4425 * The small block chain will be destroyed.
4427 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4428 StorageImpl* This,
4429 SmallBlockChainStream** ppsbChain)
4431 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4432 ULARGE_INTEGER size, offset;
4433 ULONG cbRead, cbWritten;
4434 ULARGE_INTEGER cbTotalRead;
4435 DirRef streamEntryRef;
4436 HRESULT resWrite = S_OK;
4437 HRESULT resRead;
4438 DirEntry streamEntry;
4439 BYTE *buffer;
4440 BlockChainStream *bbTempChain = NULL;
4441 BlockChainStream *bigBlockChain = NULL;
4444 * Create a temporary big block chain that doesn't have
4445 * an associated directory entry. This temporary chain will be
4446 * used to copy data from small blocks to big blocks.
4448 bbTempChain = BlockChainStream_Construct(This,
4449 &bbHeadOfChain,
4450 DIRENTRY_NULL);
4451 if(!bbTempChain) return NULL;
4453 * Grow the big block chain.
4455 size = SmallBlockChainStream_GetSize(*ppsbChain);
4456 BlockChainStream_SetSize(bbTempChain, size);
4459 * Copy the contents of the small block chain to the big block chain
4460 * by small block size increments.
4462 offset.u.LowPart = 0;
4463 offset.u.HighPart = 0;
4464 cbTotalRead.QuadPart = 0;
4466 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4469 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4470 offset,
4471 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4472 buffer,
4473 &cbRead);
4474 if (FAILED(resRead))
4475 break;
4477 if (cbRead > 0)
4479 cbTotalRead.QuadPart += cbRead;
4481 resWrite = BlockChainStream_WriteAt(bbTempChain,
4482 offset,
4483 cbRead,
4484 buffer,
4485 &cbWritten);
4487 if (FAILED(resWrite))
4488 break;
4490 offset.u.LowPart += cbRead;
4492 else
4494 resRead = STG_E_READFAULT;
4495 break;
4497 } while (cbTotalRead.QuadPart < size.QuadPart);
4498 HeapFree(GetProcessHeap(),0,buffer);
4500 size.u.HighPart = 0;
4501 size.u.LowPart = 0;
4503 if (FAILED(resRead) || FAILED(resWrite))
4505 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4506 BlockChainStream_SetSize(bbTempChain, size);
4507 BlockChainStream_Destroy(bbTempChain);
4508 return NULL;
4512 * Destroy the small block chain.
4514 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4515 SmallBlockChainStream_SetSize(*ppsbChain, size);
4516 SmallBlockChainStream_Destroy(*ppsbChain);
4517 *ppsbChain = 0;
4520 * Change the directory entry. This chain is now a big block chain
4521 * and it doesn't reside in the small blocks chain anymore.
4523 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4525 streamEntry.startingBlock = bbHeadOfChain;
4527 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4530 * Destroy the temporary entryless big block chain.
4531 * Create a new big block chain associated with this entry.
4533 BlockChainStream_Destroy(bbTempChain);
4534 bigBlockChain = BlockChainStream_Construct(This,
4535 NULL,
4536 streamEntryRef);
4538 return bigBlockChain;
4541 /******************************************************************************
4542 * Storage32Impl_BigBlocksToSmallBlocks
4544 * This method will convert a big block chain to a small block chain.
4545 * The big block chain will be destroyed on success.
4547 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4548 StorageImpl* This,
4549 BlockChainStream** ppbbChain,
4550 ULARGE_INTEGER newSize)
4552 ULARGE_INTEGER size, offset, cbTotalRead;
4553 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4554 DirRef streamEntryRef;
4555 HRESULT resWrite = S_OK, resRead = S_OK;
4556 DirEntry streamEntry;
4557 BYTE* buffer;
4558 SmallBlockChainStream* sbTempChain;
4560 TRACE("%p %p\n", This, ppbbChain);
4562 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4563 DIRENTRY_NULL);
4565 if(!sbTempChain)
4566 return NULL;
4568 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4569 size = BlockChainStream_GetSize(*ppbbChain);
4570 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4572 offset.u.HighPart = 0;
4573 offset.u.LowPart = 0;
4574 cbTotalRead.QuadPart = 0;
4575 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4576 while(cbTotalRead.QuadPart < size.QuadPart)
4578 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4579 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4580 buffer, &cbRead);
4582 if(FAILED(resRead))
4583 break;
4585 if(cbRead > 0)
4587 cbTotalRead.QuadPart += cbRead;
4589 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4590 cbRead, buffer, &cbWritten);
4592 if(FAILED(resWrite))
4593 break;
4595 offset.u.LowPart += cbRead;
4597 else
4599 resRead = STG_E_READFAULT;
4600 break;
4603 HeapFree(GetProcessHeap(), 0, buffer);
4605 size.u.HighPart = 0;
4606 size.u.LowPart = 0;
4608 if(FAILED(resRead) || FAILED(resWrite))
4610 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4611 SmallBlockChainStream_SetSize(sbTempChain, size);
4612 SmallBlockChainStream_Destroy(sbTempChain);
4613 return NULL;
4616 /* destroy the original big block chain */
4617 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4618 BlockChainStream_SetSize(*ppbbChain, size);
4619 BlockChainStream_Destroy(*ppbbChain);
4620 *ppbbChain = NULL;
4622 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4623 streamEntry.startingBlock = sbHeadOfChain;
4624 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4626 SmallBlockChainStream_Destroy(sbTempChain);
4627 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4630 static HRESULT StorageBaseImpl_CopyStream(
4631 StorageBaseImpl *dst, DirRef dst_entry,
4632 StorageBaseImpl *src, DirRef src_entry)
4634 HRESULT hr;
4635 BYTE data[4096];
4636 DirEntry srcdata;
4637 ULARGE_INTEGER bytes_copied;
4638 ULONG bytestocopy, bytesread, byteswritten;
4640 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4642 if (SUCCEEDED(hr))
4644 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4646 bytes_copied.QuadPart = 0;
4647 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4649 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4651 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4652 data, &bytesread);
4653 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4655 if (SUCCEEDED(hr))
4656 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4657 data, &byteswritten);
4658 if (SUCCEEDED(hr))
4660 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4661 bytes_copied.QuadPart += byteswritten;
4666 return hr;
4669 static HRESULT StorageBaseImpl_DupStorageTree(
4670 StorageBaseImpl *dst, DirRef *dst_entry,
4671 StorageBaseImpl *src, DirRef src_entry)
4673 HRESULT hr;
4674 DirEntry data;
4675 BOOL has_stream=FALSE;
4677 if (src_entry == DIRENTRY_NULL)
4679 *dst_entry = DIRENTRY_NULL;
4680 return S_OK;
4683 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
4684 if (SUCCEEDED(hr))
4686 has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0);
4687 data.startingBlock = BLOCK_END_OF_CHAIN;
4688 data.size.QuadPart = 0;
4690 hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild);
4693 if (SUCCEEDED(hr))
4694 hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
4696 if (SUCCEEDED(hr))
4697 hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);
4699 if (SUCCEEDED(hr))
4700 hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry);
4702 if (SUCCEEDED(hr) && has_stream)
4703 hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
4705 return hr;
4708 static HRESULT StorageBaseImpl_CopyStorageTree(
4709 StorageBaseImpl *dst, DirRef dst_entry,
4710 StorageBaseImpl *src, DirRef src_entry)
4712 HRESULT hr;
4713 DirEntry src_data, dst_data;
4714 DirRef new_root_entry;
4716 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
4718 if (SUCCEEDED(hr))
4720 hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
4723 if (SUCCEEDED(hr))
4725 hr = StorageBaseImpl_ReadDirEntry(dst, dst_entry, &dst_data);
4726 dst_data.clsid = src_data.clsid;
4727 dst_data.ctime = src_data.ctime;
4728 dst_data.mtime = src_data.mtime;
4729 dst_data.dirRootEntry = new_root_entry;
4732 if (SUCCEEDED(hr))
4733 hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
4735 return hr;
4738 static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
4740 HRESULT hr;
4741 DirEntry data;
4742 ULARGE_INTEGER zero;
4744 if (entry == DIRENTRY_NULL)
4745 return S_OK;
4747 zero.QuadPart = 0;
4749 hr = StorageBaseImpl_ReadDirEntry(This, entry, &data);
4751 if (SUCCEEDED(hr) && include_siblings)
4752 hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE);
4754 if (SUCCEEDED(hr) && include_siblings)
4755 hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE);
4757 if (SUCCEEDED(hr))
4758 hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
4760 if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
4761 hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
4763 if (SUCCEEDED(hr))
4764 hr = StorageBaseImpl_DestroyDirEntry(This, entry);
4766 return hr;
4769 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4771 DirRef result=This->firstFreeEntry;
4773 while (result < This->entries_size && This->entries[result].inuse)
4774 result++;
4776 if (result == This->entries_size)
4778 ULONG new_size = This->entries_size * 2;
4779 TransactedDirEntry *new_entries;
4781 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4782 if (!new_entries) return DIRENTRY_NULL;
4784 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4785 HeapFree(GetProcessHeap(), 0, This->entries);
4787 This->entries = new_entries;
4788 This->entries_size = new_size;
4791 This->entries[result].inuse = TRUE;
4793 This->firstFreeEntry = result+1;
4795 return result;
4798 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4799 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4801 DirRef stubEntryRef;
4802 TransactedDirEntry *entry;
4804 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4806 if (stubEntryRef != DIRENTRY_NULL)
4808 entry = &This->entries[stubEntryRef];
4810 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4812 entry->read = FALSE;
4815 return stubEntryRef;
4818 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4819 TransactedSnapshotImpl *This, DirRef entry)
4821 HRESULT hr=S_OK;
4822 DirEntry data;
4824 if (!This->entries[entry].read)
4826 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4827 This->entries[entry].transactedParentEntry,
4828 &data);
4830 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4832 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4834 if (data.leftChild == DIRENTRY_NULL)
4835 hr = E_OUTOFMEMORY;
4838 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4840 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4842 if (data.rightChild == DIRENTRY_NULL)
4843 hr = E_OUTOFMEMORY;
4846 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4848 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4850 if (data.dirRootEntry == DIRENTRY_NULL)
4851 hr = E_OUTOFMEMORY;
4854 if (SUCCEEDED(hr))
4856 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4857 This->entries[entry].read = TRUE;
4861 return hr;
4864 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4865 TransactedSnapshotImpl *This, DirRef entry)
4867 HRESULT hr = S_OK;
4869 if (!This->entries[entry].stream_dirty)
4871 DirEntry new_entrydata;
4873 memset(&new_entrydata, 0, sizeof(DirEntry));
4874 new_entrydata.name[0] = 'S';
4875 new_entrydata.sizeOfNameString = 1;
4876 new_entrydata.stgType = STGTY_STREAM;
4877 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4878 new_entrydata.leftChild = DIRENTRY_NULL;
4879 new_entrydata.rightChild = DIRENTRY_NULL;
4880 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4882 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4883 &This->entries[entry].stream_entry);
4885 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4887 hr = StorageBaseImpl_CopyStream(
4888 This->scratch, This->entries[entry].stream_entry,
4889 This->transactedParent, This->entries[entry].transactedParentEntry);
4891 if (FAILED(hr))
4892 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4895 if (SUCCEEDED(hr))
4896 This->entries[entry].stream_dirty = TRUE;
4898 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4900 /* Since this entry is modified, and we aren't using its stream data, we
4901 * no longer care about the original entry. */
4902 DirRef delete_ref;
4903 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4905 if (delete_ref != DIRENTRY_NULL)
4906 This->entries[delete_ref].deleted = TRUE;
4908 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4912 return hr;
4915 /* Find the first entry in a depth-first traversal. */
4916 static DirRef TransactedSnapshotImpl_FindFirstChild(
4917 TransactedSnapshotImpl* This, DirRef parent)
4919 DirRef cursor, prev;
4920 TransactedDirEntry *entry;
4922 cursor = parent;
4923 entry = &This->entries[cursor];
4924 while (entry->read)
4926 if (entry->data.leftChild != DIRENTRY_NULL)
4928 prev = cursor;
4929 cursor = entry->data.leftChild;
4930 entry = &This->entries[cursor];
4931 entry->parent = prev;
4933 else if (entry->data.rightChild != DIRENTRY_NULL)
4935 prev = cursor;
4936 cursor = entry->data.rightChild;
4937 entry = &This->entries[cursor];
4938 entry->parent = prev;
4940 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4942 prev = cursor;
4943 cursor = entry->data.dirRootEntry;
4944 entry = &This->entries[cursor];
4945 entry->parent = prev;
4947 else
4948 break;
4951 return cursor;
4954 /* Find the next entry in a depth-first traversal. */
4955 static DirRef TransactedSnapshotImpl_FindNextChild(
4956 TransactedSnapshotImpl* This, DirRef current)
4958 DirRef parent;
4959 TransactedDirEntry *parent_entry;
4961 parent = This->entries[current].parent;
4962 parent_entry = &This->entries[parent];
4964 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4966 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4968 This->entries[parent_entry->data.rightChild].parent = parent;
4969 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4972 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4974 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4975 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4979 return parent;
4982 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4983 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4984 TransactedSnapshotImpl* This, DirRef entry)
4986 return entry != DIRENTRY_NULL &&
4987 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4990 /* Destroy the entries created by CopyTree. */
4991 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4992 TransactedSnapshotImpl* This, DirRef stop)
4994 DirRef cursor;
4995 TransactedDirEntry *entry;
4996 ULARGE_INTEGER zero;
4998 zero.QuadPart = 0;
5000 if (!This->entries[This->base.storageDirEntry].read)
5001 return;
5003 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
5005 if (cursor == DIRENTRY_NULL)
5006 return;
5008 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
5010 while (cursor != DIRENTRY_NULL && cursor != stop)
5012 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
5014 entry = &This->entries[cursor];
5016 if (entry->stream_dirty)
5017 StorageBaseImpl_StreamSetSize(This->transactedParent,
5018 entry->newTransactedParentEntry, zero);
5020 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5021 entry->newTransactedParentEntry);
5023 entry->newTransactedParentEntry = entry->transactedParentEntry;
5026 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5030 /* Make a copy of our edited tree that we can use in the parent. */
5031 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
5033 DirRef cursor;
5034 TransactedDirEntry *entry;
5035 HRESULT hr = S_OK;
5037 cursor = This->base.storageDirEntry;
5038 entry = &This->entries[cursor];
5039 entry->parent = DIRENTRY_NULL;
5040 entry->newTransactedParentEntry = entry->transactedParentEntry;
5042 if (entry->data.dirRootEntry == DIRENTRY_NULL)
5043 return S_OK;
5045 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
5047 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
5048 entry = &This->entries[cursor];
5050 while (cursor != DIRENTRY_NULL)
5052 /* Make a copy of this entry in the transacted parent. */
5053 if (!entry->read ||
5054 (!entry->dirty && !entry->stream_dirty &&
5055 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
5056 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
5057 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
5058 entry->newTransactedParentEntry = entry->transactedParentEntry;
5059 else
5061 DirEntry newData;
5063 memcpy(&newData, &entry->data, sizeof(DirEntry));
5065 newData.size.QuadPart = 0;
5066 newData.startingBlock = BLOCK_END_OF_CHAIN;
5068 if (newData.leftChild != DIRENTRY_NULL)
5069 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
5071 if (newData.rightChild != DIRENTRY_NULL)
5072 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
5074 if (newData.dirRootEntry != DIRENTRY_NULL)
5075 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
5077 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
5078 &entry->newTransactedParentEntry);
5079 if (FAILED(hr))
5081 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5082 return hr;
5085 if (entry->stream_dirty)
5087 hr = StorageBaseImpl_CopyStream(
5088 This->transactedParent, entry->newTransactedParentEntry,
5089 This->scratch, entry->stream_entry);
5091 else if (entry->data.size.QuadPart)
5093 hr = StorageBaseImpl_StreamLink(
5094 This->transactedParent, entry->newTransactedParentEntry,
5095 entry->transactedParentEntry);
5098 if (FAILED(hr))
5100 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5101 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5102 return hr;
5106 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5107 entry = &This->entries[cursor];
5110 return hr;
5113 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
5114 IStorage* iface,
5115 DWORD grfCommitFlags) /* [in] */
5117 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
5118 TransactedDirEntry *root_entry;
5119 DirRef i, dir_root_ref;
5120 DirEntry data;
5121 ULARGE_INTEGER zero;
5122 HRESULT hr;
5123 ULONG transactionSig;
5125 zero.QuadPart = 0;
5127 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5129 /* Cannot commit a read-only transacted storage */
5130 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
5131 return STG_E_ACCESSDENIED;
5133 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
5134 if (hr == E_NOTIMPL) hr = S_OK;
5135 if (SUCCEEDED(hr))
5137 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
5138 if (SUCCEEDED(hr))
5140 if (transactionSig != This->lastTransactionSig)
5142 ERR("file was externally modified\n");
5143 hr = STG_E_NOTCURRENT;
5146 if (SUCCEEDED(hr))
5148 This->lastTransactionSig = transactionSig+1;
5149 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
5152 else if (hr == E_NOTIMPL)
5153 hr = S_OK;
5155 if (FAILED(hr)) goto end;
5157 /* To prevent data loss, we create the new structure in the file before we
5158 * delete the old one, so that in case of errors the old data is intact. We
5159 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5160 * needed in the rare situation where we have just enough free disk space to
5161 * overwrite the existing data. */
5163 root_entry = &This->entries[This->base.storageDirEntry];
5165 if (!root_entry->read)
5166 goto end;
5168 hr = TransactedSnapshotImpl_CopyTree(This);
5169 if (FAILED(hr)) goto end;
5171 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
5172 dir_root_ref = DIRENTRY_NULL;
5173 else
5174 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
5176 hr = StorageBaseImpl_Flush(This->transactedParent);
5178 /* Update the storage to use the new data in one step. */
5179 if (SUCCEEDED(hr))
5180 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5181 root_entry->transactedParentEntry, &data);
5183 if (SUCCEEDED(hr))
5185 data.dirRootEntry = dir_root_ref;
5186 data.clsid = root_entry->data.clsid;
5187 data.ctime = root_entry->data.ctime;
5188 data.mtime = root_entry->data.mtime;
5190 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
5191 root_entry->transactedParentEntry, &data);
5194 /* Try to flush after updating the root storage, but if the flush fails, keep
5195 * going, on the theory that it'll either succeed later or the subsequent
5196 * writes will fail. */
5197 StorageBaseImpl_Flush(This->transactedParent);
5199 if (SUCCEEDED(hr))
5201 /* Destroy the old now-orphaned data. */
5202 for (i=0; i<This->entries_size; i++)
5204 TransactedDirEntry *entry = &This->entries[i];
5205 if (entry->inuse)
5207 if (entry->deleted)
5209 StorageBaseImpl_StreamSetSize(This->transactedParent,
5210 entry->transactedParentEntry, zero);
5211 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5212 entry->transactedParentEntry);
5213 memset(entry, 0, sizeof(TransactedDirEntry));
5214 This->firstFreeEntry = min(i, This->firstFreeEntry);
5216 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
5218 if (entry->transactedParentEntry != DIRENTRY_NULL)
5219 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5220 entry->transactedParentEntry);
5221 if (entry->stream_dirty)
5223 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
5224 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
5225 entry->stream_dirty = FALSE;
5227 entry->dirty = FALSE;
5228 entry->transactedParentEntry = entry->newTransactedParentEntry;
5233 else
5235 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
5238 if (SUCCEEDED(hr))
5239 hr = StorageBaseImpl_Flush(This->transactedParent);
5240 end:
5241 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
5244 return hr;
5247 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
5248 IStorage* iface)
5250 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
5251 ULARGE_INTEGER zero;
5252 ULONG i;
5254 TRACE("(%p)\n", iface);
5256 /* Destroy the open objects. */
5257 StorageBaseImpl_DeleteAll(&This->base);
5259 /* Clear out the scratch file. */
5260 zero.QuadPart = 0;
5261 for (i=0; i<This->entries_size; i++)
5263 if (This->entries[i].stream_dirty)
5265 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
5266 zero);
5268 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
5272 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
5274 This->firstFreeEntry = 0;
5275 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
5277 return S_OK;
5280 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
5282 if (!This->reverted)
5284 TRACE("Storage invalidated (stg=%p)\n", This);
5286 This->reverted = TRUE;
5288 StorageBaseImpl_DeleteAll(This);
5292 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
5294 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
5296 IStorage_Revert(&This->base.IStorage_iface);
5297 IStorage_Release(&This->transactedParent->IStorage_iface);
5298 IStorage_Release(&This->scratch->IStorage_iface);
5299 HeapFree(GetProcessHeap(), 0, This->entries);
5300 HeapFree(GetProcessHeap(), 0, This);
5303 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
5305 /* We only need to flush when committing. */
5306 return S_OK;
5309 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5311 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
5313 return StorageBaseImpl_GetFilename(This->transactedParent, result);
5316 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
5317 const DirEntry *newData, DirRef *index)
5319 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5320 DirRef new_ref;
5321 TransactedDirEntry *new_entry;
5323 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
5324 if (new_ref == DIRENTRY_NULL)
5325 return E_OUTOFMEMORY;
5327 new_entry = &This->entries[new_ref];
5329 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
5330 new_entry->read = TRUE;
5331 new_entry->dirty = TRUE;
5332 memcpy(&new_entry->data, newData, sizeof(DirEntry));
5334 *index = new_ref;
5336 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
5338 return S_OK;
5341 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
5342 DirRef index, const DirEntry *data)
5344 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5345 HRESULT hr;
5347 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
5349 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5350 if (FAILED(hr)) return hr;
5352 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
5354 if (index != This->base.storageDirEntry)
5356 This->entries[index].dirty = TRUE;
5358 if (data->size.QuadPart == 0 &&
5359 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
5361 /* Since this entry is modified, and we aren't using its stream data, we
5362 * no longer care about the original entry. */
5363 DirRef delete_ref;
5364 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
5366 if (delete_ref != DIRENTRY_NULL)
5367 This->entries[delete_ref].deleted = TRUE;
5369 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5373 return S_OK;
5376 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
5377 DirRef index, DirEntry *data)
5379 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5380 HRESULT hr;
5382 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5383 if (FAILED(hr)) return hr;
5385 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
5387 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
5389 return S_OK;
5392 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
5393 DirRef index)
5395 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5397 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
5398 This->entries[index].data.size.QuadPart != 0)
5400 /* If we deleted this entry while it has stream data. We must have left the
5401 * data because some other entry is using it, and we need to leave the
5402 * original entry alone. */
5403 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
5404 This->firstFreeEntry = min(index, This->firstFreeEntry);
5406 else
5408 This->entries[index].deleted = TRUE;
5411 return S_OK;
5414 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
5415 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5417 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5419 if (This->entries[index].stream_dirty)
5421 return StorageBaseImpl_StreamReadAt(This->scratch,
5422 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
5424 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
5426 /* This stream doesn't live in the parent, and we haven't allocated storage
5427 * for it yet */
5428 *bytesRead = 0;
5429 return S_OK;
5431 else
5433 return StorageBaseImpl_StreamReadAt(This->transactedParent,
5434 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
5438 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
5439 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5441 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5442 HRESULT hr;
5444 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5445 if (FAILED(hr)) return hr;
5447 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5448 if (FAILED(hr)) return hr;
5450 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
5451 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
5453 if (SUCCEEDED(hr) && size != 0)
5454 This->entries[index].data.size.QuadPart = max(
5455 This->entries[index].data.size.QuadPart,
5456 offset.QuadPart + size);
5458 return hr;
5461 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
5462 DirRef index, ULARGE_INTEGER newsize)
5464 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5465 HRESULT hr;
5467 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5468 if (FAILED(hr)) return hr;
5470 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
5471 return S_OK;
5473 if (newsize.QuadPart == 0)
5475 /* Destroy any parent references or entries in the scratch file. */
5476 if (This->entries[index].stream_dirty)
5478 ULARGE_INTEGER zero;
5479 zero.QuadPart = 0;
5480 StorageBaseImpl_StreamSetSize(This->scratch,
5481 This->entries[index].stream_entry, zero);
5482 StorageBaseImpl_DestroyDirEntry(This->scratch,
5483 This->entries[index].stream_entry);
5484 This->entries[index].stream_dirty = FALSE;
5486 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
5488 DirRef delete_ref;
5489 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
5491 if (delete_ref != DIRENTRY_NULL)
5492 This->entries[delete_ref].deleted = TRUE;
5494 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5497 else
5499 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5500 if (FAILED(hr)) return hr;
5502 hr = StorageBaseImpl_StreamSetSize(This->scratch,
5503 This->entries[index].stream_entry, newsize);
5506 if (SUCCEEDED(hr))
5507 This->entries[index].data.size = newsize;
5509 return hr;
5512 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
5513 DirRef dst, DirRef src)
5515 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5516 HRESULT hr;
5517 TransactedDirEntry *dst_entry, *src_entry;
5519 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
5520 if (FAILED(hr)) return hr;
5522 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
5523 if (FAILED(hr)) return hr;
5525 dst_entry = &This->entries[dst];
5526 src_entry = &This->entries[src];
5528 dst_entry->stream_dirty = src_entry->stream_dirty;
5529 dst_entry->stream_entry = src_entry->stream_entry;
5530 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
5531 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
5532 dst_entry->data.size = src_entry->data.size;
5534 return S_OK;
5537 static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
5538 ULONG* result, BOOL refresh)
5540 return E_NOTIMPL;
5543 static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
5544 ULONG value)
5546 return E_NOTIMPL;
5549 static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
5551 return E_NOTIMPL;
5554 static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
5556 return E_NOTIMPL;
5559 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5561 StorageBaseImpl_QueryInterface,
5562 StorageBaseImpl_AddRef,
5563 StorageBaseImpl_Release,
5564 StorageBaseImpl_CreateStream,
5565 StorageBaseImpl_OpenStream,
5566 StorageBaseImpl_CreateStorage,
5567 StorageBaseImpl_OpenStorage,
5568 StorageBaseImpl_CopyTo,
5569 StorageBaseImpl_MoveElementTo,
5570 TransactedSnapshotImpl_Commit,
5571 TransactedSnapshotImpl_Revert,
5572 StorageBaseImpl_EnumElements,
5573 StorageBaseImpl_DestroyElement,
5574 StorageBaseImpl_RenameElement,
5575 StorageBaseImpl_SetElementTimes,
5576 StorageBaseImpl_SetClass,
5577 StorageBaseImpl_SetStateBits,
5578 StorageBaseImpl_Stat
5581 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5583 TransactedSnapshotImpl_Destroy,
5584 TransactedSnapshotImpl_Invalidate,
5585 TransactedSnapshotImpl_Flush,
5586 TransactedSnapshotImpl_GetFilename,
5587 TransactedSnapshotImpl_CreateDirEntry,
5588 TransactedSnapshotImpl_WriteDirEntry,
5589 TransactedSnapshotImpl_ReadDirEntry,
5590 TransactedSnapshotImpl_DestroyDirEntry,
5591 TransactedSnapshotImpl_StreamReadAt,
5592 TransactedSnapshotImpl_StreamWriteAt,
5593 TransactedSnapshotImpl_StreamSetSize,
5594 TransactedSnapshotImpl_StreamLink,
5595 TransactedSnapshotImpl_GetTransactionSig,
5596 TransactedSnapshotImpl_SetTransactionSig,
5597 TransactedSnapshotImpl_LockTransaction,
5598 TransactedSnapshotImpl_UnlockTransaction
5601 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5602 TransactedSnapshotImpl** result)
5604 HRESULT hr;
5606 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5607 if (*result)
5609 IStorage *scratch;
5611 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5613 /* This is OK because the property set storage functions use the IStorage functions. */
5614 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
5615 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5617 list_init(&(*result)->base.strmHead);
5619 list_init(&(*result)->base.storageHead);
5621 (*result)->base.ref = 1;
5623 (*result)->base.openFlags = parentStorage->openFlags;
5625 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5626 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
5628 /* Create a new temporary storage to act as the scratch file. */
5629 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
5630 0, &scratch);
5631 (*result)->scratch = impl_from_IStorage(scratch);
5633 if (SUCCEEDED(hr))
5635 ULONG num_entries = 20;
5637 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5638 (*result)->entries_size = num_entries;
5639 (*result)->firstFreeEntry = 0;
5641 if ((*result)->entries)
5643 /* parentStorage already has 1 reference, which we take over here. */
5644 (*result)->transactedParent = parentStorage;
5646 parentStorage->transactedChild = &(*result)->base;
5648 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5650 else
5652 IStorage_Release(scratch);
5654 hr = E_OUTOFMEMORY;
5658 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
5660 return hr;
5662 else
5663 return E_OUTOFMEMORY;
5666 static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
5668 if (!This->reverted)
5670 TRACE("Storage invalidated (stg=%p)\n", This);
5672 This->reverted = TRUE;
5674 StorageBaseImpl_DeleteAll(This);
5678 static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
5680 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
5682 TransactedSharedImpl_Invalidate(&This->base);
5683 IStorage_Release(&This->transactedParent->IStorage_iface);
5684 IStorage_Release(&This->scratch->base.IStorage_iface);
5685 HeapFree(GetProcessHeap(), 0, This);
5688 static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface)
5690 /* We only need to flush when committing. */
5691 return S_OK;
5694 static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5696 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
5698 return StorageBaseImpl_GetFilename(This->transactedParent, result);
5701 static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
5702 const DirEntry *newData, DirRef *index)
5704 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5706 return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
5707 newData, index);
5710 static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
5711 DirRef index, const DirEntry *data)
5713 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5715 return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
5716 index, data);
5719 static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
5720 DirRef index, DirEntry *data)
5722 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5724 return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
5725 index, data);
5728 static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
5729 DirRef index)
5731 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5733 return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
5734 index);
5737 static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
5738 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5740 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5742 return StorageBaseImpl_StreamReadAt(&This->scratch->base,
5743 index, offset, size, buffer, bytesRead);
5746 static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
5747 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5749 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5751 return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
5752 index, offset, size, buffer, bytesWritten);
5755 static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
5756 DirRef index, ULARGE_INTEGER newsize)
5758 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5760 return StorageBaseImpl_StreamSetSize(&This->scratch->base,
5761 index, newsize);
5764 static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
5765 DirRef dst, DirRef src)
5767 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5769 return StorageBaseImpl_StreamLink(&This->scratch->base,
5770 dst, src);
5773 static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
5774 ULONG* result, BOOL refresh)
5776 return E_NOTIMPL;
5779 static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
5780 ULONG value)
5782 return E_NOTIMPL;
5785 static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
5787 return E_NOTIMPL;
5790 static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
5792 return E_NOTIMPL;
5795 static HRESULT WINAPI TransactedSharedImpl_Commit(
5796 IStorage* iface,
5797 DWORD grfCommitFlags) /* [in] */
5799 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
5800 DirRef new_storage_ref, prev_storage_ref;
5801 DirEntry src_data, dst_data;
5802 HRESULT hr;
5803 ULONG transactionSig;
5805 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5807 /* Cannot commit a read-only transacted storage */
5808 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
5809 return STG_E_ACCESSDENIED;
5811 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
5812 if (hr == E_NOTIMPL) hr = S_OK;
5813 if (SUCCEEDED(hr))
5815 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
5816 if (SUCCEEDED(hr))
5818 if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
5819 hr = STG_E_NOTCURRENT;
5821 if (SUCCEEDED(hr))
5822 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
5824 else if (hr == E_NOTIMPL)
5825 hr = S_OK;
5827 if (SUCCEEDED(hr))
5828 hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data);
5830 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
5831 if (SUCCEEDED(hr))
5832 hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry);
5834 if (SUCCEEDED(hr))
5835 hr = StorageBaseImpl_Flush(This->transactedParent);
5837 if (SUCCEEDED(hr))
5838 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
5840 if (SUCCEEDED(hr))
5842 prev_storage_ref = dst_data.dirRootEntry;
5843 dst_data.dirRootEntry = new_storage_ref;
5844 dst_data.clsid = src_data.clsid;
5845 dst_data.ctime = src_data.ctime;
5846 dst_data.mtime = src_data.mtime;
5847 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
5850 if (SUCCEEDED(hr))
5852 /* Try to flush after updating the root storage, but if the flush fails, keep
5853 * going, on the theory that it'll either succeed later or the subsequent
5854 * writes will fail. */
5855 StorageBaseImpl_Flush(This->transactedParent);
5857 hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
5860 if (SUCCEEDED(hr))
5861 hr = StorageBaseImpl_Flush(This->transactedParent);
5863 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
5865 if (SUCCEEDED(hr))
5866 hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT);
5868 if (SUCCEEDED(hr))
5870 This->lastTransactionSig = transactionSig+1;
5874 return hr;
5877 static HRESULT WINAPI TransactedSharedImpl_Revert(
5878 IStorage* iface)
5880 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
5882 TRACE("(%p)\n", iface);
5884 /* Destroy the open objects. */
5885 StorageBaseImpl_DeleteAll(&This->base);
5887 return IStorage_Revert(&This->scratch->base.IStorage_iface);
5890 static const IStorageVtbl TransactedSharedImpl_Vtbl =
5892 StorageBaseImpl_QueryInterface,
5893 StorageBaseImpl_AddRef,
5894 StorageBaseImpl_Release,
5895 StorageBaseImpl_CreateStream,
5896 StorageBaseImpl_OpenStream,
5897 StorageBaseImpl_CreateStorage,
5898 StorageBaseImpl_OpenStorage,
5899 StorageBaseImpl_CopyTo,
5900 StorageBaseImpl_MoveElementTo,
5901 TransactedSharedImpl_Commit,
5902 TransactedSharedImpl_Revert,
5903 StorageBaseImpl_EnumElements,
5904 StorageBaseImpl_DestroyElement,
5905 StorageBaseImpl_RenameElement,
5906 StorageBaseImpl_SetElementTimes,
5907 StorageBaseImpl_SetClass,
5908 StorageBaseImpl_SetStateBits,
5909 StorageBaseImpl_Stat
5912 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl =
5914 TransactedSharedImpl_Destroy,
5915 TransactedSharedImpl_Invalidate,
5916 TransactedSharedImpl_Flush,
5917 TransactedSharedImpl_GetFilename,
5918 TransactedSharedImpl_CreateDirEntry,
5919 TransactedSharedImpl_WriteDirEntry,
5920 TransactedSharedImpl_ReadDirEntry,
5921 TransactedSharedImpl_DestroyDirEntry,
5922 TransactedSharedImpl_StreamReadAt,
5923 TransactedSharedImpl_StreamWriteAt,
5924 TransactedSharedImpl_StreamSetSize,
5925 TransactedSharedImpl_StreamLink,
5926 TransactedSharedImpl_GetTransactionSig,
5927 TransactedSharedImpl_SetTransactionSig,
5928 TransactedSharedImpl_LockTransaction,
5929 TransactedSharedImpl_UnlockTransaction
5932 static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
5933 TransactedSharedImpl** result)
5935 HRESULT hr;
5937 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
5938 if (*result)
5940 IStorage *scratch;
5942 (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
5944 /* This is OK because the property set storage functions use the IStorage functions. */
5945 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
5946 (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
5948 list_init(&(*result)->base.strmHead);
5950 list_init(&(*result)->base.storageHead);
5952 (*result)->base.ref = 1;
5954 (*result)->base.openFlags = parentStorage->openFlags;
5956 hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
5958 if (SUCCEEDED(hr))
5960 STGOPTIONS stgo;
5962 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5963 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
5965 stgo.usVersion = 1;
5966 stgo.reserved = 0;
5967 stgo.ulSectorSize = 4096;
5968 stgo.pwcsTemplateFile = NULL;
5970 /* Create a new temporary storage to act as the scratch file. */
5971 hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED,
5972 STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch);
5973 (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch);
5975 if (SUCCEEDED(hr))
5977 hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
5978 parentStorage, parentStorage->storageDirEntry);
5980 if (SUCCEEDED(hr))
5982 hr = IStorage_Commit(scratch, STGC_DEFAULT);
5984 (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry;
5985 (*result)->transactedParent = parentStorage;
5988 if (FAILED(hr))
5989 IStorage_Release(scratch);
5992 StorageBaseImpl_UnlockTransaction(parentStorage, FALSE);
5995 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
5997 return hr;
5999 else
6000 return E_OUTOFMEMORY;
6003 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
6004 BOOL toplevel, StorageBaseImpl** result)
6006 static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;
6008 if (parentStorage->openFlags & fixme_flags)
6010 fixme_flags &= ~parentStorage->openFlags;
6011 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
6014 if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) &&
6015 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE &&
6016 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE)
6018 /* Need to create a temp file for the snapshot */
6019 return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result);
6022 return TransactedSnapshotImpl_Construct(parentStorage,
6023 (TransactedSnapshotImpl**)result);
6026 static HRESULT Storage_Construct(
6027 HANDLE hFile,
6028 LPCOLESTR pwcsName,
6029 ILockBytes* pLkbyt,
6030 DWORD openFlags,
6031 BOOL fileBased,
6032 BOOL create,
6033 ULONG sector_size,
6034 StorageBaseImpl** result)
6036 StorageImpl *newStorage;
6037 StorageBaseImpl *newTransactedStorage;
6038 HRESULT hr;
6040 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
6041 if (FAILED(hr)) goto end;
6043 if (openFlags & STGM_TRANSACTED)
6045 hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
6046 if (FAILED(hr))
6047 IStorage_Release(&newStorage->base.IStorage_iface);
6048 else
6049 *result = newTransactedStorage;
6051 else
6052 *result = &newStorage->base;
6054 end:
6055 return hr;
6058 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
6060 StorageInternalImpl* This = (StorageInternalImpl*) base;
6062 if (!This->base.reverted)
6064 TRACE("Storage invalidated (stg=%p)\n", This);
6066 This->base.reverted = TRUE;
6068 This->parentStorage = NULL;
6070 StorageBaseImpl_DeleteAll(&This->base);
6072 list_remove(&This->ParentListEntry);
6076 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
6078 StorageInternalImpl* This = (StorageInternalImpl*) iface;
6080 StorageInternalImpl_Invalidate(&This->base);
6082 HeapFree(GetProcessHeap(), 0, This);
6085 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
6087 StorageInternalImpl* This = (StorageInternalImpl*) iface;
6089 return StorageBaseImpl_Flush(This->parentStorage);
6092 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6094 StorageInternalImpl* This = (StorageInternalImpl*) iface;
6096 return StorageBaseImpl_GetFilename(This->parentStorage, result);
6099 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
6100 const DirEntry *newData, DirRef *index)
6102 StorageInternalImpl* This = (StorageInternalImpl*) base;
6104 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
6105 newData, index);
6108 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
6109 DirRef index, const DirEntry *data)
6111 StorageInternalImpl* This = (StorageInternalImpl*) base;
6113 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
6114 index, data);
6117 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
6118 DirRef index, DirEntry *data)
6120 StorageInternalImpl* This = (StorageInternalImpl*) base;
6122 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
6123 index, data);
6126 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
6127 DirRef index)
6129 StorageInternalImpl* This = (StorageInternalImpl*) base;
6131 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
6132 index);
6135 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
6136 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6138 StorageInternalImpl* This = (StorageInternalImpl*) base;
6140 return StorageBaseImpl_StreamReadAt(This->parentStorage,
6141 index, offset, size, buffer, bytesRead);
6144 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
6145 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6147 StorageInternalImpl* This = (StorageInternalImpl*) base;
6149 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
6150 index, offset, size, buffer, bytesWritten);
6153 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
6154 DirRef index, ULARGE_INTEGER newsize)
6156 StorageInternalImpl* This = (StorageInternalImpl*) base;
6158 return StorageBaseImpl_StreamSetSize(This->parentStorage,
6159 index, newsize);
6162 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
6163 DirRef dst, DirRef src)
6165 StorageInternalImpl* This = (StorageInternalImpl*) base;
6167 return StorageBaseImpl_StreamLink(This->parentStorage,
6168 dst, src);
6171 static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
6172 ULONG* result, BOOL refresh)
6174 return E_NOTIMPL;
6177 static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
6178 ULONG value)
6180 return E_NOTIMPL;
6183 static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6185 return E_NOTIMPL;
6188 static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6190 return E_NOTIMPL;
6193 /******************************************************************************
6195 ** Storage32InternalImpl_Commit
6198 static HRESULT WINAPI StorageInternalImpl_Commit(
6199 IStorage* iface,
6200 DWORD grfCommitFlags) /* [in] */
6202 StorageBaseImpl* This = impl_from_IStorage(iface);
6203 TRACE("(%p,%x)\n", iface, grfCommitFlags);
6204 return StorageBaseImpl_Flush(This);
6207 /******************************************************************************
6209 ** Storage32InternalImpl_Revert
6212 static HRESULT WINAPI StorageInternalImpl_Revert(
6213 IStorage* iface)
6215 FIXME("(%p): stub\n", iface);
6216 return S_OK;
6219 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
6221 IStorage_Release(&This->parentStorage->IStorage_iface);
6222 HeapFree(GetProcessHeap(), 0, This);
6225 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
6226 IEnumSTATSTG* iface,
6227 REFIID riid,
6228 void** ppvObject)
6230 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6232 if (ppvObject==0)
6233 return E_INVALIDARG;
6235 *ppvObject = 0;
6237 if (IsEqualGUID(&IID_IUnknown, riid) ||
6238 IsEqualGUID(&IID_IEnumSTATSTG, riid))
6240 *ppvObject = This;
6241 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
6242 return S_OK;
6245 return E_NOINTERFACE;
6248 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
6249 IEnumSTATSTG* iface)
6251 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6252 return InterlockedIncrement(&This->ref);
6255 static ULONG WINAPI IEnumSTATSTGImpl_Release(
6256 IEnumSTATSTG* iface)
6258 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6260 ULONG newRef;
6262 newRef = InterlockedDecrement(&This->ref);
6264 if (newRef==0)
6266 IEnumSTATSTGImpl_Destroy(This);
6269 return newRef;
6272 static HRESULT IEnumSTATSTGImpl_GetNextRef(
6273 IEnumSTATSTGImpl* This,
6274 DirRef *ref)
6276 DirRef result = DIRENTRY_NULL;
6277 DirRef searchNode;
6278 DirEntry entry;
6279 HRESULT hr;
6280 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
6282 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
6283 This->parentStorage->storageDirEntry, &entry);
6284 searchNode = entry.dirRootEntry;
6286 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
6288 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
6290 if (SUCCEEDED(hr))
6292 LONG diff = entryNameCmp( entry.name, This->name);
6294 if (diff <= 0)
6296 searchNode = entry.rightChild;
6298 else
6300 result = searchNode;
6301 memcpy(result_name, entry.name, sizeof(result_name));
6302 searchNode = entry.leftChild;
6307 if (SUCCEEDED(hr))
6309 *ref = result;
6310 if (result != DIRENTRY_NULL)
6311 memcpy(This->name, result_name, sizeof(result_name));
6314 return hr;
6317 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
6318 IEnumSTATSTG* iface,
6319 ULONG celt,
6320 STATSTG* rgelt,
6321 ULONG* pceltFetched)
6323 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6325 DirEntry currentEntry;
6326 STATSTG* currentReturnStruct = rgelt;
6327 ULONG objectFetched = 0;
6328 DirRef currentSearchNode;
6329 HRESULT hr=S_OK;
6331 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
6332 return E_INVALIDARG;
6334 if (This->parentStorage->reverted)
6335 return STG_E_REVERTED;
6338 * To avoid the special case, get another pointer to a ULONG value if
6339 * the caller didn't supply one.
6341 if (pceltFetched==0)
6342 pceltFetched = &objectFetched;
6345 * Start the iteration, we will iterate until we hit the end of the
6346 * linked list or until we hit the number of items to iterate through
6348 *pceltFetched = 0;
6350 while ( *pceltFetched < celt )
6352 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
6354 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
6355 break;
6358 * Read the entry from the storage.
6360 StorageBaseImpl_ReadDirEntry(This->parentStorage,
6361 currentSearchNode,
6362 &currentEntry);
6365 * Copy the information to the return buffer.
6367 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
6368 currentReturnStruct,
6369 &currentEntry,
6370 STATFLAG_DEFAULT);
6373 * Step to the next item in the iteration
6375 (*pceltFetched)++;
6376 currentReturnStruct++;
6379 if (SUCCEEDED(hr) && *pceltFetched != celt)
6380 hr = S_FALSE;
6382 return hr;
6386 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
6387 IEnumSTATSTG* iface,
6388 ULONG celt)
6390 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6392 ULONG objectFetched = 0;
6393 DirRef currentSearchNode;
6394 HRESULT hr=S_OK;
6396 if (This->parentStorage->reverted)
6397 return STG_E_REVERTED;
6399 while ( (objectFetched < celt) )
6401 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
6403 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
6404 break;
6406 objectFetched++;
6409 if (SUCCEEDED(hr) && objectFetched != celt)
6410 return S_FALSE;
6412 return hr;
6415 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
6416 IEnumSTATSTG* iface)
6418 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6420 if (This->parentStorage->reverted)
6421 return STG_E_REVERTED;
6423 This->name[0] = 0;
6425 return S_OK;
6428 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
6429 IEnumSTATSTG* iface,
6430 IEnumSTATSTG** ppenum)
6432 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6433 IEnumSTATSTGImpl* newClone;
6435 if (This->parentStorage->reverted)
6436 return STG_E_REVERTED;
6438 if (ppenum==0)
6439 return E_INVALIDARG;
6441 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
6442 This->storageDirEntry);
6443 if (!newClone)
6445 *ppenum = NULL;
6446 return E_OUTOFMEMORY;
6450 * The new clone enumeration must point to the same current node as
6451 * the old one.
6453 memcpy(newClone->name, This->name, sizeof(newClone->name));
6455 *ppenum = &newClone->IEnumSTATSTG_iface;
6457 return S_OK;
6461 * Virtual function table for the IEnumSTATSTGImpl class.
6463 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
6465 IEnumSTATSTGImpl_QueryInterface,
6466 IEnumSTATSTGImpl_AddRef,
6467 IEnumSTATSTGImpl_Release,
6468 IEnumSTATSTGImpl_Next,
6469 IEnumSTATSTGImpl_Skip,
6470 IEnumSTATSTGImpl_Reset,
6471 IEnumSTATSTGImpl_Clone
6474 /******************************************************************************
6475 ** IEnumSTATSTGImpl implementation
6478 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
6479 StorageBaseImpl* parentStorage,
6480 DirRef storageDirEntry)
6482 IEnumSTATSTGImpl* newEnumeration;
6484 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
6486 if (newEnumeration)
6488 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
6489 newEnumeration->ref = 1;
6490 newEnumeration->name[0] = 0;
6493 * We want to nail-down the reference to the storage in case the
6494 * enumeration out-lives the storage in the client application.
6496 newEnumeration->parentStorage = parentStorage;
6497 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
6499 newEnumeration->storageDirEntry = storageDirEntry;
6502 return newEnumeration;
6506 * Virtual function table for the Storage32InternalImpl class.
6508 static const IStorageVtbl Storage32InternalImpl_Vtbl =
6510 StorageBaseImpl_QueryInterface,
6511 StorageBaseImpl_AddRef,
6512 StorageBaseImpl_Release,
6513 StorageBaseImpl_CreateStream,
6514 StorageBaseImpl_OpenStream,
6515 StorageBaseImpl_CreateStorage,
6516 StorageBaseImpl_OpenStorage,
6517 StorageBaseImpl_CopyTo,
6518 StorageBaseImpl_MoveElementTo,
6519 StorageInternalImpl_Commit,
6520 StorageInternalImpl_Revert,
6521 StorageBaseImpl_EnumElements,
6522 StorageBaseImpl_DestroyElement,
6523 StorageBaseImpl_RenameElement,
6524 StorageBaseImpl_SetElementTimes,
6525 StorageBaseImpl_SetClass,
6526 StorageBaseImpl_SetStateBits,
6527 StorageBaseImpl_Stat
6530 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
6532 StorageInternalImpl_Destroy,
6533 StorageInternalImpl_Invalidate,
6534 StorageInternalImpl_Flush,
6535 StorageInternalImpl_GetFilename,
6536 StorageInternalImpl_CreateDirEntry,
6537 StorageInternalImpl_WriteDirEntry,
6538 StorageInternalImpl_ReadDirEntry,
6539 StorageInternalImpl_DestroyDirEntry,
6540 StorageInternalImpl_StreamReadAt,
6541 StorageInternalImpl_StreamWriteAt,
6542 StorageInternalImpl_StreamSetSize,
6543 StorageInternalImpl_StreamLink,
6544 StorageInternalImpl_GetTransactionSig,
6545 StorageInternalImpl_SetTransactionSig,
6546 StorageInternalImpl_LockTransaction,
6547 StorageInternalImpl_UnlockTransaction
6550 /******************************************************************************
6551 ** Storage32InternalImpl implementation
6554 static StorageInternalImpl* StorageInternalImpl_Construct(
6555 StorageBaseImpl* parentStorage,
6556 DWORD openFlags,
6557 DirRef storageDirEntry)
6559 StorageInternalImpl* newStorage;
6561 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
6563 if (newStorage!=0)
6565 list_init(&newStorage->base.strmHead);
6567 list_init(&newStorage->base.storageHead);
6570 * Initialize the virtual function table.
6572 newStorage->base.IStorage_iface.lpVtbl = &Storage32InternalImpl_Vtbl;
6573 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
6574 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
6575 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
6577 newStorage->base.reverted = FALSE;
6579 newStorage->base.ref = 1;
6581 newStorage->parentStorage = parentStorage;
6584 * Keep a reference to the directory entry of this storage
6586 newStorage->base.storageDirEntry = storageDirEntry;
6588 newStorage->base.create = FALSE;
6590 return newStorage;
6593 return 0;
6596 /******************************************************************************
6597 ** StorageUtl implementation
6600 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
6602 WORD tmp;
6604 memcpy(&tmp, buffer+offset, sizeof(WORD));
6605 *value = lendian16toh(tmp);
6608 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
6610 value = htole16(value);
6611 memcpy(buffer+offset, &value, sizeof(WORD));
6614 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
6616 DWORD tmp;
6618 memcpy(&tmp, buffer+offset, sizeof(DWORD));
6619 *value = lendian32toh(tmp);
6622 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
6624 value = htole32(value);
6625 memcpy(buffer+offset, &value, sizeof(DWORD));
6628 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
6629 ULARGE_INTEGER* value)
6631 #ifdef WORDS_BIGENDIAN
6632 ULARGE_INTEGER tmp;
6634 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
6635 value->u.LowPart = htole32(tmp.u.HighPart);
6636 value->u.HighPart = htole32(tmp.u.LowPart);
6637 #else
6638 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
6639 #endif
6642 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
6643 const ULARGE_INTEGER *value)
6645 #ifdef WORDS_BIGENDIAN
6646 ULARGE_INTEGER tmp;
6648 tmp.u.LowPart = htole32(value->u.HighPart);
6649 tmp.u.HighPart = htole32(value->u.LowPart);
6650 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
6651 #else
6652 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
6653 #endif
6656 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
6658 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
6659 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
6660 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
6662 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
6665 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
6667 StorageUtl_WriteDWord(buffer, offset, value->Data1);
6668 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
6669 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
6671 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
6674 void StorageUtl_CopyDirEntryToSTATSTG(
6675 StorageBaseImpl* storage,
6676 STATSTG* destination,
6677 const DirEntry* source,
6678 int statFlags)
6681 * The copy of the string occurs only when the flag is not set
6683 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
6685 /* Use the filename for the root storage. */
6686 destination->pwcsName = 0;
6687 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
6689 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
6690 (source->name[0] == 0) )
6692 destination->pwcsName = 0;
6694 else
6696 destination->pwcsName =
6697 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
6699 strcpyW(destination->pwcsName, source->name);
6702 switch (source->stgType)
6704 case STGTY_STORAGE:
6705 case STGTY_ROOT:
6706 destination->type = STGTY_STORAGE;
6707 break;
6708 case STGTY_STREAM:
6709 destination->type = STGTY_STREAM;
6710 break;
6711 default:
6712 destination->type = STGTY_STREAM;
6713 break;
6716 destination->cbSize = source->size;
6718 currentReturnStruct->mtime = {0}; TODO
6719 currentReturnStruct->ctime = {0};
6720 currentReturnStruct->atime = {0};
6722 destination->grfMode = 0;
6723 destination->grfLocksSupported = 0;
6724 destination->clsid = source->clsid;
6725 destination->grfStateBits = 0;
6726 destination->reserved = 0;
6729 /******************************************************************************
6730 ** BlockChainStream implementation
6733 /* Read and save the index of all blocks in this stream. */
6734 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
6736 ULONG next_sector, next_offset;
6737 HRESULT hr;
6738 struct BlockChainRun *last_run;
6740 if (This->indexCacheLen == 0)
6742 last_run = NULL;
6743 next_offset = 0;
6744 next_sector = BlockChainStream_GetHeadOfChain(This);
6746 else
6748 last_run = &This->indexCache[This->indexCacheLen-1];
6749 next_offset = last_run->lastOffset+1;
6750 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
6751 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
6752 &next_sector);
6753 if (FAILED(hr)) return hr;
6756 while (next_sector != BLOCK_END_OF_CHAIN)
6758 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
6760 /* Add the current block to the cache. */
6761 if (This->indexCacheSize == 0)
6763 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
6764 if (!This->indexCache) return E_OUTOFMEMORY;
6765 This->indexCacheSize = 16;
6767 else if (This->indexCacheSize == This->indexCacheLen)
6769 struct BlockChainRun *new_cache;
6770 ULONG new_size;
6772 new_size = This->indexCacheSize * 2;
6773 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
6774 if (!new_cache) return E_OUTOFMEMORY;
6775 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
6777 HeapFree(GetProcessHeap(), 0, This->indexCache);
6778 This->indexCache = new_cache;
6779 This->indexCacheSize = new_size;
6782 This->indexCacheLen++;
6783 last_run = &This->indexCache[This->indexCacheLen-1];
6784 last_run->firstSector = next_sector;
6785 last_run->firstOffset = next_offset;
6788 last_run->lastOffset = next_offset;
6790 /* Find the next block. */
6791 next_offset++;
6792 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
6793 if (FAILED(hr)) return hr;
6796 if (This->indexCacheLen)
6798 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
6799 This->numBlocks = last_run->lastOffset+1;
6801 else
6803 This->tailIndex = BLOCK_END_OF_CHAIN;
6804 This->numBlocks = 0;
6807 return S_OK;
6810 /* Locate the nth block in this stream. */
6811 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
6813 ULONG min_offset = 0, max_offset = This->numBlocks-1;
6814 ULONG min_run = 0, max_run = This->indexCacheLen-1;
6816 if (offset >= This->numBlocks)
6817 return BLOCK_END_OF_CHAIN;
6819 while (min_run < max_run)
6821 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
6822 if (offset < This->indexCache[run_to_check].firstOffset)
6824 max_offset = This->indexCache[run_to_check].firstOffset-1;
6825 max_run = run_to_check-1;
6827 else if (offset > This->indexCache[run_to_check].lastOffset)
6829 min_offset = This->indexCache[run_to_check].lastOffset+1;
6830 min_run = run_to_check+1;
6832 else
6833 /* Block is in this run. */
6834 min_run = max_run = run_to_check;
6837 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
6840 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
6841 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
6843 BlockChainBlock *result=NULL;
6844 int i;
6846 for (i=0; i<2; i++)
6847 if (This->cachedBlocks[i].index == index)
6849 *sector = This->cachedBlocks[i].sector;
6850 *block = &This->cachedBlocks[i];
6851 return S_OK;
6854 *sector = BlockChainStream_GetSectorOfOffset(This, index);
6855 if (*sector == BLOCK_END_OF_CHAIN)
6856 return STG_E_DOCFILECORRUPT;
6858 if (create)
6860 if (This->cachedBlocks[0].index == 0xffffffff)
6861 result = &This->cachedBlocks[0];
6862 else if (This->cachedBlocks[1].index == 0xffffffff)
6863 result = &This->cachedBlocks[1];
6864 else
6866 result = &This->cachedBlocks[This->blockToEvict++];
6867 if (This->blockToEvict == 2)
6868 This->blockToEvict = 0;
6871 if (result->dirty)
6873 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
6874 return STG_E_WRITEFAULT;
6875 result->dirty = FALSE;
6878 result->read = FALSE;
6879 result->index = index;
6880 result->sector = *sector;
6883 *block = result;
6884 return S_OK;
6887 BlockChainStream* BlockChainStream_Construct(
6888 StorageImpl* parentStorage,
6889 ULONG* headOfStreamPlaceHolder,
6890 DirRef dirEntry)
6892 BlockChainStream* newStream;
6894 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
6896 newStream->parentStorage = parentStorage;
6897 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6898 newStream->ownerDirEntry = dirEntry;
6899 newStream->indexCache = NULL;
6900 newStream->indexCacheLen = 0;
6901 newStream->indexCacheSize = 0;
6902 newStream->cachedBlocks[0].index = 0xffffffff;
6903 newStream->cachedBlocks[0].dirty = FALSE;
6904 newStream->cachedBlocks[1].index = 0xffffffff;
6905 newStream->cachedBlocks[1].dirty = FALSE;
6906 newStream->blockToEvict = 0;
6908 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
6910 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
6911 HeapFree(GetProcessHeap(), 0, newStream);
6912 return NULL;
6915 return newStream;
6918 HRESULT BlockChainStream_Flush(BlockChainStream* This)
6920 int i;
6921 if (!This) return S_OK;
6922 for (i=0; i<2; i++)
6924 if (This->cachedBlocks[i].dirty)
6926 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6927 This->cachedBlocks[i].dirty = FALSE;
6928 else
6929 return STG_E_WRITEFAULT;
6932 return S_OK;
6935 void BlockChainStream_Destroy(BlockChainStream* This)
6937 if (This)
6939 BlockChainStream_Flush(This);
6940 HeapFree(GetProcessHeap(), 0, This->indexCache);
6942 HeapFree(GetProcessHeap(), 0, This);
6945 /******************************************************************************
6946 * BlockChainStream_GetHeadOfChain
6948 * Returns the head of this stream chain.
6949 * Some special chains don't have directory entries, their heads are kept in
6950 * This->headOfStreamPlaceHolder.
6953 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6955 DirEntry chainEntry;
6956 HRESULT hr;
6958 if (This->headOfStreamPlaceHolder != 0)
6959 return *(This->headOfStreamPlaceHolder);
6961 if (This->ownerDirEntry != DIRENTRY_NULL)
6963 hr = StorageImpl_ReadDirEntry(
6964 This->parentStorage,
6965 This->ownerDirEntry,
6966 &chainEntry);
6968 if (SUCCEEDED(hr))
6970 return chainEntry.startingBlock;
6974 return BLOCK_END_OF_CHAIN;
6977 /******************************************************************************
6978 * BlockChainStream_GetCount
6980 * Returns the number of blocks that comprises this chain.
6981 * This is not the size of the stream as the last block may not be full!
6983 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6985 return This->numBlocks;
6988 /******************************************************************************
6989 * BlockChainStream_ReadAt
6991 * Reads a specified number of bytes from this chain at the specified offset.
6992 * bytesRead may be NULL.
6993 * Failure will be returned if the specified number of bytes has not been read.
6995 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6996 ULARGE_INTEGER offset,
6997 ULONG size,
6998 void* buffer,
6999 ULONG* bytesRead)
7001 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
7002 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
7003 ULONG bytesToReadInBuffer;
7004 ULONG blockIndex;
7005 BYTE* bufferWalker;
7006 ULARGE_INTEGER stream_size;
7007 HRESULT hr;
7008 BlockChainBlock *cachedBlock;
7010 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
7013 * Find the first block in the stream that contains part of the buffer.
7015 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
7017 *bytesRead = 0;
7019 stream_size = BlockChainStream_GetSize(This);
7020 if (stream_size.QuadPart > offset.QuadPart)
7021 size = min(stream_size.QuadPart - offset.QuadPart, size);
7022 else
7023 return S_OK;
7026 * Start reading the buffer.
7028 bufferWalker = buffer;
7030 while (size > 0)
7032 ULARGE_INTEGER ulOffset;
7033 DWORD bytesReadAt;
7036 * Calculate how many bytes we can copy from this big block.
7038 bytesToReadInBuffer =
7039 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7041 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
7043 if (FAILED(hr))
7044 return hr;
7046 if (!cachedBlock)
7048 /* Not in cache, and we're going to read past the end of the block. */
7049 ulOffset.u.HighPart = 0;
7050 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7051 offsetInBlock;
7053 StorageImpl_ReadAt(This->parentStorage,
7054 ulOffset,
7055 bufferWalker,
7056 bytesToReadInBuffer,
7057 &bytesReadAt);
7059 else
7061 if (!cachedBlock->read)
7063 ULONG read;
7064 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7065 return STG_E_READFAULT;
7067 cachedBlock->read = TRUE;
7070 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
7071 bytesReadAt = bytesToReadInBuffer;
7074 blockNoInSequence++;
7075 bufferWalker += bytesReadAt;
7076 size -= bytesReadAt;
7077 *bytesRead += bytesReadAt;
7078 offsetInBlock = 0; /* There is no offset on the next block */
7080 if (bytesToReadInBuffer != bytesReadAt)
7081 break;
7084 return S_OK;
7087 /******************************************************************************
7088 * BlockChainStream_WriteAt
7090 * Writes the specified number of bytes to this chain at the specified offset.
7091 * Will fail if not all specified number of bytes have been written.
7093 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
7094 ULARGE_INTEGER offset,
7095 ULONG size,
7096 const void* buffer,
7097 ULONG* bytesWritten)
7099 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
7100 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
7101 ULONG bytesToWrite;
7102 ULONG blockIndex;
7103 const BYTE* bufferWalker;
7104 HRESULT hr;
7105 BlockChainBlock *cachedBlock;
7107 *bytesWritten = 0;
7108 bufferWalker = buffer;
7110 while (size > 0)
7112 ULARGE_INTEGER ulOffset;
7113 DWORD bytesWrittenAt;
7116 * Calculate how many bytes we can copy to this big block.
7118 bytesToWrite =
7119 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7121 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
7123 /* BlockChainStream_SetSize should have already been called to ensure we have
7124 * enough blocks in the chain to write into */
7125 if (FAILED(hr))
7127 ERR("not enough blocks in chain to write data\n");
7128 return hr;
7131 if (!cachedBlock)
7133 /* Not in cache, and we're going to write past the end of the block. */
7134 ulOffset.u.HighPart = 0;
7135 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7136 offsetInBlock;
7138 StorageImpl_WriteAt(This->parentStorage,
7139 ulOffset,
7140 bufferWalker,
7141 bytesToWrite,
7142 &bytesWrittenAt);
7144 else
7146 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
7148 ULONG read;
7149 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7150 return STG_E_READFAULT;
7153 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
7154 bytesWrittenAt = bytesToWrite;
7155 cachedBlock->read = TRUE;
7156 cachedBlock->dirty = TRUE;
7159 blockNoInSequence++;
7160 bufferWalker += bytesWrittenAt;
7161 size -= bytesWrittenAt;
7162 *bytesWritten += bytesWrittenAt;
7163 offsetInBlock = 0; /* There is no offset on the next block */
7165 if (bytesWrittenAt != bytesToWrite)
7166 break;
7169 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7172 /******************************************************************************
7173 * BlockChainStream_Shrink
7175 * Shrinks this chain in the big block depot.
7177 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
7178 ULARGE_INTEGER newSize)
7180 ULONG blockIndex;
7181 ULONG numBlocks;
7182 int i;
7185 * Figure out how many blocks are needed to contain the new size
7187 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
7189 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
7190 numBlocks++;
7192 if (numBlocks)
7195 * Go to the new end of chain
7197 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
7199 /* Mark the new end of chain */
7200 StorageImpl_SetNextBlockInChain(
7201 This->parentStorage,
7202 blockIndex,
7203 BLOCK_END_OF_CHAIN);
7205 This->tailIndex = blockIndex;
7207 else
7209 if (This->headOfStreamPlaceHolder != 0)
7211 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
7213 else
7215 DirEntry chainEntry;
7216 assert(This->ownerDirEntry != DIRENTRY_NULL);
7218 StorageImpl_ReadDirEntry(
7219 This->parentStorage,
7220 This->ownerDirEntry,
7221 &chainEntry);
7223 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7225 StorageImpl_WriteDirEntry(
7226 This->parentStorage,
7227 This->ownerDirEntry,
7228 &chainEntry);
7231 This->tailIndex = BLOCK_END_OF_CHAIN;
7234 This->numBlocks = numBlocks;
7237 * Mark the extra blocks as free
7239 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
7241 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
7242 StorageImpl_FreeBigBlock(This->parentStorage,
7243 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
7244 if (last_run->lastOffset == last_run->firstOffset)
7245 This->indexCacheLen--;
7246 else
7247 last_run->lastOffset--;
7251 * Reset the last accessed block cache.
7253 for (i=0; i<2; i++)
7255 if (This->cachedBlocks[i].index >= numBlocks)
7257 This->cachedBlocks[i].index = 0xffffffff;
7258 This->cachedBlocks[i].dirty = FALSE;
7262 return TRUE;
7265 /******************************************************************************
7266 * BlockChainStream_Enlarge
7268 * Grows this chain in the big block depot.
7270 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
7271 ULARGE_INTEGER newSize)
7273 ULONG blockIndex, currentBlock;
7274 ULONG newNumBlocks;
7275 ULONG oldNumBlocks = 0;
7277 blockIndex = BlockChainStream_GetHeadOfChain(This);
7280 * Empty chain. Create the head.
7282 if (blockIndex == BLOCK_END_OF_CHAIN)
7284 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7285 StorageImpl_SetNextBlockInChain(This->parentStorage,
7286 blockIndex,
7287 BLOCK_END_OF_CHAIN);
7289 if (This->headOfStreamPlaceHolder != 0)
7291 *(This->headOfStreamPlaceHolder) = blockIndex;
7293 else
7295 DirEntry chainEntry;
7296 assert(This->ownerDirEntry != DIRENTRY_NULL);
7298 StorageImpl_ReadDirEntry(
7299 This->parentStorage,
7300 This->ownerDirEntry,
7301 &chainEntry);
7303 chainEntry.startingBlock = blockIndex;
7305 StorageImpl_WriteDirEntry(
7306 This->parentStorage,
7307 This->ownerDirEntry,
7308 &chainEntry);
7311 This->tailIndex = blockIndex;
7312 This->numBlocks = 1;
7316 * Figure out how many blocks are needed to contain this stream
7318 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
7320 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
7321 newNumBlocks++;
7324 * Go to the current end of chain
7326 if (This->tailIndex == BLOCK_END_OF_CHAIN)
7328 currentBlock = blockIndex;
7330 while (blockIndex != BLOCK_END_OF_CHAIN)
7332 This->numBlocks++;
7333 currentBlock = blockIndex;
7335 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
7336 &blockIndex)))
7337 return FALSE;
7340 This->tailIndex = currentBlock;
7343 currentBlock = This->tailIndex;
7344 oldNumBlocks = This->numBlocks;
7347 * Add new blocks to the chain
7349 if (oldNumBlocks < newNumBlocks)
7351 while (oldNumBlocks < newNumBlocks)
7353 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7355 StorageImpl_SetNextBlockInChain(
7356 This->parentStorage,
7357 currentBlock,
7358 blockIndex);
7360 StorageImpl_SetNextBlockInChain(
7361 This->parentStorage,
7362 blockIndex,
7363 BLOCK_END_OF_CHAIN);
7365 currentBlock = blockIndex;
7366 oldNumBlocks++;
7369 This->tailIndex = blockIndex;
7370 This->numBlocks = newNumBlocks;
7373 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
7374 return FALSE;
7376 return TRUE;
7379 /******************************************************************************
7380 * BlockChainStream_SetSize
7382 * Sets the size of this stream. The big block depot will be updated.
7383 * The file will grow if we grow the chain.
7385 * TODO: Free the actual blocks in the file when we shrink the chain.
7386 * Currently, the blocks are still in the file. So the file size
7387 * doesn't shrink even if we shrink streams.
7389 BOOL BlockChainStream_SetSize(
7390 BlockChainStream* This,
7391 ULARGE_INTEGER newSize)
7393 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
7395 if (newSize.u.LowPart == size.u.LowPart)
7396 return TRUE;
7398 if (newSize.u.LowPart < size.u.LowPart)
7400 BlockChainStream_Shrink(This, newSize);
7402 else
7404 BlockChainStream_Enlarge(This, newSize);
7407 return TRUE;
7410 /******************************************************************************
7411 * BlockChainStream_GetSize
7413 * Returns the size of this chain.
7414 * Will return the block count if this chain doesn't have a directory entry.
7416 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
7418 DirEntry chainEntry;
7420 if(This->headOfStreamPlaceHolder == NULL)
7423 * This chain has a directory entry so use the size value from there.
7425 StorageImpl_ReadDirEntry(
7426 This->parentStorage,
7427 This->ownerDirEntry,
7428 &chainEntry);
7430 return chainEntry.size;
7432 else
7435 * this chain is a chain that does not have a directory entry, figure out the
7436 * size by making the product number of used blocks times the
7437 * size of them
7439 ULARGE_INTEGER result;
7440 result.u.HighPart = 0;
7442 result.u.LowPart =
7443 BlockChainStream_GetCount(This) *
7444 This->parentStorage->bigBlockSize;
7446 return result;
7450 /******************************************************************************
7451 ** SmallBlockChainStream implementation
7454 SmallBlockChainStream* SmallBlockChainStream_Construct(
7455 StorageImpl* parentStorage,
7456 ULONG* headOfStreamPlaceHolder,
7457 DirRef dirEntry)
7459 SmallBlockChainStream* newStream;
7461 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
7463 newStream->parentStorage = parentStorage;
7464 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7465 newStream->ownerDirEntry = dirEntry;
7467 return newStream;
7470 void SmallBlockChainStream_Destroy(
7471 SmallBlockChainStream* This)
7473 HeapFree(GetProcessHeap(), 0, This);
7476 /******************************************************************************
7477 * SmallBlockChainStream_GetHeadOfChain
7479 * Returns the head of this chain of small blocks.
7481 static ULONG SmallBlockChainStream_GetHeadOfChain(
7482 SmallBlockChainStream* This)
7484 DirEntry chainEntry;
7485 HRESULT hr;
7487 if (This->headOfStreamPlaceHolder != NULL)
7488 return *(This->headOfStreamPlaceHolder);
7490 if (This->ownerDirEntry)
7492 hr = StorageImpl_ReadDirEntry(
7493 This->parentStorage,
7494 This->ownerDirEntry,
7495 &chainEntry);
7497 if (SUCCEEDED(hr))
7499 return chainEntry.startingBlock;
7504 return BLOCK_END_OF_CHAIN;
7507 /******************************************************************************
7508 * SmallBlockChainStream_GetNextBlockInChain
7510 * Returns the index of the next small block in this chain.
7512 * Return Values:
7513 * - BLOCK_END_OF_CHAIN: end of this chain
7514 * - BLOCK_UNUSED: small block 'blockIndex' is free
7516 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
7517 SmallBlockChainStream* This,
7518 ULONG blockIndex,
7519 ULONG* nextBlockInChain)
7521 ULARGE_INTEGER offsetOfBlockInDepot;
7522 DWORD buffer;
7523 ULONG bytesRead;
7524 HRESULT res;
7526 *nextBlockInChain = BLOCK_END_OF_CHAIN;
7528 offsetOfBlockInDepot.u.HighPart = 0;
7529 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
7532 * Read those bytes in the buffer from the small block file.
7534 res = BlockChainStream_ReadAt(
7535 This->parentStorage->smallBlockDepotChain,
7536 offsetOfBlockInDepot,
7537 sizeof(DWORD),
7538 &buffer,
7539 &bytesRead);
7541 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
7542 res = STG_E_READFAULT;
7544 if (SUCCEEDED(res))
7546 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
7547 return S_OK;
7550 return res;
7553 /******************************************************************************
7554 * SmallBlockChainStream_SetNextBlockInChain
7556 * Writes the index of the next block of the specified block in the small
7557 * block depot.
7558 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7559 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7561 static void SmallBlockChainStream_SetNextBlockInChain(
7562 SmallBlockChainStream* This,
7563 ULONG blockIndex,
7564 ULONG nextBlock)
7566 ULARGE_INTEGER offsetOfBlockInDepot;
7567 DWORD buffer;
7568 ULONG bytesWritten;
7570 offsetOfBlockInDepot.u.HighPart = 0;
7571 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
7573 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
7576 * Read those bytes in the buffer from the small block file.
7578 BlockChainStream_WriteAt(
7579 This->parentStorage->smallBlockDepotChain,
7580 offsetOfBlockInDepot,
7581 sizeof(DWORD),
7582 &buffer,
7583 &bytesWritten);
7586 /******************************************************************************
7587 * SmallBlockChainStream_FreeBlock
7589 * Flag small block 'blockIndex' as free in the small block depot.
7591 static void SmallBlockChainStream_FreeBlock(
7592 SmallBlockChainStream* This,
7593 ULONG blockIndex)
7595 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
7598 /******************************************************************************
7599 * SmallBlockChainStream_GetNextFreeBlock
7601 * Returns the index of a free small block. The small block depot will be
7602 * enlarged if necessary. The small block chain will also be enlarged if
7603 * necessary.
7605 static ULONG SmallBlockChainStream_GetNextFreeBlock(
7606 SmallBlockChainStream* This)
7608 ULARGE_INTEGER offsetOfBlockInDepot;
7609 DWORD buffer;
7610 ULONG bytesRead;
7611 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
7612 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
7613 HRESULT res = S_OK;
7614 ULONG smallBlocksPerBigBlock;
7615 DirEntry rootEntry;
7616 ULONG blocksRequired;
7617 ULARGE_INTEGER old_size, size_required;
7619 offsetOfBlockInDepot.u.HighPart = 0;
7622 * Scan the small block depot for a free block
7624 while (nextBlockIndex != BLOCK_UNUSED)
7626 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
7628 res = BlockChainStream_ReadAt(
7629 This->parentStorage->smallBlockDepotChain,
7630 offsetOfBlockInDepot,
7631 sizeof(DWORD),
7632 &buffer,
7633 &bytesRead);
7636 * If we run out of space for the small block depot, enlarge it
7638 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
7640 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
7642 if (nextBlockIndex != BLOCK_UNUSED)
7643 blockIndex++;
7645 else
7647 ULONG count =
7648 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
7650 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
7651 ULARGE_INTEGER newSize, offset;
7652 ULONG bytesWritten;
7654 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
7655 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
7658 * Initialize all the small blocks to free
7660 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
7661 offset.QuadPart = count * This->parentStorage->bigBlockSize;
7662 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
7663 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
7665 StorageImpl_SaveFileHeader(This->parentStorage);
7669 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
7671 smallBlocksPerBigBlock =
7672 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
7675 * Verify if we have to allocate big blocks to contain small blocks
7677 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
7679 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
7681 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
7683 if (size_required.QuadPart > old_size.QuadPart)
7685 BlockChainStream_SetSize(
7686 This->parentStorage->smallBlockRootChain,
7687 size_required);
7689 StorageImpl_ReadDirEntry(
7690 This->parentStorage,
7691 This->parentStorage->base.storageDirEntry,
7692 &rootEntry);
7694 rootEntry.size = size_required;
7696 StorageImpl_WriteDirEntry(
7697 This->parentStorage,
7698 This->parentStorage->base.storageDirEntry,
7699 &rootEntry);
7702 return blockIndex;
7705 /******************************************************************************
7706 * SmallBlockChainStream_ReadAt
7708 * Reads a specified number of bytes from this chain at the specified offset.
7709 * bytesRead may be NULL.
7710 * Failure will be returned if the specified number of bytes has not been read.
7712 HRESULT SmallBlockChainStream_ReadAt(
7713 SmallBlockChainStream* This,
7714 ULARGE_INTEGER offset,
7715 ULONG size,
7716 void* buffer,
7717 ULONG* bytesRead)
7719 HRESULT rc = S_OK;
7720 ULARGE_INTEGER offsetInBigBlockFile;
7721 ULONG blockNoInSequence =
7722 offset.u.LowPart / This->parentStorage->smallBlockSize;
7724 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7725 ULONG bytesToReadInBuffer;
7726 ULONG blockIndex;
7727 ULONG bytesReadFromBigBlockFile;
7728 BYTE* bufferWalker;
7729 ULARGE_INTEGER stream_size;
7732 * This should never happen on a small block file.
7734 assert(offset.u.HighPart==0);
7736 *bytesRead = 0;
7738 stream_size = SmallBlockChainStream_GetSize(This);
7739 if (stream_size.QuadPart > offset.QuadPart)
7740 size = min(stream_size.QuadPart - offset.QuadPart, size);
7741 else
7742 return S_OK;
7745 * Find the first block in the stream that contains part of the buffer.
7747 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7749 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7751 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7752 if(FAILED(rc))
7753 return rc;
7754 blockNoInSequence--;
7758 * Start reading the buffer.
7760 bufferWalker = buffer;
7762 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7765 * Calculate how many bytes we can copy from this small block.
7767 bytesToReadInBuffer =
7768 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7771 * Calculate the offset of the small block in the small block file.
7773 offsetInBigBlockFile.u.HighPart = 0;
7774 offsetInBigBlockFile.u.LowPart =
7775 blockIndex * This->parentStorage->smallBlockSize;
7777 offsetInBigBlockFile.u.LowPart += offsetInBlock;
7780 * Read those bytes in the buffer from the small block file.
7781 * The small block has already been identified so it shouldn't fail
7782 * unless the file is corrupt.
7784 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
7785 offsetInBigBlockFile,
7786 bytesToReadInBuffer,
7787 bufferWalker,
7788 &bytesReadFromBigBlockFile);
7790 if (FAILED(rc))
7791 return rc;
7793 if (!bytesReadFromBigBlockFile)
7794 return STG_E_DOCFILECORRUPT;
7797 * Step to the next big block.
7799 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7800 if(FAILED(rc))
7801 return STG_E_DOCFILECORRUPT;
7803 bufferWalker += bytesReadFromBigBlockFile;
7804 size -= bytesReadFromBigBlockFile;
7805 *bytesRead += bytesReadFromBigBlockFile;
7806 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
7809 return S_OK;
7812 /******************************************************************************
7813 * SmallBlockChainStream_WriteAt
7815 * Writes the specified number of bytes to this chain at the specified offset.
7816 * Will fail if not all specified number of bytes have been written.
7818 HRESULT SmallBlockChainStream_WriteAt(
7819 SmallBlockChainStream* This,
7820 ULARGE_INTEGER offset,
7821 ULONG size,
7822 const void* buffer,
7823 ULONG* bytesWritten)
7825 ULARGE_INTEGER offsetInBigBlockFile;
7826 ULONG blockNoInSequence =
7827 offset.u.LowPart / This->parentStorage->smallBlockSize;
7829 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7830 ULONG bytesToWriteInBuffer;
7831 ULONG blockIndex;
7832 ULONG bytesWrittenToBigBlockFile;
7833 const BYTE* bufferWalker;
7834 HRESULT res;
7837 * This should never happen on a small block file.
7839 assert(offset.u.HighPart==0);
7842 * Find the first block in the stream that contains part of the buffer.
7844 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7846 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7848 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
7849 return STG_E_DOCFILECORRUPT;
7850 blockNoInSequence--;
7854 * Start writing the buffer.
7856 *bytesWritten = 0;
7857 bufferWalker = buffer;
7858 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7861 * Calculate how many bytes we can copy to this small block.
7863 bytesToWriteInBuffer =
7864 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7867 * Calculate the offset of the small block in the small block file.
7869 offsetInBigBlockFile.u.HighPart = 0;
7870 offsetInBigBlockFile.u.LowPart =
7871 blockIndex * This->parentStorage->smallBlockSize;
7873 offsetInBigBlockFile.u.LowPart += offsetInBlock;
7876 * Write those bytes in the buffer to the small block file.
7878 res = BlockChainStream_WriteAt(
7879 This->parentStorage->smallBlockRootChain,
7880 offsetInBigBlockFile,
7881 bytesToWriteInBuffer,
7882 bufferWalker,
7883 &bytesWrittenToBigBlockFile);
7884 if (FAILED(res))
7885 return res;
7888 * Step to the next big block.
7890 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7891 &blockIndex)))
7892 return FALSE;
7893 bufferWalker += bytesWrittenToBigBlockFile;
7894 size -= bytesWrittenToBigBlockFile;
7895 *bytesWritten += bytesWrittenToBigBlockFile;
7896 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7899 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7902 /******************************************************************************
7903 * SmallBlockChainStream_Shrink
7905 * Shrinks this chain in the small block depot.
7907 static BOOL SmallBlockChainStream_Shrink(
7908 SmallBlockChainStream* This,
7909 ULARGE_INTEGER newSize)
7911 ULONG blockIndex, extraBlock;
7912 ULONG numBlocks;
7913 ULONG count = 0;
7915 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7917 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7918 numBlocks++;
7920 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7923 * Go to the new end of chain
7925 while (count < numBlocks)
7927 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7928 &blockIndex)))
7929 return FALSE;
7930 count++;
7934 * If the count is 0, we have a special case, the head of the chain was
7935 * just freed.
7937 if (count == 0)
7939 DirEntry chainEntry;
7941 StorageImpl_ReadDirEntry(This->parentStorage,
7942 This->ownerDirEntry,
7943 &chainEntry);
7945 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7947 StorageImpl_WriteDirEntry(This->parentStorage,
7948 This->ownerDirEntry,
7949 &chainEntry);
7952 * We start freeing the chain at the head block.
7954 extraBlock = blockIndex;
7956 else
7958 /* Get the next block before marking the new end */
7959 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7960 &extraBlock)))
7961 return FALSE;
7963 /* Mark the new end of chain */
7964 SmallBlockChainStream_SetNextBlockInChain(
7965 This,
7966 blockIndex,
7967 BLOCK_END_OF_CHAIN);
7971 * Mark the extra blocks as free
7973 while (extraBlock != BLOCK_END_OF_CHAIN)
7975 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7976 &blockIndex)))
7977 return FALSE;
7978 SmallBlockChainStream_FreeBlock(This, extraBlock);
7979 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7980 extraBlock = blockIndex;
7983 return TRUE;
7986 /******************************************************************************
7987 * SmallBlockChainStream_Enlarge
7989 * Grows this chain in the small block depot.
7991 static BOOL SmallBlockChainStream_Enlarge(
7992 SmallBlockChainStream* This,
7993 ULARGE_INTEGER newSize)
7995 ULONG blockIndex, currentBlock;
7996 ULONG newNumBlocks;
7997 ULONG oldNumBlocks = 0;
7999 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8002 * Empty chain. Create the head.
8004 if (blockIndex == BLOCK_END_OF_CHAIN)
8006 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8007 SmallBlockChainStream_SetNextBlockInChain(
8008 This,
8009 blockIndex,
8010 BLOCK_END_OF_CHAIN);
8012 if (This->headOfStreamPlaceHolder != NULL)
8014 *(This->headOfStreamPlaceHolder) = blockIndex;
8016 else
8018 DirEntry chainEntry;
8020 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
8021 &chainEntry);
8023 chainEntry.startingBlock = blockIndex;
8025 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
8026 &chainEntry);
8030 currentBlock = blockIndex;
8033 * Figure out how many blocks are needed to contain this stream
8035 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8037 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8038 newNumBlocks++;
8041 * Go to the current end of chain
8043 while (blockIndex != BLOCK_END_OF_CHAIN)
8045 oldNumBlocks++;
8046 currentBlock = blockIndex;
8047 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
8048 return FALSE;
8052 * Add new blocks to the chain
8054 while (oldNumBlocks < newNumBlocks)
8056 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8057 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
8059 SmallBlockChainStream_SetNextBlockInChain(
8060 This,
8061 blockIndex,
8062 BLOCK_END_OF_CHAIN);
8064 currentBlock = blockIndex;
8065 oldNumBlocks++;
8068 return TRUE;
8071 /******************************************************************************
8072 * SmallBlockChainStream_SetSize
8074 * Sets the size of this stream.
8075 * The file will grow if we grow the chain.
8077 * TODO: Free the actual blocks in the file when we shrink the chain.
8078 * Currently, the blocks are still in the file. So the file size
8079 * doesn't shrink even if we shrink streams.
8081 BOOL SmallBlockChainStream_SetSize(
8082 SmallBlockChainStream* This,
8083 ULARGE_INTEGER newSize)
8085 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
8087 if (newSize.u.LowPart == size.u.LowPart)
8088 return TRUE;
8090 if (newSize.u.LowPart < size.u.LowPart)
8092 SmallBlockChainStream_Shrink(This, newSize);
8094 else
8096 SmallBlockChainStream_Enlarge(This, newSize);
8099 return TRUE;
8102 /******************************************************************************
8103 * SmallBlockChainStream_GetCount
8105 * Returns the number of small blocks that comprises this chain.
8106 * This is not the size of the stream as the last block may not be full!
8109 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
8111 ULONG blockIndex;
8112 ULONG count = 0;
8114 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8116 while(blockIndex != BLOCK_END_OF_CHAIN)
8118 count++;
8120 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
8121 blockIndex, &blockIndex)))
8122 return 0;
8125 return count;
8128 /******************************************************************************
8129 * SmallBlockChainStream_GetSize
8131 * Returns the size of this chain.
8133 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
8135 DirEntry chainEntry;
8137 if(This->headOfStreamPlaceHolder != NULL)
8139 ULARGE_INTEGER result;
8140 result.u.HighPart = 0;
8142 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
8143 This->parentStorage->smallBlockSize;
8145 return result;
8148 StorageImpl_ReadDirEntry(
8149 This->parentStorage,
8150 This->ownerDirEntry,
8151 &chainEntry);
8153 return chainEntry.size;
8156 static HRESULT create_storagefile(
8157 LPCOLESTR pwcsName,
8158 DWORD grfMode,
8159 DWORD grfAttrs,
8160 STGOPTIONS* pStgOptions,
8161 REFIID riid,
8162 void** ppstgOpen)
8164 StorageBaseImpl* newStorage = 0;
8165 HANDLE hFile = INVALID_HANDLE_VALUE;
8166 HRESULT hr = STG_E_INVALIDFLAG;
8167 DWORD shareMode;
8168 DWORD accessMode;
8169 DWORD creationMode;
8170 DWORD fileAttributes;
8171 WCHAR tempFileName[MAX_PATH];
8173 if (ppstgOpen == 0)
8174 return STG_E_INVALIDPOINTER;
8176 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
8177 return STG_E_INVALIDPARAMETER;
8179 /* if no share mode given then DENY_NONE is the default */
8180 if (STGM_SHARE_MODE(grfMode) == 0)
8181 grfMode |= STGM_SHARE_DENY_NONE;
8183 if ( FAILED( validateSTGM(grfMode) ))
8184 goto end;
8186 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8187 switch(STGM_ACCESS_MODE(grfMode))
8189 case STGM_WRITE:
8190 case STGM_READWRITE:
8191 break;
8192 default:
8193 goto end;
8196 /* in direct mode, can only use SHARE_EXCLUSIVE */
8197 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
8198 goto end;
8200 /* but in transacted mode, any share mode is valid */
8203 * Generate a unique name.
8205 if (pwcsName == 0)
8207 WCHAR tempPath[MAX_PATH];
8208 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
8210 memset(tempPath, 0, sizeof(tempPath));
8211 memset(tempFileName, 0, sizeof(tempFileName));
8213 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
8214 tempPath[0] = '.';
8216 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
8217 pwcsName = tempFileName;
8218 else
8220 hr = STG_E_INSUFFICIENTMEMORY;
8221 goto end;
8224 creationMode = TRUNCATE_EXISTING;
8226 else
8228 creationMode = GetCreationModeFromSTGM(grfMode);
8232 * Interpret the STGM value grfMode
8234 shareMode = GetShareModeFromSTGM(grfMode);
8235 accessMode = GetAccessModeFromSTGM(grfMode);
8237 if (grfMode & STGM_DELETEONRELEASE)
8238 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
8239 else
8240 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
8242 *ppstgOpen = 0;
8244 hFile = CreateFileW(pwcsName,
8245 accessMode,
8246 shareMode,
8247 NULL,
8248 creationMode,
8249 fileAttributes,
8252 if (hFile == INVALID_HANDLE_VALUE)
8254 if(GetLastError() == ERROR_FILE_EXISTS)
8255 hr = STG_E_FILEALREADYEXISTS;
8256 else
8257 hr = E_FAIL;
8258 goto end;
8262 * Allocate and initialize the new IStorage32object.
8264 hr = Storage_Construct(
8265 hFile,
8266 pwcsName,
8267 NULL,
8268 grfMode,
8269 TRUE,
8270 TRUE,
8271 pStgOptions->ulSectorSize,
8272 &newStorage);
8274 if (FAILED(hr))
8276 goto end;
8279 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
8280 IStorage_Release(&newStorage->IStorage_iface);
8282 end:
8283 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
8285 return hr;
8288 /******************************************************************************
8289 * StgCreateDocfile [OLE32.@]
8290 * Creates a new compound file storage object
8292 * PARAMS
8293 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8294 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8295 * reserved [ ?] unused?, usually 0
8296 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8298 * RETURNS
8299 * S_OK if the file was successfully created
8300 * some STG_E_ value if error
8301 * NOTES
8302 * if pwcsName is NULL, create file with new unique name
8303 * the function can returns
8304 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8305 * (unrealized now)
8307 HRESULT WINAPI StgCreateDocfile(
8308 LPCOLESTR pwcsName,
8309 DWORD grfMode,
8310 DWORD reserved,
8311 IStorage **ppstgOpen)
8313 STGOPTIONS stgoptions = {1, 0, 512};
8315 TRACE("(%s, %x, %d, %p)\n",
8316 debugstr_w(pwcsName), grfMode,
8317 reserved, ppstgOpen);
8319 if (ppstgOpen == 0)
8320 return STG_E_INVALIDPOINTER;
8321 if (reserved != 0)
8322 return STG_E_INVALIDPARAMETER;
8324 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
8327 /******************************************************************************
8328 * StgCreateStorageEx [OLE32.@]
8330 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8332 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8333 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8335 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
8337 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8338 return STG_E_INVALIDPARAMETER;
8341 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
8343 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8344 return STG_E_INVALIDPARAMETER;
8347 if (stgfmt == STGFMT_FILE)
8349 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8350 return STG_E_INVALIDPARAMETER;
8353 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
8355 STGOPTIONS defaultOptions = {1, 0, 512};
8357 if (!pStgOptions) pStgOptions = &defaultOptions;
8358 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
8362 ERR("Invalid stgfmt argument\n");
8363 return STG_E_INVALIDPARAMETER;
8366 /******************************************************************************
8367 * StgCreatePropSetStg [OLE32.@]
8369 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
8370 IPropertySetStorage **propset)
8372 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
8373 if (reserved)
8374 return STG_E_INVALIDPARAMETER;
8376 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
8379 /******************************************************************************
8380 * StgOpenStorageEx [OLE32.@]
8382 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8384 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8385 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8387 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
8389 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8390 return STG_E_INVALIDPARAMETER;
8393 switch (stgfmt)
8395 case STGFMT_FILE:
8396 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8397 return STG_E_INVALIDPARAMETER;
8399 case STGFMT_STORAGE:
8400 break;
8402 case STGFMT_DOCFILE:
8403 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
8405 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8406 return STG_E_INVALIDPARAMETER;
8408 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8409 break;
8411 case STGFMT_ANY:
8412 WARN("STGFMT_ANY assuming storage\n");
8413 break;
8415 default:
8416 return STG_E_INVALIDPARAMETER;
8419 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
8423 /******************************************************************************
8424 * StgOpenStorage [OLE32.@]
8426 HRESULT WINAPI StgOpenStorage(
8427 const OLECHAR *pwcsName,
8428 IStorage *pstgPriority,
8429 DWORD grfMode,
8430 SNB snbExclude,
8431 DWORD reserved,
8432 IStorage **ppstgOpen)
8434 StorageBaseImpl* newStorage = 0;
8435 HRESULT hr = S_OK;
8436 HANDLE hFile = 0;
8437 DWORD shareMode;
8438 DWORD accessMode;
8439 LPWSTR temp_name = NULL;
8441 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8442 debugstr_w(pwcsName), pstgPriority, grfMode,
8443 snbExclude, reserved, ppstgOpen);
8445 if (pstgPriority)
8447 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8448 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
8449 if (FAILED(hr)) goto end;
8450 pwcsName = temp_name;
8451 TRACE("using filename %s\n", debugstr_w(temp_name));
8454 if (pwcsName == 0)
8456 hr = STG_E_INVALIDNAME;
8457 goto end;
8460 if (ppstgOpen == 0)
8462 hr = STG_E_INVALIDPOINTER;
8463 goto end;
8466 if (reserved)
8468 hr = STG_E_INVALIDPARAMETER;
8469 goto end;
8472 if (grfMode & STGM_PRIORITY)
8474 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
8475 return STG_E_INVALIDFLAG;
8476 if (grfMode & STGM_DELETEONRELEASE)
8477 return STG_E_INVALIDFUNCTION;
8478 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
8479 return STG_E_INVALIDFLAG;
8480 grfMode &= ~0xf0; /* remove the existing sharing mode */
8481 grfMode |= STGM_SHARE_DENY_NONE;
8485 * Validate the sharing mode
8487 if (grfMode & STGM_DIRECT_SWMR)
8489 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
8490 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
8492 hr = STG_E_INVALIDFLAG;
8493 goto end;
8496 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
8497 switch(STGM_SHARE_MODE(grfMode))
8499 case STGM_SHARE_EXCLUSIVE:
8500 case STGM_SHARE_DENY_WRITE:
8501 break;
8502 default:
8503 hr = STG_E_INVALIDFLAG;
8504 goto end;
8507 if ( FAILED( validateSTGM(grfMode) ) ||
8508 (grfMode&STGM_CREATE))
8510 hr = STG_E_INVALIDFLAG;
8511 goto end;
8514 /* shared reading requires transacted or single writer mode */
8515 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
8516 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
8517 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
8519 hr = STG_E_INVALIDFLAG;
8520 goto end;
8524 * Interpret the STGM value grfMode
8526 shareMode = GetShareModeFromSTGM(grfMode);
8527 accessMode = GetAccessModeFromSTGM(grfMode);
8529 *ppstgOpen = 0;
8531 hFile = CreateFileW( pwcsName,
8532 accessMode,
8533 shareMode,
8534 NULL,
8535 OPEN_EXISTING,
8536 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
8539 if (hFile==INVALID_HANDLE_VALUE)
8541 DWORD last_error = GetLastError();
8543 hr = E_FAIL;
8545 switch (last_error)
8547 case ERROR_FILE_NOT_FOUND:
8548 hr = STG_E_FILENOTFOUND;
8549 break;
8551 case ERROR_PATH_NOT_FOUND:
8552 hr = STG_E_PATHNOTFOUND;
8553 break;
8555 case ERROR_ACCESS_DENIED:
8556 case ERROR_WRITE_PROTECT:
8557 hr = STG_E_ACCESSDENIED;
8558 break;
8560 case ERROR_SHARING_VIOLATION:
8561 hr = STG_E_SHAREVIOLATION;
8562 break;
8564 default:
8565 hr = E_FAIL;
8568 goto end;
8572 * Refuse to open the file if it's too small to be a structured storage file
8573 * FIXME: verify the file when reading instead of here
8575 if (GetFileSize(hFile, NULL) < 0x100)
8577 CloseHandle(hFile);
8578 hr = STG_E_FILEALREADYEXISTS;
8579 goto end;
8583 * Allocate and initialize the new IStorage32object.
8585 hr = Storage_Construct(
8586 hFile,
8587 pwcsName,
8588 NULL,
8589 grfMode,
8590 TRUE,
8591 FALSE,
8592 512,
8593 &newStorage);
8595 if (FAILED(hr))
8598 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8600 if(hr == STG_E_INVALIDHEADER)
8601 hr = STG_E_FILEALREADYEXISTS;
8602 goto end;
8605 *ppstgOpen = &newStorage->IStorage_iface;
8607 end:
8608 CoTaskMemFree(temp_name);
8609 if (pstgPriority) IStorage_Release(pstgPriority);
8610 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
8611 return hr;
8614 /******************************************************************************
8615 * StgCreateDocfileOnILockBytes [OLE32.@]
8617 HRESULT WINAPI StgCreateDocfileOnILockBytes(
8618 ILockBytes *plkbyt,
8619 DWORD grfMode,
8620 DWORD reserved,
8621 IStorage** ppstgOpen)
8623 StorageBaseImpl* newStorage = 0;
8624 HRESULT hr = S_OK;
8626 if ((ppstgOpen == 0) || (plkbyt == 0))
8627 return STG_E_INVALIDPOINTER;
8630 * Allocate and initialize the new IStorage object.
8632 hr = Storage_Construct(
8635 plkbyt,
8636 grfMode,
8637 FALSE,
8638 TRUE,
8639 512,
8640 &newStorage);
8642 if (FAILED(hr))
8644 return hr;
8647 *ppstgOpen = &newStorage->IStorage_iface;
8649 return hr;
8652 /******************************************************************************
8653 * StgOpenStorageOnILockBytes [OLE32.@]
8655 HRESULT WINAPI StgOpenStorageOnILockBytes(
8656 ILockBytes *plkbyt,
8657 IStorage *pstgPriority,
8658 DWORD grfMode,
8659 SNB snbExclude,
8660 DWORD reserved,
8661 IStorage **ppstgOpen)
8663 StorageBaseImpl* newStorage = 0;
8664 HRESULT hr = S_OK;
8666 if ((plkbyt == 0) || (ppstgOpen == 0))
8667 return STG_E_INVALIDPOINTER;
8669 if ( FAILED( validateSTGM(grfMode) ))
8670 return STG_E_INVALIDFLAG;
8672 *ppstgOpen = 0;
8675 * Allocate and initialize the new IStorage object.
8677 hr = Storage_Construct(
8680 plkbyt,
8681 grfMode,
8682 FALSE,
8683 FALSE,
8684 512,
8685 &newStorage);
8687 if (FAILED(hr))
8689 return hr;
8692 *ppstgOpen = &newStorage->IStorage_iface;
8694 return hr;
8697 /******************************************************************************
8698 * StgSetTimes [ole32.@]
8699 * StgSetTimes [OLE32.@]
8703 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
8704 FILETIME const *patime, FILETIME const *pmtime)
8706 IStorage *stg = NULL;
8707 HRESULT r;
8709 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
8711 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
8712 0, 0, &stg);
8713 if( SUCCEEDED(r) )
8715 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
8716 IStorage_Release(stg);
8719 return r;
8722 /******************************************************************************
8723 * StgIsStorageILockBytes [OLE32.@]
8725 * Determines if the ILockBytes contains a storage object.
8727 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
8729 BYTE sig[sizeof(STORAGE_magic)];
8730 ULARGE_INTEGER offset;
8731 ULONG read = 0;
8733 offset.u.HighPart = 0;
8734 offset.u.LowPart = 0;
8736 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
8738 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
8739 return S_OK;
8741 return S_FALSE;
8744 /******************************************************************************
8745 * WriteClassStg [OLE32.@]
8747 * This method will store the specified CLSID in the specified storage object
8749 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
8751 if(!pStg)
8752 return E_INVALIDARG;
8754 if(!rclsid)
8755 return STG_E_INVALIDPOINTER;
8757 return IStorage_SetClass(pStg, rclsid);
8760 /***********************************************************************
8761 * ReadClassStg (OLE32.@)
8763 * This method reads the CLSID previously written to a storage object with
8764 * the WriteClassStg.
8766 * PARAMS
8767 * pstg [I] IStorage pointer
8768 * pclsid [O] Pointer to where the CLSID is written
8770 * RETURNS
8771 * Success: S_OK.
8772 * Failure: HRESULT code.
8774 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
8776 STATSTG pstatstg;
8777 HRESULT hRes;
8779 TRACE("(%p, %p)\n", pstg, pclsid);
8781 if(!pstg || !pclsid)
8782 return E_INVALIDARG;
8785 * read a STATSTG structure (contains the clsid) from the storage
8787 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
8789 if(SUCCEEDED(hRes))
8790 *pclsid=pstatstg.clsid;
8792 return hRes;
8795 /***********************************************************************
8796 * OleLoadFromStream (OLE32.@)
8798 * This function loads an object from stream
8800 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
8802 CLSID clsid;
8803 HRESULT res;
8804 LPPERSISTSTREAM xstm;
8806 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
8808 res=ReadClassStm(pStm,&clsid);
8809 if (FAILED(res))
8810 return res;
8811 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
8812 if (FAILED(res))
8813 return res;
8814 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
8815 if (FAILED(res)) {
8816 IUnknown_Release((IUnknown*)*ppvObj);
8817 return res;
8819 res=IPersistStream_Load(xstm,pStm);
8820 IPersistStream_Release(xstm);
8821 /* FIXME: all refcounts ok at this point? I think they should be:
8822 * pStm : unchanged
8823 * ppvObj : 1
8824 * xstm : 0 (released)
8826 return res;
8829 /***********************************************************************
8830 * OleSaveToStream (OLE32.@)
8832 * This function saves an object with the IPersistStream interface on it
8833 * to the specified stream.
8835 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
8838 CLSID clsid;
8839 HRESULT res;
8841 TRACE("(%p,%p)\n",pPStm,pStm);
8843 res=IPersistStream_GetClassID(pPStm,&clsid);
8845 if (SUCCEEDED(res)){
8847 res=WriteClassStm(pStm,&clsid);
8849 if (SUCCEEDED(res))
8851 res=IPersistStream_Save(pPStm,pStm,TRUE);
8854 TRACE("Finished Save\n");
8855 return res;
8858 /****************************************************************************
8859 * This method validate a STGM parameter that can contain the values below
8861 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8862 * The stgm values contained in 0xffff0000 are bitmasks.
8864 * STGM_DIRECT 0x00000000
8865 * STGM_TRANSACTED 0x00010000
8866 * STGM_SIMPLE 0x08000000
8868 * STGM_READ 0x00000000
8869 * STGM_WRITE 0x00000001
8870 * STGM_READWRITE 0x00000002
8872 * STGM_SHARE_DENY_NONE 0x00000040
8873 * STGM_SHARE_DENY_READ 0x00000030
8874 * STGM_SHARE_DENY_WRITE 0x00000020
8875 * STGM_SHARE_EXCLUSIVE 0x00000010
8877 * STGM_PRIORITY 0x00040000
8878 * STGM_DELETEONRELEASE 0x04000000
8880 * STGM_CREATE 0x00001000
8881 * STGM_CONVERT 0x00020000
8882 * STGM_FAILIFTHERE 0x00000000
8884 * STGM_NOSCRATCH 0x00100000
8885 * STGM_NOSNAPSHOT 0x00200000
8887 static HRESULT validateSTGM(DWORD stgm)
8889 DWORD access = STGM_ACCESS_MODE(stgm);
8890 DWORD share = STGM_SHARE_MODE(stgm);
8891 DWORD create = STGM_CREATE_MODE(stgm);
8893 if (stgm&~STGM_KNOWN_FLAGS)
8895 ERR("unknown flags %08x\n", stgm);
8896 return E_FAIL;
8899 switch (access)
8901 case STGM_READ:
8902 case STGM_WRITE:
8903 case STGM_READWRITE:
8904 break;
8905 default:
8906 return E_FAIL;
8909 switch (share)
8911 case STGM_SHARE_DENY_NONE:
8912 case STGM_SHARE_DENY_READ:
8913 case STGM_SHARE_DENY_WRITE:
8914 case STGM_SHARE_EXCLUSIVE:
8915 break;
8916 case 0:
8917 if (!(stgm & STGM_TRANSACTED))
8918 return E_FAIL;
8919 break;
8920 default:
8921 return E_FAIL;
8924 switch (create)
8926 case STGM_CREATE:
8927 case STGM_FAILIFTHERE:
8928 break;
8929 default:
8930 return E_FAIL;
8934 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8936 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8937 return E_FAIL;
8940 * STGM_CREATE | STGM_CONVERT
8941 * if both are false, STGM_FAILIFTHERE is set to TRUE
8943 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8944 return E_FAIL;
8947 * STGM_NOSCRATCH requires STGM_TRANSACTED
8949 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8950 return E_FAIL;
8953 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8954 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8956 if ( (stgm & STGM_NOSNAPSHOT) &&
8957 (!(stgm & STGM_TRANSACTED) ||
8958 share == STGM_SHARE_EXCLUSIVE ||
8959 share == STGM_SHARE_DENY_WRITE) )
8960 return E_FAIL;
8962 return S_OK;
8965 /****************************************************************************
8966 * GetShareModeFromSTGM
8968 * This method will return a share mode flag from a STGM value.
8969 * The STGM value is assumed valid.
8971 static DWORD GetShareModeFromSTGM(DWORD stgm)
8973 switch (STGM_SHARE_MODE(stgm))
8975 case 0:
8976 assert(stgm & STGM_TRANSACTED);
8977 /* fall-through */
8978 case STGM_SHARE_DENY_NONE:
8979 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8980 case STGM_SHARE_DENY_READ:
8981 return FILE_SHARE_WRITE;
8982 case STGM_SHARE_DENY_WRITE:
8983 case STGM_SHARE_EXCLUSIVE:
8984 return FILE_SHARE_READ;
8986 ERR("Invalid share mode!\n");
8987 assert(0);
8988 return 0;
8991 /****************************************************************************
8992 * GetAccessModeFromSTGM
8994 * This method will return an access mode flag from a STGM value.
8995 * The STGM value is assumed valid.
8997 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8999 switch (STGM_ACCESS_MODE(stgm))
9001 case STGM_READ:
9002 return GENERIC_READ;
9003 case STGM_WRITE:
9004 case STGM_READWRITE:
9005 return GENERIC_READ | GENERIC_WRITE;
9007 ERR("Invalid access mode!\n");
9008 assert(0);
9009 return 0;
9012 /****************************************************************************
9013 * GetCreationModeFromSTGM
9015 * This method will return a creation mode flag from a STGM value.
9016 * The STGM value is assumed valid.
9018 static DWORD GetCreationModeFromSTGM(DWORD stgm)
9020 switch(STGM_CREATE_MODE(stgm))
9022 case STGM_CREATE:
9023 return CREATE_ALWAYS;
9024 case STGM_CONVERT:
9025 FIXME("STGM_CONVERT not implemented!\n");
9026 return CREATE_NEW;
9027 case STGM_FAILIFTHERE:
9028 return CREATE_NEW;
9030 ERR("Invalid create mode!\n");
9031 assert(0);
9032 return 0;
9036 /*************************************************************************
9037 * OLECONVERT_LoadOLE10 [Internal]
9039 * Loads the OLE10 STREAM to memory
9041 * PARAMS
9042 * pOleStream [I] The OLESTREAM
9043 * pData [I] Data Structure for the OLESTREAM Data
9045 * RETURNS
9046 * Success: S_OK
9047 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9048 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9050 * NOTES
9051 * This function is used by OleConvertOLESTREAMToIStorage only.
9053 * Memory allocated for pData must be freed by the caller
9055 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
9057 DWORD dwSize;
9058 HRESULT hRes = S_OK;
9059 int nTryCnt=0;
9060 int max_try = 6;
9062 pData->pData = NULL;
9063 pData->pstrOleObjFileName = NULL;
9065 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
9067 /* Get the OleID */
9068 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9069 if(dwSize != sizeof(pData->dwOleID))
9071 hRes = CONVERT10_E_OLESTREAM_GET;
9073 else if(pData->dwOleID != OLESTREAM_ID)
9075 hRes = CONVERT10_E_OLESTREAM_FMT;
9077 else
9079 hRes = S_OK;
9080 break;
9084 if(hRes == S_OK)
9086 /* Get the TypeID... more info needed for this field */
9087 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9088 if(dwSize != sizeof(pData->dwTypeID))
9090 hRes = CONVERT10_E_OLESTREAM_GET;
9093 if(hRes == S_OK)
9095 if(pData->dwTypeID != 0)
9097 /* Get the length of the OleTypeName */
9098 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9099 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9101 hRes = CONVERT10_E_OLESTREAM_GET;
9104 if(hRes == S_OK)
9106 if(pData->dwOleTypeNameLength > 0)
9108 /* Get the OleTypeName */
9109 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9110 if(dwSize != pData->dwOleTypeNameLength)
9112 hRes = CONVERT10_E_OLESTREAM_GET;
9116 if(bStrem1)
9118 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
9119 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
9121 hRes = CONVERT10_E_OLESTREAM_GET;
9123 if(hRes == S_OK)
9125 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
9126 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
9127 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
9128 if(pData->pstrOleObjFileName)
9130 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
9131 if(dwSize != pData->dwOleObjFileNameLength)
9133 hRes = CONVERT10_E_OLESTREAM_GET;
9136 else
9137 hRes = CONVERT10_E_OLESTREAM_GET;
9140 else
9142 /* Get the Width of the Metafile */
9143 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9144 if(dwSize != sizeof(pData->dwMetaFileWidth))
9146 hRes = CONVERT10_E_OLESTREAM_GET;
9148 if(hRes == S_OK)
9150 /* Get the Height of the Metafile */
9151 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9152 if(dwSize != sizeof(pData->dwMetaFileHeight))
9154 hRes = CONVERT10_E_OLESTREAM_GET;
9158 if(hRes == S_OK)
9160 /* Get the Length of the Data */
9161 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9162 if(dwSize != sizeof(pData->dwDataLength))
9164 hRes = CONVERT10_E_OLESTREAM_GET;
9168 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
9170 if(!bStrem1) /* if it is a second OLE stream data */
9172 pData->dwDataLength -= 8;
9173 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
9174 if(dwSize != sizeof(pData->strUnknown))
9176 hRes = CONVERT10_E_OLESTREAM_GET;
9180 if(hRes == S_OK)
9182 if(pData->dwDataLength > 0)
9184 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
9186 /* Get Data (ex. IStorage, Metafile, or BMP) */
9187 if(pData->pData)
9189 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
9190 if(dwSize != pData->dwDataLength)
9192 hRes = CONVERT10_E_OLESTREAM_GET;
9195 else
9197 hRes = CONVERT10_E_OLESTREAM_GET;
9203 return hRes;
9206 /*************************************************************************
9207 * OLECONVERT_SaveOLE10 [Internal]
9209 * Saves the OLE10 STREAM From memory
9211 * PARAMS
9212 * pData [I] Data Structure for the OLESTREAM Data
9213 * pOleStream [I] The OLESTREAM to save
9215 * RETURNS
9216 * Success: S_OK
9217 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9219 * NOTES
9220 * This function is used by OleConvertIStorageToOLESTREAM only.
9223 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
9225 DWORD dwSize;
9226 HRESULT hRes = S_OK;
9229 /* Set the OleID */
9230 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9231 if(dwSize != sizeof(pData->dwOleID))
9233 hRes = CONVERT10_E_OLESTREAM_PUT;
9236 if(hRes == S_OK)
9238 /* Set the TypeID */
9239 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9240 if(dwSize != sizeof(pData->dwTypeID))
9242 hRes = CONVERT10_E_OLESTREAM_PUT;
9246 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
9248 /* Set the Length of the OleTypeName */
9249 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9250 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9252 hRes = CONVERT10_E_OLESTREAM_PUT;
9255 if(hRes == S_OK)
9257 if(pData->dwOleTypeNameLength > 0)
9259 /* Set the OleTypeName */
9260 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9261 if(dwSize != pData->dwOleTypeNameLength)
9263 hRes = CONVERT10_E_OLESTREAM_PUT;
9268 if(hRes == S_OK)
9270 /* Set the width of the Metafile */
9271 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9272 if(dwSize != sizeof(pData->dwMetaFileWidth))
9274 hRes = CONVERT10_E_OLESTREAM_PUT;
9278 if(hRes == S_OK)
9280 /* Set the height of the Metafile */
9281 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9282 if(dwSize != sizeof(pData->dwMetaFileHeight))
9284 hRes = CONVERT10_E_OLESTREAM_PUT;
9288 if(hRes == S_OK)
9290 /* Set the length of the Data */
9291 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9292 if(dwSize != sizeof(pData->dwDataLength))
9294 hRes = CONVERT10_E_OLESTREAM_PUT;
9298 if(hRes == S_OK)
9300 if(pData->dwDataLength > 0)
9302 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9303 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
9304 if(dwSize != pData->dwDataLength)
9306 hRes = CONVERT10_E_OLESTREAM_PUT;
9311 return hRes;
9314 /*************************************************************************
9315 * OLECONVERT_GetOLE20FromOLE10[Internal]
9317 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9318 * opens it, and copies the content to the dest IStorage for
9319 * OleConvertOLESTREAMToIStorage
9322 * PARAMS
9323 * pDestStorage [I] The IStorage to copy the data to
9324 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9325 * nBufferLength [I] The size of the buffer
9327 * RETURNS
9328 * Nothing
9330 * NOTES
9334 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
9336 HRESULT hRes;
9337 HANDLE hFile;
9338 IStorage *pTempStorage;
9339 DWORD dwNumOfBytesWritten;
9340 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9341 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9343 /* Create a temp File */
9344 GetTempPathW(MAX_PATH, wstrTempDir);
9345 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9346 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
9348 if(hFile != INVALID_HANDLE_VALUE)
9350 /* Write IStorage Data to File */
9351 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
9352 CloseHandle(hFile);
9354 /* Open and copy temp storage to the Dest Storage */
9355 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
9356 if(hRes == S_OK)
9358 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
9359 IStorage_Release(pTempStorage);
9361 DeleteFileW(wstrTempFile);
9366 /*************************************************************************
9367 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9369 * Saves the OLE10 STREAM From memory
9371 * PARAMS
9372 * pStorage [I] The Src IStorage to copy
9373 * pData [I] The Dest Memory to write to.
9375 * RETURNS
9376 * The size in bytes allocated for pData
9378 * NOTES
9379 * Memory allocated for pData must be freed by the caller
9381 * Used by OleConvertIStorageToOLESTREAM only.
9384 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
9386 HANDLE hFile;
9387 HRESULT hRes;
9388 DWORD nDataLength = 0;
9389 IStorage *pTempStorage;
9390 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9391 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9393 *pData = NULL;
9395 /* Create temp Storage */
9396 GetTempPathW(MAX_PATH, wstrTempDir);
9397 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9398 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
9400 if(hRes == S_OK)
9402 /* Copy Src Storage to the Temp Storage */
9403 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
9404 IStorage_Release(pTempStorage);
9406 /* Open Temp Storage as a file and copy to memory */
9407 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9408 if(hFile != INVALID_HANDLE_VALUE)
9410 nDataLength = GetFileSize(hFile, NULL);
9411 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
9412 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
9413 CloseHandle(hFile);
9415 DeleteFileW(wstrTempFile);
9417 return nDataLength;
9420 /*************************************************************************
9421 * STORAGE_CreateOleStream [Internal]
9423 * Creates the "\001OLE" stream in the IStorage if necessary.
9425 * PARAMS
9426 * storage [I] Dest storage to create the stream in
9427 * flags [I] flags to be set for newly created stream
9429 * RETURNS
9430 * HRESULT return value
9432 * NOTES
9434 * This stream is still unknown, MS Word seems to have extra data
9435 * but since the data is stored in the OLESTREAM there should be
9436 * no need to recreate the stream. If the stream is manually
9437 * deleted it will create it with this default data.
9440 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
9442 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9443 static const DWORD version_magic = 0x02000001;
9444 IStream *stream;
9445 HRESULT hr;
9447 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
9448 if (hr == S_OK)
9450 struct empty_1ole_stream {
9451 DWORD version_magic;
9452 DWORD flags;
9453 DWORD update_options;
9454 DWORD reserved;
9455 DWORD mon_stream_size;
9457 struct empty_1ole_stream stream_data;
9459 stream_data.version_magic = version_magic;
9460 stream_data.flags = flags;
9461 stream_data.update_options = 0;
9462 stream_data.reserved = 0;
9463 stream_data.mon_stream_size = 0;
9465 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
9466 IStream_Release(stream);
9469 return hr;
9472 /* write a string to a stream, preceded by its length */
9473 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
9475 HRESULT r;
9476 LPSTR str;
9477 DWORD len = 0;
9479 if( string )
9480 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
9481 r = IStream_Write( stm, &len, sizeof(len), NULL);
9482 if( FAILED( r ) )
9483 return r;
9484 if(len == 0)
9485 return r;
9486 str = CoTaskMemAlloc( len );
9487 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
9488 r = IStream_Write( stm, str, len, NULL);
9489 CoTaskMemFree( str );
9490 return r;
9493 /* read a string preceded by its length from a stream */
9494 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
9496 HRESULT r;
9497 DWORD len, count = 0;
9498 LPSTR str;
9499 LPWSTR wstr;
9501 r = IStream_Read( stm, &len, sizeof(len), &count );
9502 if( FAILED( r ) )
9503 return r;
9504 if( count != sizeof(len) )
9505 return E_OUTOFMEMORY;
9507 TRACE("%d bytes\n",len);
9509 str = CoTaskMemAlloc( len );
9510 if( !str )
9511 return E_OUTOFMEMORY;
9512 count = 0;
9513 r = IStream_Read( stm, str, len, &count );
9514 if( FAILED( r ) )
9515 return r;
9516 if( count != len )
9518 CoTaskMemFree( str );
9519 return E_OUTOFMEMORY;
9522 TRACE("Read string %s\n",debugstr_an(str,len));
9524 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
9525 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
9526 if( wstr )
9528 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
9529 wstr[len] = 0;
9531 CoTaskMemFree( str );
9533 *string = wstr;
9535 return r;
9539 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
9540 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
9542 IStream *pstm;
9543 HRESULT r = S_OK;
9544 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9546 static const BYTE unknown1[12] =
9547 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9548 0xFF, 0xFF, 0xFF, 0xFF};
9549 static const BYTE unknown2[16] =
9550 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9551 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9553 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
9554 debugstr_w(lpszUserType), debugstr_w(szClipName),
9555 debugstr_w(szProgIDName));
9557 /* Create a CompObj stream */
9558 r = IStorage_CreateStream(pstg, szwStreamName,
9559 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
9560 if( FAILED (r) )
9561 return r;
9563 /* Write CompObj Structure to stream */
9564 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
9566 if( SUCCEEDED( r ) )
9567 r = WriteClassStm( pstm, clsid );
9569 if( SUCCEEDED( r ) )
9570 r = STREAM_WriteString( pstm, lpszUserType );
9571 if( SUCCEEDED( r ) )
9572 r = STREAM_WriteString( pstm, szClipName );
9573 if( SUCCEEDED( r ) )
9574 r = STREAM_WriteString( pstm, szProgIDName );
9575 if( SUCCEEDED( r ) )
9576 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
9578 IStream_Release( pstm );
9580 return r;
9583 /***********************************************************************
9584 * WriteFmtUserTypeStg (OLE32.@)
9586 HRESULT WINAPI WriteFmtUserTypeStg(
9587 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
9589 STATSTG stat;
9590 HRESULT r;
9591 WCHAR szwClipName[0x40];
9592 CLSID clsid;
9593 LPWSTR wstrProgID = NULL;
9594 DWORD n;
9596 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
9598 /* get the clipboard format name */
9599 if( cf )
9601 n = GetClipboardFormatNameW( cf, szwClipName,
9602 sizeof(szwClipName)/sizeof(szwClipName[0]) );
9603 szwClipName[n]=0;
9606 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
9608 r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
9609 if(SUCCEEDED(r))
9610 clsid = stat.clsid;
9611 else
9612 clsid = CLSID_NULL;
9614 ProgIDFromCLSID(&clsid, &wstrProgID);
9616 TRACE("progid is %s\n",debugstr_w(wstrProgID));
9618 r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
9619 cf ? szwClipName : NULL, wstrProgID );
9621 CoTaskMemFree(wstrProgID);
9623 return r;
9627 /******************************************************************************
9628 * ReadFmtUserTypeStg [OLE32.@]
9630 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
9632 HRESULT r;
9633 IStream *stm = 0;
9634 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
9635 unsigned char unknown1[12];
9636 unsigned char unknown2[16];
9637 DWORD count;
9638 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
9639 CLSID clsid;
9641 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
9643 r = IStorage_OpenStream( pstg, szCompObj, NULL,
9644 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
9645 if( FAILED ( r ) )
9647 WARN("Failed to open stream r = %08x\n", r);
9648 return r;
9651 /* read the various parts of the structure */
9652 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
9653 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
9654 goto end;
9655 r = ReadClassStm( stm, &clsid );
9656 if( FAILED( r ) )
9657 goto end;
9659 r = STREAM_ReadString( stm, &szCLSIDName );
9660 if( FAILED( r ) )
9661 goto end;
9663 r = STREAM_ReadString( stm, &szOleTypeName );
9664 if( FAILED( r ) )
9665 goto end;
9667 r = STREAM_ReadString( stm, &szProgIDName );
9668 if( FAILED( r ) )
9669 goto end;
9671 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
9672 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
9673 goto end;
9675 /* ok, success... now we just need to store what we found */
9676 if( pcf )
9677 *pcf = RegisterClipboardFormatW( szOleTypeName );
9679 if( lplpszUserType )
9681 *lplpszUserType = szCLSIDName;
9682 szCLSIDName = NULL;
9685 end:
9686 CoTaskMemFree( szCLSIDName );
9687 CoTaskMemFree( szOleTypeName );
9688 CoTaskMemFree( szProgIDName );
9689 IStream_Release( stm );
9691 return r;
9695 /*************************************************************************
9696 * OLECONVERT_CreateCompObjStream [Internal]
9698 * Creates a "\001CompObj" is the destination IStorage if necessary.
9700 * PARAMS
9701 * pStorage [I] The dest IStorage to create the CompObj Stream
9702 * if necessary.
9703 * strOleTypeName [I] The ProgID
9705 * RETURNS
9706 * Success: S_OK
9707 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9709 * NOTES
9710 * This function is used by OleConvertOLESTREAMToIStorage only.
9712 * The stream data is stored in the OLESTREAM and there should be
9713 * no need to recreate the stream. If the stream is manually
9714 * deleted it will attempt to create it by querying the registry.
9718 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
9720 IStream *pStream;
9721 HRESULT hStorageRes, hRes = S_OK;
9722 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
9723 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9724 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
9726 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9727 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
9729 /* Initialize the CompObj structure */
9730 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
9731 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
9732 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
9735 /* Create a CompObj stream if it doesn't exist */
9736 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
9737 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9738 if(hStorageRes == S_OK)
9740 /* copy the OleTypeName to the compobj struct */
9741 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
9742 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
9744 /* copy the OleTypeName to the compobj struct */
9745 /* Note: in the test made, these were Identical */
9746 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
9747 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
9749 /* Get the CLSID */
9750 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
9751 bufferW, OLESTREAM_MAX_STR_LEN );
9752 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
9754 if(hRes == S_OK)
9756 HKEY hKey;
9757 LONG hErr;
9758 /* Get the CLSID Default Name from the Registry */
9759 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
9760 if(hErr == ERROR_SUCCESS)
9762 char strTemp[OLESTREAM_MAX_STR_LEN];
9763 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
9764 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
9765 if(hErr == ERROR_SUCCESS)
9767 strcpy(IStorageCompObj.strCLSIDName, strTemp);
9769 RegCloseKey(hKey);
9773 /* Write CompObj Structure to stream */
9774 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
9776 WriteClassStm(pStream,&(IStorageCompObj.clsid));
9778 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
9779 if(IStorageCompObj.dwCLSIDNameLength > 0)
9781 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
9783 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
9784 if(IStorageCompObj.dwOleTypeNameLength > 0)
9786 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
9788 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
9789 if(IStorageCompObj.dwProgIDNameLength > 0)
9791 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
9793 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
9794 IStream_Release(pStream);
9796 return hRes;
9800 /*************************************************************************
9801 * OLECONVERT_CreateOlePresStream[Internal]
9803 * Creates the "\002OlePres000" Stream with the Metafile data
9805 * PARAMS
9806 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
9807 * dwExtentX [I] Width of the Metafile
9808 * dwExtentY [I] Height of the Metafile
9809 * pData [I] Metafile data
9810 * dwDataLength [I] Size of the Metafile data
9812 * RETURNS
9813 * Success: S_OK
9814 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9816 * NOTES
9817 * This function is used by OleConvertOLESTREAMToIStorage only.
9820 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
9822 HRESULT hRes;
9823 IStream *pStream;
9824 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9825 BYTE pOlePresStreamHeader [] =
9827 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
9828 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9829 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9830 0x00, 0x00, 0x00, 0x00
9833 BYTE pOlePresStreamHeaderEmpty [] =
9835 0x00, 0x00, 0x00, 0x00,
9836 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9837 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9838 0x00, 0x00, 0x00, 0x00
9841 /* Create the OlePres000 Stream */
9842 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9843 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9845 if(hRes == S_OK)
9847 DWORD nHeaderSize;
9848 OLECONVERT_ISTORAGE_OLEPRES OlePres;
9850 memset(&OlePres, 0, sizeof(OlePres));
9851 /* Do we have any metafile data to save */
9852 if(dwDataLength > 0)
9854 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
9855 nHeaderSize = sizeof(pOlePresStreamHeader);
9857 else
9859 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
9860 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
9862 /* Set width and height of the metafile */
9863 OlePres.dwExtentX = dwExtentX;
9864 OlePres.dwExtentY = -dwExtentY;
9866 /* Set Data and Length */
9867 if(dwDataLength > sizeof(METAFILEPICT16))
9869 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
9870 OlePres.pData = &(pData[8]);
9872 /* Save OlePres000 Data to Stream */
9873 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
9874 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
9875 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
9876 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
9877 if(OlePres.dwSize > 0)
9879 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
9881 IStream_Release(pStream);
9885 /*************************************************************************
9886 * OLECONVERT_CreateOle10NativeStream [Internal]
9888 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9890 * PARAMS
9891 * pStorage [I] Dest storage to create the stream in
9892 * pData [I] Ole10 Native Data (ex. bmp)
9893 * dwDataLength [I] Size of the Ole10 Native Data
9895 * RETURNS
9896 * Nothing
9898 * NOTES
9899 * This function is used by OleConvertOLESTREAMToIStorage only.
9901 * Might need to verify the data and return appropriate error message
9904 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
9906 HRESULT hRes;
9907 IStream *pStream;
9908 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9910 /* Create the Ole10Native Stream */
9911 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9912 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9914 if(hRes == S_OK)
9916 /* Write info to stream */
9917 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9918 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9919 IStream_Release(pStream);
9924 /*************************************************************************
9925 * OLECONVERT_GetOLE10ProgID [Internal]
9927 * Finds the ProgID (or OleTypeID) from the IStorage
9929 * PARAMS
9930 * pStorage [I] The Src IStorage to get the ProgID
9931 * strProgID [I] the ProgID string to get
9932 * dwSize [I] the size of the string
9934 * RETURNS
9935 * Success: S_OK
9936 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9938 * NOTES
9939 * This function is used by OleConvertIStorageToOLESTREAM only.
9943 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9945 HRESULT hRes;
9946 IStream *pStream;
9947 LARGE_INTEGER iSeekPos;
9948 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9949 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9951 /* Open the CompObj Stream */
9952 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9953 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9954 if(hRes == S_OK)
9957 /*Get the OleType from the CompObj Stream */
9958 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9959 iSeekPos.u.HighPart = 0;
9961 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9962 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9963 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9964 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9965 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9966 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9967 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9969 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9970 if(*dwSize > 0)
9972 IStream_Read(pStream, strProgID, *dwSize, NULL);
9974 IStream_Release(pStream);
9976 else
9978 STATSTG stat;
9979 LPOLESTR wstrProgID;
9981 /* Get the OleType from the registry */
9982 REFCLSID clsid = &(stat.clsid);
9983 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9984 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9985 if(hRes == S_OK)
9987 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9988 CoTaskMemFree(wstrProgID);
9992 return hRes;
9995 /*************************************************************************
9996 * OLECONVERT_GetOle10PresData [Internal]
9998 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10000 * PARAMS
10001 * pStorage [I] Src IStroage
10002 * pOleStream [I] Dest OleStream Mem Struct
10004 * RETURNS
10005 * Nothing
10007 * NOTES
10008 * This function is used by OleConvertIStorageToOLESTREAM only.
10010 * Memory allocated for pData must be freed by the caller
10014 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10017 HRESULT hRes;
10018 IStream *pStream;
10019 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10021 /* Initialize Default data for OLESTREAM */
10022 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10023 pOleStreamData[0].dwTypeID = 2;
10024 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10025 pOleStreamData[1].dwTypeID = 0;
10026 pOleStreamData[0].dwMetaFileWidth = 0;
10027 pOleStreamData[0].dwMetaFileHeight = 0;
10028 pOleStreamData[0].pData = NULL;
10029 pOleStreamData[1].pData = NULL;
10031 /* Open Ole10Native Stream */
10032 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10033 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10034 if(hRes == S_OK)
10037 /* Read Size and Data */
10038 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
10039 if(pOleStreamData->dwDataLength > 0)
10041 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
10042 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
10044 IStream_Release(pStream);
10050 /*************************************************************************
10051 * OLECONVERT_GetOle20PresData[Internal]
10053 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10055 * PARAMS
10056 * pStorage [I] Src IStroage
10057 * pOleStreamData [I] Dest OleStream Mem Struct
10059 * RETURNS
10060 * Nothing
10062 * NOTES
10063 * This function is used by OleConvertIStorageToOLESTREAM only.
10065 * Memory allocated for pData must be freed by the caller
10067 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10069 HRESULT hRes;
10070 IStream *pStream;
10071 OLECONVERT_ISTORAGE_OLEPRES olePress;
10072 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10074 /* Initialize Default data for OLESTREAM */
10075 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10076 pOleStreamData[0].dwTypeID = 2;
10077 pOleStreamData[0].dwMetaFileWidth = 0;
10078 pOleStreamData[0].dwMetaFileHeight = 0;
10079 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
10080 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10081 pOleStreamData[1].dwTypeID = 0;
10082 pOleStreamData[1].dwOleTypeNameLength = 0;
10083 pOleStreamData[1].strOleTypeName[0] = 0;
10084 pOleStreamData[1].dwMetaFileWidth = 0;
10085 pOleStreamData[1].dwMetaFileHeight = 0;
10086 pOleStreamData[1].pData = NULL;
10087 pOleStreamData[1].dwDataLength = 0;
10090 /* Open OlePress000 stream */
10091 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10092 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10093 if(hRes == S_OK)
10095 LARGE_INTEGER iSeekPos;
10096 METAFILEPICT16 MetaFilePict;
10097 static const char strMetafilePictName[] = "METAFILEPICT";
10099 /* Set the TypeID for a Metafile */
10100 pOleStreamData[1].dwTypeID = 5;
10102 /* Set the OleTypeName to Metafile */
10103 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
10104 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
10106 iSeekPos.u.HighPart = 0;
10107 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
10109 /* Get Presentation Data */
10110 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10111 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
10112 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
10113 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
10115 /*Set width and Height */
10116 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
10117 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
10118 if(olePress.dwSize > 0)
10120 /* Set Length */
10121 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
10123 /* Set MetaFilePict struct */
10124 MetaFilePict.mm = 8;
10125 MetaFilePict.xExt = olePress.dwExtentX;
10126 MetaFilePict.yExt = olePress.dwExtentY;
10127 MetaFilePict.hMF = 0;
10129 /* Get Metafile Data */
10130 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
10131 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
10132 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
10134 IStream_Release(pStream);
10138 /*************************************************************************
10139 * OleConvertOLESTREAMToIStorage [OLE32.@]
10141 * Read info on MSDN
10143 * TODO
10144 * DVTARGETDEVICE parameter is not handled
10145 * Still unsure of some mem fields for OLE 10 Stream
10146 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10147 * and "\001OLE" streams
10150 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
10151 LPOLESTREAM pOleStream,
10152 LPSTORAGE pstg,
10153 const DVTARGETDEVICE* ptd)
10155 int i;
10156 HRESULT hRes=S_OK;
10157 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10159 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
10161 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10163 if(ptd != NULL)
10165 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10168 if(pstg == NULL || pOleStream == NULL)
10170 hRes = E_INVALIDARG;
10173 if(hRes == S_OK)
10175 /* Load the OLESTREAM to Memory */
10176 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
10179 if(hRes == S_OK)
10181 /* Load the OLESTREAM to Memory (part 2)*/
10182 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
10185 if(hRes == S_OK)
10188 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
10190 /* Do we have the IStorage Data in the OLESTREAM */
10191 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
10193 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10194 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
10196 else
10198 /* It must be an original OLE 1.0 source */
10199 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10202 else
10204 /* It must be an original OLE 1.0 source */
10205 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10208 /* Create CompObj Stream if necessary */
10209 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
10210 if(hRes == S_OK)
10212 /*Create the Ole Stream if necessary */
10213 STORAGE_CreateOleStream(pstg, 0);
10218 /* Free allocated memory */
10219 for(i=0; i < 2; i++)
10221 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10222 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
10223 pOleStreamData[i].pstrOleObjFileName = NULL;
10225 return hRes;
10228 /*************************************************************************
10229 * OleConvertIStorageToOLESTREAM [OLE32.@]
10231 * Read info on MSDN
10233 * Read info on MSDN
10235 * TODO
10236 * Still unsure of some mem fields for OLE 10 Stream
10237 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10238 * and "\001OLE" streams.
10241 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
10242 LPSTORAGE pstg,
10243 LPOLESTREAM pOleStream)
10245 int i;
10246 HRESULT hRes = S_OK;
10247 IStream *pStream;
10248 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10249 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10251 TRACE("%p %p\n", pstg, pOleStream);
10253 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10255 if(pstg == NULL || pOleStream == NULL)
10257 hRes = E_INVALIDARG;
10259 if(hRes == S_OK)
10261 /* Get the ProgID */
10262 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
10263 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
10265 if(hRes == S_OK)
10267 /* Was it originally Ole10 */
10268 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
10269 if(hRes == S_OK)
10271 IStream_Release(pStream);
10272 /* Get Presentation Data for Ole10Native */
10273 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
10275 else
10277 /* Get Presentation Data (OLE20) */
10278 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
10281 /* Save OLESTREAM */
10282 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
10283 if(hRes == S_OK)
10285 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
10290 /* Free allocated memory */
10291 for(i=0; i < 2; i++)
10293 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10296 return hRes;
10299 enum stream_1ole_flags {
10300 OleStream_LinkedObject = 0x00000001,
10301 OleStream_Convert = 0x00000004
10304 /***********************************************************************
10305 * GetConvertStg (OLE32.@)
10307 HRESULT WINAPI GetConvertStg(IStorage *stg)
10309 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10310 static const DWORD version_magic = 0x02000001;
10311 DWORD header[2];
10312 IStream *stream;
10313 HRESULT hr;
10315 TRACE("%p\n", stg);
10317 if (!stg) return E_INVALIDARG;
10319 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
10320 if (FAILED(hr)) return hr;
10322 hr = IStream_Read(stream, header, sizeof(header), NULL);
10323 IStream_Release(stream);
10324 if (FAILED(hr)) return hr;
10326 if (header[0] != version_magic)
10328 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
10329 return E_FAIL;
10332 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
10335 /***********************************************************************
10336 * SetConvertStg (OLE32.@)
10338 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
10340 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10341 DWORD flags = convert ? OleStream_Convert : 0;
10342 IStream *stream;
10343 DWORD header[2];
10344 HRESULT hr;
10346 TRACE("(%p, %d)\n", storage, convert);
10348 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
10349 if (FAILED(hr))
10351 if (hr != STG_E_FILENOTFOUND)
10352 return hr;
10354 return STORAGE_CreateOleStream(storage, flags);
10357 hr = IStream_Read(stream, header, sizeof(header), NULL);
10358 if (FAILED(hr))
10360 IStream_Release(stream);
10361 return hr;
10364 /* update flag if differs */
10365 if ((header[1] ^ flags) & OleStream_Convert)
10367 LARGE_INTEGER pos = {{0}};
10369 if (header[1] & OleStream_Convert)
10370 flags = header[1] & ~OleStream_Convert;
10371 else
10372 flags = header[1] | OleStream_Convert;
10374 pos.QuadPart = sizeof(DWORD);
10375 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
10376 if (FAILED(hr))
10378 IStream_Release(stream);
10379 return hr;
10382 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
10385 IStream_Release(stream);
10386 return hr;
10389 /******************************************************************************
10390 * StgIsStorageFile [OLE32.@]
10391 * Verify if the file contains a storage object
10393 * PARAMS
10394 * fn [ I] Filename
10396 * RETURNS
10397 * S_OK if file has magic bytes as a storage object
10398 * S_FALSE if file is not storage
10400 HRESULT WINAPI
10401 StgIsStorageFile(LPCOLESTR fn)
10403 HANDLE hf;
10404 BYTE magic[8];
10405 DWORD bytes_read;
10407 TRACE("%s\n", debugstr_w(fn));
10408 hf = CreateFileW(fn, GENERIC_READ,
10409 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
10410 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
10412 if (hf == INVALID_HANDLE_VALUE)
10413 return STG_E_FILENOTFOUND;
10415 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
10417 WARN(" unable to read file\n");
10418 CloseHandle(hf);
10419 return S_FALSE;
10422 CloseHandle(hf);
10424 if (bytes_read != 8) {
10425 TRACE(" too short\n");
10426 return S_FALSE;
10429 if (!memcmp(magic,STORAGE_magic,8)) {
10430 TRACE(" -> YES\n");
10431 return S_OK;
10434 TRACE(" -> Invalid header.\n");
10435 return S_FALSE;
10438 /***********************************************************************
10439 * WriteClassStm (OLE32.@)
10441 * Writes a CLSID to a stream.
10443 * PARAMS
10444 * pStm [I] Stream to write to.
10445 * rclsid [I] CLSID to write.
10447 * RETURNS
10448 * Success: S_OK.
10449 * Failure: HRESULT code.
10451 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
10453 TRACE("(%p,%p)\n",pStm,rclsid);
10455 if (!pStm || !rclsid)
10456 return E_INVALIDARG;
10458 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
10461 /***********************************************************************
10462 * ReadClassStm (OLE32.@)
10464 * Reads a CLSID from a stream.
10466 * PARAMS
10467 * pStm [I] Stream to read from.
10468 * rclsid [O] CLSID to read.
10470 * RETURNS
10471 * Success: S_OK.
10472 * Failure: HRESULT code.
10474 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
10476 ULONG nbByte;
10477 HRESULT res;
10479 TRACE("(%p,%p)\n",pStm,pclsid);
10481 if (!pStm || !pclsid)
10482 return E_INVALIDARG;
10484 /* clear the output args */
10485 *pclsid = CLSID_NULL;
10487 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
10489 if (FAILED(res))
10490 return res;
10492 if (nbByte != sizeof(CLSID))
10493 return STG_E_READFAULT;
10494 else
10495 return S_OK;