x3daudio1_5: Add stub dll.
[wine.git] / dlls / ole32 / storage32.c
blob5e434e01d0dff7602aa3aed05b4e04f849ca0163
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 HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create);
106 static void StorageImpl_Destroy(StorageBaseImpl* iface);
107 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
108 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
109 static HRESULT StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer, ULONG *read );
110 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
111 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
112 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
113 static void StorageImpl_SaveFileHeader(StorageImpl* This);
114 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset, ULARGE_INTEGER cb, DWORD dwLockType);
116 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex);
117 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
118 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
119 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
120 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
122 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
123 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
124 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
126 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
127 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
128 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
129 ULONG blockIndex, ULONG offset, DWORD value);
130 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
131 ULONG blockIndex, ULONG offset, DWORD* value);
133 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
134 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
136 typedef struct TransactedDirEntry
138 /* If applicable, a reference to the original DirEntry in the transacted
139 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
140 DirRef transactedParentEntry;
142 /* True if this entry is being used. */
143 BOOL inuse;
145 /* True if data is up to date. */
146 BOOL read;
148 /* True if this entry has been modified. */
149 BOOL dirty;
151 /* True if this entry's stream has been modified. */
152 BOOL stream_dirty;
154 /* True if this entry has been deleted in the transacted storage, but the
155 * delete has not yet been committed. */
156 BOOL deleted;
158 /* If this entry's stream has been modified, a reference to where the stream
159 * is stored in the snapshot file. */
160 DirRef stream_entry;
162 /* This directory entry's data, including any changes that have been made. */
163 DirEntry data;
165 /* A reference to the parent of this node. This is only valid while we are
166 * committing changes. */
167 DirRef parent;
169 /* A reference to a newly-created entry in the transacted parent. This is
170 * always equal to transactedParentEntry except when committing changes. */
171 DirRef newTransactedParentEntry;
172 } TransactedDirEntry;
174 /****************************************************************************
175 * Transacted storage object.
177 typedef struct TransactedSnapshotImpl
179 struct StorageBaseImpl base;
182 * Modified streams are temporarily saved to the scratch file.
184 StorageBaseImpl *scratch;
186 /* The directory structure is kept here, so that we can track how these
187 * entries relate to those in the parent storage. */
188 TransactedDirEntry *entries;
189 ULONG entries_size;
190 ULONG firstFreeEntry;
193 * Changes are committed to the transacted parent.
195 StorageBaseImpl *transactedParent;
197 /* The transaction signature from when we last committed */
198 ULONG lastTransactionSig;
199 } TransactedSnapshotImpl;
201 typedef struct TransactedSharedImpl
203 struct StorageBaseImpl base;
206 * Snapshot and uncommitted changes go here.
208 TransactedSnapshotImpl *scratch;
211 * Changes are committed to the transacted parent.
213 StorageBaseImpl *transactedParent;
215 /* The transaction signature from when we last committed */
216 ULONG lastTransactionSig;
217 } TransactedSharedImpl;
219 /* Generic function to create a transacted wrapper for a direct storage object. */
220 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, BOOL toplevel, StorageBaseImpl** result);
222 /* OLESTREAM memory structure to use for Get and Put Routines */
223 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
224 typedef struct
226 DWORD dwOleID;
227 DWORD dwTypeID;
228 DWORD dwOleTypeNameLength;
229 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
230 CHAR *pstrOleObjFileName;
231 DWORD dwOleObjFileNameLength;
232 DWORD dwMetaFileWidth;
233 DWORD dwMetaFileHeight;
234 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
235 DWORD dwDataLength;
236 BYTE *pData;
237 }OLECONVERT_OLESTREAM_DATA;
239 /* CompObj Stream structure */
240 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
241 typedef struct
243 BYTE byUnknown1[12];
244 CLSID clsid;
245 DWORD dwCLSIDNameLength;
246 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
247 DWORD dwOleTypeNameLength;
248 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
249 DWORD dwProgIDNameLength;
250 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
251 BYTE byUnknown2[16];
252 }OLECONVERT_ISTORAGE_COMPOBJ;
255 /* Ole Presentation Stream structure */
256 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
257 typedef struct
259 BYTE byUnknown1[28];
260 DWORD dwExtentX;
261 DWORD dwExtentY;
262 DWORD dwSize;
263 BYTE *pData;
264 }OLECONVERT_ISTORAGE_OLEPRES;
268 /***********************************************************************
269 * Forward declaration of internal functions used by the method DestroyElement
271 static HRESULT deleteStorageContents(
272 StorageBaseImpl *parentStorage,
273 DirRef indexToDelete,
274 DirEntry entryDataToDelete);
276 static HRESULT deleteStreamContents(
277 StorageBaseImpl *parentStorage,
278 DirRef indexToDelete,
279 DirEntry entryDataToDelete);
281 static HRESULT removeFromTree(
282 StorageBaseImpl *This,
283 DirRef parentStorageIndex,
284 DirRef deletedIndex);
286 /***********************************************************************
287 * Declaration of the functions used to manipulate DirEntry
290 static HRESULT insertIntoTree(
291 StorageBaseImpl *This,
292 DirRef parentStorageIndex,
293 DirRef newEntryIndex);
295 static LONG entryNameCmp(
296 const OLECHAR *name1,
297 const OLECHAR *name2);
299 static DirRef findElement(
300 StorageBaseImpl *storage,
301 DirRef storageEntry,
302 const OLECHAR *name,
303 DirEntry *data);
305 static HRESULT findTreeParent(
306 StorageBaseImpl *storage,
307 DirRef storageEntry,
308 const OLECHAR *childName,
309 DirEntry *parentData,
310 DirRef *parentEntry,
311 ULONG *relation);
313 /***********************************************************************
314 * Declaration of miscellaneous functions...
316 static HRESULT validateSTGM(DWORD stgmValue);
318 static DWORD GetShareModeFromSTGM(DWORD stgm);
319 static DWORD GetAccessModeFromSTGM(DWORD stgm);
320 static DWORD GetCreationModeFromSTGM(DWORD stgm);
322 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
325 /****************************************************************************
326 * IEnumSTATSTGImpl definitions.
328 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
329 * This class allows iterating through the content of a storage and to find
330 * specific items inside it.
332 struct IEnumSTATSTGImpl
334 IEnumSTATSTG IEnumSTATSTG_iface;
336 LONG ref; /* Reference count */
337 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
338 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
340 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
343 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
345 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
349 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
350 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
352 /************************************************************************
353 ** Block Functions
356 static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
358 return (ULONGLONG)(index+1) * This->bigBlockSize;
361 /************************************************************************
362 ** Storage32BaseImpl implementation
364 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
365 ULARGE_INTEGER offset,
366 void* buffer,
367 ULONG size,
368 ULONG* bytesRead)
370 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
373 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
374 ULARGE_INTEGER offset,
375 const void* buffer,
376 const ULONG size,
377 ULONG* bytesWritten)
379 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
382 /************************************************************************
383 * Storage32BaseImpl_QueryInterface (IUnknown)
385 * This method implements the common QueryInterface for all IStorage32
386 * implementations contained in this file.
388 * See Windows documentation for more details on IUnknown methods.
390 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
391 IStorage* iface,
392 REFIID riid,
393 void** ppvObject)
395 StorageBaseImpl *This = impl_from_IStorage(iface);
397 if (!ppvObject)
398 return E_INVALIDARG;
400 *ppvObject = 0;
402 if (IsEqualGUID(&IID_IUnknown, riid) ||
403 IsEqualGUID(&IID_IStorage, riid))
405 *ppvObject = &This->IStorage_iface;
407 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
409 *ppvObject = &This->IPropertySetStorage_iface;
411 /* locking interface is reported for writer only */
412 else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
414 *ppvObject = &This->IDirectWriterLock_iface;
416 else
417 return E_NOINTERFACE;
419 IStorage_AddRef(iface);
421 return S_OK;
424 /************************************************************************
425 * Storage32BaseImpl_AddRef (IUnknown)
427 * This method implements the common AddRef for all IStorage32
428 * implementations contained in this file.
430 * See Windows documentation for more details on IUnknown methods.
432 static ULONG WINAPI StorageBaseImpl_AddRef(
433 IStorage* iface)
435 StorageBaseImpl *This = impl_from_IStorage(iface);
436 ULONG ref = InterlockedIncrement(&This->ref);
438 TRACE("(%p) AddRef to %d\n", This, ref);
440 return ref;
443 /************************************************************************
444 * Storage32BaseImpl_Release (IUnknown)
446 * This method implements the common Release for all IStorage32
447 * implementations contained in this file.
449 * See Windows documentation for more details on IUnknown methods.
451 static ULONG WINAPI StorageBaseImpl_Release(
452 IStorage* iface)
454 StorageBaseImpl *This = impl_from_IStorage(iface);
456 ULONG ref = InterlockedDecrement(&This->ref);
458 TRACE("(%p) ReleaseRef to %d\n", This, ref);
460 if (ref == 0)
463 * Since we are using a system of base-classes, we want to call the
464 * destructor of the appropriate derived class. To do this, we are
465 * using virtual functions to implement the destructor.
467 StorageBaseImpl_Destroy(This);
470 return ref;
473 /************************************************************************
474 * Storage32BaseImpl_OpenStream (IStorage)
476 * This method will open the specified stream object from the current storage.
478 * See Windows documentation for more details on IStorage methods.
480 static HRESULT WINAPI StorageBaseImpl_OpenStream(
481 IStorage* iface,
482 const OLECHAR* pwcsName, /* [string][in] */
483 void* reserved1, /* [unique][in] */
484 DWORD grfMode, /* [in] */
485 DWORD reserved2, /* [in] */
486 IStream** ppstm) /* [out] */
488 StorageBaseImpl *This = impl_from_IStorage(iface);
489 StgStreamImpl* newStream;
490 DirEntry currentEntry;
491 DirRef streamEntryRef;
492 HRESULT res = STG_E_UNKNOWN;
494 TRACE("(%p, %s, %p, %x, %d, %p)\n",
495 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
497 if ( (pwcsName==NULL) || (ppstm==0) )
499 res = E_INVALIDARG;
500 goto end;
503 *ppstm = NULL;
505 if ( FAILED( validateSTGM(grfMode) ) ||
506 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
508 res = STG_E_INVALIDFLAG;
509 goto end;
513 * As documented.
515 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
517 res = STG_E_INVALIDFUNCTION;
518 goto end;
521 if (This->reverted)
523 res = STG_E_REVERTED;
524 goto end;
528 * Check that we're compatible with the parent's storage mode, but
529 * only if we are not in transacted mode
531 if(!(This->openFlags & STGM_TRANSACTED)) {
532 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
534 res = STG_E_INVALIDFLAG;
535 goto end;
540 * Search for the element with the given name
542 streamEntryRef = findElement(
543 This,
544 This->storageDirEntry,
545 pwcsName,
546 &currentEntry);
549 * If it was found, construct the stream object and return a pointer to it.
551 if ( (streamEntryRef!=DIRENTRY_NULL) &&
552 (currentEntry.stgType==STGTY_STREAM) )
554 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
556 /* A single stream cannot be opened a second time. */
557 res = STG_E_ACCESSDENIED;
558 goto end;
561 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
563 if (newStream)
565 newStream->grfMode = grfMode;
566 *ppstm = &newStream->IStream_iface;
568 IStream_AddRef(*ppstm);
570 res = S_OK;
571 goto end;
574 res = E_OUTOFMEMORY;
575 goto end;
578 res = STG_E_FILENOTFOUND;
580 end:
581 if (res == S_OK)
582 TRACE("<-- IStream %p\n", *ppstm);
583 TRACE("<-- %08x\n", res);
584 return res;
587 /************************************************************************
588 * Storage32BaseImpl_OpenStorage (IStorage)
590 * This method will open a new storage object from the current storage.
592 * See Windows documentation for more details on IStorage methods.
594 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
595 IStorage* iface,
596 const OLECHAR* pwcsName, /* [string][unique][in] */
597 IStorage* pstgPriority, /* [unique][in] */
598 DWORD grfMode, /* [in] */
599 SNB snbExclude, /* [unique][in] */
600 DWORD reserved, /* [in] */
601 IStorage** ppstg) /* [out] */
603 StorageBaseImpl *This = impl_from_IStorage(iface);
604 StorageInternalImpl* newStorage;
605 StorageBaseImpl* newTransactedStorage;
606 DirEntry currentEntry;
607 DirRef storageEntryRef;
608 HRESULT res = STG_E_UNKNOWN;
610 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
611 iface, debugstr_w(pwcsName), pstgPriority,
612 grfMode, snbExclude, reserved, ppstg);
614 if ((pwcsName==NULL) || (ppstg==0) )
616 res = E_INVALIDARG;
617 goto end;
620 if (This->openFlags & STGM_SIMPLE)
622 res = STG_E_INVALIDFUNCTION;
623 goto end;
626 /* as documented */
627 if (snbExclude != NULL)
629 res = STG_E_INVALIDPARAMETER;
630 goto end;
633 if ( FAILED( validateSTGM(grfMode) ))
635 res = STG_E_INVALIDFLAG;
636 goto end;
640 * As documented.
642 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
643 (grfMode & STGM_DELETEONRELEASE) ||
644 (grfMode & STGM_PRIORITY) )
646 res = STG_E_INVALIDFUNCTION;
647 goto end;
650 if (This->reverted)
651 return STG_E_REVERTED;
654 * Check that we're compatible with the parent's storage mode,
655 * but only if we are not transacted
657 if(!(This->openFlags & STGM_TRANSACTED)) {
658 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
660 res = STG_E_ACCESSDENIED;
661 goto end;
665 *ppstg = NULL;
667 storageEntryRef = findElement(
668 This,
669 This->storageDirEntry,
670 pwcsName,
671 &currentEntry);
673 if ( (storageEntryRef!=DIRENTRY_NULL) &&
674 (currentEntry.stgType==STGTY_STORAGE) )
676 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
678 /* A single storage cannot be opened a second time. */
679 res = STG_E_ACCESSDENIED;
680 goto end;
683 newStorage = StorageInternalImpl_Construct(
684 This,
685 grfMode,
686 storageEntryRef);
688 if (newStorage != 0)
690 if (grfMode & STGM_TRANSACTED)
692 res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
694 if (FAILED(res))
696 HeapFree(GetProcessHeap(), 0, newStorage);
697 goto end;
700 *ppstg = &newTransactedStorage->IStorage_iface;
702 else
704 *ppstg = &newStorage->base.IStorage_iface;
707 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
709 res = S_OK;
710 goto end;
713 res = STG_E_INSUFFICIENTMEMORY;
714 goto end;
717 res = STG_E_FILENOTFOUND;
719 end:
720 TRACE("<-- %08x\n", res);
721 return res;
724 /************************************************************************
725 * Storage32BaseImpl_EnumElements (IStorage)
727 * This method will create an enumerator object that can be used to
728 * retrieve information about all the elements in the storage object.
730 * See Windows documentation for more details on IStorage methods.
732 static HRESULT WINAPI StorageBaseImpl_EnumElements(
733 IStorage* iface,
734 DWORD reserved1, /* [in] */
735 void* reserved2, /* [size_is][unique][in] */
736 DWORD reserved3, /* [in] */
737 IEnumSTATSTG** ppenum) /* [out] */
739 StorageBaseImpl *This = impl_from_IStorage(iface);
740 IEnumSTATSTGImpl* newEnum;
742 TRACE("(%p, %d, %p, %d, %p)\n",
743 iface, reserved1, reserved2, reserved3, ppenum);
745 if (!ppenum)
746 return E_INVALIDARG;
748 if (This->reverted)
749 return STG_E_REVERTED;
751 newEnum = IEnumSTATSTGImpl_Construct(
752 This,
753 This->storageDirEntry);
755 if (newEnum)
757 *ppenum = &newEnum->IEnumSTATSTG_iface;
758 return S_OK;
761 return E_OUTOFMEMORY;
764 /************************************************************************
765 * Storage32BaseImpl_Stat (IStorage)
767 * This method will retrieve information about this storage object.
769 * See Windows documentation for more details on IStorage methods.
771 static HRESULT WINAPI StorageBaseImpl_Stat(
772 IStorage* iface,
773 STATSTG* pstatstg, /* [out] */
774 DWORD grfStatFlag) /* [in] */
776 StorageBaseImpl *This = impl_from_IStorage(iface);
777 DirEntry currentEntry;
778 HRESULT res = STG_E_UNKNOWN;
780 TRACE("(%p, %p, %x)\n",
781 iface, pstatstg, grfStatFlag);
783 if (!pstatstg)
785 res = E_INVALIDARG;
786 goto end;
789 if (This->reverted)
791 res = STG_E_REVERTED;
792 goto end;
795 res = StorageBaseImpl_ReadDirEntry(
796 This,
797 This->storageDirEntry,
798 &currentEntry);
800 if (SUCCEEDED(res))
802 StorageUtl_CopyDirEntryToSTATSTG(
803 This,
804 pstatstg,
805 &currentEntry,
806 grfStatFlag);
808 pstatstg->grfMode = This->openFlags;
809 pstatstg->grfStateBits = This->stateBits;
812 end:
813 if (res == S_OK)
815 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);
817 TRACE("<-- %08x\n", res);
818 return res;
821 /************************************************************************
822 * Storage32BaseImpl_RenameElement (IStorage)
824 * This method will rename the specified element.
826 * See Windows documentation for more details on IStorage methods.
828 static HRESULT WINAPI StorageBaseImpl_RenameElement(
829 IStorage* iface,
830 const OLECHAR* pwcsOldName, /* [in] */
831 const OLECHAR* pwcsNewName) /* [in] */
833 StorageBaseImpl *This = impl_from_IStorage(iface);
834 DirEntry currentEntry;
835 DirRef currentEntryRef;
837 TRACE("(%p, %s, %s)\n",
838 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
840 if (This->reverted)
841 return STG_E_REVERTED;
843 currentEntryRef = findElement(This,
844 This->storageDirEntry,
845 pwcsNewName,
846 &currentEntry);
848 if (currentEntryRef != DIRENTRY_NULL)
851 * There is already an element with the new name
853 return STG_E_FILEALREADYEXISTS;
857 * Search for the old element name
859 currentEntryRef = findElement(This,
860 This->storageDirEntry,
861 pwcsOldName,
862 &currentEntry);
864 if (currentEntryRef != DIRENTRY_NULL)
866 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
867 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
869 WARN("Element is already open; cannot rename.\n");
870 return STG_E_ACCESSDENIED;
873 /* Remove the element from its current position in the tree */
874 removeFromTree(This, This->storageDirEntry,
875 currentEntryRef);
877 /* Change the name of the element */
878 strcpyW(currentEntry.name, pwcsNewName);
880 /* Delete any sibling links */
881 currentEntry.leftChild = DIRENTRY_NULL;
882 currentEntry.rightChild = DIRENTRY_NULL;
884 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
885 &currentEntry);
887 /* Insert the element in a new position in the tree */
888 insertIntoTree(This, This->storageDirEntry,
889 currentEntryRef);
891 else
894 * There is no element with the old name
896 return STG_E_FILENOTFOUND;
899 return StorageBaseImpl_Flush(This);
902 /************************************************************************
903 * Storage32BaseImpl_CreateStream (IStorage)
905 * This method will create a stream object within this storage
907 * See Windows documentation for more details on IStorage methods.
909 static HRESULT WINAPI StorageBaseImpl_CreateStream(
910 IStorage* iface,
911 const OLECHAR* pwcsName, /* [string][in] */
912 DWORD grfMode, /* [in] */
913 DWORD reserved1, /* [in] */
914 DWORD reserved2, /* [in] */
915 IStream** ppstm) /* [out] */
917 StorageBaseImpl *This = impl_from_IStorage(iface);
918 StgStreamImpl* newStream;
919 DirEntry currentEntry, newStreamEntry;
920 DirRef currentEntryRef, newStreamEntryRef;
921 HRESULT hr;
923 TRACE("(%p, %s, %x, %d, %d, %p)\n",
924 iface, debugstr_w(pwcsName), grfMode,
925 reserved1, reserved2, ppstm);
927 if (ppstm == 0)
928 return STG_E_INVALIDPOINTER;
930 if (pwcsName == 0)
931 return STG_E_INVALIDNAME;
933 if (reserved1 || reserved2)
934 return STG_E_INVALIDPARAMETER;
936 if ( FAILED( validateSTGM(grfMode) ))
937 return STG_E_INVALIDFLAG;
939 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
940 return STG_E_INVALIDFLAG;
942 if (This->reverted)
943 return STG_E_REVERTED;
946 * As documented.
948 if ((grfMode & STGM_DELETEONRELEASE) ||
949 (grfMode & STGM_TRANSACTED))
950 return STG_E_INVALIDFUNCTION;
953 * Don't worry about permissions in transacted mode, as we can always write
954 * changes; we just can't always commit them.
956 if(!(This->openFlags & STGM_TRANSACTED)) {
957 /* Can't create a stream on read-only storage */
958 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
959 return STG_E_ACCESSDENIED;
961 /* Can't create a stream with greater access than the parent. */
962 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
963 return STG_E_ACCESSDENIED;
966 if(This->openFlags & STGM_SIMPLE)
967 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
969 *ppstm = 0;
971 currentEntryRef = findElement(This,
972 This->storageDirEntry,
973 pwcsName,
974 &currentEntry);
976 if (currentEntryRef != DIRENTRY_NULL)
979 * An element with this name already exists
981 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
983 IStorage_DestroyElement(iface, pwcsName);
985 else
986 return STG_E_FILEALREADYEXISTS;
990 * memset the empty entry
992 memset(&newStreamEntry, 0, sizeof(DirEntry));
994 newStreamEntry.sizeOfNameString =
995 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
997 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
998 return STG_E_INVALIDNAME;
1000 strcpyW(newStreamEntry.name, pwcsName);
1002 newStreamEntry.stgType = STGTY_STREAM;
1003 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
1004 newStreamEntry.size.u.LowPart = 0;
1005 newStreamEntry.size.u.HighPart = 0;
1007 newStreamEntry.leftChild = DIRENTRY_NULL;
1008 newStreamEntry.rightChild = DIRENTRY_NULL;
1009 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
1011 /* call CoFileTime to get the current time
1012 newStreamEntry.ctime
1013 newStreamEntry.mtime
1016 /* newStreamEntry.clsid */
1019 * Create an entry with the new data
1021 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
1022 if (FAILED(hr))
1023 return hr;
1026 * Insert the new entry in the parent storage's tree.
1028 hr = insertIntoTree(
1029 This,
1030 This->storageDirEntry,
1031 newStreamEntryRef);
1032 if (FAILED(hr))
1034 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
1035 return hr;
1039 * Open the stream to return it.
1041 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1043 if (newStream)
1045 *ppstm = &newStream->IStream_iface;
1046 IStream_AddRef(*ppstm);
1048 else
1050 return STG_E_INSUFFICIENTMEMORY;
1053 return StorageBaseImpl_Flush(This);
1056 /************************************************************************
1057 * Storage32BaseImpl_SetClass (IStorage)
1059 * This method will write the specified CLSID in the directory entry of this
1060 * storage.
1062 * See Windows documentation for more details on IStorage methods.
1064 static HRESULT WINAPI StorageBaseImpl_SetClass(
1065 IStorage* iface,
1066 REFCLSID clsid) /* [in] */
1068 StorageBaseImpl *This = impl_from_IStorage(iface);
1069 HRESULT hRes;
1070 DirEntry currentEntry;
1072 TRACE("(%p, %p)\n", iface, clsid);
1074 if (This->reverted)
1075 return STG_E_REVERTED;
1077 hRes = StorageBaseImpl_ReadDirEntry(This,
1078 This->storageDirEntry,
1079 &currentEntry);
1080 if (SUCCEEDED(hRes))
1082 currentEntry.clsid = *clsid;
1084 hRes = StorageBaseImpl_WriteDirEntry(This,
1085 This->storageDirEntry,
1086 &currentEntry);
1089 if (SUCCEEDED(hRes))
1090 hRes = StorageBaseImpl_Flush(This);
1092 return hRes;
1095 /************************************************************************
1096 ** Storage32Impl implementation
1099 /************************************************************************
1100 * Storage32BaseImpl_CreateStorage (IStorage)
1102 * This method will create the storage object within the provided storage.
1104 * See Windows documentation for more details on IStorage methods.
1106 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1107 IStorage* iface,
1108 const OLECHAR *pwcsName, /* [string][in] */
1109 DWORD grfMode, /* [in] */
1110 DWORD reserved1, /* [in] */
1111 DWORD reserved2, /* [in] */
1112 IStorage **ppstg) /* [out] */
1114 StorageBaseImpl* This = impl_from_IStorage(iface);
1116 DirEntry currentEntry;
1117 DirEntry newEntry;
1118 DirRef currentEntryRef;
1119 DirRef newEntryRef;
1120 HRESULT hr;
1122 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1123 iface, debugstr_w(pwcsName), grfMode,
1124 reserved1, reserved2, ppstg);
1126 if (ppstg == 0)
1127 return STG_E_INVALIDPOINTER;
1129 if (This->openFlags & STGM_SIMPLE)
1131 return STG_E_INVALIDFUNCTION;
1134 if (pwcsName == 0)
1135 return STG_E_INVALIDNAME;
1137 *ppstg = NULL;
1139 if ( FAILED( validateSTGM(grfMode) ) ||
1140 (grfMode & STGM_DELETEONRELEASE) )
1142 WARN("bad grfMode: 0x%x\n", grfMode);
1143 return STG_E_INVALIDFLAG;
1146 if (This->reverted)
1147 return STG_E_REVERTED;
1150 * Check that we're compatible with the parent's storage mode
1152 if ( !(This->openFlags & STGM_TRANSACTED) &&
1153 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1155 WARN("access denied\n");
1156 return STG_E_ACCESSDENIED;
1159 currentEntryRef = findElement(This,
1160 This->storageDirEntry,
1161 pwcsName,
1162 &currentEntry);
1164 if (currentEntryRef != DIRENTRY_NULL)
1167 * An element with this name already exists
1169 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1170 ((This->openFlags & STGM_TRANSACTED) ||
1171 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1173 hr = IStorage_DestroyElement(iface, pwcsName);
1174 if (FAILED(hr))
1175 return hr;
1177 else
1179 WARN("file already exists\n");
1180 return STG_E_FILEALREADYEXISTS;
1183 else if (!(This->openFlags & STGM_TRANSACTED) &&
1184 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1186 WARN("read-only storage\n");
1187 return STG_E_ACCESSDENIED;
1190 memset(&newEntry, 0, sizeof(DirEntry));
1192 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1194 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1196 FIXME("name too long\n");
1197 return STG_E_INVALIDNAME;
1200 strcpyW(newEntry.name, pwcsName);
1202 newEntry.stgType = STGTY_STORAGE;
1203 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1204 newEntry.size.u.LowPart = 0;
1205 newEntry.size.u.HighPart = 0;
1207 newEntry.leftChild = DIRENTRY_NULL;
1208 newEntry.rightChild = DIRENTRY_NULL;
1209 newEntry.dirRootEntry = DIRENTRY_NULL;
1211 /* call CoFileTime to get the current time
1212 newEntry.ctime
1213 newEntry.mtime
1216 /* newEntry.clsid */
1219 * Create a new directory entry for the storage
1221 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1222 if (FAILED(hr))
1223 return hr;
1226 * Insert the new directory entry into the parent storage's tree
1228 hr = insertIntoTree(
1229 This,
1230 This->storageDirEntry,
1231 newEntryRef);
1232 if (FAILED(hr))
1234 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1235 return hr;
1239 * Open it to get a pointer to return.
1241 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1243 if( (hr != S_OK) || (*ppstg == NULL))
1245 return hr;
1248 if (SUCCEEDED(hr))
1249 hr = StorageBaseImpl_Flush(This);
1251 return S_OK;
1255 /***************************************************************************
1257 * Internal Method
1259 * Reserve a directory entry in the file and initialize it.
1261 static HRESULT StorageImpl_CreateDirEntry(
1262 StorageBaseImpl *base,
1263 const DirEntry *newData,
1264 DirRef *index)
1266 StorageImpl *storage = (StorageImpl*)base;
1267 ULONG currentEntryIndex = 0;
1268 ULONG newEntryIndex = DIRENTRY_NULL;
1269 HRESULT hr = S_OK;
1270 BYTE currentData[RAW_DIRENTRY_SIZE];
1271 WORD sizeOfNameString;
1275 hr = StorageImpl_ReadRawDirEntry(storage,
1276 currentEntryIndex,
1277 currentData);
1279 if (SUCCEEDED(hr))
1281 StorageUtl_ReadWord(
1282 currentData,
1283 OFFSET_PS_NAMELENGTH,
1284 &sizeOfNameString);
1286 if (sizeOfNameString == 0)
1289 * The entry exists and is available, we found it.
1291 newEntryIndex = currentEntryIndex;
1294 else
1297 * We exhausted the directory entries, we will create more space below
1299 newEntryIndex = currentEntryIndex;
1301 currentEntryIndex++;
1303 } while (newEntryIndex == DIRENTRY_NULL);
1306 * grow the directory stream
1308 if (FAILED(hr))
1310 BYTE emptyData[RAW_DIRENTRY_SIZE];
1311 ULARGE_INTEGER newSize;
1312 ULONG entryIndex;
1313 ULONG lastEntry = 0;
1314 ULONG blockCount = 0;
1317 * obtain the new count of blocks in the directory stream
1319 blockCount = BlockChainStream_GetCount(
1320 storage->rootBlockChain)+1;
1323 * initialize the size used by the directory stream
1325 newSize.QuadPart = (ULONGLONG)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 = offset.QuadPart + size;
2606 if (newSize.QuadPart > data.size.QuadPart)
2608 hr = StorageImpl_StreamSetSize(base, index, newSize);
2609 if (FAILED(hr))
2610 return hr;
2612 hr = StorageImpl_ReadDirEntry(This, index, &data);
2613 if (FAILED(hr)) return hr;
2616 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2618 SmallBlockChainStream *stream;
2620 stream = SmallBlockChainStream_Construct(This, NULL, index);
2621 if (!stream) return E_OUTOFMEMORY;
2623 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2625 SmallBlockChainStream_Destroy(stream);
2627 return hr;
2629 else
2631 BlockChainStream *stream;
2633 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2634 if (!stream) return E_OUTOFMEMORY;
2636 return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2640 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2641 DirRef src)
2643 StorageImpl *This = (StorageImpl*)base;
2644 DirEntry dst_data, src_data;
2645 HRESULT hr;
2647 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2649 if (SUCCEEDED(hr))
2650 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2652 if (SUCCEEDED(hr))
2654 StorageImpl_DeleteCachedBlockChainStream(This, src);
2655 dst_data.startingBlock = src_data.startingBlock;
2656 dst_data.size = src_data.size;
2658 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2661 return hr;
2664 static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
2665 ULONG* result, BOOL refresh)
2667 StorageImpl *This = (StorageImpl*)base;
2668 HRESULT hr=S_OK;
2669 DWORD oldTransactionSig = This->transactionSig;
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 StorageUtl_ReadDWord(data, 0, &This->transactionSig);
2685 if (oldTransactionSig != This->transactionSig)
2687 /* Someone else wrote to this, so toss all cached information. */
2688 TRACE("signature changed\n");
2690 hr = StorageImpl_Refresh(This, FALSE, FALSE);
2693 if (FAILED(hr))
2694 This->transactionSig = oldTransactionSig;
2698 *result = This->transactionSig;
2700 return hr;
2703 static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
2704 ULONG value)
2706 StorageImpl *This = (StorageImpl*)base;
2708 This->transactionSig = value;
2709 StorageImpl_SaveFileHeader(This);
2711 return S_OK;
2714 static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
2716 StorageImpl *This = (StorageImpl*)base;
2717 HRESULT hr;
2718 ULARGE_INTEGER offset, cb;
2720 if (write)
2722 /* Synchronous grab of second priority range, the commit lock, and the
2723 * lock-checking lock. */
2724 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
2725 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
2727 else
2729 offset.QuadPart = RANGELOCK_COMMIT;
2730 cb.QuadPart = 1;
2733 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
2735 if (hr == STG_E_INVALIDFUNCTION)
2736 hr = S_OK;
2738 return hr;
2741 static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
2743 StorageImpl *This = (StorageImpl*)base;
2744 HRESULT hr;
2745 ULARGE_INTEGER offset, cb;
2747 if (write)
2749 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
2750 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
2752 else
2754 offset.QuadPart = RANGELOCK_COMMIT;
2755 cb.QuadPart = 1;
2758 hr = ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2760 if (hr == STG_E_INVALIDFUNCTION)
2761 hr = S_OK;
2763 return hr;
2766 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2768 StorageImpl *This = (StorageImpl*) iface;
2769 STATSTG statstg;
2770 HRESULT hr;
2772 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2774 *result = statstg.pwcsName;
2776 return hr;
2779 static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
2781 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2782 return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
2785 static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
2787 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2788 return IStorage_AddRef(&This->IStorage_iface);
2791 static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
2793 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2794 return IStorage_Release(&This->IStorage_iface);
2797 static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
2799 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2800 FIXME("(%p)->(%d): stub\n", This, timeout);
2801 return E_NOTIMPL;
2804 static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
2806 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2807 FIXME("(%p): stub\n", This);
2808 return E_NOTIMPL;
2811 static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
2813 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2814 FIXME("(%p): stub\n", This);
2815 return E_NOTIMPL;
2818 static const IDirectWriterLockVtbl DirectWriterLockVtbl =
2820 directwriterlock_QueryInterface,
2821 directwriterlock_AddRef,
2822 directwriterlock_Release,
2823 directwriterlock_WaitForWriteAccess,
2824 directwriterlock_ReleaseWriteAccess,
2825 directwriterlock_HaveWriteAccess
2829 * Virtual function table for the IStorage32Impl class.
2831 static const IStorageVtbl Storage32Impl_Vtbl =
2833 StorageBaseImpl_QueryInterface,
2834 StorageBaseImpl_AddRef,
2835 StorageBaseImpl_Release,
2836 StorageBaseImpl_CreateStream,
2837 StorageBaseImpl_OpenStream,
2838 StorageBaseImpl_CreateStorage,
2839 StorageBaseImpl_OpenStorage,
2840 StorageBaseImpl_CopyTo,
2841 StorageBaseImpl_MoveElementTo,
2842 StorageImpl_Commit,
2843 StorageImpl_Revert,
2844 StorageBaseImpl_EnumElements,
2845 StorageBaseImpl_DestroyElement,
2846 StorageBaseImpl_RenameElement,
2847 StorageBaseImpl_SetElementTimes,
2848 StorageBaseImpl_SetClass,
2849 StorageBaseImpl_SetStateBits,
2850 StorageBaseImpl_Stat
2853 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2855 StorageImpl_Destroy,
2856 StorageImpl_Invalidate,
2857 StorageImpl_Flush,
2858 StorageImpl_GetFilename,
2859 StorageImpl_CreateDirEntry,
2860 StorageImpl_BaseWriteDirEntry,
2861 StorageImpl_BaseReadDirEntry,
2862 StorageImpl_DestroyDirEntry,
2863 StorageImpl_StreamReadAt,
2864 StorageImpl_StreamWriteAt,
2865 StorageImpl_StreamSetSize,
2866 StorageImpl_StreamLink,
2867 StorageImpl_GetTransactionSig,
2868 StorageImpl_SetTransactionSig,
2869 StorageImpl_LockTransaction,
2870 StorageImpl_UnlockTransaction
2873 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
2874 ULARGE_INTEGER cb, DWORD dwLockType)
2876 HRESULT hr;
2877 int delay = 0;
2879 /* if it's a FileLockBytesImpl use LockFileEx in blocking mode */
2880 if (SUCCEEDED(FileLockBytesImpl_LockRegionSync(This->lockBytes, offset, cb)))
2881 return S_OK;
2883 /* otherwise we have to fake it based on an async lock */
2886 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
2888 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
2890 Sleep(delay);
2891 if (delay < 150) delay++;
2893 } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION);
2895 return hr;
2898 static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
2899 ULONG end, HRESULT fail_hr)
2901 HRESULT hr;
2902 ULARGE_INTEGER offset, cb;
2904 offset.QuadPart = start;
2905 cb.QuadPart = 1 + end - start;
2907 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2908 if (SUCCEEDED(hr)) ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2910 if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
2911 return fail_hr;
2912 else
2913 return S_OK;
2916 static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
2918 HRESULT hr=S_OK;
2919 int i, j;
2920 ULARGE_INTEGER offset, cb;
2922 cb.QuadPart = 1;
2924 for (i=start; i<=end; i++)
2926 offset.QuadPart = i;
2927 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2928 if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION)
2929 break;
2932 if (SUCCEEDED(hr))
2934 for (j=0; j<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); j++)
2936 if (This->locked_bytes[j] == 0)
2938 This->locked_bytes[j] = i;
2939 break;
2944 return hr;
2947 static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
2949 HRESULT hr;
2950 ULARGE_INTEGER offset;
2951 ULARGE_INTEGER cb;
2952 DWORD share_mode = STGM_SHARE_MODE(openFlags);
2954 if (openFlags & STGM_NOSNAPSHOT)
2956 /* STGM_NOSNAPSHOT implies deny write */
2957 if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
2958 else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
2961 /* Wrap all other locking inside a single lock so we can check ranges safely */
2962 offset.QuadPart = RANGELOCK_CHECKLOCKS;
2963 cb.QuadPart = 1;
2964 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
2966 /* If the ILockBytes doesn't support locking that's ok. */
2967 if (FAILED(hr)) return S_OK;
2969 hr = S_OK;
2971 /* First check for any conflicting locks. */
2972 if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY)
2973 hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
2975 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
2976 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
2978 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
2979 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
2981 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
2982 hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
2984 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
2985 hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
2987 /* Then grab our locks. */
2988 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
2990 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
2991 if (SUCCEEDED(hr))
2992 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
2995 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
2996 hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
2998 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
2999 hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
3001 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
3002 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
3004 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
3005 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
3007 if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
3008 hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
3010 offset.QuadPart = RANGELOCK_CHECKLOCKS;
3011 cb.QuadPart = 1;
3012 ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
3014 return hr;
3017 static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
3019 HRESULT hr=S_OK;
3020 DirEntry currentEntry;
3021 DirRef currentEntryRef;
3022 BlockChainStream *blockChainStream;
3024 if (create)
3026 ULARGE_INTEGER size;
3027 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
3029 /* Discard any existing data. */
3030 size.QuadPart = 0;
3031 ILockBytes_SetSize(This->lockBytes, size);
3034 * Initialize all header variables:
3035 * - The big block depot consists of one block and it is at block 0
3036 * - The directory table starts at block 1
3037 * - There is no small block depot
3039 memset( This->bigBlockDepotStart,
3040 BLOCK_UNUSED,
3041 sizeof(This->bigBlockDepotStart));
3043 This->bigBlockDepotCount = 1;
3044 This->bigBlockDepotStart[0] = 0;
3045 This->rootStartBlock = 1;
3046 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
3047 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
3048 if (This->bigBlockSize == 4096)
3049 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
3050 else
3051 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
3052 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
3053 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
3054 This->extBigBlockDepotCount = 0;
3056 StorageImpl_SaveFileHeader(This);
3059 * Add one block for the big block depot and one block for the directory table
3061 size.u.HighPart = 0;
3062 size.u.LowPart = This->bigBlockSize * 3;
3063 ILockBytes_SetSize(This->lockBytes, size);
3066 * Initialize the big block depot
3068 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3069 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
3070 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
3071 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
3073 else
3076 * Load the header for the file.
3078 hr = StorageImpl_LoadFileHeader(This);
3080 if (FAILED(hr))
3082 return hr;
3087 * There is no block depot cached yet.
3089 This->indexBlockDepotCached = 0xFFFFFFFF;
3090 This->indexExtBlockDepotCached = 0xFFFFFFFF;
3093 * Start searching for free blocks with block 0.
3095 This->prevFreeBlock = 0;
3097 This->firstFreeSmallBlock = 0;
3099 /* Read the extended big block depot locations. */
3100 if (This->extBigBlockDepotCount != 0)
3102 ULONG current_block = This->extBigBlockDepotStart;
3103 ULONG cache_size = This->extBigBlockDepotCount * 2;
3104 ULONG i;
3106 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
3107 if (!This->extBigBlockDepotLocations)
3109 return E_OUTOFMEMORY;
3112 This->extBigBlockDepotLocationsSize = cache_size;
3114 for (i=0; i<This->extBigBlockDepotCount; i++)
3116 if (current_block == BLOCK_END_OF_CHAIN)
3118 WARN("File has too few extended big block depot blocks.\n");
3119 return STG_E_DOCFILECORRUPT;
3121 This->extBigBlockDepotLocations[i] = current_block;
3122 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
3125 else
3127 This->extBigBlockDepotLocations = NULL;
3128 This->extBigBlockDepotLocationsSize = 0;
3132 * Create the block chain abstractions.
3134 if(!(blockChainStream =
3135 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
3137 return STG_E_READFAULT;
3139 if (!new_object)
3140 BlockChainStream_Destroy(This->rootBlockChain);
3141 This->rootBlockChain = blockChainStream;
3143 if(!(blockChainStream =
3144 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
3145 DIRENTRY_NULL)))
3147 return STG_E_READFAULT;
3149 if (!new_object)
3150 BlockChainStream_Destroy(This->smallBlockDepotChain);
3151 This->smallBlockDepotChain = blockChainStream;
3154 * Write the root storage entry (memory only)
3156 if (create)
3158 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
3159 DirEntry rootEntry;
3161 * Initialize the directory table
3163 memset(&rootEntry, 0, sizeof(rootEntry));
3164 strcpyW(rootEntry.name, rootentryW);
3165 rootEntry.sizeOfNameString = sizeof(rootentryW);
3166 rootEntry.stgType = STGTY_ROOT;
3167 rootEntry.leftChild = DIRENTRY_NULL;
3168 rootEntry.rightChild = DIRENTRY_NULL;
3169 rootEntry.dirRootEntry = DIRENTRY_NULL;
3170 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
3171 rootEntry.size.u.HighPart = 0;
3172 rootEntry.size.u.LowPart = 0;
3174 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
3178 * Find the ID of the root storage.
3180 currentEntryRef = 0;
3184 hr = StorageImpl_ReadDirEntry(
3185 This,
3186 currentEntryRef,
3187 &currentEntry);
3189 if (SUCCEEDED(hr))
3191 if ( (currentEntry.sizeOfNameString != 0 ) &&
3192 (currentEntry.stgType == STGTY_ROOT) )
3194 This->base.storageDirEntry = currentEntryRef;
3198 currentEntryRef++;
3200 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
3202 if (FAILED(hr))
3204 return STG_E_READFAULT;
3208 * Create the block chain abstraction for the small block root chain.
3210 if(!(blockChainStream =
3211 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
3213 return STG_E_READFAULT;
3215 if (!new_object)
3216 BlockChainStream_Destroy(This->smallBlockRootChain);
3217 This->smallBlockRootChain = blockChainStream;
3219 if (!new_object)
3221 int i;
3222 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
3224 BlockChainStream_Destroy(This->blockChainCache[i]);
3225 This->blockChainCache[i] = NULL;
3229 return hr;
3232 static HRESULT StorageImpl_Construct(
3233 HANDLE hFile,
3234 LPCOLESTR pwcsName,
3235 ILockBytes* pLkbyt,
3236 DWORD openFlags,
3237 BOOL fileBased,
3238 BOOL create,
3239 ULONG sector_size,
3240 StorageImpl** result)
3242 StorageImpl* This;
3243 HRESULT hr = S_OK;
3245 if ( FAILED( validateSTGM(openFlags) ))
3246 return STG_E_INVALIDFLAG;
3248 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
3249 if (!This)
3250 return E_OUTOFMEMORY;
3252 memset(This, 0, sizeof(StorageImpl));
3254 list_init(&This->base.strmHead);
3256 list_init(&This->base.storageHead);
3258 This->base.IStorage_iface.lpVtbl = &Storage32Impl_Vtbl;
3259 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
3260 This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
3261 This->base.baseVtbl = &StorageImpl_BaseVtbl;
3262 This->base.openFlags = (openFlags & ~STGM_CREATE);
3263 This->base.ref = 1;
3264 This->base.create = create;
3266 if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
3267 This->base.lockingrole = SWMR_Writer;
3268 else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
3269 This->base.lockingrole = SWMR_Reader;
3270 else
3271 This->base.lockingrole = SWMR_None;
3273 This->base.reverted = FALSE;
3276 * Initialize the big block cache.
3278 This->bigBlockSize = sector_size;
3279 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
3280 if (hFile)
3281 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
3282 else
3284 This->lockBytes = pLkbyt;
3285 ILockBytes_AddRef(pLkbyt);
3288 if (SUCCEEDED(hr))
3289 hr = StorageImpl_GrabLocks(This, openFlags);
3291 if (SUCCEEDED(hr))
3292 hr = StorageImpl_Refresh(This, TRUE, create);
3294 if (FAILED(hr))
3296 IStorage_Release(&This->base.IStorage_iface);
3297 *result = NULL;
3299 else
3301 StorageImpl_Flush(&This->base);
3302 *result = This;
3305 return hr;
3308 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
3310 StorageImpl *This = (StorageImpl*) iface;
3312 StorageBaseImpl_DeleteAll(&This->base);
3314 This->base.reverted = TRUE;
3317 static void StorageImpl_Destroy(StorageBaseImpl* iface)
3319 StorageImpl *This = (StorageImpl*) iface;
3320 int i;
3321 TRACE("(%p)\n", This);
3323 StorageImpl_Flush(iface);
3325 StorageImpl_Invalidate(iface);
3327 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3329 BlockChainStream_Destroy(This->smallBlockRootChain);
3330 BlockChainStream_Destroy(This->rootBlockChain);
3331 BlockChainStream_Destroy(This->smallBlockDepotChain);
3333 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
3334 BlockChainStream_Destroy(This->blockChainCache[i]);
3336 for (i=0; i<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); i++)
3338 ULARGE_INTEGER offset, cb;
3339 cb.QuadPart = 1;
3340 if (This->locked_bytes[i] != 0)
3342 offset.QuadPart = This->locked_bytes[i];
3343 ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
3347 if (This->lockBytes)
3348 ILockBytes_Release(This->lockBytes);
3349 HeapFree(GetProcessHeap(), 0, This);
3352 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
3354 StorageImpl *This = (StorageImpl*)storage;
3355 int i;
3356 HRESULT hr;
3357 TRACE("(%p)\n", This);
3359 hr = BlockChainStream_Flush(This->smallBlockRootChain);
3361 if (SUCCEEDED(hr))
3362 hr = BlockChainStream_Flush(This->rootBlockChain);
3364 if (SUCCEEDED(hr))
3365 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
3367 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
3368 if (This->blockChainCache[i])
3369 hr = BlockChainStream_Flush(This->blockChainCache[i]);
3371 if (SUCCEEDED(hr))
3372 hr = ILockBytes_Flush(This->lockBytes);
3374 return hr;
3377 /******************************************************************************
3378 * Storage32Impl_GetNextFreeBigBlock
3380 * Returns the index of the next free big block.
3381 * If the big block depot is filled, this method will enlarge it.
3384 static ULONG StorageImpl_GetNextFreeBigBlock(
3385 StorageImpl* This)
3387 ULONG depotBlockIndexPos;
3388 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3389 ULONG depotBlockOffset;
3390 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3391 ULONG nextBlockIndex = BLOCK_SPECIAL;
3392 int depotIndex = 0;
3393 ULONG freeBlock = BLOCK_UNUSED;
3394 ULONG read;
3395 ULARGE_INTEGER neededSize;
3396 STATSTG statstg;
3398 depotIndex = This->prevFreeBlock / blocksPerDepot;
3399 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
3402 * Scan the entire big block depot until we find a block marked free
3404 while (nextBlockIndex != BLOCK_UNUSED)
3406 if (depotIndex < COUNT_BBDEPOTINHEADER)
3408 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
3411 * Grow the primary depot.
3413 if (depotBlockIndexPos == BLOCK_UNUSED)
3415 depotBlockIndexPos = depotIndex*blocksPerDepot;
3418 * Add a block depot.
3420 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
3421 This->bigBlockDepotCount++;
3422 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3425 * Flag it as a block depot.
3427 StorageImpl_SetNextBlockInChain(This,
3428 depotBlockIndexPos,
3429 BLOCK_SPECIAL);
3431 /* Save new header information.
3433 StorageImpl_SaveFileHeader(This);
3436 else
3438 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3440 if (depotBlockIndexPos == BLOCK_UNUSED)
3443 * Grow the extended depot.
3445 ULONG extIndex = BLOCK_UNUSED;
3446 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3447 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3449 if (extBlockOffset == 0)
3451 /* We need an extended block.
3453 extIndex = Storage32Impl_AddExtBlockDepot(This);
3454 This->extBigBlockDepotCount++;
3455 depotBlockIndexPos = extIndex + 1;
3457 else
3458 depotBlockIndexPos = depotIndex * blocksPerDepot;
3461 * Add a block depot and mark it in the extended block.
3463 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
3464 This->bigBlockDepotCount++;
3465 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3467 /* Flag the block depot.
3469 StorageImpl_SetNextBlockInChain(This,
3470 depotBlockIndexPos,
3471 BLOCK_SPECIAL);
3473 /* If necessary, flag the extended depot block.
3475 if (extIndex != BLOCK_UNUSED)
3476 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3478 /* Save header information.
3480 StorageImpl_SaveFileHeader(This);
3484 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
3486 if (read)
3488 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3489 ( nextBlockIndex != BLOCK_UNUSED))
3491 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3493 if (nextBlockIndex == BLOCK_UNUSED)
3495 freeBlock = (depotIndex * blocksPerDepot) +
3496 (depotBlockOffset/sizeof(ULONG));
3499 depotBlockOffset += sizeof(ULONG);
3503 depotIndex++;
3504 depotBlockOffset = 0;
3508 * make sure that the block physically exists before using it
3510 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3512 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3514 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3515 ILockBytes_SetSize(This->lockBytes, neededSize);
3517 This->prevFreeBlock = freeBlock;
3519 return freeBlock;
3522 /******************************************************************************
3523 * Storage32Impl_AddBlockDepot
3525 * This will create a depot block, essentially it is a block initialized
3526 * to BLOCK_UNUSEDs.
3528 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
3530 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3531 ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
3532 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3533 ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
3536 * Initialize blocks as free
3538 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3540 /* Reserve the range lock sector */
3541 if (depotIndex == rangeLockDepot)
3543 ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
3546 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3549 /******************************************************************************
3550 * Storage32Impl_GetExtDepotBlock
3552 * Returns the index of the block that corresponds to the specified depot
3553 * index. This method is only for depot indexes equal or greater than
3554 * COUNT_BBDEPOTINHEADER.
3556 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3558 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3559 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3560 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3561 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3562 ULONG blockIndex = BLOCK_UNUSED;
3563 ULONG extBlockIndex;
3564 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3565 int index, num_blocks;
3567 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3569 if (extBlockCount >= This->extBigBlockDepotCount)
3570 return BLOCK_UNUSED;
3572 if (This->indexExtBlockDepotCached != extBlockCount)
3574 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3576 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
3578 num_blocks = This->bigBlockSize / 4;
3580 for (index = 0; index < num_blocks; index++)
3582 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3583 This->extBlockDepotCached[index] = blockIndex;
3586 This->indexExtBlockDepotCached = extBlockCount;
3589 blockIndex = This->extBlockDepotCached[extBlockOffset];
3591 return blockIndex;
3594 /******************************************************************************
3595 * Storage32Impl_SetExtDepotBlock
3597 * Associates the specified block index to the specified depot index.
3598 * This method is only for depot indexes equal or greater than
3599 * COUNT_BBDEPOTINHEADER.
3601 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3603 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3604 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3605 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3606 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3607 ULONG extBlockIndex;
3609 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3611 assert(extBlockCount < This->extBigBlockDepotCount);
3613 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3615 if (extBlockIndex != BLOCK_UNUSED)
3617 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3618 extBlockOffset * sizeof(ULONG),
3619 blockIndex);
3622 if (This->indexExtBlockDepotCached == extBlockCount)
3624 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3628 /******************************************************************************
3629 * Storage32Impl_AddExtBlockDepot
3631 * Creates an extended depot block.
3633 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3635 ULONG numExtBlocks = This->extBigBlockDepotCount;
3636 ULONG nextExtBlock = This->extBigBlockDepotStart;
3637 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3638 ULONG index = BLOCK_UNUSED;
3639 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3640 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3641 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3643 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3644 blocksPerDepotBlock;
3646 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3649 * The first extended block.
3651 This->extBigBlockDepotStart = index;
3653 else
3656 * Find the last existing extended block.
3658 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3661 * Add the new extended block to the chain.
3663 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3664 index);
3668 * Initialize this block.
3670 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3671 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3673 /* Add the block to our cache. */
3674 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3676 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3677 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3679 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3680 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3682 This->extBigBlockDepotLocations = new_cache;
3683 This->extBigBlockDepotLocationsSize = new_cache_size;
3685 This->extBigBlockDepotLocations[numExtBlocks] = index;
3687 return index;
3690 /******************************************************************************
3691 * Storage32Impl_FreeBigBlock
3693 * This method will flag the specified block as free in the big block depot.
3695 static void StorageImpl_FreeBigBlock(
3696 StorageImpl* This,
3697 ULONG blockIndex)
3699 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3701 if (blockIndex < This->prevFreeBlock)
3702 This->prevFreeBlock = blockIndex;
3705 /************************************************************************
3706 * Storage32Impl_GetNextBlockInChain
3708 * This method will retrieve the block index of the next big block in
3709 * in the chain.
3711 * Params: This - Pointer to the Storage object.
3712 * blockIndex - Index of the block to retrieve the chain
3713 * for.
3714 * nextBlockIndex - receives the return value.
3716 * Returns: This method returns the index of the next block in the chain.
3717 * It will return the constants:
3718 * BLOCK_SPECIAL - If the block given was not part of a
3719 * chain.
3720 * BLOCK_END_OF_CHAIN - If the block given was the last in
3721 * a chain.
3722 * BLOCK_UNUSED - If the block given was not past of a chain
3723 * and is available.
3724 * BLOCK_EXTBBDEPOT - This block is part of the extended
3725 * big block depot.
3727 * See Windows documentation for more details on IStorage methods.
3729 static HRESULT StorageImpl_GetNextBlockInChain(
3730 StorageImpl* This,
3731 ULONG blockIndex,
3732 ULONG* nextBlockIndex)
3734 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3735 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3736 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3737 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3738 ULONG read;
3739 ULONG depotBlockIndexPos;
3740 int index, num_blocks;
3742 *nextBlockIndex = BLOCK_SPECIAL;
3744 if(depotBlockCount >= This->bigBlockDepotCount)
3746 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3747 This->bigBlockDepotCount);
3748 return STG_E_READFAULT;
3752 * Cache the currently accessed depot block.
3754 if (depotBlockCount != This->indexBlockDepotCached)
3756 This->indexBlockDepotCached = depotBlockCount;
3758 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3760 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3762 else
3765 * We have to look in the extended depot.
3767 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3770 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
3772 if (!read)
3773 return STG_E_READFAULT;
3775 num_blocks = This->bigBlockSize / 4;
3777 for (index = 0; index < num_blocks; index++)
3779 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3780 This->blockDepotCached[index] = *nextBlockIndex;
3784 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3786 return S_OK;
3789 /******************************************************************************
3790 * Storage32Impl_GetNextExtendedBlock
3792 * Given an extended block this method will return the next extended block.
3794 * NOTES:
3795 * The last ULONG of an extended block is the block index of the next
3796 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3797 * depot.
3799 * Return values:
3800 * - The index of the next extended block
3801 * - BLOCK_UNUSED: there is no next extended block.
3802 * - Any other return values denotes failure.
3804 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3806 ULONG nextBlockIndex = BLOCK_SPECIAL;
3807 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3809 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3810 &nextBlockIndex);
3812 return nextBlockIndex;
3815 /******************************************************************************
3816 * Storage32Impl_SetNextBlockInChain
3818 * This method will write the index of the specified block's next block
3819 * in the big block depot.
3821 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3822 * do the following
3824 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3825 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3826 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3829 static void StorageImpl_SetNextBlockInChain(
3830 StorageImpl* This,
3831 ULONG blockIndex,
3832 ULONG nextBlock)
3834 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3835 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3836 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3837 ULONG depotBlockIndexPos;
3839 assert(depotBlockCount < This->bigBlockDepotCount);
3840 assert(blockIndex != nextBlock);
3842 if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
3843 /* This should never happen (storage file format spec forbids it), but
3844 * older versions of Wine may have generated broken files. We don't want to
3845 * assert and potentially lose data, but we do want to know if this ever
3846 * happens in a newly-created file. */
3847 ERR("Using range lock page\n");
3849 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3851 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3853 else
3856 * We have to look in the extended depot.
3858 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3861 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3862 nextBlock);
3864 * Update the cached block depot, if necessary.
3866 if (depotBlockCount == This->indexBlockDepotCached)
3868 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3872 /******************************************************************************
3873 * Storage32Impl_LoadFileHeader
3875 * This method will read in the file header
3877 static HRESULT StorageImpl_LoadFileHeader(
3878 StorageImpl* This)
3880 HRESULT hr;
3881 BYTE headerBigBlock[HEADER_SIZE];
3882 int index;
3883 ULARGE_INTEGER offset;
3884 DWORD bytes_read;
3886 TRACE("\n");
3888 * Get a pointer to the big block of data containing the header.
3890 offset.u.HighPart = 0;
3891 offset.u.LowPart = 0;
3892 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3893 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3894 hr = STG_E_FILENOTFOUND;
3897 * Extract the information from the header.
3899 if (SUCCEEDED(hr))
3902 * Check for the "magic number" signature and return an error if it is not
3903 * found.
3905 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3907 return STG_E_OLDFORMAT;
3910 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3912 return STG_E_INVALIDHEADER;
3915 StorageUtl_ReadWord(
3916 headerBigBlock,
3917 OFFSET_BIGBLOCKSIZEBITS,
3918 &This->bigBlockSizeBits);
3920 StorageUtl_ReadWord(
3921 headerBigBlock,
3922 OFFSET_SMALLBLOCKSIZEBITS,
3923 &This->smallBlockSizeBits);
3925 StorageUtl_ReadDWord(
3926 headerBigBlock,
3927 OFFSET_BBDEPOTCOUNT,
3928 &This->bigBlockDepotCount);
3930 StorageUtl_ReadDWord(
3931 headerBigBlock,
3932 OFFSET_ROOTSTARTBLOCK,
3933 &This->rootStartBlock);
3935 StorageUtl_ReadDWord(
3936 headerBigBlock,
3937 OFFSET_TRANSACTIONSIG,
3938 &This->transactionSig);
3940 StorageUtl_ReadDWord(
3941 headerBigBlock,
3942 OFFSET_SMALLBLOCKLIMIT,
3943 &This->smallBlockLimit);
3945 StorageUtl_ReadDWord(
3946 headerBigBlock,
3947 OFFSET_SBDEPOTSTART,
3948 &This->smallBlockDepotStart);
3950 StorageUtl_ReadDWord(
3951 headerBigBlock,
3952 OFFSET_EXTBBDEPOTSTART,
3953 &This->extBigBlockDepotStart);
3955 StorageUtl_ReadDWord(
3956 headerBigBlock,
3957 OFFSET_EXTBBDEPOTCOUNT,
3958 &This->extBigBlockDepotCount);
3960 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3962 StorageUtl_ReadDWord(
3963 headerBigBlock,
3964 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3965 &(This->bigBlockDepotStart[index]));
3969 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3971 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3972 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3975 * Right now, the code is making some assumptions about the size of the
3976 * blocks, just make sure they are what we're expecting.
3978 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3979 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3980 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3982 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3983 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3984 hr = STG_E_INVALIDHEADER;
3986 else
3987 hr = S_OK;
3990 return hr;
3993 /******************************************************************************
3994 * Storage32Impl_SaveFileHeader
3996 * This method will save to the file the header
3998 static void StorageImpl_SaveFileHeader(
3999 StorageImpl* This)
4001 BYTE headerBigBlock[HEADER_SIZE];
4002 int index;
4003 HRESULT hr;
4004 ULARGE_INTEGER offset;
4005 DWORD bytes_read, bytes_written;
4006 DWORD major_version, dirsectorcount;
4009 * Get a pointer to the big block of data containing the header.
4011 offset.u.HighPart = 0;
4012 offset.u.LowPart = 0;
4013 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
4014 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
4015 hr = STG_E_FILENOTFOUND;
4017 if (This->bigBlockSizeBits == 0x9)
4018 major_version = 3;
4019 else if (This->bigBlockSizeBits == 0xc)
4020 major_version = 4;
4021 else
4023 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
4024 major_version = 4;
4028 * If the block read failed, the file is probably new.
4030 if (FAILED(hr))
4033 * Initialize for all unknown fields.
4035 memset(headerBigBlock, 0, HEADER_SIZE);
4038 * Initialize the magic number.
4040 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
4044 * Write the information to the header.
4046 StorageUtl_WriteWord(
4047 headerBigBlock,
4048 OFFSET_MINORVERSION,
4049 0x3e);
4051 StorageUtl_WriteWord(
4052 headerBigBlock,
4053 OFFSET_MAJORVERSION,
4054 major_version);
4056 StorageUtl_WriteWord(
4057 headerBigBlock,
4058 OFFSET_BYTEORDERMARKER,
4059 (WORD)-2);
4061 StorageUtl_WriteWord(
4062 headerBigBlock,
4063 OFFSET_BIGBLOCKSIZEBITS,
4064 This->bigBlockSizeBits);
4066 StorageUtl_WriteWord(
4067 headerBigBlock,
4068 OFFSET_SMALLBLOCKSIZEBITS,
4069 This->smallBlockSizeBits);
4071 if (major_version >= 4)
4073 if (This->rootBlockChain)
4074 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
4075 else
4076 /* This file is being created, and it will start out with one block. */
4077 dirsectorcount = 1;
4079 else
4080 /* This field must be 0 in versions older than 4 */
4081 dirsectorcount = 0;
4083 StorageUtl_WriteDWord(
4084 headerBigBlock,
4085 OFFSET_DIRSECTORCOUNT,
4086 dirsectorcount);
4088 StorageUtl_WriteDWord(
4089 headerBigBlock,
4090 OFFSET_BBDEPOTCOUNT,
4091 This->bigBlockDepotCount);
4093 StorageUtl_WriteDWord(
4094 headerBigBlock,
4095 OFFSET_ROOTSTARTBLOCK,
4096 This->rootStartBlock);
4098 StorageUtl_WriteDWord(
4099 headerBigBlock,
4100 OFFSET_TRANSACTIONSIG,
4101 This->transactionSig);
4103 StorageUtl_WriteDWord(
4104 headerBigBlock,
4105 OFFSET_SMALLBLOCKLIMIT,
4106 This->smallBlockLimit);
4108 StorageUtl_WriteDWord(
4109 headerBigBlock,
4110 OFFSET_SBDEPOTSTART,
4111 This->smallBlockDepotStart);
4113 StorageUtl_WriteDWord(
4114 headerBigBlock,
4115 OFFSET_SBDEPOTCOUNT,
4116 This->smallBlockDepotChain ?
4117 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
4119 StorageUtl_WriteDWord(
4120 headerBigBlock,
4121 OFFSET_EXTBBDEPOTSTART,
4122 This->extBigBlockDepotStart);
4124 StorageUtl_WriteDWord(
4125 headerBigBlock,
4126 OFFSET_EXTBBDEPOTCOUNT,
4127 This->extBigBlockDepotCount);
4129 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
4131 StorageUtl_WriteDWord(
4132 headerBigBlock,
4133 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
4134 (This->bigBlockDepotStart[index]));
4138 * Write the big block back to the file.
4140 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
4143 /******************************************************************************
4144 * StorageImpl_ReadRawDirEntry
4146 * This method will read the raw data from a directory entry in the file.
4148 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4150 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
4152 ULARGE_INTEGER offset;
4153 HRESULT hr;
4154 ULONG bytesRead;
4156 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
4158 hr = BlockChainStream_ReadAt(
4159 This->rootBlockChain,
4160 offset,
4161 RAW_DIRENTRY_SIZE,
4162 buffer,
4163 &bytesRead);
4165 if (bytesRead != RAW_DIRENTRY_SIZE)
4166 return STG_E_READFAULT;
4168 return hr;
4171 /******************************************************************************
4172 * StorageImpl_WriteRawDirEntry
4174 * This method will write the raw data from a directory entry in the file.
4176 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4178 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
4180 ULARGE_INTEGER offset;
4181 ULONG bytesRead;
4183 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
4185 return BlockChainStream_WriteAt(
4186 This->rootBlockChain,
4187 offset,
4188 RAW_DIRENTRY_SIZE,
4189 buffer,
4190 &bytesRead);
4193 /******************************************************************************
4194 * UpdateRawDirEntry
4196 * Update raw directory entry data from the fields in newData.
4198 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4200 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
4202 memset(buffer, 0, RAW_DIRENTRY_SIZE);
4204 memcpy(
4205 buffer + OFFSET_PS_NAME,
4206 newData->name,
4207 DIRENTRY_NAME_BUFFER_LEN );
4209 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
4211 StorageUtl_WriteWord(
4212 buffer,
4213 OFFSET_PS_NAMELENGTH,
4214 newData->sizeOfNameString);
4216 StorageUtl_WriteDWord(
4217 buffer,
4218 OFFSET_PS_LEFTCHILD,
4219 newData->leftChild);
4221 StorageUtl_WriteDWord(
4222 buffer,
4223 OFFSET_PS_RIGHTCHILD,
4224 newData->rightChild);
4226 StorageUtl_WriteDWord(
4227 buffer,
4228 OFFSET_PS_DIRROOT,
4229 newData->dirRootEntry);
4231 StorageUtl_WriteGUID(
4232 buffer,
4233 OFFSET_PS_GUID,
4234 &newData->clsid);
4236 StorageUtl_WriteDWord(
4237 buffer,
4238 OFFSET_PS_CTIMELOW,
4239 newData->ctime.dwLowDateTime);
4241 StorageUtl_WriteDWord(
4242 buffer,
4243 OFFSET_PS_CTIMEHIGH,
4244 newData->ctime.dwHighDateTime);
4246 StorageUtl_WriteDWord(
4247 buffer,
4248 OFFSET_PS_MTIMELOW,
4249 newData->mtime.dwLowDateTime);
4251 StorageUtl_WriteDWord(
4252 buffer,
4253 OFFSET_PS_MTIMEHIGH,
4254 newData->ctime.dwHighDateTime);
4256 StorageUtl_WriteDWord(
4257 buffer,
4258 OFFSET_PS_STARTBLOCK,
4259 newData->startingBlock);
4261 StorageUtl_WriteDWord(
4262 buffer,
4263 OFFSET_PS_SIZE,
4264 newData->size.u.LowPart);
4266 StorageUtl_WriteDWord(
4267 buffer,
4268 OFFSET_PS_SIZE_HIGH,
4269 newData->size.u.HighPart);
4272 /******************************************************************************
4273 * Storage32Impl_ReadDirEntry
4275 * This method will read the specified directory entry.
4277 HRESULT StorageImpl_ReadDirEntry(
4278 StorageImpl* This,
4279 DirRef index,
4280 DirEntry* buffer)
4282 BYTE currentEntry[RAW_DIRENTRY_SIZE];
4283 HRESULT readRes;
4285 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
4287 if (SUCCEEDED(readRes))
4289 memset(buffer->name, 0, sizeof(buffer->name));
4290 memcpy(
4291 buffer->name,
4292 (WCHAR *)currentEntry+OFFSET_PS_NAME,
4293 DIRENTRY_NAME_BUFFER_LEN );
4294 TRACE("storage name: %s\n", debugstr_w(buffer->name));
4296 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
4298 StorageUtl_ReadWord(
4299 currentEntry,
4300 OFFSET_PS_NAMELENGTH,
4301 &buffer->sizeOfNameString);
4303 StorageUtl_ReadDWord(
4304 currentEntry,
4305 OFFSET_PS_LEFTCHILD,
4306 &buffer->leftChild);
4308 StorageUtl_ReadDWord(
4309 currentEntry,
4310 OFFSET_PS_RIGHTCHILD,
4311 &buffer->rightChild);
4313 StorageUtl_ReadDWord(
4314 currentEntry,
4315 OFFSET_PS_DIRROOT,
4316 &buffer->dirRootEntry);
4318 StorageUtl_ReadGUID(
4319 currentEntry,
4320 OFFSET_PS_GUID,
4321 &buffer->clsid);
4323 StorageUtl_ReadDWord(
4324 currentEntry,
4325 OFFSET_PS_CTIMELOW,
4326 &buffer->ctime.dwLowDateTime);
4328 StorageUtl_ReadDWord(
4329 currentEntry,
4330 OFFSET_PS_CTIMEHIGH,
4331 &buffer->ctime.dwHighDateTime);
4333 StorageUtl_ReadDWord(
4334 currentEntry,
4335 OFFSET_PS_MTIMELOW,
4336 &buffer->mtime.dwLowDateTime);
4338 StorageUtl_ReadDWord(
4339 currentEntry,
4340 OFFSET_PS_MTIMEHIGH,
4341 &buffer->mtime.dwHighDateTime);
4343 StorageUtl_ReadDWord(
4344 currentEntry,
4345 OFFSET_PS_STARTBLOCK,
4346 &buffer->startingBlock);
4348 StorageUtl_ReadDWord(
4349 currentEntry,
4350 OFFSET_PS_SIZE,
4351 &buffer->size.u.LowPart);
4353 StorageUtl_ReadDWord(
4354 currentEntry,
4355 OFFSET_PS_SIZE_HIGH,
4356 &buffer->size.u.HighPart);
4359 return readRes;
4362 /*********************************************************************
4363 * Write the specified directory entry to the file
4365 HRESULT StorageImpl_WriteDirEntry(
4366 StorageImpl* This,
4367 DirRef index,
4368 const DirEntry* buffer)
4370 BYTE currentEntry[RAW_DIRENTRY_SIZE];
4372 UpdateRawDirEntry(currentEntry, buffer);
4374 return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
4377 static HRESULT StorageImpl_ReadBigBlock(
4378 StorageImpl* This,
4379 ULONG blockIndex,
4380 void* buffer,
4381 ULONG* out_read)
4383 ULARGE_INTEGER ulOffset;
4384 DWORD read=0;
4385 HRESULT hr;
4387 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4389 hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
4391 if (SUCCEEDED(hr) && read < This->bigBlockSize)
4393 /* File ends during this block; fill the rest with 0's. */
4394 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
4397 if (out_read) *out_read = read;
4399 return hr;
4402 static BOOL StorageImpl_ReadDWordFromBigBlock(
4403 StorageImpl* This,
4404 ULONG blockIndex,
4405 ULONG offset,
4406 DWORD* value)
4408 ULARGE_INTEGER ulOffset;
4409 DWORD read;
4410 DWORD tmp;
4412 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4413 ulOffset.QuadPart += offset;
4415 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
4416 *value = lendian32toh(tmp);
4417 return (read == sizeof(DWORD));
4420 static BOOL StorageImpl_WriteBigBlock(
4421 StorageImpl* This,
4422 ULONG blockIndex,
4423 const void* buffer)
4425 ULARGE_INTEGER ulOffset;
4426 DWORD wrote;
4428 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4430 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
4431 return (wrote == This->bigBlockSize);
4434 static BOOL StorageImpl_WriteDWordToBigBlock(
4435 StorageImpl* This,
4436 ULONG blockIndex,
4437 ULONG offset,
4438 DWORD value)
4440 ULARGE_INTEGER ulOffset;
4441 DWORD wrote;
4443 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4444 ulOffset.QuadPart += offset;
4446 value = htole32(value);
4447 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4448 return (wrote == sizeof(DWORD));
4451 /******************************************************************************
4452 * Storage32Impl_SmallBlocksToBigBlocks
4454 * This method will convert a small block chain to a big block chain.
4455 * The small block chain will be destroyed.
4457 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4458 StorageImpl* This,
4459 SmallBlockChainStream** ppsbChain)
4461 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4462 ULARGE_INTEGER size, offset;
4463 ULONG cbRead, cbWritten;
4464 ULARGE_INTEGER cbTotalRead;
4465 DirRef streamEntryRef;
4466 HRESULT resWrite = S_OK;
4467 HRESULT resRead;
4468 DirEntry streamEntry;
4469 BYTE *buffer;
4470 BlockChainStream *bbTempChain = NULL;
4471 BlockChainStream *bigBlockChain = NULL;
4474 * Create a temporary big block chain that doesn't have
4475 * an associated directory entry. This temporary chain will be
4476 * used to copy data from small blocks to big blocks.
4478 bbTempChain = BlockChainStream_Construct(This,
4479 &bbHeadOfChain,
4480 DIRENTRY_NULL);
4481 if(!bbTempChain) return NULL;
4483 * Grow the big block chain.
4485 size = SmallBlockChainStream_GetSize(*ppsbChain);
4486 BlockChainStream_SetSize(bbTempChain, size);
4489 * Copy the contents of the small block chain to the big block chain
4490 * by small block size increments.
4492 offset.u.LowPart = 0;
4493 offset.u.HighPart = 0;
4494 cbTotalRead.QuadPart = 0;
4496 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4499 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4500 offset,
4501 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4502 buffer,
4503 &cbRead);
4504 if (FAILED(resRead))
4505 break;
4507 if (cbRead > 0)
4509 cbTotalRead.QuadPart += cbRead;
4511 resWrite = BlockChainStream_WriteAt(bbTempChain,
4512 offset,
4513 cbRead,
4514 buffer,
4515 &cbWritten);
4517 if (FAILED(resWrite))
4518 break;
4520 offset.u.LowPart += cbRead;
4522 else
4524 resRead = STG_E_READFAULT;
4525 break;
4527 } while (cbTotalRead.QuadPart < size.QuadPart);
4528 HeapFree(GetProcessHeap(),0,buffer);
4530 size.u.HighPart = 0;
4531 size.u.LowPart = 0;
4533 if (FAILED(resRead) || FAILED(resWrite))
4535 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4536 BlockChainStream_SetSize(bbTempChain, size);
4537 BlockChainStream_Destroy(bbTempChain);
4538 return NULL;
4542 * Destroy the small block chain.
4544 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4545 SmallBlockChainStream_SetSize(*ppsbChain, size);
4546 SmallBlockChainStream_Destroy(*ppsbChain);
4547 *ppsbChain = 0;
4550 * Change the directory entry. This chain is now a big block chain
4551 * and it doesn't reside in the small blocks chain anymore.
4553 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4555 streamEntry.startingBlock = bbHeadOfChain;
4557 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4560 * Destroy the temporary entryless big block chain.
4561 * Create a new big block chain associated with this entry.
4563 BlockChainStream_Destroy(bbTempChain);
4564 bigBlockChain = BlockChainStream_Construct(This,
4565 NULL,
4566 streamEntryRef);
4568 return bigBlockChain;
4571 /******************************************************************************
4572 * Storage32Impl_BigBlocksToSmallBlocks
4574 * This method will convert a big block chain to a small block chain.
4575 * The big block chain will be destroyed on success.
4577 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4578 StorageImpl* This,
4579 BlockChainStream** ppbbChain,
4580 ULARGE_INTEGER newSize)
4582 ULARGE_INTEGER size, offset, cbTotalRead;
4583 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4584 DirRef streamEntryRef;
4585 HRESULT resWrite = S_OK, resRead = S_OK;
4586 DirEntry streamEntry;
4587 BYTE* buffer;
4588 SmallBlockChainStream* sbTempChain;
4590 TRACE("%p %p\n", This, ppbbChain);
4592 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4593 DIRENTRY_NULL);
4595 if(!sbTempChain)
4596 return NULL;
4598 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4599 size = BlockChainStream_GetSize(*ppbbChain);
4600 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4602 offset.u.HighPart = 0;
4603 offset.u.LowPart = 0;
4604 cbTotalRead.QuadPart = 0;
4605 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4606 while(cbTotalRead.QuadPart < size.QuadPart)
4608 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4609 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4610 buffer, &cbRead);
4612 if(FAILED(resRead))
4613 break;
4615 if(cbRead > 0)
4617 cbTotalRead.QuadPart += cbRead;
4619 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4620 cbRead, buffer, &cbWritten);
4622 if(FAILED(resWrite))
4623 break;
4625 offset.u.LowPart += cbRead;
4627 else
4629 resRead = STG_E_READFAULT;
4630 break;
4633 HeapFree(GetProcessHeap(), 0, buffer);
4635 size.u.HighPart = 0;
4636 size.u.LowPart = 0;
4638 if(FAILED(resRead) || FAILED(resWrite))
4640 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4641 SmallBlockChainStream_SetSize(sbTempChain, size);
4642 SmallBlockChainStream_Destroy(sbTempChain);
4643 return NULL;
4646 /* destroy the original big block chain */
4647 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4648 BlockChainStream_SetSize(*ppbbChain, size);
4649 BlockChainStream_Destroy(*ppbbChain);
4650 *ppbbChain = NULL;
4652 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4653 streamEntry.startingBlock = sbHeadOfChain;
4654 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4656 SmallBlockChainStream_Destroy(sbTempChain);
4657 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4660 static HRESULT StorageBaseImpl_CopyStream(
4661 StorageBaseImpl *dst, DirRef dst_entry,
4662 StorageBaseImpl *src, DirRef src_entry)
4664 HRESULT hr;
4665 BYTE data[4096];
4666 DirEntry srcdata;
4667 ULARGE_INTEGER bytes_copied;
4668 ULONG bytestocopy, bytesread, byteswritten;
4670 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4672 if (SUCCEEDED(hr))
4674 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4676 bytes_copied.QuadPart = 0;
4677 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4679 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4681 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4682 data, &bytesread);
4683 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4685 if (SUCCEEDED(hr))
4686 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4687 data, &byteswritten);
4688 if (SUCCEEDED(hr))
4690 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4691 bytes_copied.QuadPart += byteswritten;
4696 return hr;
4699 static HRESULT StorageBaseImpl_DupStorageTree(
4700 StorageBaseImpl *dst, DirRef *dst_entry,
4701 StorageBaseImpl *src, DirRef src_entry)
4703 HRESULT hr;
4704 DirEntry data;
4705 BOOL has_stream=FALSE;
4707 if (src_entry == DIRENTRY_NULL)
4709 *dst_entry = DIRENTRY_NULL;
4710 return S_OK;
4713 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
4714 if (SUCCEEDED(hr))
4716 has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0);
4717 data.startingBlock = BLOCK_END_OF_CHAIN;
4718 data.size.QuadPart = 0;
4720 hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild);
4723 if (SUCCEEDED(hr))
4724 hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
4726 if (SUCCEEDED(hr))
4727 hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);
4729 if (SUCCEEDED(hr))
4730 hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry);
4732 if (SUCCEEDED(hr) && has_stream)
4733 hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
4735 return hr;
4738 static HRESULT StorageBaseImpl_CopyStorageTree(
4739 StorageBaseImpl *dst, DirRef dst_entry,
4740 StorageBaseImpl *src, DirRef src_entry)
4742 HRESULT hr;
4743 DirEntry src_data, dst_data;
4744 DirRef new_root_entry;
4746 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
4748 if (SUCCEEDED(hr))
4750 hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
4753 if (SUCCEEDED(hr))
4755 hr = StorageBaseImpl_ReadDirEntry(dst, dst_entry, &dst_data);
4756 dst_data.clsid = src_data.clsid;
4757 dst_data.ctime = src_data.ctime;
4758 dst_data.mtime = src_data.mtime;
4759 dst_data.dirRootEntry = new_root_entry;
4762 if (SUCCEEDED(hr))
4763 hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
4765 return hr;
4768 static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
4770 HRESULT hr;
4771 DirEntry data;
4772 ULARGE_INTEGER zero;
4774 if (entry == DIRENTRY_NULL)
4775 return S_OK;
4777 zero.QuadPart = 0;
4779 hr = StorageBaseImpl_ReadDirEntry(This, entry, &data);
4781 if (SUCCEEDED(hr) && include_siblings)
4782 hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE);
4784 if (SUCCEEDED(hr) && include_siblings)
4785 hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE);
4787 if (SUCCEEDED(hr))
4788 hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
4790 if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
4791 hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
4793 if (SUCCEEDED(hr))
4794 hr = StorageBaseImpl_DestroyDirEntry(This, entry);
4796 return hr;
4799 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4801 DirRef result=This->firstFreeEntry;
4803 while (result < This->entries_size && This->entries[result].inuse)
4804 result++;
4806 if (result == This->entries_size)
4808 ULONG new_size = This->entries_size * 2;
4809 TransactedDirEntry *new_entries;
4811 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4812 if (!new_entries) return DIRENTRY_NULL;
4814 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4815 HeapFree(GetProcessHeap(), 0, This->entries);
4817 This->entries = new_entries;
4818 This->entries_size = new_size;
4821 This->entries[result].inuse = TRUE;
4823 This->firstFreeEntry = result+1;
4825 return result;
4828 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4829 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4831 DirRef stubEntryRef;
4832 TransactedDirEntry *entry;
4834 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4836 if (stubEntryRef != DIRENTRY_NULL)
4838 entry = &This->entries[stubEntryRef];
4840 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4842 entry->read = FALSE;
4845 return stubEntryRef;
4848 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4849 TransactedSnapshotImpl *This, DirRef entry)
4851 HRESULT hr=S_OK;
4852 DirEntry data;
4854 if (!This->entries[entry].read)
4856 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4857 This->entries[entry].transactedParentEntry,
4858 &data);
4860 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4862 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4864 if (data.leftChild == DIRENTRY_NULL)
4865 hr = E_OUTOFMEMORY;
4868 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4870 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4872 if (data.rightChild == DIRENTRY_NULL)
4873 hr = E_OUTOFMEMORY;
4876 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4878 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4880 if (data.dirRootEntry == DIRENTRY_NULL)
4881 hr = E_OUTOFMEMORY;
4884 if (SUCCEEDED(hr))
4886 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4887 This->entries[entry].read = TRUE;
4891 return hr;
4894 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4895 TransactedSnapshotImpl *This, DirRef entry)
4897 HRESULT hr = S_OK;
4899 if (!This->entries[entry].stream_dirty)
4901 DirEntry new_entrydata;
4903 memset(&new_entrydata, 0, sizeof(DirEntry));
4904 new_entrydata.name[0] = 'S';
4905 new_entrydata.sizeOfNameString = 1;
4906 new_entrydata.stgType = STGTY_STREAM;
4907 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4908 new_entrydata.leftChild = DIRENTRY_NULL;
4909 new_entrydata.rightChild = DIRENTRY_NULL;
4910 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4912 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4913 &This->entries[entry].stream_entry);
4915 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4917 hr = StorageBaseImpl_CopyStream(
4918 This->scratch, This->entries[entry].stream_entry,
4919 This->transactedParent, This->entries[entry].transactedParentEntry);
4921 if (FAILED(hr))
4922 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4925 if (SUCCEEDED(hr))
4926 This->entries[entry].stream_dirty = TRUE;
4928 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4930 /* Since this entry is modified, and we aren't using its stream data, we
4931 * no longer care about the original entry. */
4932 DirRef delete_ref;
4933 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4935 if (delete_ref != DIRENTRY_NULL)
4936 This->entries[delete_ref].deleted = TRUE;
4938 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4942 return hr;
4945 /* Find the first entry in a depth-first traversal. */
4946 static DirRef TransactedSnapshotImpl_FindFirstChild(
4947 TransactedSnapshotImpl* This, DirRef parent)
4949 DirRef cursor, prev;
4950 TransactedDirEntry *entry;
4952 cursor = parent;
4953 entry = &This->entries[cursor];
4954 while (entry->read)
4956 if (entry->data.leftChild != DIRENTRY_NULL)
4958 prev = cursor;
4959 cursor = entry->data.leftChild;
4960 entry = &This->entries[cursor];
4961 entry->parent = prev;
4963 else if (entry->data.rightChild != DIRENTRY_NULL)
4965 prev = cursor;
4966 cursor = entry->data.rightChild;
4967 entry = &This->entries[cursor];
4968 entry->parent = prev;
4970 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4972 prev = cursor;
4973 cursor = entry->data.dirRootEntry;
4974 entry = &This->entries[cursor];
4975 entry->parent = prev;
4977 else
4978 break;
4981 return cursor;
4984 /* Find the next entry in a depth-first traversal. */
4985 static DirRef TransactedSnapshotImpl_FindNextChild(
4986 TransactedSnapshotImpl* This, DirRef current)
4988 DirRef parent;
4989 TransactedDirEntry *parent_entry;
4991 parent = This->entries[current].parent;
4992 parent_entry = &This->entries[parent];
4994 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4996 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4998 This->entries[parent_entry->data.rightChild].parent = parent;
4999 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
5002 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
5004 This->entries[parent_entry->data.dirRootEntry].parent = parent;
5005 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
5009 return parent;
5012 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5013 static inline BOOL TransactedSnapshotImpl_MadeCopy(
5014 TransactedSnapshotImpl* This, DirRef entry)
5016 return entry != DIRENTRY_NULL &&
5017 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
5020 /* Destroy the entries created by CopyTree. */
5021 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5022 TransactedSnapshotImpl* This, DirRef stop)
5024 DirRef cursor;
5025 TransactedDirEntry *entry;
5026 ULARGE_INTEGER zero;
5028 zero.QuadPart = 0;
5030 if (!This->entries[This->base.storageDirEntry].read)
5031 return;
5033 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
5035 if (cursor == DIRENTRY_NULL)
5036 return;
5038 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
5040 while (cursor != DIRENTRY_NULL && cursor != stop)
5042 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
5044 entry = &This->entries[cursor];
5046 if (entry->stream_dirty)
5047 StorageBaseImpl_StreamSetSize(This->transactedParent,
5048 entry->newTransactedParentEntry, zero);
5050 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5051 entry->newTransactedParentEntry);
5053 entry->newTransactedParentEntry = entry->transactedParentEntry;
5056 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5060 /* Make a copy of our edited tree that we can use in the parent. */
5061 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
5063 DirRef cursor;
5064 TransactedDirEntry *entry;
5065 HRESULT hr = S_OK;
5067 cursor = This->base.storageDirEntry;
5068 entry = &This->entries[cursor];
5069 entry->parent = DIRENTRY_NULL;
5070 entry->newTransactedParentEntry = entry->transactedParentEntry;
5072 if (entry->data.dirRootEntry == DIRENTRY_NULL)
5073 return S_OK;
5075 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
5077 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
5078 entry = &This->entries[cursor];
5080 while (cursor != DIRENTRY_NULL)
5082 /* Make a copy of this entry in the transacted parent. */
5083 if (!entry->read ||
5084 (!entry->dirty && !entry->stream_dirty &&
5085 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
5086 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
5087 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
5088 entry->newTransactedParentEntry = entry->transactedParentEntry;
5089 else
5091 DirEntry newData;
5093 memcpy(&newData, &entry->data, sizeof(DirEntry));
5095 newData.size.QuadPart = 0;
5096 newData.startingBlock = BLOCK_END_OF_CHAIN;
5098 if (newData.leftChild != DIRENTRY_NULL)
5099 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
5101 if (newData.rightChild != DIRENTRY_NULL)
5102 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
5104 if (newData.dirRootEntry != DIRENTRY_NULL)
5105 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
5107 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
5108 &entry->newTransactedParentEntry);
5109 if (FAILED(hr))
5111 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5112 return hr;
5115 if (entry->stream_dirty)
5117 hr = StorageBaseImpl_CopyStream(
5118 This->transactedParent, entry->newTransactedParentEntry,
5119 This->scratch, entry->stream_entry);
5121 else if (entry->data.size.QuadPart)
5123 hr = StorageBaseImpl_StreamLink(
5124 This->transactedParent, entry->newTransactedParentEntry,
5125 entry->transactedParentEntry);
5128 if (FAILED(hr))
5130 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5131 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5132 return hr;
5136 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5137 entry = &This->entries[cursor];
5140 return hr;
5143 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
5144 IStorage* iface,
5145 DWORD grfCommitFlags) /* [in] */
5147 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
5148 TransactedDirEntry *root_entry;
5149 DirRef i, dir_root_ref;
5150 DirEntry data;
5151 ULARGE_INTEGER zero;
5152 HRESULT hr;
5153 ULONG transactionSig;
5155 zero.QuadPart = 0;
5157 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5159 /* Cannot commit a read-only transacted storage */
5160 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
5161 return STG_E_ACCESSDENIED;
5163 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
5164 if (hr == E_NOTIMPL) hr = S_OK;
5165 if (SUCCEEDED(hr))
5167 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
5168 if (SUCCEEDED(hr))
5170 if (transactionSig != This->lastTransactionSig)
5172 ERR("file was externally modified\n");
5173 hr = STG_E_NOTCURRENT;
5176 if (SUCCEEDED(hr))
5178 This->lastTransactionSig = transactionSig+1;
5179 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
5182 else if (hr == E_NOTIMPL)
5183 hr = S_OK;
5185 if (FAILED(hr)) goto end;
5187 /* To prevent data loss, we create the new structure in the file before we
5188 * delete the old one, so that in case of errors the old data is intact. We
5189 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5190 * needed in the rare situation where we have just enough free disk space to
5191 * overwrite the existing data. */
5193 root_entry = &This->entries[This->base.storageDirEntry];
5195 if (!root_entry->read)
5196 goto end;
5198 hr = TransactedSnapshotImpl_CopyTree(This);
5199 if (FAILED(hr)) goto end;
5201 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
5202 dir_root_ref = DIRENTRY_NULL;
5203 else
5204 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
5206 hr = StorageBaseImpl_Flush(This->transactedParent);
5208 /* Update the storage to use the new data in one step. */
5209 if (SUCCEEDED(hr))
5210 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5211 root_entry->transactedParentEntry, &data);
5213 if (SUCCEEDED(hr))
5215 data.dirRootEntry = dir_root_ref;
5216 data.clsid = root_entry->data.clsid;
5217 data.ctime = root_entry->data.ctime;
5218 data.mtime = root_entry->data.mtime;
5220 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
5221 root_entry->transactedParentEntry, &data);
5224 /* Try to flush after updating the root storage, but if the flush fails, keep
5225 * going, on the theory that it'll either succeed later or the subsequent
5226 * writes will fail. */
5227 StorageBaseImpl_Flush(This->transactedParent);
5229 if (SUCCEEDED(hr))
5231 /* Destroy the old now-orphaned data. */
5232 for (i=0; i<This->entries_size; i++)
5234 TransactedDirEntry *entry = &This->entries[i];
5235 if (entry->inuse)
5237 if (entry->deleted)
5239 StorageBaseImpl_StreamSetSize(This->transactedParent,
5240 entry->transactedParentEntry, zero);
5241 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5242 entry->transactedParentEntry);
5243 memset(entry, 0, sizeof(TransactedDirEntry));
5244 This->firstFreeEntry = min(i, This->firstFreeEntry);
5246 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
5248 if (entry->transactedParentEntry != DIRENTRY_NULL)
5249 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5250 entry->transactedParentEntry);
5251 if (entry->stream_dirty)
5253 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
5254 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
5255 entry->stream_dirty = FALSE;
5257 entry->dirty = FALSE;
5258 entry->transactedParentEntry = entry->newTransactedParentEntry;
5263 else
5265 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
5268 if (SUCCEEDED(hr))
5269 hr = StorageBaseImpl_Flush(This->transactedParent);
5270 end:
5271 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
5274 return hr;
5277 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
5278 IStorage* iface)
5280 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
5281 ULARGE_INTEGER zero;
5282 ULONG i;
5284 TRACE("(%p)\n", iface);
5286 /* Destroy the open objects. */
5287 StorageBaseImpl_DeleteAll(&This->base);
5289 /* Clear out the scratch file. */
5290 zero.QuadPart = 0;
5291 for (i=0; i<This->entries_size; i++)
5293 if (This->entries[i].stream_dirty)
5295 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
5296 zero);
5298 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
5302 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
5304 This->firstFreeEntry = 0;
5305 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
5307 return S_OK;
5310 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
5312 if (!This->reverted)
5314 TRACE("Storage invalidated (stg=%p)\n", This);
5316 This->reverted = TRUE;
5318 StorageBaseImpl_DeleteAll(This);
5322 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
5324 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
5326 IStorage_Revert(&This->base.IStorage_iface);
5327 IStorage_Release(&This->transactedParent->IStorage_iface);
5328 IStorage_Release(&This->scratch->IStorage_iface);
5329 HeapFree(GetProcessHeap(), 0, This->entries);
5330 HeapFree(GetProcessHeap(), 0, This);
5333 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
5335 /* We only need to flush when committing. */
5336 return S_OK;
5339 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5341 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
5343 return StorageBaseImpl_GetFilename(This->transactedParent, result);
5346 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
5347 const DirEntry *newData, DirRef *index)
5349 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5350 DirRef new_ref;
5351 TransactedDirEntry *new_entry;
5353 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
5354 if (new_ref == DIRENTRY_NULL)
5355 return E_OUTOFMEMORY;
5357 new_entry = &This->entries[new_ref];
5359 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
5360 new_entry->read = TRUE;
5361 new_entry->dirty = TRUE;
5362 memcpy(&new_entry->data, newData, sizeof(DirEntry));
5364 *index = new_ref;
5366 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
5368 return S_OK;
5371 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
5372 DirRef index, const DirEntry *data)
5374 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5375 HRESULT hr;
5377 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
5379 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5380 if (FAILED(hr)) return hr;
5382 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
5384 if (index != This->base.storageDirEntry)
5386 This->entries[index].dirty = TRUE;
5388 if (data->size.QuadPart == 0 &&
5389 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
5391 /* Since this entry is modified, and we aren't using its stream data, we
5392 * no longer care about the original entry. */
5393 DirRef delete_ref;
5394 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
5396 if (delete_ref != DIRENTRY_NULL)
5397 This->entries[delete_ref].deleted = TRUE;
5399 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5403 return S_OK;
5406 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
5407 DirRef index, DirEntry *data)
5409 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5410 HRESULT hr;
5412 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5413 if (FAILED(hr)) return hr;
5415 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
5417 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
5419 return S_OK;
5422 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
5423 DirRef index)
5425 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5427 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
5428 This->entries[index].data.size.QuadPart != 0)
5430 /* If we deleted this entry while it has stream data. We must have left the
5431 * data because some other entry is using it, and we need to leave the
5432 * original entry alone. */
5433 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
5434 This->firstFreeEntry = min(index, This->firstFreeEntry);
5436 else
5438 This->entries[index].deleted = TRUE;
5441 return S_OK;
5444 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
5445 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5447 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5449 if (This->entries[index].stream_dirty)
5451 return StorageBaseImpl_StreamReadAt(This->scratch,
5452 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
5454 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
5456 /* This stream doesn't live in the parent, and we haven't allocated storage
5457 * for it yet */
5458 *bytesRead = 0;
5459 return S_OK;
5461 else
5463 return StorageBaseImpl_StreamReadAt(This->transactedParent,
5464 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
5468 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
5469 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5471 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5472 HRESULT hr;
5474 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5475 if (FAILED(hr)) return hr;
5477 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5478 if (FAILED(hr)) return hr;
5480 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
5481 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
5483 if (SUCCEEDED(hr) && size != 0)
5484 This->entries[index].data.size.QuadPart = max(
5485 This->entries[index].data.size.QuadPart,
5486 offset.QuadPart + size);
5488 return hr;
5491 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
5492 DirRef index, ULARGE_INTEGER newsize)
5494 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5495 HRESULT hr;
5497 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5498 if (FAILED(hr)) return hr;
5500 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
5501 return S_OK;
5503 if (newsize.QuadPart == 0)
5505 /* Destroy any parent references or entries in the scratch file. */
5506 if (This->entries[index].stream_dirty)
5508 ULARGE_INTEGER zero;
5509 zero.QuadPart = 0;
5510 StorageBaseImpl_StreamSetSize(This->scratch,
5511 This->entries[index].stream_entry, zero);
5512 StorageBaseImpl_DestroyDirEntry(This->scratch,
5513 This->entries[index].stream_entry);
5514 This->entries[index].stream_dirty = FALSE;
5516 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
5518 DirRef delete_ref;
5519 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
5521 if (delete_ref != DIRENTRY_NULL)
5522 This->entries[delete_ref].deleted = TRUE;
5524 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5527 else
5529 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5530 if (FAILED(hr)) return hr;
5532 hr = StorageBaseImpl_StreamSetSize(This->scratch,
5533 This->entries[index].stream_entry, newsize);
5536 if (SUCCEEDED(hr))
5537 This->entries[index].data.size = newsize;
5539 return hr;
5542 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
5543 DirRef dst, DirRef src)
5545 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5546 HRESULT hr;
5547 TransactedDirEntry *dst_entry, *src_entry;
5549 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
5550 if (FAILED(hr)) return hr;
5552 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
5553 if (FAILED(hr)) return hr;
5555 dst_entry = &This->entries[dst];
5556 src_entry = &This->entries[src];
5558 dst_entry->stream_dirty = src_entry->stream_dirty;
5559 dst_entry->stream_entry = src_entry->stream_entry;
5560 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
5561 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
5562 dst_entry->data.size = src_entry->data.size;
5564 return S_OK;
5567 static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
5568 ULONG* result, BOOL refresh)
5570 return E_NOTIMPL;
5573 static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
5574 ULONG value)
5576 return E_NOTIMPL;
5579 static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
5581 return E_NOTIMPL;
5584 static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
5586 return E_NOTIMPL;
5589 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5591 StorageBaseImpl_QueryInterface,
5592 StorageBaseImpl_AddRef,
5593 StorageBaseImpl_Release,
5594 StorageBaseImpl_CreateStream,
5595 StorageBaseImpl_OpenStream,
5596 StorageBaseImpl_CreateStorage,
5597 StorageBaseImpl_OpenStorage,
5598 StorageBaseImpl_CopyTo,
5599 StorageBaseImpl_MoveElementTo,
5600 TransactedSnapshotImpl_Commit,
5601 TransactedSnapshotImpl_Revert,
5602 StorageBaseImpl_EnumElements,
5603 StorageBaseImpl_DestroyElement,
5604 StorageBaseImpl_RenameElement,
5605 StorageBaseImpl_SetElementTimes,
5606 StorageBaseImpl_SetClass,
5607 StorageBaseImpl_SetStateBits,
5608 StorageBaseImpl_Stat
5611 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5613 TransactedSnapshotImpl_Destroy,
5614 TransactedSnapshotImpl_Invalidate,
5615 TransactedSnapshotImpl_Flush,
5616 TransactedSnapshotImpl_GetFilename,
5617 TransactedSnapshotImpl_CreateDirEntry,
5618 TransactedSnapshotImpl_WriteDirEntry,
5619 TransactedSnapshotImpl_ReadDirEntry,
5620 TransactedSnapshotImpl_DestroyDirEntry,
5621 TransactedSnapshotImpl_StreamReadAt,
5622 TransactedSnapshotImpl_StreamWriteAt,
5623 TransactedSnapshotImpl_StreamSetSize,
5624 TransactedSnapshotImpl_StreamLink,
5625 TransactedSnapshotImpl_GetTransactionSig,
5626 TransactedSnapshotImpl_SetTransactionSig,
5627 TransactedSnapshotImpl_LockTransaction,
5628 TransactedSnapshotImpl_UnlockTransaction
5631 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5632 TransactedSnapshotImpl** result)
5634 HRESULT hr;
5636 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5637 if (*result)
5639 IStorage *scratch;
5641 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5643 /* This is OK because the property set storage functions use the IStorage functions. */
5644 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
5645 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5647 list_init(&(*result)->base.strmHead);
5649 list_init(&(*result)->base.storageHead);
5651 (*result)->base.ref = 1;
5653 (*result)->base.openFlags = parentStorage->openFlags;
5655 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5656 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
5658 /* Create a new temporary storage to act as the scratch file. */
5659 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
5660 0, &scratch);
5661 (*result)->scratch = impl_from_IStorage(scratch);
5663 if (SUCCEEDED(hr))
5665 ULONG num_entries = 20;
5667 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5668 (*result)->entries_size = num_entries;
5669 (*result)->firstFreeEntry = 0;
5671 if ((*result)->entries)
5673 /* parentStorage already has 1 reference, which we take over here. */
5674 (*result)->transactedParent = parentStorage;
5676 parentStorage->transactedChild = &(*result)->base;
5678 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5680 else
5682 IStorage_Release(scratch);
5684 hr = E_OUTOFMEMORY;
5688 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
5690 return hr;
5692 else
5693 return E_OUTOFMEMORY;
5696 static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
5698 if (!This->reverted)
5700 TRACE("Storage invalidated (stg=%p)\n", This);
5702 This->reverted = TRUE;
5704 StorageBaseImpl_DeleteAll(This);
5708 static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
5710 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
5712 TransactedSharedImpl_Invalidate(&This->base);
5713 IStorage_Release(&This->transactedParent->IStorage_iface);
5714 IStorage_Release(&This->scratch->base.IStorage_iface);
5715 HeapFree(GetProcessHeap(), 0, This);
5718 static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface)
5720 /* We only need to flush when committing. */
5721 return S_OK;
5724 static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5726 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
5728 return StorageBaseImpl_GetFilename(This->transactedParent, result);
5731 static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
5732 const DirEntry *newData, DirRef *index)
5734 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5736 return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
5737 newData, index);
5740 static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
5741 DirRef index, const DirEntry *data)
5743 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5745 return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
5746 index, data);
5749 static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
5750 DirRef index, DirEntry *data)
5752 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5754 return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
5755 index, data);
5758 static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
5759 DirRef index)
5761 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5763 return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
5764 index);
5767 static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
5768 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5770 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5772 return StorageBaseImpl_StreamReadAt(&This->scratch->base,
5773 index, offset, size, buffer, bytesRead);
5776 static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
5777 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5779 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5781 return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
5782 index, offset, size, buffer, bytesWritten);
5785 static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
5786 DirRef index, ULARGE_INTEGER newsize)
5788 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5790 return StorageBaseImpl_StreamSetSize(&This->scratch->base,
5791 index, newsize);
5794 static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
5795 DirRef dst, DirRef src)
5797 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5799 return StorageBaseImpl_StreamLink(&This->scratch->base,
5800 dst, src);
5803 static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
5804 ULONG* result, BOOL refresh)
5806 return E_NOTIMPL;
5809 static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
5810 ULONG value)
5812 return E_NOTIMPL;
5815 static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
5817 return E_NOTIMPL;
5820 static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
5822 return E_NOTIMPL;
5825 static HRESULT WINAPI TransactedSharedImpl_Commit(
5826 IStorage* iface,
5827 DWORD grfCommitFlags) /* [in] */
5829 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
5830 DirRef new_storage_ref, prev_storage_ref;
5831 DirEntry src_data, dst_data;
5832 HRESULT hr;
5833 ULONG transactionSig;
5835 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5837 /* Cannot commit a read-only transacted storage */
5838 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
5839 return STG_E_ACCESSDENIED;
5841 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
5842 if (hr == E_NOTIMPL) hr = S_OK;
5843 if (SUCCEEDED(hr))
5845 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
5846 if (SUCCEEDED(hr))
5848 if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
5849 hr = STG_E_NOTCURRENT;
5851 if (SUCCEEDED(hr))
5852 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
5854 else if (hr == E_NOTIMPL)
5855 hr = S_OK;
5857 if (SUCCEEDED(hr))
5858 hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data);
5860 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
5861 if (SUCCEEDED(hr))
5862 hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry);
5864 if (SUCCEEDED(hr))
5865 hr = StorageBaseImpl_Flush(This->transactedParent);
5867 if (SUCCEEDED(hr))
5868 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
5870 if (SUCCEEDED(hr))
5872 prev_storage_ref = dst_data.dirRootEntry;
5873 dst_data.dirRootEntry = new_storage_ref;
5874 dst_data.clsid = src_data.clsid;
5875 dst_data.ctime = src_data.ctime;
5876 dst_data.mtime = src_data.mtime;
5877 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
5880 if (SUCCEEDED(hr))
5882 /* Try to flush after updating the root storage, but if the flush fails, keep
5883 * going, on the theory that it'll either succeed later or the subsequent
5884 * writes will fail. */
5885 StorageBaseImpl_Flush(This->transactedParent);
5887 hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
5890 if (SUCCEEDED(hr))
5891 hr = StorageBaseImpl_Flush(This->transactedParent);
5893 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
5895 if (SUCCEEDED(hr))
5896 hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT);
5898 if (SUCCEEDED(hr))
5900 This->lastTransactionSig = transactionSig+1;
5904 return hr;
5907 static HRESULT WINAPI TransactedSharedImpl_Revert(
5908 IStorage* iface)
5910 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
5912 TRACE("(%p)\n", iface);
5914 /* Destroy the open objects. */
5915 StorageBaseImpl_DeleteAll(&This->base);
5917 return IStorage_Revert(&This->scratch->base.IStorage_iface);
5920 static const IStorageVtbl TransactedSharedImpl_Vtbl =
5922 StorageBaseImpl_QueryInterface,
5923 StorageBaseImpl_AddRef,
5924 StorageBaseImpl_Release,
5925 StorageBaseImpl_CreateStream,
5926 StorageBaseImpl_OpenStream,
5927 StorageBaseImpl_CreateStorage,
5928 StorageBaseImpl_OpenStorage,
5929 StorageBaseImpl_CopyTo,
5930 StorageBaseImpl_MoveElementTo,
5931 TransactedSharedImpl_Commit,
5932 TransactedSharedImpl_Revert,
5933 StorageBaseImpl_EnumElements,
5934 StorageBaseImpl_DestroyElement,
5935 StorageBaseImpl_RenameElement,
5936 StorageBaseImpl_SetElementTimes,
5937 StorageBaseImpl_SetClass,
5938 StorageBaseImpl_SetStateBits,
5939 StorageBaseImpl_Stat
5942 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl =
5944 TransactedSharedImpl_Destroy,
5945 TransactedSharedImpl_Invalidate,
5946 TransactedSharedImpl_Flush,
5947 TransactedSharedImpl_GetFilename,
5948 TransactedSharedImpl_CreateDirEntry,
5949 TransactedSharedImpl_WriteDirEntry,
5950 TransactedSharedImpl_ReadDirEntry,
5951 TransactedSharedImpl_DestroyDirEntry,
5952 TransactedSharedImpl_StreamReadAt,
5953 TransactedSharedImpl_StreamWriteAt,
5954 TransactedSharedImpl_StreamSetSize,
5955 TransactedSharedImpl_StreamLink,
5956 TransactedSharedImpl_GetTransactionSig,
5957 TransactedSharedImpl_SetTransactionSig,
5958 TransactedSharedImpl_LockTransaction,
5959 TransactedSharedImpl_UnlockTransaction
5962 static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
5963 TransactedSharedImpl** result)
5965 HRESULT hr;
5967 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
5968 if (*result)
5970 IStorage *scratch;
5972 (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
5974 /* This is OK because the property set storage functions use the IStorage functions. */
5975 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
5976 (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
5978 list_init(&(*result)->base.strmHead);
5980 list_init(&(*result)->base.storageHead);
5982 (*result)->base.ref = 1;
5984 (*result)->base.openFlags = parentStorage->openFlags;
5986 hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
5988 if (SUCCEEDED(hr))
5990 STGOPTIONS stgo;
5992 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5993 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
5995 stgo.usVersion = 1;
5996 stgo.reserved = 0;
5997 stgo.ulSectorSize = 4096;
5998 stgo.pwcsTemplateFile = NULL;
6000 /* Create a new temporary storage to act as the scratch file. */
6001 hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED,
6002 STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch);
6003 (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch);
6005 if (SUCCEEDED(hr))
6007 hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
6008 parentStorage, parentStorage->storageDirEntry);
6010 if (SUCCEEDED(hr))
6012 hr = IStorage_Commit(scratch, STGC_DEFAULT);
6014 (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry;
6015 (*result)->transactedParent = parentStorage;
6018 if (FAILED(hr))
6019 IStorage_Release(scratch);
6022 StorageBaseImpl_UnlockTransaction(parentStorage, FALSE);
6025 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6027 return hr;
6029 else
6030 return E_OUTOFMEMORY;
6033 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
6034 BOOL toplevel, StorageBaseImpl** result)
6036 static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;
6038 if (parentStorage->openFlags & fixme_flags)
6040 fixme_flags &= ~parentStorage->openFlags;
6041 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
6044 if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) &&
6045 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE &&
6046 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE)
6048 /* Need to create a temp file for the snapshot */
6049 return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result);
6052 return TransactedSnapshotImpl_Construct(parentStorage,
6053 (TransactedSnapshotImpl**)result);
6056 static HRESULT Storage_Construct(
6057 HANDLE hFile,
6058 LPCOLESTR pwcsName,
6059 ILockBytes* pLkbyt,
6060 DWORD openFlags,
6061 BOOL fileBased,
6062 BOOL create,
6063 ULONG sector_size,
6064 StorageBaseImpl** result)
6066 StorageImpl *newStorage;
6067 StorageBaseImpl *newTransactedStorage;
6068 HRESULT hr;
6070 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
6071 if (FAILED(hr)) goto end;
6073 if (openFlags & STGM_TRANSACTED)
6075 hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
6076 if (FAILED(hr))
6077 IStorage_Release(&newStorage->base.IStorage_iface);
6078 else
6079 *result = newTransactedStorage;
6081 else
6082 *result = &newStorage->base;
6084 end:
6085 return hr;
6088 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
6090 StorageInternalImpl* This = (StorageInternalImpl*) base;
6092 if (!This->base.reverted)
6094 TRACE("Storage invalidated (stg=%p)\n", This);
6096 This->base.reverted = TRUE;
6098 This->parentStorage = NULL;
6100 StorageBaseImpl_DeleteAll(&This->base);
6102 list_remove(&This->ParentListEntry);
6106 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
6108 StorageInternalImpl* This = (StorageInternalImpl*) iface;
6110 StorageInternalImpl_Invalidate(&This->base);
6112 HeapFree(GetProcessHeap(), 0, This);
6115 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
6117 StorageInternalImpl* This = (StorageInternalImpl*) iface;
6119 return StorageBaseImpl_Flush(This->parentStorage);
6122 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6124 StorageInternalImpl* This = (StorageInternalImpl*) iface;
6126 return StorageBaseImpl_GetFilename(This->parentStorage, result);
6129 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
6130 const DirEntry *newData, DirRef *index)
6132 StorageInternalImpl* This = (StorageInternalImpl*) base;
6134 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
6135 newData, index);
6138 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
6139 DirRef index, const DirEntry *data)
6141 StorageInternalImpl* This = (StorageInternalImpl*) base;
6143 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
6144 index, data);
6147 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
6148 DirRef index, DirEntry *data)
6150 StorageInternalImpl* This = (StorageInternalImpl*) base;
6152 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
6153 index, data);
6156 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
6157 DirRef index)
6159 StorageInternalImpl* This = (StorageInternalImpl*) base;
6161 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
6162 index);
6165 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
6166 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6168 StorageInternalImpl* This = (StorageInternalImpl*) base;
6170 return StorageBaseImpl_StreamReadAt(This->parentStorage,
6171 index, offset, size, buffer, bytesRead);
6174 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
6175 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6177 StorageInternalImpl* This = (StorageInternalImpl*) base;
6179 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
6180 index, offset, size, buffer, bytesWritten);
6183 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
6184 DirRef index, ULARGE_INTEGER newsize)
6186 StorageInternalImpl* This = (StorageInternalImpl*) base;
6188 return StorageBaseImpl_StreamSetSize(This->parentStorage,
6189 index, newsize);
6192 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
6193 DirRef dst, DirRef src)
6195 StorageInternalImpl* This = (StorageInternalImpl*) base;
6197 return StorageBaseImpl_StreamLink(This->parentStorage,
6198 dst, src);
6201 static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
6202 ULONG* result, BOOL refresh)
6204 return E_NOTIMPL;
6207 static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
6208 ULONG value)
6210 return E_NOTIMPL;
6213 static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6215 return E_NOTIMPL;
6218 static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6220 return E_NOTIMPL;
6223 /******************************************************************************
6225 ** Storage32InternalImpl_Commit
6228 static HRESULT WINAPI StorageInternalImpl_Commit(
6229 IStorage* iface,
6230 DWORD grfCommitFlags) /* [in] */
6232 StorageBaseImpl* This = impl_from_IStorage(iface);
6233 TRACE("(%p,%x)\n", iface, grfCommitFlags);
6234 return StorageBaseImpl_Flush(This);
6237 /******************************************************************************
6239 ** Storage32InternalImpl_Revert
6242 static HRESULT WINAPI StorageInternalImpl_Revert(
6243 IStorage* iface)
6245 FIXME("(%p): stub\n", iface);
6246 return S_OK;
6249 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
6251 IStorage_Release(&This->parentStorage->IStorage_iface);
6252 HeapFree(GetProcessHeap(), 0, This);
6255 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
6256 IEnumSTATSTG* iface,
6257 REFIID riid,
6258 void** ppvObject)
6260 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6262 if (ppvObject==0)
6263 return E_INVALIDARG;
6265 *ppvObject = 0;
6267 if (IsEqualGUID(&IID_IUnknown, riid) ||
6268 IsEqualGUID(&IID_IEnumSTATSTG, riid))
6270 *ppvObject = This;
6271 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
6272 return S_OK;
6275 return E_NOINTERFACE;
6278 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
6279 IEnumSTATSTG* iface)
6281 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6282 return InterlockedIncrement(&This->ref);
6285 static ULONG WINAPI IEnumSTATSTGImpl_Release(
6286 IEnumSTATSTG* iface)
6288 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6290 ULONG newRef;
6292 newRef = InterlockedDecrement(&This->ref);
6294 if (newRef==0)
6296 IEnumSTATSTGImpl_Destroy(This);
6299 return newRef;
6302 static HRESULT IEnumSTATSTGImpl_GetNextRef(
6303 IEnumSTATSTGImpl* This,
6304 DirRef *ref)
6306 DirRef result = DIRENTRY_NULL;
6307 DirRef searchNode;
6308 DirEntry entry;
6309 HRESULT hr;
6310 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
6312 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
6313 This->parentStorage->storageDirEntry, &entry);
6314 searchNode = entry.dirRootEntry;
6316 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
6318 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
6320 if (SUCCEEDED(hr))
6322 LONG diff = entryNameCmp( entry.name, This->name);
6324 if (diff <= 0)
6326 searchNode = entry.rightChild;
6328 else
6330 result = searchNode;
6331 memcpy(result_name, entry.name, sizeof(result_name));
6332 searchNode = entry.leftChild;
6337 if (SUCCEEDED(hr))
6339 *ref = result;
6340 if (result != DIRENTRY_NULL)
6341 memcpy(This->name, result_name, sizeof(result_name));
6344 return hr;
6347 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
6348 IEnumSTATSTG* iface,
6349 ULONG celt,
6350 STATSTG* rgelt,
6351 ULONG* pceltFetched)
6353 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6355 DirEntry currentEntry;
6356 STATSTG* currentReturnStruct = rgelt;
6357 ULONG objectFetched = 0;
6358 DirRef currentSearchNode;
6359 HRESULT hr=S_OK;
6361 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
6362 return E_INVALIDARG;
6364 if (This->parentStorage->reverted)
6365 return STG_E_REVERTED;
6368 * To avoid the special case, get another pointer to a ULONG value if
6369 * the caller didn't supply one.
6371 if (pceltFetched==0)
6372 pceltFetched = &objectFetched;
6375 * Start the iteration, we will iterate until we hit the end of the
6376 * linked list or until we hit the number of items to iterate through
6378 *pceltFetched = 0;
6380 while ( *pceltFetched < celt )
6382 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
6384 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
6385 break;
6388 * Read the entry from the storage.
6390 StorageBaseImpl_ReadDirEntry(This->parentStorage,
6391 currentSearchNode,
6392 &currentEntry);
6395 * Copy the information to the return buffer.
6397 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
6398 currentReturnStruct,
6399 &currentEntry,
6400 STATFLAG_DEFAULT);
6403 * Step to the next item in the iteration
6405 (*pceltFetched)++;
6406 currentReturnStruct++;
6409 if (SUCCEEDED(hr) && *pceltFetched != celt)
6410 hr = S_FALSE;
6412 return hr;
6416 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
6417 IEnumSTATSTG* iface,
6418 ULONG celt)
6420 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6422 ULONG objectFetched = 0;
6423 DirRef currentSearchNode;
6424 HRESULT hr=S_OK;
6426 if (This->parentStorage->reverted)
6427 return STG_E_REVERTED;
6429 while ( (objectFetched < celt) )
6431 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
6433 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
6434 break;
6436 objectFetched++;
6439 if (SUCCEEDED(hr) && objectFetched != celt)
6440 return S_FALSE;
6442 return hr;
6445 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
6446 IEnumSTATSTG* iface)
6448 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6450 if (This->parentStorage->reverted)
6451 return STG_E_REVERTED;
6453 This->name[0] = 0;
6455 return S_OK;
6458 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
6459 IEnumSTATSTG* iface,
6460 IEnumSTATSTG** ppenum)
6462 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6463 IEnumSTATSTGImpl* newClone;
6465 if (This->parentStorage->reverted)
6466 return STG_E_REVERTED;
6468 if (ppenum==0)
6469 return E_INVALIDARG;
6471 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
6472 This->storageDirEntry);
6473 if (!newClone)
6475 *ppenum = NULL;
6476 return E_OUTOFMEMORY;
6480 * The new clone enumeration must point to the same current node as
6481 * the old one.
6483 memcpy(newClone->name, This->name, sizeof(newClone->name));
6485 *ppenum = &newClone->IEnumSTATSTG_iface;
6487 return S_OK;
6491 * Virtual function table for the IEnumSTATSTGImpl class.
6493 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
6495 IEnumSTATSTGImpl_QueryInterface,
6496 IEnumSTATSTGImpl_AddRef,
6497 IEnumSTATSTGImpl_Release,
6498 IEnumSTATSTGImpl_Next,
6499 IEnumSTATSTGImpl_Skip,
6500 IEnumSTATSTGImpl_Reset,
6501 IEnumSTATSTGImpl_Clone
6504 /******************************************************************************
6505 ** IEnumSTATSTGImpl implementation
6508 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
6509 StorageBaseImpl* parentStorage,
6510 DirRef storageDirEntry)
6512 IEnumSTATSTGImpl* newEnumeration;
6514 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
6516 if (newEnumeration)
6518 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
6519 newEnumeration->ref = 1;
6520 newEnumeration->name[0] = 0;
6523 * We want to nail-down the reference to the storage in case the
6524 * enumeration out-lives the storage in the client application.
6526 newEnumeration->parentStorage = parentStorage;
6527 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
6529 newEnumeration->storageDirEntry = storageDirEntry;
6532 return newEnumeration;
6536 * Virtual function table for the Storage32InternalImpl class.
6538 static const IStorageVtbl Storage32InternalImpl_Vtbl =
6540 StorageBaseImpl_QueryInterface,
6541 StorageBaseImpl_AddRef,
6542 StorageBaseImpl_Release,
6543 StorageBaseImpl_CreateStream,
6544 StorageBaseImpl_OpenStream,
6545 StorageBaseImpl_CreateStorage,
6546 StorageBaseImpl_OpenStorage,
6547 StorageBaseImpl_CopyTo,
6548 StorageBaseImpl_MoveElementTo,
6549 StorageInternalImpl_Commit,
6550 StorageInternalImpl_Revert,
6551 StorageBaseImpl_EnumElements,
6552 StorageBaseImpl_DestroyElement,
6553 StorageBaseImpl_RenameElement,
6554 StorageBaseImpl_SetElementTimes,
6555 StorageBaseImpl_SetClass,
6556 StorageBaseImpl_SetStateBits,
6557 StorageBaseImpl_Stat
6560 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
6562 StorageInternalImpl_Destroy,
6563 StorageInternalImpl_Invalidate,
6564 StorageInternalImpl_Flush,
6565 StorageInternalImpl_GetFilename,
6566 StorageInternalImpl_CreateDirEntry,
6567 StorageInternalImpl_WriteDirEntry,
6568 StorageInternalImpl_ReadDirEntry,
6569 StorageInternalImpl_DestroyDirEntry,
6570 StorageInternalImpl_StreamReadAt,
6571 StorageInternalImpl_StreamWriteAt,
6572 StorageInternalImpl_StreamSetSize,
6573 StorageInternalImpl_StreamLink,
6574 StorageInternalImpl_GetTransactionSig,
6575 StorageInternalImpl_SetTransactionSig,
6576 StorageInternalImpl_LockTransaction,
6577 StorageInternalImpl_UnlockTransaction
6580 /******************************************************************************
6581 ** Storage32InternalImpl implementation
6584 static StorageInternalImpl* StorageInternalImpl_Construct(
6585 StorageBaseImpl* parentStorage,
6586 DWORD openFlags,
6587 DirRef storageDirEntry)
6589 StorageInternalImpl* newStorage;
6591 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
6593 if (newStorage!=0)
6595 list_init(&newStorage->base.strmHead);
6597 list_init(&newStorage->base.storageHead);
6600 * Initialize the virtual function table.
6602 newStorage->base.IStorage_iface.lpVtbl = &Storage32InternalImpl_Vtbl;
6603 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
6604 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
6605 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
6607 newStorage->base.reverted = FALSE;
6609 newStorage->base.ref = 1;
6611 newStorage->parentStorage = parentStorage;
6614 * Keep a reference to the directory entry of this storage
6616 newStorage->base.storageDirEntry = storageDirEntry;
6618 newStorage->base.create = FALSE;
6620 return newStorage;
6623 return 0;
6626 /******************************************************************************
6627 ** StorageUtl implementation
6630 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
6632 WORD tmp;
6634 memcpy(&tmp, buffer+offset, sizeof(WORD));
6635 *value = lendian16toh(tmp);
6638 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
6640 value = htole16(value);
6641 memcpy(buffer+offset, &value, sizeof(WORD));
6644 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
6646 DWORD tmp;
6648 memcpy(&tmp, buffer+offset, sizeof(DWORD));
6649 *value = lendian32toh(tmp);
6652 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
6654 value = htole32(value);
6655 memcpy(buffer+offset, &value, sizeof(DWORD));
6658 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
6659 ULARGE_INTEGER* value)
6661 #ifdef WORDS_BIGENDIAN
6662 ULARGE_INTEGER tmp;
6664 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
6665 value->u.LowPart = htole32(tmp.u.HighPart);
6666 value->u.HighPart = htole32(tmp.u.LowPart);
6667 #else
6668 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
6669 #endif
6672 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
6673 const ULARGE_INTEGER *value)
6675 #ifdef WORDS_BIGENDIAN
6676 ULARGE_INTEGER tmp;
6678 tmp.u.LowPart = htole32(value->u.HighPart);
6679 tmp.u.HighPart = htole32(value->u.LowPart);
6680 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
6681 #else
6682 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
6683 #endif
6686 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
6688 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
6689 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
6690 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
6692 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
6695 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
6697 StorageUtl_WriteDWord(buffer, offset, value->Data1);
6698 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
6699 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
6701 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
6704 void StorageUtl_CopyDirEntryToSTATSTG(
6705 StorageBaseImpl* storage,
6706 STATSTG* destination,
6707 const DirEntry* source,
6708 int statFlags)
6711 * The copy of the string occurs only when the flag is not set
6713 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
6715 /* Use the filename for the root storage. */
6716 destination->pwcsName = 0;
6717 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
6719 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
6720 (source->name[0] == 0) )
6722 destination->pwcsName = 0;
6724 else
6726 destination->pwcsName =
6727 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
6729 strcpyW(destination->pwcsName, source->name);
6732 switch (source->stgType)
6734 case STGTY_STORAGE:
6735 case STGTY_ROOT:
6736 destination->type = STGTY_STORAGE;
6737 break;
6738 case STGTY_STREAM:
6739 destination->type = STGTY_STREAM;
6740 break;
6741 default:
6742 destination->type = STGTY_STREAM;
6743 break;
6746 destination->cbSize = source->size;
6748 currentReturnStruct->mtime = {0}; TODO
6749 currentReturnStruct->ctime = {0};
6750 currentReturnStruct->atime = {0};
6752 destination->grfMode = 0;
6753 destination->grfLocksSupported = 0;
6754 destination->clsid = source->clsid;
6755 destination->grfStateBits = 0;
6756 destination->reserved = 0;
6759 /******************************************************************************
6760 ** BlockChainStream implementation
6763 /* Read and save the index of all blocks in this stream. */
6764 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
6766 ULONG next_sector, next_offset;
6767 HRESULT hr;
6768 struct BlockChainRun *last_run;
6770 if (This->indexCacheLen == 0)
6772 last_run = NULL;
6773 next_offset = 0;
6774 next_sector = BlockChainStream_GetHeadOfChain(This);
6776 else
6778 last_run = &This->indexCache[This->indexCacheLen-1];
6779 next_offset = last_run->lastOffset+1;
6780 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
6781 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
6782 &next_sector);
6783 if (FAILED(hr)) return hr;
6786 while (next_sector != BLOCK_END_OF_CHAIN)
6788 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
6790 /* Add the current block to the cache. */
6791 if (This->indexCacheSize == 0)
6793 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
6794 if (!This->indexCache) return E_OUTOFMEMORY;
6795 This->indexCacheSize = 16;
6797 else if (This->indexCacheSize == This->indexCacheLen)
6799 struct BlockChainRun *new_cache;
6800 ULONG new_size;
6802 new_size = This->indexCacheSize * 2;
6803 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
6804 if (!new_cache) return E_OUTOFMEMORY;
6805 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
6807 HeapFree(GetProcessHeap(), 0, This->indexCache);
6808 This->indexCache = new_cache;
6809 This->indexCacheSize = new_size;
6812 This->indexCacheLen++;
6813 last_run = &This->indexCache[This->indexCacheLen-1];
6814 last_run->firstSector = next_sector;
6815 last_run->firstOffset = next_offset;
6818 last_run->lastOffset = next_offset;
6820 /* Find the next block. */
6821 next_offset++;
6822 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
6823 if (FAILED(hr)) return hr;
6826 if (This->indexCacheLen)
6828 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
6829 This->numBlocks = last_run->lastOffset+1;
6831 else
6833 This->tailIndex = BLOCK_END_OF_CHAIN;
6834 This->numBlocks = 0;
6837 return S_OK;
6840 /* Locate the nth block in this stream. */
6841 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
6843 ULONG min_offset = 0, max_offset = This->numBlocks-1;
6844 ULONG min_run = 0, max_run = This->indexCacheLen-1;
6846 if (offset >= This->numBlocks)
6847 return BLOCK_END_OF_CHAIN;
6849 while (min_run < max_run)
6851 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
6852 if (offset < This->indexCache[run_to_check].firstOffset)
6854 max_offset = This->indexCache[run_to_check].firstOffset-1;
6855 max_run = run_to_check-1;
6857 else if (offset > This->indexCache[run_to_check].lastOffset)
6859 min_offset = This->indexCache[run_to_check].lastOffset+1;
6860 min_run = run_to_check+1;
6862 else
6863 /* Block is in this run. */
6864 min_run = max_run = run_to_check;
6867 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
6870 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
6871 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
6873 BlockChainBlock *result=NULL;
6874 int i;
6876 for (i=0; i<2; i++)
6877 if (This->cachedBlocks[i].index == index)
6879 *sector = This->cachedBlocks[i].sector;
6880 *block = &This->cachedBlocks[i];
6881 return S_OK;
6884 *sector = BlockChainStream_GetSectorOfOffset(This, index);
6885 if (*sector == BLOCK_END_OF_CHAIN)
6886 return STG_E_DOCFILECORRUPT;
6888 if (create)
6890 if (This->cachedBlocks[0].index == 0xffffffff)
6891 result = &This->cachedBlocks[0];
6892 else if (This->cachedBlocks[1].index == 0xffffffff)
6893 result = &This->cachedBlocks[1];
6894 else
6896 result = &This->cachedBlocks[This->blockToEvict++];
6897 if (This->blockToEvict == 2)
6898 This->blockToEvict = 0;
6901 if (result->dirty)
6903 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
6904 return STG_E_WRITEFAULT;
6905 result->dirty = FALSE;
6908 result->read = FALSE;
6909 result->index = index;
6910 result->sector = *sector;
6913 *block = result;
6914 return S_OK;
6917 BlockChainStream* BlockChainStream_Construct(
6918 StorageImpl* parentStorage,
6919 ULONG* headOfStreamPlaceHolder,
6920 DirRef dirEntry)
6922 BlockChainStream* newStream;
6924 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
6926 newStream->parentStorage = parentStorage;
6927 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6928 newStream->ownerDirEntry = dirEntry;
6929 newStream->indexCache = NULL;
6930 newStream->indexCacheLen = 0;
6931 newStream->indexCacheSize = 0;
6932 newStream->cachedBlocks[0].index = 0xffffffff;
6933 newStream->cachedBlocks[0].dirty = FALSE;
6934 newStream->cachedBlocks[1].index = 0xffffffff;
6935 newStream->cachedBlocks[1].dirty = FALSE;
6936 newStream->blockToEvict = 0;
6938 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
6940 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
6941 HeapFree(GetProcessHeap(), 0, newStream);
6942 return NULL;
6945 return newStream;
6948 HRESULT BlockChainStream_Flush(BlockChainStream* This)
6950 int i;
6951 if (!This) return S_OK;
6952 for (i=0; i<2; i++)
6954 if (This->cachedBlocks[i].dirty)
6956 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6957 This->cachedBlocks[i].dirty = FALSE;
6958 else
6959 return STG_E_WRITEFAULT;
6962 return S_OK;
6965 void BlockChainStream_Destroy(BlockChainStream* This)
6967 if (This)
6969 BlockChainStream_Flush(This);
6970 HeapFree(GetProcessHeap(), 0, This->indexCache);
6972 HeapFree(GetProcessHeap(), 0, This);
6975 /******************************************************************************
6976 * BlockChainStream_GetHeadOfChain
6978 * Returns the head of this stream chain.
6979 * Some special chains don't have directory entries, their heads are kept in
6980 * This->headOfStreamPlaceHolder.
6983 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6985 DirEntry chainEntry;
6986 HRESULT hr;
6988 if (This->headOfStreamPlaceHolder != 0)
6989 return *(This->headOfStreamPlaceHolder);
6991 if (This->ownerDirEntry != DIRENTRY_NULL)
6993 hr = StorageImpl_ReadDirEntry(
6994 This->parentStorage,
6995 This->ownerDirEntry,
6996 &chainEntry);
6998 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
6999 return chainEntry.startingBlock;
7002 return BLOCK_END_OF_CHAIN;
7005 /******************************************************************************
7006 * BlockChainStream_GetCount
7008 * Returns the number of blocks that comprises this chain.
7009 * This is not the size of the stream as the last block may not be full!
7011 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
7013 return This->numBlocks;
7016 /******************************************************************************
7017 * BlockChainStream_ReadAt
7019 * Reads a specified number of bytes from this chain at the specified offset.
7020 * bytesRead may be NULL.
7021 * Failure will be returned if the specified number of bytes has not been read.
7023 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
7024 ULARGE_INTEGER offset,
7025 ULONG size,
7026 void* buffer,
7027 ULONG* bytesRead)
7029 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7030 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7031 ULONG bytesToReadInBuffer;
7032 ULONG blockIndex;
7033 BYTE* bufferWalker;
7034 ULARGE_INTEGER stream_size;
7035 HRESULT hr;
7036 BlockChainBlock *cachedBlock;
7038 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
7041 * Find the first block in the stream that contains part of the buffer.
7043 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
7045 *bytesRead = 0;
7047 stream_size = BlockChainStream_GetSize(This);
7048 if (stream_size.QuadPart > offset.QuadPart)
7049 size = min(stream_size.QuadPart - offset.QuadPart, size);
7050 else
7051 return S_OK;
7054 * Start reading the buffer.
7056 bufferWalker = buffer;
7058 while (size > 0)
7060 ULARGE_INTEGER ulOffset;
7061 DWORD bytesReadAt;
7064 * Calculate how many bytes we can copy from this big block.
7066 bytesToReadInBuffer =
7067 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7069 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
7071 if (FAILED(hr))
7072 return hr;
7074 if (!cachedBlock)
7076 /* Not in cache, and we're going to read past the end of the block. */
7077 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7078 offsetInBlock;
7080 StorageImpl_ReadAt(This->parentStorage,
7081 ulOffset,
7082 bufferWalker,
7083 bytesToReadInBuffer,
7084 &bytesReadAt);
7086 else
7088 if (!cachedBlock->read)
7090 ULONG read;
7091 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7092 return STG_E_READFAULT;
7094 cachedBlock->read = TRUE;
7097 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
7098 bytesReadAt = bytesToReadInBuffer;
7101 blockNoInSequence++;
7102 bufferWalker += bytesReadAt;
7103 size -= bytesReadAt;
7104 *bytesRead += bytesReadAt;
7105 offsetInBlock = 0; /* There is no offset on the next block */
7107 if (bytesToReadInBuffer != bytesReadAt)
7108 break;
7111 return S_OK;
7114 /******************************************************************************
7115 * BlockChainStream_WriteAt
7117 * Writes the specified number of bytes to this chain at the specified offset.
7118 * Will fail if not all specified number of bytes have been written.
7120 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
7121 ULARGE_INTEGER offset,
7122 ULONG size,
7123 const void* buffer,
7124 ULONG* bytesWritten)
7126 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7127 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7128 ULONG bytesToWrite;
7129 ULONG blockIndex;
7130 const BYTE* bufferWalker;
7131 HRESULT hr;
7132 BlockChainBlock *cachedBlock;
7134 *bytesWritten = 0;
7135 bufferWalker = buffer;
7137 while (size > 0)
7139 ULARGE_INTEGER ulOffset;
7140 DWORD bytesWrittenAt;
7143 * Calculate how many bytes we can copy to this big block.
7145 bytesToWrite =
7146 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7148 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
7150 /* BlockChainStream_SetSize should have already been called to ensure we have
7151 * enough blocks in the chain to write into */
7152 if (FAILED(hr))
7154 ERR("not enough blocks in chain to write data\n");
7155 return hr;
7158 if (!cachedBlock)
7160 /* Not in cache, and we're going to write past the end of the block. */
7161 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7162 offsetInBlock;
7164 StorageImpl_WriteAt(This->parentStorage,
7165 ulOffset,
7166 bufferWalker,
7167 bytesToWrite,
7168 &bytesWrittenAt);
7170 else
7172 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
7174 ULONG read;
7175 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7176 return STG_E_READFAULT;
7179 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
7180 bytesWrittenAt = bytesToWrite;
7181 cachedBlock->read = TRUE;
7182 cachedBlock->dirty = TRUE;
7185 blockNoInSequence++;
7186 bufferWalker += bytesWrittenAt;
7187 size -= bytesWrittenAt;
7188 *bytesWritten += bytesWrittenAt;
7189 offsetInBlock = 0; /* There is no offset on the next block */
7191 if (bytesWrittenAt != bytesToWrite)
7192 break;
7195 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7198 /******************************************************************************
7199 * BlockChainStream_Shrink
7201 * Shrinks this chain in the big block depot.
7203 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
7204 ULARGE_INTEGER newSize)
7206 ULONG blockIndex;
7207 ULONG numBlocks;
7208 int i;
7211 * Figure out how many blocks are needed to contain the new size
7213 numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7215 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7216 numBlocks++;
7218 if (numBlocks)
7221 * Go to the new end of chain
7223 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
7225 /* Mark the new end of chain */
7226 StorageImpl_SetNextBlockInChain(
7227 This->parentStorage,
7228 blockIndex,
7229 BLOCK_END_OF_CHAIN);
7231 This->tailIndex = blockIndex;
7233 else
7235 if (This->headOfStreamPlaceHolder != 0)
7237 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
7239 else
7241 DirEntry chainEntry;
7242 assert(This->ownerDirEntry != DIRENTRY_NULL);
7244 StorageImpl_ReadDirEntry(
7245 This->parentStorage,
7246 This->ownerDirEntry,
7247 &chainEntry);
7249 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7251 StorageImpl_WriteDirEntry(
7252 This->parentStorage,
7253 This->ownerDirEntry,
7254 &chainEntry);
7257 This->tailIndex = BLOCK_END_OF_CHAIN;
7260 This->numBlocks = numBlocks;
7263 * Mark the extra blocks as free
7265 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
7267 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
7268 StorageImpl_FreeBigBlock(This->parentStorage,
7269 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
7270 if (last_run->lastOffset == last_run->firstOffset)
7271 This->indexCacheLen--;
7272 else
7273 last_run->lastOffset--;
7277 * Reset the last accessed block cache.
7279 for (i=0; i<2; i++)
7281 if (This->cachedBlocks[i].index >= numBlocks)
7283 This->cachedBlocks[i].index = 0xffffffff;
7284 This->cachedBlocks[i].dirty = FALSE;
7288 return TRUE;
7291 /******************************************************************************
7292 * BlockChainStream_Enlarge
7294 * Grows this chain in the big block depot.
7296 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
7297 ULARGE_INTEGER newSize)
7299 ULONG blockIndex, currentBlock;
7300 ULONG newNumBlocks;
7301 ULONG oldNumBlocks = 0;
7303 blockIndex = BlockChainStream_GetHeadOfChain(This);
7306 * Empty chain. Create the head.
7308 if (blockIndex == BLOCK_END_OF_CHAIN)
7310 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7311 StorageImpl_SetNextBlockInChain(This->parentStorage,
7312 blockIndex,
7313 BLOCK_END_OF_CHAIN);
7315 if (This->headOfStreamPlaceHolder != 0)
7317 *(This->headOfStreamPlaceHolder) = blockIndex;
7319 else
7321 DirEntry chainEntry;
7322 assert(This->ownerDirEntry != DIRENTRY_NULL);
7324 StorageImpl_ReadDirEntry(
7325 This->parentStorage,
7326 This->ownerDirEntry,
7327 &chainEntry);
7329 chainEntry.startingBlock = blockIndex;
7331 StorageImpl_WriteDirEntry(
7332 This->parentStorage,
7333 This->ownerDirEntry,
7334 &chainEntry);
7337 This->tailIndex = blockIndex;
7338 This->numBlocks = 1;
7342 * Figure out how many blocks are needed to contain this stream
7344 newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7346 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7347 newNumBlocks++;
7350 * Go to the current end of chain
7352 if (This->tailIndex == BLOCK_END_OF_CHAIN)
7354 currentBlock = blockIndex;
7356 while (blockIndex != BLOCK_END_OF_CHAIN)
7358 This->numBlocks++;
7359 currentBlock = blockIndex;
7361 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
7362 &blockIndex)))
7363 return FALSE;
7366 This->tailIndex = currentBlock;
7369 currentBlock = This->tailIndex;
7370 oldNumBlocks = This->numBlocks;
7373 * Add new blocks to the chain
7375 if (oldNumBlocks < newNumBlocks)
7377 while (oldNumBlocks < newNumBlocks)
7379 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7381 StorageImpl_SetNextBlockInChain(
7382 This->parentStorage,
7383 currentBlock,
7384 blockIndex);
7386 StorageImpl_SetNextBlockInChain(
7387 This->parentStorage,
7388 blockIndex,
7389 BLOCK_END_OF_CHAIN);
7391 currentBlock = blockIndex;
7392 oldNumBlocks++;
7395 This->tailIndex = blockIndex;
7396 This->numBlocks = newNumBlocks;
7399 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
7400 return FALSE;
7402 return TRUE;
7405 /******************************************************************************
7406 * BlockChainStream_SetSize
7408 * Sets the size of this stream. The big block depot will be updated.
7409 * The file will grow if we grow the chain.
7411 * TODO: Free the actual blocks in the file when we shrink the chain.
7412 * Currently, the blocks are still in the file. So the file size
7413 * doesn't shrink even if we shrink streams.
7415 BOOL BlockChainStream_SetSize(
7416 BlockChainStream* This,
7417 ULARGE_INTEGER newSize)
7419 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
7421 if (newSize.QuadPart == size.QuadPart)
7422 return TRUE;
7424 if (newSize.QuadPart < size.QuadPart)
7426 BlockChainStream_Shrink(This, newSize);
7428 else
7430 BlockChainStream_Enlarge(This, newSize);
7433 return TRUE;
7436 /******************************************************************************
7437 * BlockChainStream_GetSize
7439 * Returns the size of this chain.
7440 * Will return the block count if this chain doesn't have a directory entry.
7442 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
7444 DirEntry chainEntry;
7446 if(This->headOfStreamPlaceHolder == NULL)
7449 * This chain has a directory entry so use the size value from there.
7451 StorageImpl_ReadDirEntry(
7452 This->parentStorage,
7453 This->ownerDirEntry,
7454 &chainEntry);
7456 return chainEntry.size;
7458 else
7461 * this chain is a chain that does not have a directory entry, figure out the
7462 * size by making the product number of used blocks times the
7463 * size of them
7465 ULARGE_INTEGER result;
7466 result.QuadPart =
7467 (ULONGLONG)BlockChainStream_GetCount(This) *
7468 This->parentStorage->bigBlockSize;
7470 return result;
7474 /******************************************************************************
7475 ** SmallBlockChainStream implementation
7478 SmallBlockChainStream* SmallBlockChainStream_Construct(
7479 StorageImpl* parentStorage,
7480 ULONG* headOfStreamPlaceHolder,
7481 DirRef dirEntry)
7483 SmallBlockChainStream* newStream;
7485 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
7487 newStream->parentStorage = parentStorage;
7488 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7489 newStream->ownerDirEntry = dirEntry;
7491 return newStream;
7494 void SmallBlockChainStream_Destroy(
7495 SmallBlockChainStream* This)
7497 HeapFree(GetProcessHeap(), 0, This);
7500 /******************************************************************************
7501 * SmallBlockChainStream_GetHeadOfChain
7503 * Returns the head of this chain of small blocks.
7505 static ULONG SmallBlockChainStream_GetHeadOfChain(
7506 SmallBlockChainStream* This)
7508 DirEntry chainEntry;
7509 HRESULT hr;
7511 if (This->headOfStreamPlaceHolder != NULL)
7512 return *(This->headOfStreamPlaceHolder);
7514 if (This->ownerDirEntry)
7516 hr = StorageImpl_ReadDirEntry(
7517 This->parentStorage,
7518 This->ownerDirEntry,
7519 &chainEntry);
7521 if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
7522 return chainEntry.startingBlock;
7525 return BLOCK_END_OF_CHAIN;
7528 /******************************************************************************
7529 * SmallBlockChainStream_GetNextBlockInChain
7531 * Returns the index of the next small block in this chain.
7533 * Return Values:
7534 * - BLOCK_END_OF_CHAIN: end of this chain
7535 * - BLOCK_UNUSED: small block 'blockIndex' is free
7537 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
7538 SmallBlockChainStream* This,
7539 ULONG blockIndex,
7540 ULONG* nextBlockInChain)
7542 ULARGE_INTEGER offsetOfBlockInDepot;
7543 DWORD buffer;
7544 ULONG bytesRead;
7545 HRESULT res;
7547 *nextBlockInChain = BLOCK_END_OF_CHAIN;
7549 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7552 * Read those bytes in the buffer from the small block file.
7554 res = BlockChainStream_ReadAt(
7555 This->parentStorage->smallBlockDepotChain,
7556 offsetOfBlockInDepot,
7557 sizeof(DWORD),
7558 &buffer,
7559 &bytesRead);
7561 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
7562 res = STG_E_READFAULT;
7564 if (SUCCEEDED(res))
7566 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
7567 return S_OK;
7570 return res;
7573 /******************************************************************************
7574 * SmallBlockChainStream_SetNextBlockInChain
7576 * Writes the index of the next block of the specified block in the small
7577 * block depot.
7578 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7579 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7581 static void SmallBlockChainStream_SetNextBlockInChain(
7582 SmallBlockChainStream* This,
7583 ULONG blockIndex,
7584 ULONG nextBlock)
7586 ULARGE_INTEGER offsetOfBlockInDepot;
7587 DWORD buffer;
7588 ULONG bytesWritten;
7590 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7592 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
7595 * Read those bytes in the buffer from the small block file.
7597 BlockChainStream_WriteAt(
7598 This->parentStorage->smallBlockDepotChain,
7599 offsetOfBlockInDepot,
7600 sizeof(DWORD),
7601 &buffer,
7602 &bytesWritten);
7605 /******************************************************************************
7606 * SmallBlockChainStream_FreeBlock
7608 * Flag small block 'blockIndex' as free in the small block depot.
7610 static void SmallBlockChainStream_FreeBlock(
7611 SmallBlockChainStream* This,
7612 ULONG blockIndex)
7614 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
7617 /******************************************************************************
7618 * SmallBlockChainStream_GetNextFreeBlock
7620 * Returns the index of a free small block. The small block depot will be
7621 * enlarged if necessary. The small block chain will also be enlarged if
7622 * necessary.
7624 static ULONG SmallBlockChainStream_GetNextFreeBlock(
7625 SmallBlockChainStream* This)
7627 ULARGE_INTEGER offsetOfBlockInDepot;
7628 DWORD buffer;
7629 ULONG bytesRead;
7630 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
7631 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
7632 HRESULT res = S_OK;
7633 ULONG smallBlocksPerBigBlock;
7634 DirEntry rootEntry;
7635 ULONG blocksRequired;
7636 ULARGE_INTEGER old_size, size_required;
7638 offsetOfBlockInDepot.u.HighPart = 0;
7641 * Scan the small block depot for a free block
7643 while (nextBlockIndex != BLOCK_UNUSED)
7645 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7647 res = BlockChainStream_ReadAt(
7648 This->parentStorage->smallBlockDepotChain,
7649 offsetOfBlockInDepot,
7650 sizeof(DWORD),
7651 &buffer,
7652 &bytesRead);
7655 * If we run out of space for the small block depot, enlarge it
7657 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
7659 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
7661 if (nextBlockIndex != BLOCK_UNUSED)
7662 blockIndex++;
7664 else
7666 ULONG count =
7667 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
7669 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
7670 ULARGE_INTEGER newSize, offset;
7671 ULONG bytesWritten;
7673 newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize;
7674 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
7677 * Initialize all the small blocks to free
7679 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
7680 offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize;
7681 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
7682 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
7684 StorageImpl_SaveFileHeader(This->parentStorage);
7688 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
7690 smallBlocksPerBigBlock =
7691 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
7694 * Verify if we have to allocate big blocks to contain small blocks
7696 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
7698 size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize;
7700 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
7702 if (size_required.QuadPart > old_size.QuadPart)
7704 BlockChainStream_SetSize(
7705 This->parentStorage->smallBlockRootChain,
7706 size_required);
7708 StorageImpl_ReadDirEntry(
7709 This->parentStorage,
7710 This->parentStorage->base.storageDirEntry,
7711 &rootEntry);
7713 rootEntry.size = size_required;
7715 StorageImpl_WriteDirEntry(
7716 This->parentStorage,
7717 This->parentStorage->base.storageDirEntry,
7718 &rootEntry);
7721 return blockIndex;
7724 /******************************************************************************
7725 * SmallBlockChainStream_ReadAt
7727 * Reads a specified number of bytes from this chain at the specified offset.
7728 * bytesRead may be NULL.
7729 * Failure will be returned if the specified number of bytes has not been read.
7731 HRESULT SmallBlockChainStream_ReadAt(
7732 SmallBlockChainStream* This,
7733 ULARGE_INTEGER offset,
7734 ULONG size,
7735 void* buffer,
7736 ULONG* bytesRead)
7738 HRESULT rc = S_OK;
7739 ULARGE_INTEGER offsetInBigBlockFile;
7740 ULONG blockNoInSequence =
7741 offset.u.LowPart / This->parentStorage->smallBlockSize;
7743 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7744 ULONG bytesToReadInBuffer;
7745 ULONG blockIndex;
7746 ULONG bytesReadFromBigBlockFile;
7747 BYTE* bufferWalker;
7748 ULARGE_INTEGER stream_size;
7751 * This should never happen on a small block file.
7753 assert(offset.u.HighPart==0);
7755 *bytesRead = 0;
7757 stream_size = SmallBlockChainStream_GetSize(This);
7758 if (stream_size.QuadPart > offset.QuadPart)
7759 size = min(stream_size.QuadPart - offset.QuadPart, size);
7760 else
7761 return S_OK;
7764 * Find the first block in the stream that contains part of the buffer.
7766 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7768 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7770 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7771 if(FAILED(rc))
7772 return rc;
7773 blockNoInSequence--;
7777 * Start reading the buffer.
7779 bufferWalker = buffer;
7781 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7784 * Calculate how many bytes we can copy from this small block.
7786 bytesToReadInBuffer =
7787 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7790 * Calculate the offset of the small block in the small block file.
7792 offsetInBigBlockFile.QuadPart =
7793 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
7795 offsetInBigBlockFile.QuadPart += offsetInBlock;
7798 * Read those bytes in the buffer from the small block file.
7799 * The small block has already been identified so it shouldn't fail
7800 * unless the file is corrupt.
7802 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
7803 offsetInBigBlockFile,
7804 bytesToReadInBuffer,
7805 bufferWalker,
7806 &bytesReadFromBigBlockFile);
7808 if (FAILED(rc))
7809 return rc;
7811 if (!bytesReadFromBigBlockFile)
7812 return STG_E_DOCFILECORRUPT;
7815 * Step to the next big block.
7817 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7818 if(FAILED(rc))
7819 return STG_E_DOCFILECORRUPT;
7821 bufferWalker += bytesReadFromBigBlockFile;
7822 size -= bytesReadFromBigBlockFile;
7823 *bytesRead += bytesReadFromBigBlockFile;
7824 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
7827 return S_OK;
7830 /******************************************************************************
7831 * SmallBlockChainStream_WriteAt
7833 * Writes the specified number of bytes to this chain at the specified offset.
7834 * Will fail if not all specified number of bytes have been written.
7836 HRESULT SmallBlockChainStream_WriteAt(
7837 SmallBlockChainStream* This,
7838 ULARGE_INTEGER offset,
7839 ULONG size,
7840 const void* buffer,
7841 ULONG* bytesWritten)
7843 ULARGE_INTEGER offsetInBigBlockFile;
7844 ULONG blockNoInSequence =
7845 offset.u.LowPart / This->parentStorage->smallBlockSize;
7847 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7848 ULONG bytesToWriteInBuffer;
7849 ULONG blockIndex;
7850 ULONG bytesWrittenToBigBlockFile;
7851 const BYTE* bufferWalker;
7852 HRESULT res;
7855 * This should never happen on a small block file.
7857 assert(offset.u.HighPart==0);
7860 * Find the first block in the stream that contains part of the buffer.
7862 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7864 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7866 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
7867 return STG_E_DOCFILECORRUPT;
7868 blockNoInSequence--;
7872 * Start writing the buffer.
7874 *bytesWritten = 0;
7875 bufferWalker = buffer;
7876 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7879 * Calculate how many bytes we can copy to this small block.
7881 bytesToWriteInBuffer =
7882 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7885 * Calculate the offset of the small block in the small block file.
7887 offsetInBigBlockFile.QuadPart =
7888 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
7890 offsetInBigBlockFile.QuadPart += offsetInBlock;
7893 * Write those bytes in the buffer to the small block file.
7895 res = BlockChainStream_WriteAt(
7896 This->parentStorage->smallBlockRootChain,
7897 offsetInBigBlockFile,
7898 bytesToWriteInBuffer,
7899 bufferWalker,
7900 &bytesWrittenToBigBlockFile);
7901 if (FAILED(res))
7902 return res;
7905 * Step to the next big block.
7907 res = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7908 if (FAILED(res))
7909 return res;
7910 bufferWalker += bytesWrittenToBigBlockFile;
7911 size -= bytesWrittenToBigBlockFile;
7912 *bytesWritten += bytesWrittenToBigBlockFile;
7913 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7916 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7919 /******************************************************************************
7920 * SmallBlockChainStream_Shrink
7922 * Shrinks this chain in the small block depot.
7924 static BOOL SmallBlockChainStream_Shrink(
7925 SmallBlockChainStream* This,
7926 ULARGE_INTEGER newSize)
7928 ULONG blockIndex, extraBlock;
7929 ULONG numBlocks;
7930 ULONG count = 0;
7932 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7934 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7935 numBlocks++;
7937 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7940 * Go to the new end of chain
7942 while (count < numBlocks)
7944 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7945 &blockIndex)))
7946 return FALSE;
7947 count++;
7951 * If the count is 0, we have a special case, the head of the chain was
7952 * just freed.
7954 if (count == 0)
7956 DirEntry chainEntry;
7958 StorageImpl_ReadDirEntry(This->parentStorage,
7959 This->ownerDirEntry,
7960 &chainEntry);
7962 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7964 StorageImpl_WriteDirEntry(This->parentStorage,
7965 This->ownerDirEntry,
7966 &chainEntry);
7969 * We start freeing the chain at the head block.
7971 extraBlock = blockIndex;
7973 else
7975 /* Get the next block before marking the new end */
7976 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7977 &extraBlock)))
7978 return FALSE;
7980 /* Mark the new end of chain */
7981 SmallBlockChainStream_SetNextBlockInChain(
7982 This,
7983 blockIndex,
7984 BLOCK_END_OF_CHAIN);
7988 * Mark the extra blocks as free
7990 while (extraBlock != BLOCK_END_OF_CHAIN)
7992 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7993 &blockIndex)))
7994 return FALSE;
7995 SmallBlockChainStream_FreeBlock(This, extraBlock);
7996 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7997 extraBlock = blockIndex;
8000 return TRUE;
8003 /******************************************************************************
8004 * SmallBlockChainStream_Enlarge
8006 * Grows this chain in the small block depot.
8008 static BOOL SmallBlockChainStream_Enlarge(
8009 SmallBlockChainStream* This,
8010 ULARGE_INTEGER newSize)
8012 ULONG blockIndex, currentBlock;
8013 ULONG newNumBlocks;
8014 ULONG oldNumBlocks = 0;
8016 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8019 * Empty chain. Create the head.
8021 if (blockIndex == BLOCK_END_OF_CHAIN)
8023 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8024 SmallBlockChainStream_SetNextBlockInChain(
8025 This,
8026 blockIndex,
8027 BLOCK_END_OF_CHAIN);
8029 if (This->headOfStreamPlaceHolder != NULL)
8031 *(This->headOfStreamPlaceHolder) = blockIndex;
8033 else
8035 DirEntry chainEntry;
8037 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
8038 &chainEntry);
8040 chainEntry.startingBlock = blockIndex;
8042 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
8043 &chainEntry);
8047 currentBlock = blockIndex;
8050 * Figure out how many blocks are needed to contain this stream
8052 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8054 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8055 newNumBlocks++;
8058 * Go to the current end of chain
8060 while (blockIndex != BLOCK_END_OF_CHAIN)
8062 oldNumBlocks++;
8063 currentBlock = blockIndex;
8064 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
8065 return FALSE;
8069 * Add new blocks to the chain
8071 while (oldNumBlocks < newNumBlocks)
8073 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8074 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
8076 SmallBlockChainStream_SetNextBlockInChain(
8077 This,
8078 blockIndex,
8079 BLOCK_END_OF_CHAIN);
8081 currentBlock = blockIndex;
8082 oldNumBlocks++;
8085 return TRUE;
8088 /******************************************************************************
8089 * SmallBlockChainStream_SetSize
8091 * Sets the size of this stream.
8092 * The file will grow if we grow the chain.
8094 * TODO: Free the actual blocks in the file when we shrink the chain.
8095 * Currently, the blocks are still in the file. So the file size
8096 * doesn't shrink even if we shrink streams.
8098 BOOL SmallBlockChainStream_SetSize(
8099 SmallBlockChainStream* This,
8100 ULARGE_INTEGER newSize)
8102 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
8104 if (newSize.u.LowPart == size.u.LowPart)
8105 return TRUE;
8107 if (newSize.u.LowPart < size.u.LowPart)
8109 SmallBlockChainStream_Shrink(This, newSize);
8111 else
8113 SmallBlockChainStream_Enlarge(This, newSize);
8116 return TRUE;
8119 /******************************************************************************
8120 * SmallBlockChainStream_GetCount
8122 * Returns the number of small blocks that comprises this chain.
8123 * This is not the size of the stream as the last block may not be full!
8126 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
8128 ULONG blockIndex;
8129 ULONG count = 0;
8131 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8133 while(blockIndex != BLOCK_END_OF_CHAIN)
8135 count++;
8137 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
8138 blockIndex, &blockIndex)))
8139 return 0;
8142 return count;
8145 /******************************************************************************
8146 * SmallBlockChainStream_GetSize
8148 * Returns the size of this chain.
8150 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
8152 DirEntry chainEntry;
8154 if(This->headOfStreamPlaceHolder != NULL)
8156 ULARGE_INTEGER result;
8157 result.u.HighPart = 0;
8159 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
8160 This->parentStorage->smallBlockSize;
8162 return result;
8165 StorageImpl_ReadDirEntry(
8166 This->parentStorage,
8167 This->ownerDirEntry,
8168 &chainEntry);
8170 return chainEntry.size;
8173 static HRESULT create_storagefile(
8174 LPCOLESTR pwcsName,
8175 DWORD grfMode,
8176 DWORD grfAttrs,
8177 STGOPTIONS* pStgOptions,
8178 REFIID riid,
8179 void** ppstgOpen)
8181 StorageBaseImpl* newStorage = 0;
8182 HANDLE hFile = INVALID_HANDLE_VALUE;
8183 HRESULT hr = STG_E_INVALIDFLAG;
8184 DWORD shareMode;
8185 DWORD accessMode;
8186 DWORD creationMode;
8187 DWORD fileAttributes;
8188 WCHAR tempFileName[MAX_PATH];
8190 if (ppstgOpen == 0)
8191 return STG_E_INVALIDPOINTER;
8193 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
8194 return STG_E_INVALIDPARAMETER;
8196 /* if no share mode given then DENY_NONE is the default */
8197 if (STGM_SHARE_MODE(grfMode) == 0)
8198 grfMode |= STGM_SHARE_DENY_NONE;
8200 if ( FAILED( validateSTGM(grfMode) ))
8201 goto end;
8203 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8204 switch(STGM_ACCESS_MODE(grfMode))
8206 case STGM_WRITE:
8207 case STGM_READWRITE:
8208 break;
8209 default:
8210 goto end;
8213 /* in direct mode, can only use SHARE_EXCLUSIVE */
8214 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
8215 goto end;
8217 /* but in transacted mode, any share mode is valid */
8220 * Generate a unique name.
8222 if (pwcsName == 0)
8224 WCHAR tempPath[MAX_PATH];
8225 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
8227 memset(tempPath, 0, sizeof(tempPath));
8228 memset(tempFileName, 0, sizeof(tempFileName));
8230 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
8231 tempPath[0] = '.';
8233 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
8234 pwcsName = tempFileName;
8235 else
8237 hr = STG_E_INSUFFICIENTMEMORY;
8238 goto end;
8241 creationMode = TRUNCATE_EXISTING;
8243 else
8245 creationMode = GetCreationModeFromSTGM(grfMode);
8249 * Interpret the STGM value grfMode
8251 shareMode = GetShareModeFromSTGM(grfMode);
8252 accessMode = GetAccessModeFromSTGM(grfMode);
8254 if (grfMode & STGM_DELETEONRELEASE)
8255 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
8256 else
8257 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
8259 *ppstgOpen = 0;
8261 hFile = CreateFileW(pwcsName,
8262 accessMode,
8263 shareMode,
8264 NULL,
8265 creationMode,
8266 fileAttributes,
8269 if (hFile == INVALID_HANDLE_VALUE)
8271 if(GetLastError() == ERROR_FILE_EXISTS)
8272 hr = STG_E_FILEALREADYEXISTS;
8273 else
8274 hr = E_FAIL;
8275 goto end;
8279 * Allocate and initialize the new IStorage32object.
8281 hr = Storage_Construct(
8282 hFile,
8283 pwcsName,
8284 NULL,
8285 grfMode,
8286 TRUE,
8287 TRUE,
8288 pStgOptions->ulSectorSize,
8289 &newStorage);
8291 if (FAILED(hr))
8293 goto end;
8296 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
8297 IStorage_Release(&newStorage->IStorage_iface);
8299 end:
8300 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
8302 return hr;
8305 /******************************************************************************
8306 * StgCreateDocfile [OLE32.@]
8307 * Creates a new compound file storage object
8309 * PARAMS
8310 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8311 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8312 * reserved [ ?] unused?, usually 0
8313 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8315 * RETURNS
8316 * S_OK if the file was successfully created
8317 * some STG_E_ value if error
8318 * NOTES
8319 * if pwcsName is NULL, create file with new unique name
8320 * the function can returns
8321 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8322 * (unrealized now)
8324 HRESULT WINAPI StgCreateDocfile(
8325 LPCOLESTR pwcsName,
8326 DWORD grfMode,
8327 DWORD reserved,
8328 IStorage **ppstgOpen)
8330 STGOPTIONS stgoptions = {1, 0, 512};
8332 TRACE("(%s, %x, %d, %p)\n",
8333 debugstr_w(pwcsName), grfMode,
8334 reserved, ppstgOpen);
8336 if (ppstgOpen == 0)
8337 return STG_E_INVALIDPOINTER;
8338 if (reserved != 0)
8339 return STG_E_INVALIDPARAMETER;
8341 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
8344 /******************************************************************************
8345 * StgCreateStorageEx [OLE32.@]
8347 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8349 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8350 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8352 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
8354 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8355 return STG_E_INVALIDPARAMETER;
8358 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
8360 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8361 return STG_E_INVALIDPARAMETER;
8364 if (stgfmt == STGFMT_FILE)
8366 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8367 return STG_E_INVALIDPARAMETER;
8370 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
8372 STGOPTIONS defaultOptions = {1, 0, 512};
8374 if (!pStgOptions) pStgOptions = &defaultOptions;
8375 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
8379 ERR("Invalid stgfmt argument\n");
8380 return STG_E_INVALIDPARAMETER;
8383 /******************************************************************************
8384 * StgCreatePropSetStg [OLE32.@]
8386 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
8387 IPropertySetStorage **propset)
8389 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
8390 if (reserved)
8391 return STG_E_INVALIDPARAMETER;
8393 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
8396 /******************************************************************************
8397 * StgOpenStorageEx [OLE32.@]
8399 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8401 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8402 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8404 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
8406 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8407 return STG_E_INVALIDPARAMETER;
8410 switch (stgfmt)
8412 case STGFMT_FILE:
8413 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8414 return STG_E_INVALIDPARAMETER;
8416 case STGFMT_STORAGE:
8417 break;
8419 case STGFMT_DOCFILE:
8420 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
8422 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8423 return STG_E_INVALIDPARAMETER;
8425 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8426 break;
8428 case STGFMT_ANY:
8429 WARN("STGFMT_ANY assuming storage\n");
8430 break;
8432 default:
8433 return STG_E_INVALIDPARAMETER;
8436 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
8440 /******************************************************************************
8441 * StgOpenStorage [OLE32.@]
8443 HRESULT WINAPI StgOpenStorage(
8444 const OLECHAR *pwcsName,
8445 IStorage *pstgPriority,
8446 DWORD grfMode,
8447 SNB snbExclude,
8448 DWORD reserved,
8449 IStorage **ppstgOpen)
8451 StorageBaseImpl* newStorage = 0;
8452 HRESULT hr = S_OK;
8453 HANDLE hFile = 0;
8454 DWORD shareMode;
8455 DWORD accessMode;
8456 LPWSTR temp_name = NULL;
8458 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8459 debugstr_w(pwcsName), pstgPriority, grfMode,
8460 snbExclude, reserved, ppstgOpen);
8462 if (pstgPriority)
8464 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8465 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
8466 if (FAILED(hr)) goto end;
8467 pwcsName = temp_name;
8468 TRACE("using filename %s\n", debugstr_w(temp_name));
8471 if (pwcsName == 0)
8473 hr = STG_E_INVALIDNAME;
8474 goto end;
8477 if (ppstgOpen == 0)
8479 hr = STG_E_INVALIDPOINTER;
8480 goto end;
8483 if (reserved)
8485 hr = STG_E_INVALIDPARAMETER;
8486 goto end;
8489 if (grfMode & STGM_PRIORITY)
8491 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
8492 return STG_E_INVALIDFLAG;
8493 if (grfMode & STGM_DELETEONRELEASE)
8494 return STG_E_INVALIDFUNCTION;
8495 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
8496 return STG_E_INVALIDFLAG;
8497 grfMode &= ~0xf0; /* remove the existing sharing mode */
8498 grfMode |= STGM_SHARE_DENY_NONE;
8502 * Validate the sharing mode
8504 if (grfMode & STGM_DIRECT_SWMR)
8506 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
8507 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
8509 hr = STG_E_INVALIDFLAG;
8510 goto end;
8513 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
8514 switch(STGM_SHARE_MODE(grfMode))
8516 case STGM_SHARE_EXCLUSIVE:
8517 case STGM_SHARE_DENY_WRITE:
8518 break;
8519 default:
8520 hr = STG_E_INVALIDFLAG;
8521 goto end;
8524 if ( FAILED( validateSTGM(grfMode) ) ||
8525 (grfMode&STGM_CREATE))
8527 hr = STG_E_INVALIDFLAG;
8528 goto end;
8531 /* shared reading requires transacted or single writer mode */
8532 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
8533 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
8534 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
8536 hr = STG_E_INVALIDFLAG;
8537 goto end;
8541 * Interpret the STGM value grfMode
8543 shareMode = GetShareModeFromSTGM(grfMode);
8544 accessMode = GetAccessModeFromSTGM(grfMode);
8546 *ppstgOpen = 0;
8548 hFile = CreateFileW( pwcsName,
8549 accessMode,
8550 shareMode,
8551 NULL,
8552 OPEN_EXISTING,
8553 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
8556 if (hFile==INVALID_HANDLE_VALUE)
8558 DWORD last_error = GetLastError();
8560 hr = E_FAIL;
8562 switch (last_error)
8564 case ERROR_FILE_NOT_FOUND:
8565 hr = STG_E_FILENOTFOUND;
8566 break;
8568 case ERROR_PATH_NOT_FOUND:
8569 hr = STG_E_PATHNOTFOUND;
8570 break;
8572 case ERROR_ACCESS_DENIED:
8573 case ERROR_WRITE_PROTECT:
8574 hr = STG_E_ACCESSDENIED;
8575 break;
8577 case ERROR_SHARING_VIOLATION:
8578 hr = STG_E_SHAREVIOLATION;
8579 break;
8581 default:
8582 hr = E_FAIL;
8585 goto end;
8589 * Refuse to open the file if it's too small to be a structured storage file
8590 * FIXME: verify the file when reading instead of here
8592 if (GetFileSize(hFile, NULL) < 0x100)
8594 CloseHandle(hFile);
8595 hr = STG_E_FILEALREADYEXISTS;
8596 goto end;
8600 * Allocate and initialize the new IStorage32object.
8602 hr = Storage_Construct(
8603 hFile,
8604 pwcsName,
8605 NULL,
8606 grfMode,
8607 TRUE,
8608 FALSE,
8609 512,
8610 &newStorage);
8612 if (FAILED(hr))
8615 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8617 if(hr == STG_E_INVALIDHEADER)
8618 hr = STG_E_FILEALREADYEXISTS;
8619 goto end;
8622 *ppstgOpen = &newStorage->IStorage_iface;
8624 end:
8625 CoTaskMemFree(temp_name);
8626 if (pstgPriority) IStorage_Release(pstgPriority);
8627 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
8628 return hr;
8631 /******************************************************************************
8632 * StgCreateDocfileOnILockBytes [OLE32.@]
8634 HRESULT WINAPI StgCreateDocfileOnILockBytes(
8635 ILockBytes *plkbyt,
8636 DWORD grfMode,
8637 DWORD reserved,
8638 IStorage** ppstgOpen)
8640 StorageBaseImpl* newStorage = 0;
8641 HRESULT hr = S_OK;
8643 if ((ppstgOpen == 0) || (plkbyt == 0))
8644 return STG_E_INVALIDPOINTER;
8647 * Allocate and initialize the new IStorage object.
8649 hr = Storage_Construct(
8652 plkbyt,
8653 grfMode,
8654 FALSE,
8655 TRUE,
8656 512,
8657 &newStorage);
8659 if (FAILED(hr))
8661 return hr;
8664 *ppstgOpen = &newStorage->IStorage_iface;
8666 return hr;
8669 /******************************************************************************
8670 * StgOpenStorageOnILockBytes [OLE32.@]
8672 HRESULT WINAPI StgOpenStorageOnILockBytes(
8673 ILockBytes *plkbyt,
8674 IStorage *pstgPriority,
8675 DWORD grfMode,
8676 SNB snbExclude,
8677 DWORD reserved,
8678 IStorage **ppstgOpen)
8680 StorageBaseImpl* newStorage = 0;
8681 HRESULT hr = S_OK;
8683 if ((plkbyt == 0) || (ppstgOpen == 0))
8684 return STG_E_INVALIDPOINTER;
8686 if ( FAILED( validateSTGM(grfMode) ))
8687 return STG_E_INVALIDFLAG;
8689 *ppstgOpen = 0;
8692 * Allocate and initialize the new IStorage object.
8694 hr = Storage_Construct(
8697 plkbyt,
8698 grfMode,
8699 FALSE,
8700 FALSE,
8701 512,
8702 &newStorage);
8704 if (FAILED(hr))
8706 return hr;
8709 *ppstgOpen = &newStorage->IStorage_iface;
8711 return hr;
8714 /******************************************************************************
8715 * StgSetTimes [ole32.@]
8716 * StgSetTimes [OLE32.@]
8720 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
8721 FILETIME const *patime, FILETIME const *pmtime)
8723 IStorage *stg = NULL;
8724 HRESULT r;
8726 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
8728 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
8729 0, 0, &stg);
8730 if( SUCCEEDED(r) )
8732 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
8733 IStorage_Release(stg);
8736 return r;
8739 /******************************************************************************
8740 * StgIsStorageILockBytes [OLE32.@]
8742 * Determines if the ILockBytes contains a storage object.
8744 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
8746 BYTE sig[sizeof(STORAGE_magic)];
8747 ULARGE_INTEGER offset;
8748 ULONG read = 0;
8750 offset.u.HighPart = 0;
8751 offset.u.LowPart = 0;
8753 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
8755 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
8756 return S_OK;
8758 return S_FALSE;
8761 /******************************************************************************
8762 * WriteClassStg [OLE32.@]
8764 * This method will store the specified CLSID in the specified storage object
8766 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
8768 if(!pStg)
8769 return E_INVALIDARG;
8771 if(!rclsid)
8772 return STG_E_INVALIDPOINTER;
8774 return IStorage_SetClass(pStg, rclsid);
8777 /***********************************************************************
8778 * ReadClassStg (OLE32.@)
8780 * This method reads the CLSID previously written to a storage object with
8781 * the WriteClassStg.
8783 * PARAMS
8784 * pstg [I] IStorage pointer
8785 * pclsid [O] Pointer to where the CLSID is written
8787 * RETURNS
8788 * Success: S_OK.
8789 * Failure: HRESULT code.
8791 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
8793 STATSTG pstatstg;
8794 HRESULT hRes;
8796 TRACE("(%p, %p)\n", pstg, pclsid);
8798 if(!pstg || !pclsid)
8799 return E_INVALIDARG;
8802 * read a STATSTG structure (contains the clsid) from the storage
8804 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
8806 if(SUCCEEDED(hRes))
8807 *pclsid=pstatstg.clsid;
8809 return hRes;
8812 /***********************************************************************
8813 * OleLoadFromStream (OLE32.@)
8815 * This function loads an object from stream
8817 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
8819 CLSID clsid;
8820 HRESULT res;
8821 LPPERSISTSTREAM xstm;
8823 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
8825 res=ReadClassStm(pStm,&clsid);
8826 if (FAILED(res))
8827 return res;
8828 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
8829 if (FAILED(res))
8830 return res;
8831 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
8832 if (FAILED(res)) {
8833 IUnknown_Release((IUnknown*)*ppvObj);
8834 return res;
8836 res=IPersistStream_Load(xstm,pStm);
8837 IPersistStream_Release(xstm);
8838 /* FIXME: all refcounts ok at this point? I think they should be:
8839 * pStm : unchanged
8840 * ppvObj : 1
8841 * xstm : 0 (released)
8843 return res;
8846 /***********************************************************************
8847 * OleSaveToStream (OLE32.@)
8849 * This function saves an object with the IPersistStream interface on it
8850 * to the specified stream.
8852 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
8855 CLSID clsid;
8856 HRESULT res;
8858 TRACE("(%p,%p)\n",pPStm,pStm);
8860 res=IPersistStream_GetClassID(pPStm,&clsid);
8862 if (SUCCEEDED(res)){
8864 res=WriteClassStm(pStm,&clsid);
8866 if (SUCCEEDED(res))
8868 res=IPersistStream_Save(pPStm,pStm,TRUE);
8871 TRACE("Finished Save\n");
8872 return res;
8875 /****************************************************************************
8876 * This method validate a STGM parameter that can contain the values below
8878 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8879 * The stgm values contained in 0xffff0000 are bitmasks.
8881 * STGM_DIRECT 0x00000000
8882 * STGM_TRANSACTED 0x00010000
8883 * STGM_SIMPLE 0x08000000
8885 * STGM_READ 0x00000000
8886 * STGM_WRITE 0x00000001
8887 * STGM_READWRITE 0x00000002
8889 * STGM_SHARE_DENY_NONE 0x00000040
8890 * STGM_SHARE_DENY_READ 0x00000030
8891 * STGM_SHARE_DENY_WRITE 0x00000020
8892 * STGM_SHARE_EXCLUSIVE 0x00000010
8894 * STGM_PRIORITY 0x00040000
8895 * STGM_DELETEONRELEASE 0x04000000
8897 * STGM_CREATE 0x00001000
8898 * STGM_CONVERT 0x00020000
8899 * STGM_FAILIFTHERE 0x00000000
8901 * STGM_NOSCRATCH 0x00100000
8902 * STGM_NOSNAPSHOT 0x00200000
8904 static HRESULT validateSTGM(DWORD stgm)
8906 DWORD access = STGM_ACCESS_MODE(stgm);
8907 DWORD share = STGM_SHARE_MODE(stgm);
8908 DWORD create = STGM_CREATE_MODE(stgm);
8910 if (stgm&~STGM_KNOWN_FLAGS)
8912 ERR("unknown flags %08x\n", stgm);
8913 return E_FAIL;
8916 switch (access)
8918 case STGM_READ:
8919 case STGM_WRITE:
8920 case STGM_READWRITE:
8921 break;
8922 default:
8923 return E_FAIL;
8926 switch (share)
8928 case STGM_SHARE_DENY_NONE:
8929 case STGM_SHARE_DENY_READ:
8930 case STGM_SHARE_DENY_WRITE:
8931 case STGM_SHARE_EXCLUSIVE:
8932 break;
8933 case 0:
8934 if (!(stgm & STGM_TRANSACTED))
8935 return E_FAIL;
8936 break;
8937 default:
8938 return E_FAIL;
8941 switch (create)
8943 case STGM_CREATE:
8944 case STGM_FAILIFTHERE:
8945 break;
8946 default:
8947 return E_FAIL;
8951 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8953 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8954 return E_FAIL;
8957 * STGM_CREATE | STGM_CONVERT
8958 * if both are false, STGM_FAILIFTHERE is set to TRUE
8960 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8961 return E_FAIL;
8964 * STGM_NOSCRATCH requires STGM_TRANSACTED
8966 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8967 return E_FAIL;
8970 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8971 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8973 if ( (stgm & STGM_NOSNAPSHOT) &&
8974 (!(stgm & STGM_TRANSACTED) ||
8975 share == STGM_SHARE_EXCLUSIVE ||
8976 share == STGM_SHARE_DENY_WRITE) )
8977 return E_FAIL;
8979 return S_OK;
8982 /****************************************************************************
8983 * GetShareModeFromSTGM
8985 * This method will return a share mode flag from a STGM value.
8986 * The STGM value is assumed valid.
8988 static DWORD GetShareModeFromSTGM(DWORD stgm)
8990 switch (STGM_SHARE_MODE(stgm))
8992 case 0:
8993 assert(stgm & STGM_TRANSACTED);
8994 /* fall-through */
8995 case STGM_SHARE_DENY_NONE:
8996 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8997 case STGM_SHARE_DENY_READ:
8998 return FILE_SHARE_WRITE;
8999 case STGM_SHARE_DENY_WRITE:
9000 case STGM_SHARE_EXCLUSIVE:
9001 return FILE_SHARE_READ;
9003 ERR("Invalid share mode!\n");
9004 assert(0);
9005 return 0;
9008 /****************************************************************************
9009 * GetAccessModeFromSTGM
9011 * This method will return an access mode flag from a STGM value.
9012 * The STGM value is assumed valid.
9014 static DWORD GetAccessModeFromSTGM(DWORD stgm)
9016 switch (STGM_ACCESS_MODE(stgm))
9018 case STGM_READ:
9019 return GENERIC_READ;
9020 case STGM_WRITE:
9021 case STGM_READWRITE:
9022 return GENERIC_READ | GENERIC_WRITE;
9024 ERR("Invalid access mode!\n");
9025 assert(0);
9026 return 0;
9029 /****************************************************************************
9030 * GetCreationModeFromSTGM
9032 * This method will return a creation mode flag from a STGM value.
9033 * The STGM value is assumed valid.
9035 static DWORD GetCreationModeFromSTGM(DWORD stgm)
9037 switch(STGM_CREATE_MODE(stgm))
9039 case STGM_CREATE:
9040 return CREATE_ALWAYS;
9041 case STGM_CONVERT:
9042 FIXME("STGM_CONVERT not implemented!\n");
9043 return CREATE_NEW;
9044 case STGM_FAILIFTHERE:
9045 return CREATE_NEW;
9047 ERR("Invalid create mode!\n");
9048 assert(0);
9049 return 0;
9053 /*************************************************************************
9054 * OLECONVERT_LoadOLE10 [Internal]
9056 * Loads the OLE10 STREAM to memory
9058 * PARAMS
9059 * pOleStream [I] The OLESTREAM
9060 * pData [I] Data Structure for the OLESTREAM Data
9062 * RETURNS
9063 * Success: S_OK
9064 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9065 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9067 * NOTES
9068 * This function is used by OleConvertOLESTREAMToIStorage only.
9070 * Memory allocated for pData must be freed by the caller
9072 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
9074 DWORD dwSize;
9075 HRESULT hRes = S_OK;
9076 int nTryCnt=0;
9077 int max_try = 6;
9079 pData->pData = NULL;
9080 pData->pstrOleObjFileName = NULL;
9082 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
9084 /* Get the OleID */
9085 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9086 if(dwSize != sizeof(pData->dwOleID))
9088 hRes = CONVERT10_E_OLESTREAM_GET;
9090 else if(pData->dwOleID != OLESTREAM_ID)
9092 hRes = CONVERT10_E_OLESTREAM_FMT;
9094 else
9096 hRes = S_OK;
9097 break;
9101 if(hRes == S_OK)
9103 /* Get the TypeID... more info needed for this field */
9104 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9105 if(dwSize != sizeof(pData->dwTypeID))
9107 hRes = CONVERT10_E_OLESTREAM_GET;
9110 if(hRes == S_OK)
9112 if(pData->dwTypeID != 0)
9114 /* Get the length of the OleTypeName */
9115 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9116 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9118 hRes = CONVERT10_E_OLESTREAM_GET;
9121 if(hRes == S_OK)
9123 if(pData->dwOleTypeNameLength > 0)
9125 /* Get the OleTypeName */
9126 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9127 if(dwSize != pData->dwOleTypeNameLength)
9129 hRes = CONVERT10_E_OLESTREAM_GET;
9133 if(bStrem1)
9135 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
9136 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
9138 hRes = CONVERT10_E_OLESTREAM_GET;
9140 if(hRes == S_OK)
9142 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
9143 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
9144 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
9145 if(pData->pstrOleObjFileName)
9147 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
9148 if(dwSize != pData->dwOleObjFileNameLength)
9150 hRes = CONVERT10_E_OLESTREAM_GET;
9153 else
9154 hRes = CONVERT10_E_OLESTREAM_GET;
9157 else
9159 /* Get the Width of the Metafile */
9160 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9161 if(dwSize != sizeof(pData->dwMetaFileWidth))
9163 hRes = CONVERT10_E_OLESTREAM_GET;
9165 if(hRes == S_OK)
9167 /* Get the Height of the Metafile */
9168 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9169 if(dwSize != sizeof(pData->dwMetaFileHeight))
9171 hRes = CONVERT10_E_OLESTREAM_GET;
9175 if(hRes == S_OK)
9177 /* Get the Length of the Data */
9178 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9179 if(dwSize != sizeof(pData->dwDataLength))
9181 hRes = CONVERT10_E_OLESTREAM_GET;
9185 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
9187 if(!bStrem1) /* if it is a second OLE stream data */
9189 pData->dwDataLength -= 8;
9190 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
9191 if(dwSize != sizeof(pData->strUnknown))
9193 hRes = CONVERT10_E_OLESTREAM_GET;
9197 if(hRes == S_OK)
9199 if(pData->dwDataLength > 0)
9201 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
9203 /* Get Data (ex. IStorage, Metafile, or BMP) */
9204 if(pData->pData)
9206 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
9207 if(dwSize != pData->dwDataLength)
9209 hRes = CONVERT10_E_OLESTREAM_GET;
9212 else
9214 hRes = CONVERT10_E_OLESTREAM_GET;
9220 return hRes;
9223 /*************************************************************************
9224 * OLECONVERT_SaveOLE10 [Internal]
9226 * Saves the OLE10 STREAM From memory
9228 * PARAMS
9229 * pData [I] Data Structure for the OLESTREAM Data
9230 * pOleStream [I] The OLESTREAM to save
9232 * RETURNS
9233 * Success: S_OK
9234 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9236 * NOTES
9237 * This function is used by OleConvertIStorageToOLESTREAM only.
9240 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
9242 DWORD dwSize;
9243 HRESULT hRes = S_OK;
9246 /* Set the OleID */
9247 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9248 if(dwSize != sizeof(pData->dwOleID))
9250 hRes = CONVERT10_E_OLESTREAM_PUT;
9253 if(hRes == S_OK)
9255 /* Set the TypeID */
9256 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9257 if(dwSize != sizeof(pData->dwTypeID))
9259 hRes = CONVERT10_E_OLESTREAM_PUT;
9263 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
9265 /* Set the Length of the OleTypeName */
9266 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9267 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9269 hRes = CONVERT10_E_OLESTREAM_PUT;
9272 if(hRes == S_OK)
9274 if(pData->dwOleTypeNameLength > 0)
9276 /* Set the OleTypeName */
9277 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9278 if(dwSize != pData->dwOleTypeNameLength)
9280 hRes = CONVERT10_E_OLESTREAM_PUT;
9285 if(hRes == S_OK)
9287 /* Set the width of the Metafile */
9288 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9289 if(dwSize != sizeof(pData->dwMetaFileWidth))
9291 hRes = CONVERT10_E_OLESTREAM_PUT;
9295 if(hRes == S_OK)
9297 /* Set the height of the Metafile */
9298 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9299 if(dwSize != sizeof(pData->dwMetaFileHeight))
9301 hRes = CONVERT10_E_OLESTREAM_PUT;
9305 if(hRes == S_OK)
9307 /* Set the length of the Data */
9308 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9309 if(dwSize != sizeof(pData->dwDataLength))
9311 hRes = CONVERT10_E_OLESTREAM_PUT;
9315 if(hRes == S_OK)
9317 if(pData->dwDataLength > 0)
9319 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9320 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
9321 if(dwSize != pData->dwDataLength)
9323 hRes = CONVERT10_E_OLESTREAM_PUT;
9328 return hRes;
9331 /*************************************************************************
9332 * OLECONVERT_GetOLE20FromOLE10[Internal]
9334 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9335 * opens it, and copies the content to the dest IStorage for
9336 * OleConvertOLESTREAMToIStorage
9339 * PARAMS
9340 * pDestStorage [I] The IStorage to copy the data to
9341 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9342 * nBufferLength [I] The size of the buffer
9344 * RETURNS
9345 * Nothing
9347 * NOTES
9351 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
9353 HRESULT hRes;
9354 HANDLE hFile;
9355 IStorage *pTempStorage;
9356 DWORD dwNumOfBytesWritten;
9357 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9358 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9360 /* Create a temp File */
9361 GetTempPathW(MAX_PATH, wstrTempDir);
9362 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9363 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
9365 if(hFile != INVALID_HANDLE_VALUE)
9367 /* Write IStorage Data to File */
9368 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
9369 CloseHandle(hFile);
9371 /* Open and copy temp storage to the Dest Storage */
9372 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
9373 if(hRes == S_OK)
9375 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
9376 IStorage_Release(pTempStorage);
9378 DeleteFileW(wstrTempFile);
9383 /*************************************************************************
9384 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9386 * Saves the OLE10 STREAM From memory
9388 * PARAMS
9389 * pStorage [I] The Src IStorage to copy
9390 * pData [I] The Dest Memory to write to.
9392 * RETURNS
9393 * The size in bytes allocated for pData
9395 * NOTES
9396 * Memory allocated for pData must be freed by the caller
9398 * Used by OleConvertIStorageToOLESTREAM only.
9401 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
9403 HANDLE hFile;
9404 HRESULT hRes;
9405 DWORD nDataLength = 0;
9406 IStorage *pTempStorage;
9407 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9408 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9410 *pData = NULL;
9412 /* Create temp Storage */
9413 GetTempPathW(MAX_PATH, wstrTempDir);
9414 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9415 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
9417 if(hRes == S_OK)
9419 /* Copy Src Storage to the Temp Storage */
9420 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
9421 IStorage_Release(pTempStorage);
9423 /* Open Temp Storage as a file and copy to memory */
9424 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9425 if(hFile != INVALID_HANDLE_VALUE)
9427 nDataLength = GetFileSize(hFile, NULL);
9428 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
9429 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
9430 CloseHandle(hFile);
9432 DeleteFileW(wstrTempFile);
9434 return nDataLength;
9437 /*************************************************************************
9438 * STORAGE_CreateOleStream [Internal]
9440 * Creates the "\001OLE" stream in the IStorage if necessary.
9442 * PARAMS
9443 * storage [I] Dest storage to create the stream in
9444 * flags [I] flags to be set for newly created stream
9446 * RETURNS
9447 * HRESULT return value
9449 * NOTES
9451 * This stream is still unknown, MS Word seems to have extra data
9452 * but since the data is stored in the OLESTREAM there should be
9453 * no need to recreate the stream. If the stream is manually
9454 * deleted it will create it with this default data.
9457 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
9459 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9460 static const DWORD version_magic = 0x02000001;
9461 IStream *stream;
9462 HRESULT hr;
9464 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
9465 if (hr == S_OK)
9467 struct empty_1ole_stream {
9468 DWORD version_magic;
9469 DWORD flags;
9470 DWORD update_options;
9471 DWORD reserved;
9472 DWORD mon_stream_size;
9474 struct empty_1ole_stream stream_data;
9476 stream_data.version_magic = version_magic;
9477 stream_data.flags = flags;
9478 stream_data.update_options = 0;
9479 stream_data.reserved = 0;
9480 stream_data.mon_stream_size = 0;
9482 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
9483 IStream_Release(stream);
9486 return hr;
9489 /* write a string to a stream, preceded by its length */
9490 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
9492 HRESULT r;
9493 LPSTR str;
9494 DWORD len = 0;
9496 if( string )
9497 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
9498 r = IStream_Write( stm, &len, sizeof(len), NULL);
9499 if( FAILED( r ) )
9500 return r;
9501 if(len == 0)
9502 return r;
9503 str = CoTaskMemAlloc( len );
9504 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
9505 r = IStream_Write( stm, str, len, NULL);
9506 CoTaskMemFree( str );
9507 return r;
9510 /* read a string preceded by its length from a stream */
9511 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
9513 HRESULT r;
9514 DWORD len, count = 0;
9515 LPSTR str;
9516 LPWSTR wstr;
9518 r = IStream_Read( stm, &len, sizeof(len), &count );
9519 if( FAILED( r ) )
9520 return r;
9521 if( count != sizeof(len) )
9522 return E_OUTOFMEMORY;
9524 TRACE("%d bytes\n",len);
9526 str = CoTaskMemAlloc( len );
9527 if( !str )
9528 return E_OUTOFMEMORY;
9529 count = 0;
9530 r = IStream_Read( stm, str, len, &count );
9531 if( FAILED( r ) )
9532 return r;
9533 if( count != len )
9535 CoTaskMemFree( str );
9536 return E_OUTOFMEMORY;
9539 TRACE("Read string %s\n",debugstr_an(str,len));
9541 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
9542 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
9543 if( wstr )
9545 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
9546 wstr[len] = 0;
9548 CoTaskMemFree( str );
9550 *string = wstr;
9552 return r;
9556 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
9557 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
9559 IStream *pstm;
9560 HRESULT r = S_OK;
9561 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9563 static const BYTE unknown1[12] =
9564 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9565 0xFF, 0xFF, 0xFF, 0xFF};
9566 static const BYTE unknown2[16] =
9567 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9568 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9570 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
9571 debugstr_w(lpszUserType), debugstr_w(szClipName),
9572 debugstr_w(szProgIDName));
9574 /* Create a CompObj stream */
9575 r = IStorage_CreateStream(pstg, szwStreamName,
9576 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
9577 if( FAILED (r) )
9578 return r;
9580 /* Write CompObj Structure to stream */
9581 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
9583 if( SUCCEEDED( r ) )
9584 r = WriteClassStm( pstm, clsid );
9586 if( SUCCEEDED( r ) )
9587 r = STREAM_WriteString( pstm, lpszUserType );
9588 if( SUCCEEDED( r ) )
9589 r = STREAM_WriteString( pstm, szClipName );
9590 if( SUCCEEDED( r ) )
9591 r = STREAM_WriteString( pstm, szProgIDName );
9592 if( SUCCEEDED( r ) )
9593 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
9595 IStream_Release( pstm );
9597 return r;
9600 /***********************************************************************
9601 * WriteFmtUserTypeStg (OLE32.@)
9603 HRESULT WINAPI WriteFmtUserTypeStg(
9604 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
9606 STATSTG stat;
9607 HRESULT r;
9608 WCHAR szwClipName[0x40];
9609 CLSID clsid;
9610 LPWSTR wstrProgID = NULL;
9611 DWORD n;
9613 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
9615 /* get the clipboard format name */
9616 if( cf )
9618 n = GetClipboardFormatNameW( cf, szwClipName,
9619 sizeof(szwClipName)/sizeof(szwClipName[0]) );
9620 szwClipName[n]=0;
9623 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
9625 r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
9626 if(SUCCEEDED(r))
9627 clsid = stat.clsid;
9628 else
9629 clsid = CLSID_NULL;
9631 ProgIDFromCLSID(&clsid, &wstrProgID);
9633 TRACE("progid is %s\n",debugstr_w(wstrProgID));
9635 r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
9636 cf ? szwClipName : NULL, wstrProgID );
9638 CoTaskMemFree(wstrProgID);
9640 return r;
9644 /******************************************************************************
9645 * ReadFmtUserTypeStg [OLE32.@]
9647 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
9649 HRESULT r;
9650 IStream *stm = 0;
9651 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
9652 unsigned char unknown1[12];
9653 unsigned char unknown2[16];
9654 DWORD count;
9655 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
9656 CLSID clsid;
9658 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
9660 r = IStorage_OpenStream( pstg, szCompObj, NULL,
9661 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
9662 if( FAILED ( r ) )
9664 WARN("Failed to open stream r = %08x\n", r);
9665 return r;
9668 /* read the various parts of the structure */
9669 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
9670 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
9671 goto end;
9672 r = ReadClassStm( stm, &clsid );
9673 if( FAILED( r ) )
9674 goto end;
9676 r = STREAM_ReadString( stm, &szCLSIDName );
9677 if( FAILED( r ) )
9678 goto end;
9680 r = STREAM_ReadString( stm, &szOleTypeName );
9681 if( FAILED( r ) )
9682 goto end;
9684 r = STREAM_ReadString( stm, &szProgIDName );
9685 if( FAILED( r ) )
9686 goto end;
9688 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
9689 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
9690 goto end;
9692 /* ok, success... now we just need to store what we found */
9693 if( pcf )
9694 *pcf = RegisterClipboardFormatW( szOleTypeName );
9696 if( lplpszUserType )
9698 *lplpszUserType = szCLSIDName;
9699 szCLSIDName = NULL;
9702 end:
9703 CoTaskMemFree( szCLSIDName );
9704 CoTaskMemFree( szOleTypeName );
9705 CoTaskMemFree( szProgIDName );
9706 IStream_Release( stm );
9708 return r;
9712 /*************************************************************************
9713 * OLECONVERT_CreateCompObjStream [Internal]
9715 * Creates a "\001CompObj" is the destination IStorage if necessary.
9717 * PARAMS
9718 * pStorage [I] The dest IStorage to create the CompObj Stream
9719 * if necessary.
9720 * strOleTypeName [I] The ProgID
9722 * RETURNS
9723 * Success: S_OK
9724 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9726 * NOTES
9727 * This function is used by OleConvertOLESTREAMToIStorage only.
9729 * The stream data is stored in the OLESTREAM and there should be
9730 * no need to recreate the stream. If the stream is manually
9731 * deleted it will attempt to create it by querying the registry.
9735 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
9737 IStream *pStream;
9738 HRESULT hStorageRes, hRes = S_OK;
9739 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
9740 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9741 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
9743 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9744 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
9746 /* Initialize the CompObj structure */
9747 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
9748 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
9749 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
9752 /* Create a CompObj stream if it doesn't exist */
9753 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
9754 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9755 if(hStorageRes == S_OK)
9757 /* copy the OleTypeName to the compobj struct */
9758 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
9759 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
9761 /* copy the OleTypeName to the compobj struct */
9762 /* Note: in the test made, these were Identical */
9763 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
9764 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
9766 /* Get the CLSID */
9767 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
9768 bufferW, OLESTREAM_MAX_STR_LEN );
9769 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
9771 if(hRes == S_OK)
9773 HKEY hKey;
9774 LONG hErr;
9775 /* Get the CLSID Default Name from the Registry */
9776 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
9777 if(hErr == ERROR_SUCCESS)
9779 char strTemp[OLESTREAM_MAX_STR_LEN];
9780 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
9781 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
9782 if(hErr == ERROR_SUCCESS)
9784 strcpy(IStorageCompObj.strCLSIDName, strTemp);
9786 RegCloseKey(hKey);
9790 /* Write CompObj Structure to stream */
9791 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
9793 WriteClassStm(pStream,&(IStorageCompObj.clsid));
9795 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
9796 if(IStorageCompObj.dwCLSIDNameLength > 0)
9798 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
9800 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
9801 if(IStorageCompObj.dwOleTypeNameLength > 0)
9803 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
9805 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
9806 if(IStorageCompObj.dwProgIDNameLength > 0)
9808 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
9810 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
9811 IStream_Release(pStream);
9813 return hRes;
9817 /*************************************************************************
9818 * OLECONVERT_CreateOlePresStream[Internal]
9820 * Creates the "\002OlePres000" Stream with the Metafile data
9822 * PARAMS
9823 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
9824 * dwExtentX [I] Width of the Metafile
9825 * dwExtentY [I] Height of the Metafile
9826 * pData [I] Metafile data
9827 * dwDataLength [I] Size of the Metafile data
9829 * RETURNS
9830 * Success: S_OK
9831 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9833 * NOTES
9834 * This function is used by OleConvertOLESTREAMToIStorage only.
9837 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
9839 HRESULT hRes;
9840 IStream *pStream;
9841 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9842 BYTE pOlePresStreamHeader [] =
9844 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
9845 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9846 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9847 0x00, 0x00, 0x00, 0x00
9850 BYTE pOlePresStreamHeaderEmpty [] =
9852 0x00, 0x00, 0x00, 0x00,
9853 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9854 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9855 0x00, 0x00, 0x00, 0x00
9858 /* Create the OlePres000 Stream */
9859 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9860 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9862 if(hRes == S_OK)
9864 DWORD nHeaderSize;
9865 OLECONVERT_ISTORAGE_OLEPRES OlePres;
9867 memset(&OlePres, 0, sizeof(OlePres));
9868 /* Do we have any metafile data to save */
9869 if(dwDataLength > 0)
9871 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
9872 nHeaderSize = sizeof(pOlePresStreamHeader);
9874 else
9876 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
9877 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
9879 /* Set width and height of the metafile */
9880 OlePres.dwExtentX = dwExtentX;
9881 OlePres.dwExtentY = -dwExtentY;
9883 /* Set Data and Length */
9884 if(dwDataLength > sizeof(METAFILEPICT16))
9886 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
9887 OlePres.pData = &(pData[8]);
9889 /* Save OlePres000 Data to Stream */
9890 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
9891 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
9892 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
9893 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
9894 if(OlePres.dwSize > 0)
9896 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
9898 IStream_Release(pStream);
9902 /*************************************************************************
9903 * OLECONVERT_CreateOle10NativeStream [Internal]
9905 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9907 * PARAMS
9908 * pStorage [I] Dest storage to create the stream in
9909 * pData [I] Ole10 Native Data (ex. bmp)
9910 * dwDataLength [I] Size of the Ole10 Native Data
9912 * RETURNS
9913 * Nothing
9915 * NOTES
9916 * This function is used by OleConvertOLESTREAMToIStorage only.
9918 * Might need to verify the data and return appropriate error message
9921 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
9923 HRESULT hRes;
9924 IStream *pStream;
9925 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9927 /* Create the Ole10Native Stream */
9928 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9929 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9931 if(hRes == S_OK)
9933 /* Write info to stream */
9934 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9935 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9936 IStream_Release(pStream);
9941 /*************************************************************************
9942 * OLECONVERT_GetOLE10ProgID [Internal]
9944 * Finds the ProgID (or OleTypeID) from the IStorage
9946 * PARAMS
9947 * pStorage [I] The Src IStorage to get the ProgID
9948 * strProgID [I] the ProgID string to get
9949 * dwSize [I] the size of the string
9951 * RETURNS
9952 * Success: S_OK
9953 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9955 * NOTES
9956 * This function is used by OleConvertIStorageToOLESTREAM only.
9960 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9962 HRESULT hRes;
9963 IStream *pStream;
9964 LARGE_INTEGER iSeekPos;
9965 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9966 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9968 /* Open the CompObj Stream */
9969 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9970 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9971 if(hRes == S_OK)
9974 /*Get the OleType from the CompObj Stream */
9975 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9976 iSeekPos.u.HighPart = 0;
9978 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9979 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9980 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9981 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9982 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9983 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9984 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9986 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9987 if(*dwSize > 0)
9989 IStream_Read(pStream, strProgID, *dwSize, NULL);
9991 IStream_Release(pStream);
9993 else
9995 STATSTG stat;
9996 LPOLESTR wstrProgID;
9998 /* Get the OleType from the registry */
9999 REFCLSID clsid = &(stat.clsid);
10000 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
10001 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
10002 if(hRes == S_OK)
10004 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
10005 CoTaskMemFree(wstrProgID);
10009 return hRes;
10012 /*************************************************************************
10013 * OLECONVERT_GetOle10PresData [Internal]
10015 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10017 * PARAMS
10018 * pStorage [I] Src IStroage
10019 * pOleStream [I] Dest OleStream Mem Struct
10021 * RETURNS
10022 * Nothing
10024 * NOTES
10025 * This function is used by OleConvertIStorageToOLESTREAM only.
10027 * Memory allocated for pData must be freed by the caller
10031 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10034 HRESULT hRes;
10035 IStream *pStream;
10036 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10038 /* Initialize Default data for OLESTREAM */
10039 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10040 pOleStreamData[0].dwTypeID = 2;
10041 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10042 pOleStreamData[1].dwTypeID = 0;
10043 pOleStreamData[0].dwMetaFileWidth = 0;
10044 pOleStreamData[0].dwMetaFileHeight = 0;
10045 pOleStreamData[0].pData = NULL;
10046 pOleStreamData[1].pData = NULL;
10048 /* Open Ole10Native Stream */
10049 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10050 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10051 if(hRes == S_OK)
10054 /* Read Size and Data */
10055 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
10056 if(pOleStreamData->dwDataLength > 0)
10058 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
10059 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
10061 IStream_Release(pStream);
10067 /*************************************************************************
10068 * OLECONVERT_GetOle20PresData[Internal]
10070 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10072 * PARAMS
10073 * pStorage [I] Src IStroage
10074 * pOleStreamData [I] Dest OleStream Mem Struct
10076 * RETURNS
10077 * Nothing
10079 * NOTES
10080 * This function is used by OleConvertIStorageToOLESTREAM only.
10082 * Memory allocated for pData must be freed by the caller
10084 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10086 HRESULT hRes;
10087 IStream *pStream;
10088 OLECONVERT_ISTORAGE_OLEPRES olePress;
10089 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10091 /* Initialize Default data for OLESTREAM */
10092 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10093 pOleStreamData[0].dwTypeID = 2;
10094 pOleStreamData[0].dwMetaFileWidth = 0;
10095 pOleStreamData[0].dwMetaFileHeight = 0;
10096 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
10097 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10098 pOleStreamData[1].dwTypeID = 0;
10099 pOleStreamData[1].dwOleTypeNameLength = 0;
10100 pOleStreamData[1].strOleTypeName[0] = 0;
10101 pOleStreamData[1].dwMetaFileWidth = 0;
10102 pOleStreamData[1].dwMetaFileHeight = 0;
10103 pOleStreamData[1].pData = NULL;
10104 pOleStreamData[1].dwDataLength = 0;
10107 /* Open OlePress000 stream */
10108 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10109 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10110 if(hRes == S_OK)
10112 LARGE_INTEGER iSeekPos;
10113 METAFILEPICT16 MetaFilePict;
10114 static const char strMetafilePictName[] = "METAFILEPICT";
10116 /* Set the TypeID for a Metafile */
10117 pOleStreamData[1].dwTypeID = 5;
10119 /* Set the OleTypeName to Metafile */
10120 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
10121 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
10123 iSeekPos.u.HighPart = 0;
10124 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
10126 /* Get Presentation Data */
10127 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10128 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
10129 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
10130 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
10132 /*Set width and Height */
10133 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
10134 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
10135 if(olePress.dwSize > 0)
10137 /* Set Length */
10138 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
10140 /* Set MetaFilePict struct */
10141 MetaFilePict.mm = 8;
10142 MetaFilePict.xExt = olePress.dwExtentX;
10143 MetaFilePict.yExt = olePress.dwExtentY;
10144 MetaFilePict.hMF = 0;
10146 /* Get Metafile Data */
10147 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
10148 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
10149 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
10151 IStream_Release(pStream);
10155 /*************************************************************************
10156 * OleConvertOLESTREAMToIStorage [OLE32.@]
10158 * Read info on MSDN
10160 * TODO
10161 * DVTARGETDEVICE parameter is not handled
10162 * Still unsure of some mem fields for OLE 10 Stream
10163 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10164 * and "\001OLE" streams
10167 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
10168 LPOLESTREAM pOleStream,
10169 LPSTORAGE pstg,
10170 const DVTARGETDEVICE* ptd)
10172 int i;
10173 HRESULT hRes=S_OK;
10174 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10176 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
10178 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10180 if(ptd != NULL)
10182 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10185 if(pstg == NULL || pOleStream == NULL)
10187 hRes = E_INVALIDARG;
10190 if(hRes == S_OK)
10192 /* Load the OLESTREAM to Memory */
10193 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
10196 if(hRes == S_OK)
10198 /* Load the OLESTREAM to Memory (part 2)*/
10199 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
10202 if(hRes == S_OK)
10205 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
10207 /* Do we have the IStorage Data in the OLESTREAM */
10208 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
10210 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10211 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
10213 else
10215 /* It must be an original OLE 1.0 source */
10216 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10219 else
10221 /* It must be an original OLE 1.0 source */
10222 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10225 /* Create CompObj Stream if necessary */
10226 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
10227 if(hRes == S_OK)
10229 /*Create the Ole Stream if necessary */
10230 STORAGE_CreateOleStream(pstg, 0);
10235 /* Free allocated memory */
10236 for(i=0; i < 2; i++)
10238 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10239 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
10240 pOleStreamData[i].pstrOleObjFileName = NULL;
10242 return hRes;
10245 /*************************************************************************
10246 * OleConvertIStorageToOLESTREAM [OLE32.@]
10248 * Read info on MSDN
10250 * Read info on MSDN
10252 * TODO
10253 * Still unsure of some mem fields for OLE 10 Stream
10254 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10255 * and "\001OLE" streams.
10258 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
10259 LPSTORAGE pstg,
10260 LPOLESTREAM pOleStream)
10262 int i;
10263 HRESULT hRes = S_OK;
10264 IStream *pStream;
10265 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10266 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10268 TRACE("%p %p\n", pstg, pOleStream);
10270 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10272 if(pstg == NULL || pOleStream == NULL)
10274 hRes = E_INVALIDARG;
10276 if(hRes == S_OK)
10278 /* Get the ProgID */
10279 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
10280 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
10282 if(hRes == S_OK)
10284 /* Was it originally Ole10 */
10285 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
10286 if(hRes == S_OK)
10288 IStream_Release(pStream);
10289 /* Get Presentation Data for Ole10Native */
10290 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
10292 else
10294 /* Get Presentation Data (OLE20) */
10295 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
10298 /* Save OLESTREAM */
10299 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
10300 if(hRes == S_OK)
10302 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
10307 /* Free allocated memory */
10308 for(i=0; i < 2; i++)
10310 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10313 return hRes;
10316 enum stream_1ole_flags {
10317 OleStream_LinkedObject = 0x00000001,
10318 OleStream_Convert = 0x00000004
10321 /***********************************************************************
10322 * GetConvertStg (OLE32.@)
10324 HRESULT WINAPI GetConvertStg(IStorage *stg)
10326 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10327 static const DWORD version_magic = 0x02000001;
10328 DWORD header[2];
10329 IStream *stream;
10330 HRESULT hr;
10332 TRACE("%p\n", stg);
10334 if (!stg) return E_INVALIDARG;
10336 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
10337 if (FAILED(hr)) return hr;
10339 hr = IStream_Read(stream, header, sizeof(header), NULL);
10340 IStream_Release(stream);
10341 if (FAILED(hr)) return hr;
10343 if (header[0] != version_magic)
10345 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
10346 return E_FAIL;
10349 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
10352 /***********************************************************************
10353 * SetConvertStg (OLE32.@)
10355 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
10357 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10358 DWORD flags = convert ? OleStream_Convert : 0;
10359 IStream *stream;
10360 DWORD header[2];
10361 HRESULT hr;
10363 TRACE("(%p, %d)\n", storage, convert);
10365 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
10366 if (FAILED(hr))
10368 if (hr != STG_E_FILENOTFOUND)
10369 return hr;
10371 return STORAGE_CreateOleStream(storage, flags);
10374 hr = IStream_Read(stream, header, sizeof(header), NULL);
10375 if (FAILED(hr))
10377 IStream_Release(stream);
10378 return hr;
10381 /* update flag if differs */
10382 if ((header[1] ^ flags) & OleStream_Convert)
10384 LARGE_INTEGER pos = {{0}};
10386 if (header[1] & OleStream_Convert)
10387 flags = header[1] & ~OleStream_Convert;
10388 else
10389 flags = header[1] | OleStream_Convert;
10391 pos.QuadPart = sizeof(DWORD);
10392 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
10393 if (FAILED(hr))
10395 IStream_Release(stream);
10396 return hr;
10399 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
10402 IStream_Release(stream);
10403 return hr;
10406 /******************************************************************************
10407 * StgIsStorageFile [OLE32.@]
10408 * Verify if the file contains a storage object
10410 * PARAMS
10411 * fn [ I] Filename
10413 * RETURNS
10414 * S_OK if file has magic bytes as a storage object
10415 * S_FALSE if file is not storage
10417 HRESULT WINAPI
10418 StgIsStorageFile(LPCOLESTR fn)
10420 HANDLE hf;
10421 BYTE magic[8];
10422 DWORD bytes_read;
10424 TRACE("%s\n", debugstr_w(fn));
10425 hf = CreateFileW(fn, GENERIC_READ,
10426 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
10427 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
10429 if (hf == INVALID_HANDLE_VALUE)
10430 return STG_E_FILENOTFOUND;
10432 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
10434 WARN(" unable to read file\n");
10435 CloseHandle(hf);
10436 return S_FALSE;
10439 CloseHandle(hf);
10441 if (bytes_read != 8) {
10442 TRACE(" too short\n");
10443 return S_FALSE;
10446 if (!memcmp(magic,STORAGE_magic,8)) {
10447 TRACE(" -> YES\n");
10448 return S_OK;
10451 TRACE(" -> Invalid header.\n");
10452 return S_FALSE;
10455 /***********************************************************************
10456 * WriteClassStm (OLE32.@)
10458 * Writes a CLSID to a stream.
10460 * PARAMS
10461 * pStm [I] Stream to write to.
10462 * rclsid [I] CLSID to write.
10464 * RETURNS
10465 * Success: S_OK.
10466 * Failure: HRESULT code.
10468 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
10470 TRACE("(%p,%p)\n",pStm,rclsid);
10472 if (!pStm || !rclsid)
10473 return E_INVALIDARG;
10475 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
10478 /***********************************************************************
10479 * ReadClassStm (OLE32.@)
10481 * Reads a CLSID from a stream.
10483 * PARAMS
10484 * pStm [I] Stream to read from.
10485 * rclsid [O] CLSID to read.
10487 * RETURNS
10488 * Success: S_OK.
10489 * Failure: HRESULT code.
10491 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
10493 ULONG nbByte;
10494 HRESULT res;
10496 TRACE("(%p,%p)\n",pStm,pclsid);
10498 if (!pStm || !pclsid)
10499 return E_INVALIDARG;
10501 /* clear the output args */
10502 *pclsid = CLSID_NULL;
10504 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
10506 if (FAILED(res))
10507 return res;
10509 if (nbByte != sizeof(CLSID))
10510 return STG_E_READFAULT;
10511 else
10512 return S_OK;