mshtml: Wine Gecko 2.34-beta2 release.
[wine/wine-gecko.git] / dlls / ole32 / storage32.c
blob6ebc1e734eff8c312cda297e8b06ae1754622eb0
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 (SUCCEEDED(hr) && (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))
7000 return chainEntry.startingBlock;
7004 return BLOCK_END_OF_CHAIN;
7007 /******************************************************************************
7008 * BlockChainStream_GetCount
7010 * Returns the number of blocks that comprises this chain.
7011 * This is not the size of the stream as the last block may not be full!
7013 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
7015 return This->numBlocks;
7018 /******************************************************************************
7019 * BlockChainStream_ReadAt
7021 * Reads a specified number of bytes from this chain at the specified offset.
7022 * bytesRead may be NULL.
7023 * Failure will be returned if the specified number of bytes has not been read.
7025 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
7026 ULARGE_INTEGER offset,
7027 ULONG size,
7028 void* buffer,
7029 ULONG* bytesRead)
7031 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7032 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7033 ULONG bytesToReadInBuffer;
7034 ULONG blockIndex;
7035 BYTE* bufferWalker;
7036 ULARGE_INTEGER stream_size;
7037 HRESULT hr;
7038 BlockChainBlock *cachedBlock;
7040 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
7043 * Find the first block in the stream that contains part of the buffer.
7045 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
7047 *bytesRead = 0;
7049 stream_size = BlockChainStream_GetSize(This);
7050 if (stream_size.QuadPart > offset.QuadPart)
7051 size = min(stream_size.QuadPart - offset.QuadPart, size);
7052 else
7053 return S_OK;
7056 * Start reading the buffer.
7058 bufferWalker = buffer;
7060 while (size > 0)
7062 ULARGE_INTEGER ulOffset;
7063 DWORD bytesReadAt;
7066 * Calculate how many bytes we can copy from this big block.
7068 bytesToReadInBuffer =
7069 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7071 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
7073 if (FAILED(hr))
7074 return hr;
7076 if (!cachedBlock)
7078 /* Not in cache, and we're going to read past the end of the block. */
7079 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7080 offsetInBlock;
7082 StorageImpl_ReadAt(This->parentStorage,
7083 ulOffset,
7084 bufferWalker,
7085 bytesToReadInBuffer,
7086 &bytesReadAt);
7088 else
7090 if (!cachedBlock->read)
7092 ULONG read;
7093 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7094 return STG_E_READFAULT;
7096 cachedBlock->read = TRUE;
7099 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
7100 bytesReadAt = bytesToReadInBuffer;
7103 blockNoInSequence++;
7104 bufferWalker += bytesReadAt;
7105 size -= bytesReadAt;
7106 *bytesRead += bytesReadAt;
7107 offsetInBlock = 0; /* There is no offset on the next block */
7109 if (bytesToReadInBuffer != bytesReadAt)
7110 break;
7113 return S_OK;
7116 /******************************************************************************
7117 * BlockChainStream_WriteAt
7119 * Writes the specified number of bytes to this chain at the specified offset.
7120 * Will fail if not all specified number of bytes have been written.
7122 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
7123 ULARGE_INTEGER offset,
7124 ULONG size,
7125 const void* buffer,
7126 ULONG* bytesWritten)
7128 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7129 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7130 ULONG bytesToWrite;
7131 ULONG blockIndex;
7132 const BYTE* bufferWalker;
7133 HRESULT hr;
7134 BlockChainBlock *cachedBlock;
7136 *bytesWritten = 0;
7137 bufferWalker = buffer;
7139 while (size > 0)
7141 ULARGE_INTEGER ulOffset;
7142 DWORD bytesWrittenAt;
7145 * Calculate how many bytes we can copy to this big block.
7147 bytesToWrite =
7148 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7150 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
7152 /* BlockChainStream_SetSize should have already been called to ensure we have
7153 * enough blocks in the chain to write into */
7154 if (FAILED(hr))
7156 ERR("not enough blocks in chain to write data\n");
7157 return hr;
7160 if (!cachedBlock)
7162 /* Not in cache, and we're going to write past the end of the block. */
7163 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7164 offsetInBlock;
7166 StorageImpl_WriteAt(This->parentStorage,
7167 ulOffset,
7168 bufferWalker,
7169 bytesToWrite,
7170 &bytesWrittenAt);
7172 else
7174 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
7176 ULONG read;
7177 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7178 return STG_E_READFAULT;
7181 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
7182 bytesWrittenAt = bytesToWrite;
7183 cachedBlock->read = TRUE;
7184 cachedBlock->dirty = TRUE;
7187 blockNoInSequence++;
7188 bufferWalker += bytesWrittenAt;
7189 size -= bytesWrittenAt;
7190 *bytesWritten += bytesWrittenAt;
7191 offsetInBlock = 0; /* There is no offset on the next block */
7193 if (bytesWrittenAt != bytesToWrite)
7194 break;
7197 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7200 /******************************************************************************
7201 * BlockChainStream_Shrink
7203 * Shrinks this chain in the big block depot.
7205 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
7206 ULARGE_INTEGER newSize)
7208 ULONG blockIndex;
7209 ULONG numBlocks;
7210 int i;
7213 * Figure out how many blocks are needed to contain the new size
7215 numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7217 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7218 numBlocks++;
7220 if (numBlocks)
7223 * Go to the new end of chain
7225 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
7227 /* Mark the new end of chain */
7228 StorageImpl_SetNextBlockInChain(
7229 This->parentStorage,
7230 blockIndex,
7231 BLOCK_END_OF_CHAIN);
7233 This->tailIndex = blockIndex;
7235 else
7237 if (This->headOfStreamPlaceHolder != 0)
7239 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
7241 else
7243 DirEntry chainEntry;
7244 assert(This->ownerDirEntry != DIRENTRY_NULL);
7246 StorageImpl_ReadDirEntry(
7247 This->parentStorage,
7248 This->ownerDirEntry,
7249 &chainEntry);
7251 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7253 StorageImpl_WriteDirEntry(
7254 This->parentStorage,
7255 This->ownerDirEntry,
7256 &chainEntry);
7259 This->tailIndex = BLOCK_END_OF_CHAIN;
7262 This->numBlocks = numBlocks;
7265 * Mark the extra blocks as free
7267 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
7269 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
7270 StorageImpl_FreeBigBlock(This->parentStorage,
7271 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
7272 if (last_run->lastOffset == last_run->firstOffset)
7273 This->indexCacheLen--;
7274 else
7275 last_run->lastOffset--;
7279 * Reset the last accessed block cache.
7281 for (i=0; i<2; i++)
7283 if (This->cachedBlocks[i].index >= numBlocks)
7285 This->cachedBlocks[i].index = 0xffffffff;
7286 This->cachedBlocks[i].dirty = FALSE;
7290 return TRUE;
7293 /******************************************************************************
7294 * BlockChainStream_Enlarge
7296 * Grows this chain in the big block depot.
7298 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
7299 ULARGE_INTEGER newSize)
7301 ULONG blockIndex, currentBlock;
7302 ULONG newNumBlocks;
7303 ULONG oldNumBlocks = 0;
7305 blockIndex = BlockChainStream_GetHeadOfChain(This);
7308 * Empty chain. Create the head.
7310 if (blockIndex == BLOCK_END_OF_CHAIN)
7312 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7313 StorageImpl_SetNextBlockInChain(This->parentStorage,
7314 blockIndex,
7315 BLOCK_END_OF_CHAIN);
7317 if (This->headOfStreamPlaceHolder != 0)
7319 *(This->headOfStreamPlaceHolder) = blockIndex;
7321 else
7323 DirEntry chainEntry;
7324 assert(This->ownerDirEntry != DIRENTRY_NULL);
7326 StorageImpl_ReadDirEntry(
7327 This->parentStorage,
7328 This->ownerDirEntry,
7329 &chainEntry);
7331 chainEntry.startingBlock = blockIndex;
7333 StorageImpl_WriteDirEntry(
7334 This->parentStorage,
7335 This->ownerDirEntry,
7336 &chainEntry);
7339 This->tailIndex = blockIndex;
7340 This->numBlocks = 1;
7344 * Figure out how many blocks are needed to contain this stream
7346 newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7348 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7349 newNumBlocks++;
7352 * Go to the current end of chain
7354 if (This->tailIndex == BLOCK_END_OF_CHAIN)
7356 currentBlock = blockIndex;
7358 while (blockIndex != BLOCK_END_OF_CHAIN)
7360 This->numBlocks++;
7361 currentBlock = blockIndex;
7363 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
7364 &blockIndex)))
7365 return FALSE;
7368 This->tailIndex = currentBlock;
7371 currentBlock = This->tailIndex;
7372 oldNumBlocks = This->numBlocks;
7375 * Add new blocks to the chain
7377 if (oldNumBlocks < newNumBlocks)
7379 while (oldNumBlocks < newNumBlocks)
7381 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7383 StorageImpl_SetNextBlockInChain(
7384 This->parentStorage,
7385 currentBlock,
7386 blockIndex);
7388 StorageImpl_SetNextBlockInChain(
7389 This->parentStorage,
7390 blockIndex,
7391 BLOCK_END_OF_CHAIN);
7393 currentBlock = blockIndex;
7394 oldNumBlocks++;
7397 This->tailIndex = blockIndex;
7398 This->numBlocks = newNumBlocks;
7401 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
7402 return FALSE;
7404 return TRUE;
7407 /******************************************************************************
7408 * BlockChainStream_SetSize
7410 * Sets the size of this stream. The big block depot will be updated.
7411 * The file will grow if we grow the chain.
7413 * TODO: Free the actual blocks in the file when we shrink the chain.
7414 * Currently, the blocks are still in the file. So the file size
7415 * doesn't shrink even if we shrink streams.
7417 BOOL BlockChainStream_SetSize(
7418 BlockChainStream* This,
7419 ULARGE_INTEGER newSize)
7421 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
7423 if (newSize.QuadPart == size.QuadPart)
7424 return TRUE;
7426 if (newSize.QuadPart < size.QuadPart)
7428 BlockChainStream_Shrink(This, newSize);
7430 else
7432 BlockChainStream_Enlarge(This, newSize);
7435 return TRUE;
7438 /******************************************************************************
7439 * BlockChainStream_GetSize
7441 * Returns the size of this chain.
7442 * Will return the block count if this chain doesn't have a directory entry.
7444 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
7446 DirEntry chainEntry;
7448 if(This->headOfStreamPlaceHolder == NULL)
7451 * This chain has a directory entry so use the size value from there.
7453 StorageImpl_ReadDirEntry(
7454 This->parentStorage,
7455 This->ownerDirEntry,
7456 &chainEntry);
7458 return chainEntry.size;
7460 else
7463 * this chain is a chain that does not have a directory entry, figure out the
7464 * size by making the product number of used blocks times the
7465 * size of them
7467 ULARGE_INTEGER result;
7468 result.QuadPart =
7469 (ULONGLONG)BlockChainStream_GetCount(This) *
7470 This->parentStorage->bigBlockSize;
7472 return result;
7476 /******************************************************************************
7477 ** SmallBlockChainStream implementation
7480 SmallBlockChainStream* SmallBlockChainStream_Construct(
7481 StorageImpl* parentStorage,
7482 ULONG* headOfStreamPlaceHolder,
7483 DirRef dirEntry)
7485 SmallBlockChainStream* newStream;
7487 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
7489 newStream->parentStorage = parentStorage;
7490 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7491 newStream->ownerDirEntry = dirEntry;
7493 return newStream;
7496 void SmallBlockChainStream_Destroy(
7497 SmallBlockChainStream* This)
7499 HeapFree(GetProcessHeap(), 0, This);
7502 /******************************************************************************
7503 * SmallBlockChainStream_GetHeadOfChain
7505 * Returns the head of this chain of small blocks.
7507 static ULONG SmallBlockChainStream_GetHeadOfChain(
7508 SmallBlockChainStream* This)
7510 DirEntry chainEntry;
7511 HRESULT hr;
7513 if (This->headOfStreamPlaceHolder != NULL)
7514 return *(This->headOfStreamPlaceHolder);
7516 if (This->ownerDirEntry)
7518 hr = StorageImpl_ReadDirEntry(
7519 This->parentStorage,
7520 This->ownerDirEntry,
7521 &chainEntry);
7523 if (SUCCEEDED(hr))
7525 return chainEntry.startingBlock;
7530 return BLOCK_END_OF_CHAIN;
7533 /******************************************************************************
7534 * SmallBlockChainStream_GetNextBlockInChain
7536 * Returns the index of the next small block in this chain.
7538 * Return Values:
7539 * - BLOCK_END_OF_CHAIN: end of this chain
7540 * - BLOCK_UNUSED: small block 'blockIndex' is free
7542 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
7543 SmallBlockChainStream* This,
7544 ULONG blockIndex,
7545 ULONG* nextBlockInChain)
7547 ULARGE_INTEGER offsetOfBlockInDepot;
7548 DWORD buffer;
7549 ULONG bytesRead;
7550 HRESULT res;
7552 *nextBlockInChain = BLOCK_END_OF_CHAIN;
7554 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7557 * Read those bytes in the buffer from the small block file.
7559 res = BlockChainStream_ReadAt(
7560 This->parentStorage->smallBlockDepotChain,
7561 offsetOfBlockInDepot,
7562 sizeof(DWORD),
7563 &buffer,
7564 &bytesRead);
7566 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
7567 res = STG_E_READFAULT;
7569 if (SUCCEEDED(res))
7571 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
7572 return S_OK;
7575 return res;
7578 /******************************************************************************
7579 * SmallBlockChainStream_SetNextBlockInChain
7581 * Writes the index of the next block of the specified block in the small
7582 * block depot.
7583 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7584 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7586 static void SmallBlockChainStream_SetNextBlockInChain(
7587 SmallBlockChainStream* This,
7588 ULONG blockIndex,
7589 ULONG nextBlock)
7591 ULARGE_INTEGER offsetOfBlockInDepot;
7592 DWORD buffer;
7593 ULONG bytesWritten;
7595 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7597 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
7600 * Read those bytes in the buffer from the small block file.
7602 BlockChainStream_WriteAt(
7603 This->parentStorage->smallBlockDepotChain,
7604 offsetOfBlockInDepot,
7605 sizeof(DWORD),
7606 &buffer,
7607 &bytesWritten);
7610 /******************************************************************************
7611 * SmallBlockChainStream_FreeBlock
7613 * Flag small block 'blockIndex' as free in the small block depot.
7615 static void SmallBlockChainStream_FreeBlock(
7616 SmallBlockChainStream* This,
7617 ULONG blockIndex)
7619 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
7622 /******************************************************************************
7623 * SmallBlockChainStream_GetNextFreeBlock
7625 * Returns the index of a free small block. The small block depot will be
7626 * enlarged if necessary. The small block chain will also be enlarged if
7627 * necessary.
7629 static ULONG SmallBlockChainStream_GetNextFreeBlock(
7630 SmallBlockChainStream* This)
7632 ULARGE_INTEGER offsetOfBlockInDepot;
7633 DWORD buffer;
7634 ULONG bytesRead;
7635 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
7636 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
7637 HRESULT res = S_OK;
7638 ULONG smallBlocksPerBigBlock;
7639 DirEntry rootEntry;
7640 ULONG blocksRequired;
7641 ULARGE_INTEGER old_size, size_required;
7643 offsetOfBlockInDepot.u.HighPart = 0;
7646 * Scan the small block depot for a free block
7648 while (nextBlockIndex != BLOCK_UNUSED)
7650 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7652 res = BlockChainStream_ReadAt(
7653 This->parentStorage->smallBlockDepotChain,
7654 offsetOfBlockInDepot,
7655 sizeof(DWORD),
7656 &buffer,
7657 &bytesRead);
7660 * If we run out of space for the small block depot, enlarge it
7662 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
7664 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
7666 if (nextBlockIndex != BLOCK_UNUSED)
7667 blockIndex++;
7669 else
7671 ULONG count =
7672 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
7674 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
7675 ULARGE_INTEGER newSize, offset;
7676 ULONG bytesWritten;
7678 newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize;
7679 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
7682 * Initialize all the small blocks to free
7684 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
7685 offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize;
7686 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
7687 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
7689 StorageImpl_SaveFileHeader(This->parentStorage);
7693 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
7695 smallBlocksPerBigBlock =
7696 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
7699 * Verify if we have to allocate big blocks to contain small blocks
7701 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
7703 size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize;
7705 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
7707 if (size_required.QuadPart > old_size.QuadPart)
7709 BlockChainStream_SetSize(
7710 This->parentStorage->smallBlockRootChain,
7711 size_required);
7713 StorageImpl_ReadDirEntry(
7714 This->parentStorage,
7715 This->parentStorage->base.storageDirEntry,
7716 &rootEntry);
7718 rootEntry.size = size_required;
7720 StorageImpl_WriteDirEntry(
7721 This->parentStorage,
7722 This->parentStorage->base.storageDirEntry,
7723 &rootEntry);
7726 return blockIndex;
7729 /******************************************************************************
7730 * SmallBlockChainStream_ReadAt
7732 * Reads a specified number of bytes from this chain at the specified offset.
7733 * bytesRead may be NULL.
7734 * Failure will be returned if the specified number of bytes has not been read.
7736 HRESULT SmallBlockChainStream_ReadAt(
7737 SmallBlockChainStream* This,
7738 ULARGE_INTEGER offset,
7739 ULONG size,
7740 void* buffer,
7741 ULONG* bytesRead)
7743 HRESULT rc = S_OK;
7744 ULARGE_INTEGER offsetInBigBlockFile;
7745 ULONG blockNoInSequence =
7746 offset.u.LowPart / This->parentStorage->smallBlockSize;
7748 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7749 ULONG bytesToReadInBuffer;
7750 ULONG blockIndex;
7751 ULONG bytesReadFromBigBlockFile;
7752 BYTE* bufferWalker;
7753 ULARGE_INTEGER stream_size;
7756 * This should never happen on a small block file.
7758 assert(offset.u.HighPart==0);
7760 *bytesRead = 0;
7762 stream_size = SmallBlockChainStream_GetSize(This);
7763 if (stream_size.QuadPart > offset.QuadPart)
7764 size = min(stream_size.QuadPart - offset.QuadPart, size);
7765 else
7766 return S_OK;
7769 * Find the first block in the stream that contains part of the buffer.
7771 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7773 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7775 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7776 if(FAILED(rc))
7777 return rc;
7778 blockNoInSequence--;
7782 * Start reading the buffer.
7784 bufferWalker = buffer;
7786 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7789 * Calculate how many bytes we can copy from this small block.
7791 bytesToReadInBuffer =
7792 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7795 * Calculate the offset of the small block in the small block file.
7797 offsetInBigBlockFile.QuadPart =
7798 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
7800 offsetInBigBlockFile.QuadPart += offsetInBlock;
7803 * Read those bytes in the buffer from the small block file.
7804 * The small block has already been identified so it shouldn't fail
7805 * unless the file is corrupt.
7807 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
7808 offsetInBigBlockFile,
7809 bytesToReadInBuffer,
7810 bufferWalker,
7811 &bytesReadFromBigBlockFile);
7813 if (FAILED(rc))
7814 return rc;
7816 if (!bytesReadFromBigBlockFile)
7817 return STG_E_DOCFILECORRUPT;
7820 * Step to the next big block.
7822 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7823 if(FAILED(rc))
7824 return STG_E_DOCFILECORRUPT;
7826 bufferWalker += bytesReadFromBigBlockFile;
7827 size -= bytesReadFromBigBlockFile;
7828 *bytesRead += bytesReadFromBigBlockFile;
7829 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
7832 return S_OK;
7835 /******************************************************************************
7836 * SmallBlockChainStream_WriteAt
7838 * Writes the specified number of bytes to this chain at the specified offset.
7839 * Will fail if not all specified number of bytes have been written.
7841 HRESULT SmallBlockChainStream_WriteAt(
7842 SmallBlockChainStream* This,
7843 ULARGE_INTEGER offset,
7844 ULONG size,
7845 const void* buffer,
7846 ULONG* bytesWritten)
7848 ULARGE_INTEGER offsetInBigBlockFile;
7849 ULONG blockNoInSequence =
7850 offset.u.LowPart / This->parentStorage->smallBlockSize;
7852 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7853 ULONG bytesToWriteInBuffer;
7854 ULONG blockIndex;
7855 ULONG bytesWrittenToBigBlockFile;
7856 const BYTE* bufferWalker;
7857 HRESULT res;
7860 * This should never happen on a small block file.
7862 assert(offset.u.HighPart==0);
7865 * Find the first block in the stream that contains part of the buffer.
7867 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7869 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7871 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
7872 return STG_E_DOCFILECORRUPT;
7873 blockNoInSequence--;
7877 * Start writing the buffer.
7879 *bytesWritten = 0;
7880 bufferWalker = buffer;
7881 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7884 * Calculate how many bytes we can copy to this small block.
7886 bytesToWriteInBuffer =
7887 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7890 * Calculate the offset of the small block in the small block file.
7892 offsetInBigBlockFile.QuadPart =
7893 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
7895 offsetInBigBlockFile.QuadPart += offsetInBlock;
7898 * Write those bytes in the buffer to the small block file.
7900 res = BlockChainStream_WriteAt(
7901 This->parentStorage->smallBlockRootChain,
7902 offsetInBigBlockFile,
7903 bytesToWriteInBuffer,
7904 bufferWalker,
7905 &bytesWrittenToBigBlockFile);
7906 if (FAILED(res))
7907 return res;
7910 * Step to the next big block.
7912 res = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7913 if (FAILED(res))
7914 return res;
7915 bufferWalker += bytesWrittenToBigBlockFile;
7916 size -= bytesWrittenToBigBlockFile;
7917 *bytesWritten += bytesWrittenToBigBlockFile;
7918 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7921 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7924 /******************************************************************************
7925 * SmallBlockChainStream_Shrink
7927 * Shrinks this chain in the small block depot.
7929 static BOOL SmallBlockChainStream_Shrink(
7930 SmallBlockChainStream* This,
7931 ULARGE_INTEGER newSize)
7933 ULONG blockIndex, extraBlock;
7934 ULONG numBlocks;
7935 ULONG count = 0;
7937 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7939 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7940 numBlocks++;
7942 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7945 * Go to the new end of chain
7947 while (count < numBlocks)
7949 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7950 &blockIndex)))
7951 return FALSE;
7952 count++;
7956 * If the count is 0, we have a special case, the head of the chain was
7957 * just freed.
7959 if (count == 0)
7961 DirEntry chainEntry;
7963 StorageImpl_ReadDirEntry(This->parentStorage,
7964 This->ownerDirEntry,
7965 &chainEntry);
7967 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7969 StorageImpl_WriteDirEntry(This->parentStorage,
7970 This->ownerDirEntry,
7971 &chainEntry);
7974 * We start freeing the chain at the head block.
7976 extraBlock = blockIndex;
7978 else
7980 /* Get the next block before marking the new end */
7981 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7982 &extraBlock)))
7983 return FALSE;
7985 /* Mark the new end of chain */
7986 SmallBlockChainStream_SetNextBlockInChain(
7987 This,
7988 blockIndex,
7989 BLOCK_END_OF_CHAIN);
7993 * Mark the extra blocks as free
7995 while (extraBlock != BLOCK_END_OF_CHAIN)
7997 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7998 &blockIndex)))
7999 return FALSE;
8000 SmallBlockChainStream_FreeBlock(This, extraBlock);
8001 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
8002 extraBlock = blockIndex;
8005 return TRUE;
8008 /******************************************************************************
8009 * SmallBlockChainStream_Enlarge
8011 * Grows this chain in the small block depot.
8013 static BOOL SmallBlockChainStream_Enlarge(
8014 SmallBlockChainStream* This,
8015 ULARGE_INTEGER newSize)
8017 ULONG blockIndex, currentBlock;
8018 ULONG newNumBlocks;
8019 ULONG oldNumBlocks = 0;
8021 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8024 * Empty chain. Create the head.
8026 if (blockIndex == BLOCK_END_OF_CHAIN)
8028 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8029 SmallBlockChainStream_SetNextBlockInChain(
8030 This,
8031 blockIndex,
8032 BLOCK_END_OF_CHAIN);
8034 if (This->headOfStreamPlaceHolder != NULL)
8036 *(This->headOfStreamPlaceHolder) = blockIndex;
8038 else
8040 DirEntry chainEntry;
8042 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
8043 &chainEntry);
8045 chainEntry.startingBlock = blockIndex;
8047 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
8048 &chainEntry);
8052 currentBlock = blockIndex;
8055 * Figure out how many blocks are needed to contain this stream
8057 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8059 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8060 newNumBlocks++;
8063 * Go to the current end of chain
8065 while (blockIndex != BLOCK_END_OF_CHAIN)
8067 oldNumBlocks++;
8068 currentBlock = blockIndex;
8069 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
8070 return FALSE;
8074 * Add new blocks to the chain
8076 while (oldNumBlocks < newNumBlocks)
8078 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8079 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
8081 SmallBlockChainStream_SetNextBlockInChain(
8082 This,
8083 blockIndex,
8084 BLOCK_END_OF_CHAIN);
8086 currentBlock = blockIndex;
8087 oldNumBlocks++;
8090 return TRUE;
8093 /******************************************************************************
8094 * SmallBlockChainStream_SetSize
8096 * Sets the size of this stream.
8097 * The file will grow if we grow the chain.
8099 * TODO: Free the actual blocks in the file when we shrink the chain.
8100 * Currently, the blocks are still in the file. So the file size
8101 * doesn't shrink even if we shrink streams.
8103 BOOL SmallBlockChainStream_SetSize(
8104 SmallBlockChainStream* This,
8105 ULARGE_INTEGER newSize)
8107 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
8109 if (newSize.u.LowPart == size.u.LowPart)
8110 return TRUE;
8112 if (newSize.u.LowPart < size.u.LowPart)
8114 SmallBlockChainStream_Shrink(This, newSize);
8116 else
8118 SmallBlockChainStream_Enlarge(This, newSize);
8121 return TRUE;
8124 /******************************************************************************
8125 * SmallBlockChainStream_GetCount
8127 * Returns the number of small blocks that comprises this chain.
8128 * This is not the size of the stream as the last block may not be full!
8131 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
8133 ULONG blockIndex;
8134 ULONG count = 0;
8136 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8138 while(blockIndex != BLOCK_END_OF_CHAIN)
8140 count++;
8142 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
8143 blockIndex, &blockIndex)))
8144 return 0;
8147 return count;
8150 /******************************************************************************
8151 * SmallBlockChainStream_GetSize
8153 * Returns the size of this chain.
8155 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
8157 DirEntry chainEntry;
8159 if(This->headOfStreamPlaceHolder != NULL)
8161 ULARGE_INTEGER result;
8162 result.u.HighPart = 0;
8164 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
8165 This->parentStorage->smallBlockSize;
8167 return result;
8170 StorageImpl_ReadDirEntry(
8171 This->parentStorage,
8172 This->ownerDirEntry,
8173 &chainEntry);
8175 return chainEntry.size;
8178 static HRESULT create_storagefile(
8179 LPCOLESTR pwcsName,
8180 DWORD grfMode,
8181 DWORD grfAttrs,
8182 STGOPTIONS* pStgOptions,
8183 REFIID riid,
8184 void** ppstgOpen)
8186 StorageBaseImpl* newStorage = 0;
8187 HANDLE hFile = INVALID_HANDLE_VALUE;
8188 HRESULT hr = STG_E_INVALIDFLAG;
8189 DWORD shareMode;
8190 DWORD accessMode;
8191 DWORD creationMode;
8192 DWORD fileAttributes;
8193 WCHAR tempFileName[MAX_PATH];
8195 if (ppstgOpen == 0)
8196 return STG_E_INVALIDPOINTER;
8198 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
8199 return STG_E_INVALIDPARAMETER;
8201 /* if no share mode given then DENY_NONE is the default */
8202 if (STGM_SHARE_MODE(grfMode) == 0)
8203 grfMode |= STGM_SHARE_DENY_NONE;
8205 if ( FAILED( validateSTGM(grfMode) ))
8206 goto end;
8208 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8209 switch(STGM_ACCESS_MODE(grfMode))
8211 case STGM_WRITE:
8212 case STGM_READWRITE:
8213 break;
8214 default:
8215 goto end;
8218 /* in direct mode, can only use SHARE_EXCLUSIVE */
8219 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
8220 goto end;
8222 /* but in transacted mode, any share mode is valid */
8225 * Generate a unique name.
8227 if (pwcsName == 0)
8229 WCHAR tempPath[MAX_PATH];
8230 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
8232 memset(tempPath, 0, sizeof(tempPath));
8233 memset(tempFileName, 0, sizeof(tempFileName));
8235 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
8236 tempPath[0] = '.';
8238 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
8239 pwcsName = tempFileName;
8240 else
8242 hr = STG_E_INSUFFICIENTMEMORY;
8243 goto end;
8246 creationMode = TRUNCATE_EXISTING;
8248 else
8250 creationMode = GetCreationModeFromSTGM(grfMode);
8254 * Interpret the STGM value grfMode
8256 shareMode = GetShareModeFromSTGM(grfMode);
8257 accessMode = GetAccessModeFromSTGM(grfMode);
8259 if (grfMode & STGM_DELETEONRELEASE)
8260 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
8261 else
8262 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
8264 *ppstgOpen = 0;
8266 hFile = CreateFileW(pwcsName,
8267 accessMode,
8268 shareMode,
8269 NULL,
8270 creationMode,
8271 fileAttributes,
8274 if (hFile == INVALID_HANDLE_VALUE)
8276 if(GetLastError() == ERROR_FILE_EXISTS)
8277 hr = STG_E_FILEALREADYEXISTS;
8278 else
8279 hr = E_FAIL;
8280 goto end;
8284 * Allocate and initialize the new IStorage32object.
8286 hr = Storage_Construct(
8287 hFile,
8288 pwcsName,
8289 NULL,
8290 grfMode,
8291 TRUE,
8292 TRUE,
8293 pStgOptions->ulSectorSize,
8294 &newStorage);
8296 if (FAILED(hr))
8298 goto end;
8301 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
8302 IStorage_Release(&newStorage->IStorage_iface);
8304 end:
8305 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
8307 return hr;
8310 /******************************************************************************
8311 * StgCreateDocfile [OLE32.@]
8312 * Creates a new compound file storage object
8314 * PARAMS
8315 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8316 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8317 * reserved [ ?] unused?, usually 0
8318 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8320 * RETURNS
8321 * S_OK if the file was successfully created
8322 * some STG_E_ value if error
8323 * NOTES
8324 * if pwcsName is NULL, create file with new unique name
8325 * the function can returns
8326 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8327 * (unrealized now)
8329 HRESULT WINAPI StgCreateDocfile(
8330 LPCOLESTR pwcsName,
8331 DWORD grfMode,
8332 DWORD reserved,
8333 IStorage **ppstgOpen)
8335 STGOPTIONS stgoptions = {1, 0, 512};
8337 TRACE("(%s, %x, %d, %p)\n",
8338 debugstr_w(pwcsName), grfMode,
8339 reserved, ppstgOpen);
8341 if (ppstgOpen == 0)
8342 return STG_E_INVALIDPOINTER;
8343 if (reserved != 0)
8344 return STG_E_INVALIDPARAMETER;
8346 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
8349 /******************************************************************************
8350 * StgCreateStorageEx [OLE32.@]
8352 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8354 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8355 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8357 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
8359 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8360 return STG_E_INVALIDPARAMETER;
8363 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
8365 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8366 return STG_E_INVALIDPARAMETER;
8369 if (stgfmt == STGFMT_FILE)
8371 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8372 return STG_E_INVALIDPARAMETER;
8375 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
8377 STGOPTIONS defaultOptions = {1, 0, 512};
8379 if (!pStgOptions) pStgOptions = &defaultOptions;
8380 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
8384 ERR("Invalid stgfmt argument\n");
8385 return STG_E_INVALIDPARAMETER;
8388 /******************************************************************************
8389 * StgCreatePropSetStg [OLE32.@]
8391 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
8392 IPropertySetStorage **propset)
8394 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
8395 if (reserved)
8396 return STG_E_INVALIDPARAMETER;
8398 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
8401 /******************************************************************************
8402 * StgOpenStorageEx [OLE32.@]
8404 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8406 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8407 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8409 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
8411 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8412 return STG_E_INVALIDPARAMETER;
8415 switch (stgfmt)
8417 case STGFMT_FILE:
8418 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8419 return STG_E_INVALIDPARAMETER;
8421 case STGFMT_STORAGE:
8422 break;
8424 case STGFMT_DOCFILE:
8425 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
8427 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8428 return STG_E_INVALIDPARAMETER;
8430 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8431 break;
8433 case STGFMT_ANY:
8434 WARN("STGFMT_ANY assuming storage\n");
8435 break;
8437 default:
8438 return STG_E_INVALIDPARAMETER;
8441 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
8445 /******************************************************************************
8446 * StgOpenStorage [OLE32.@]
8448 HRESULT WINAPI StgOpenStorage(
8449 const OLECHAR *pwcsName,
8450 IStorage *pstgPriority,
8451 DWORD grfMode,
8452 SNB snbExclude,
8453 DWORD reserved,
8454 IStorage **ppstgOpen)
8456 StorageBaseImpl* newStorage = 0;
8457 HRESULT hr = S_OK;
8458 HANDLE hFile = 0;
8459 DWORD shareMode;
8460 DWORD accessMode;
8461 LPWSTR temp_name = NULL;
8463 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8464 debugstr_w(pwcsName), pstgPriority, grfMode,
8465 snbExclude, reserved, ppstgOpen);
8467 if (pstgPriority)
8469 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8470 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
8471 if (FAILED(hr)) goto end;
8472 pwcsName = temp_name;
8473 TRACE("using filename %s\n", debugstr_w(temp_name));
8476 if (pwcsName == 0)
8478 hr = STG_E_INVALIDNAME;
8479 goto end;
8482 if (ppstgOpen == 0)
8484 hr = STG_E_INVALIDPOINTER;
8485 goto end;
8488 if (reserved)
8490 hr = STG_E_INVALIDPARAMETER;
8491 goto end;
8494 if (grfMode & STGM_PRIORITY)
8496 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
8497 return STG_E_INVALIDFLAG;
8498 if (grfMode & STGM_DELETEONRELEASE)
8499 return STG_E_INVALIDFUNCTION;
8500 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
8501 return STG_E_INVALIDFLAG;
8502 grfMode &= ~0xf0; /* remove the existing sharing mode */
8503 grfMode |= STGM_SHARE_DENY_NONE;
8507 * Validate the sharing mode
8509 if (grfMode & STGM_DIRECT_SWMR)
8511 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
8512 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
8514 hr = STG_E_INVALIDFLAG;
8515 goto end;
8518 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
8519 switch(STGM_SHARE_MODE(grfMode))
8521 case STGM_SHARE_EXCLUSIVE:
8522 case STGM_SHARE_DENY_WRITE:
8523 break;
8524 default:
8525 hr = STG_E_INVALIDFLAG;
8526 goto end;
8529 if ( FAILED( validateSTGM(grfMode) ) ||
8530 (grfMode&STGM_CREATE))
8532 hr = STG_E_INVALIDFLAG;
8533 goto end;
8536 /* shared reading requires transacted or single writer mode */
8537 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
8538 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
8539 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
8541 hr = STG_E_INVALIDFLAG;
8542 goto end;
8546 * Interpret the STGM value grfMode
8548 shareMode = GetShareModeFromSTGM(grfMode);
8549 accessMode = GetAccessModeFromSTGM(grfMode);
8551 *ppstgOpen = 0;
8553 hFile = CreateFileW( pwcsName,
8554 accessMode,
8555 shareMode,
8556 NULL,
8557 OPEN_EXISTING,
8558 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
8561 if (hFile==INVALID_HANDLE_VALUE)
8563 DWORD last_error = GetLastError();
8565 hr = E_FAIL;
8567 switch (last_error)
8569 case ERROR_FILE_NOT_FOUND:
8570 hr = STG_E_FILENOTFOUND;
8571 break;
8573 case ERROR_PATH_NOT_FOUND:
8574 hr = STG_E_PATHNOTFOUND;
8575 break;
8577 case ERROR_ACCESS_DENIED:
8578 case ERROR_WRITE_PROTECT:
8579 hr = STG_E_ACCESSDENIED;
8580 break;
8582 case ERROR_SHARING_VIOLATION:
8583 hr = STG_E_SHAREVIOLATION;
8584 break;
8586 default:
8587 hr = E_FAIL;
8590 goto end;
8594 * Refuse to open the file if it's too small to be a structured storage file
8595 * FIXME: verify the file when reading instead of here
8597 if (GetFileSize(hFile, NULL) < 0x100)
8599 CloseHandle(hFile);
8600 hr = STG_E_FILEALREADYEXISTS;
8601 goto end;
8605 * Allocate and initialize the new IStorage32object.
8607 hr = Storage_Construct(
8608 hFile,
8609 pwcsName,
8610 NULL,
8611 grfMode,
8612 TRUE,
8613 FALSE,
8614 512,
8615 &newStorage);
8617 if (FAILED(hr))
8620 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8622 if(hr == STG_E_INVALIDHEADER)
8623 hr = STG_E_FILEALREADYEXISTS;
8624 goto end;
8627 *ppstgOpen = &newStorage->IStorage_iface;
8629 end:
8630 CoTaskMemFree(temp_name);
8631 if (pstgPriority) IStorage_Release(pstgPriority);
8632 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
8633 return hr;
8636 /******************************************************************************
8637 * StgCreateDocfileOnILockBytes [OLE32.@]
8639 HRESULT WINAPI StgCreateDocfileOnILockBytes(
8640 ILockBytes *plkbyt,
8641 DWORD grfMode,
8642 DWORD reserved,
8643 IStorage** ppstgOpen)
8645 StorageBaseImpl* newStorage = 0;
8646 HRESULT hr = S_OK;
8648 if ((ppstgOpen == 0) || (plkbyt == 0))
8649 return STG_E_INVALIDPOINTER;
8652 * Allocate and initialize the new IStorage object.
8654 hr = Storage_Construct(
8657 plkbyt,
8658 grfMode,
8659 FALSE,
8660 TRUE,
8661 512,
8662 &newStorage);
8664 if (FAILED(hr))
8666 return hr;
8669 *ppstgOpen = &newStorage->IStorage_iface;
8671 return hr;
8674 /******************************************************************************
8675 * StgOpenStorageOnILockBytes [OLE32.@]
8677 HRESULT WINAPI StgOpenStorageOnILockBytes(
8678 ILockBytes *plkbyt,
8679 IStorage *pstgPriority,
8680 DWORD grfMode,
8681 SNB snbExclude,
8682 DWORD reserved,
8683 IStorage **ppstgOpen)
8685 StorageBaseImpl* newStorage = 0;
8686 HRESULT hr = S_OK;
8688 if ((plkbyt == 0) || (ppstgOpen == 0))
8689 return STG_E_INVALIDPOINTER;
8691 if ( FAILED( validateSTGM(grfMode) ))
8692 return STG_E_INVALIDFLAG;
8694 *ppstgOpen = 0;
8697 * Allocate and initialize the new IStorage object.
8699 hr = Storage_Construct(
8702 plkbyt,
8703 grfMode,
8704 FALSE,
8705 FALSE,
8706 512,
8707 &newStorage);
8709 if (FAILED(hr))
8711 return hr;
8714 *ppstgOpen = &newStorage->IStorage_iface;
8716 return hr;
8719 /******************************************************************************
8720 * StgSetTimes [ole32.@]
8721 * StgSetTimes [OLE32.@]
8725 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
8726 FILETIME const *patime, FILETIME const *pmtime)
8728 IStorage *stg = NULL;
8729 HRESULT r;
8731 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
8733 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
8734 0, 0, &stg);
8735 if( SUCCEEDED(r) )
8737 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
8738 IStorage_Release(stg);
8741 return r;
8744 /******************************************************************************
8745 * StgIsStorageILockBytes [OLE32.@]
8747 * Determines if the ILockBytes contains a storage object.
8749 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
8751 BYTE sig[sizeof(STORAGE_magic)];
8752 ULARGE_INTEGER offset;
8753 ULONG read = 0;
8755 offset.u.HighPart = 0;
8756 offset.u.LowPart = 0;
8758 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
8760 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
8761 return S_OK;
8763 return S_FALSE;
8766 /******************************************************************************
8767 * WriteClassStg [OLE32.@]
8769 * This method will store the specified CLSID in the specified storage object
8771 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
8773 if(!pStg)
8774 return E_INVALIDARG;
8776 if(!rclsid)
8777 return STG_E_INVALIDPOINTER;
8779 return IStorage_SetClass(pStg, rclsid);
8782 /***********************************************************************
8783 * ReadClassStg (OLE32.@)
8785 * This method reads the CLSID previously written to a storage object with
8786 * the WriteClassStg.
8788 * PARAMS
8789 * pstg [I] IStorage pointer
8790 * pclsid [O] Pointer to where the CLSID is written
8792 * RETURNS
8793 * Success: S_OK.
8794 * Failure: HRESULT code.
8796 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
8798 STATSTG pstatstg;
8799 HRESULT hRes;
8801 TRACE("(%p, %p)\n", pstg, pclsid);
8803 if(!pstg || !pclsid)
8804 return E_INVALIDARG;
8807 * read a STATSTG structure (contains the clsid) from the storage
8809 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
8811 if(SUCCEEDED(hRes))
8812 *pclsid=pstatstg.clsid;
8814 return hRes;
8817 /***********************************************************************
8818 * OleLoadFromStream (OLE32.@)
8820 * This function loads an object from stream
8822 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
8824 CLSID clsid;
8825 HRESULT res;
8826 LPPERSISTSTREAM xstm;
8828 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
8830 res=ReadClassStm(pStm,&clsid);
8831 if (FAILED(res))
8832 return res;
8833 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
8834 if (FAILED(res))
8835 return res;
8836 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
8837 if (FAILED(res)) {
8838 IUnknown_Release((IUnknown*)*ppvObj);
8839 return res;
8841 res=IPersistStream_Load(xstm,pStm);
8842 IPersistStream_Release(xstm);
8843 /* FIXME: all refcounts ok at this point? I think they should be:
8844 * pStm : unchanged
8845 * ppvObj : 1
8846 * xstm : 0 (released)
8848 return res;
8851 /***********************************************************************
8852 * OleSaveToStream (OLE32.@)
8854 * This function saves an object with the IPersistStream interface on it
8855 * to the specified stream.
8857 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
8860 CLSID clsid;
8861 HRESULT res;
8863 TRACE("(%p,%p)\n",pPStm,pStm);
8865 res=IPersistStream_GetClassID(pPStm,&clsid);
8867 if (SUCCEEDED(res)){
8869 res=WriteClassStm(pStm,&clsid);
8871 if (SUCCEEDED(res))
8873 res=IPersistStream_Save(pPStm,pStm,TRUE);
8876 TRACE("Finished Save\n");
8877 return res;
8880 /****************************************************************************
8881 * This method validate a STGM parameter that can contain the values below
8883 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8884 * The stgm values contained in 0xffff0000 are bitmasks.
8886 * STGM_DIRECT 0x00000000
8887 * STGM_TRANSACTED 0x00010000
8888 * STGM_SIMPLE 0x08000000
8890 * STGM_READ 0x00000000
8891 * STGM_WRITE 0x00000001
8892 * STGM_READWRITE 0x00000002
8894 * STGM_SHARE_DENY_NONE 0x00000040
8895 * STGM_SHARE_DENY_READ 0x00000030
8896 * STGM_SHARE_DENY_WRITE 0x00000020
8897 * STGM_SHARE_EXCLUSIVE 0x00000010
8899 * STGM_PRIORITY 0x00040000
8900 * STGM_DELETEONRELEASE 0x04000000
8902 * STGM_CREATE 0x00001000
8903 * STGM_CONVERT 0x00020000
8904 * STGM_FAILIFTHERE 0x00000000
8906 * STGM_NOSCRATCH 0x00100000
8907 * STGM_NOSNAPSHOT 0x00200000
8909 static HRESULT validateSTGM(DWORD stgm)
8911 DWORD access = STGM_ACCESS_MODE(stgm);
8912 DWORD share = STGM_SHARE_MODE(stgm);
8913 DWORD create = STGM_CREATE_MODE(stgm);
8915 if (stgm&~STGM_KNOWN_FLAGS)
8917 ERR("unknown flags %08x\n", stgm);
8918 return E_FAIL;
8921 switch (access)
8923 case STGM_READ:
8924 case STGM_WRITE:
8925 case STGM_READWRITE:
8926 break;
8927 default:
8928 return E_FAIL;
8931 switch (share)
8933 case STGM_SHARE_DENY_NONE:
8934 case STGM_SHARE_DENY_READ:
8935 case STGM_SHARE_DENY_WRITE:
8936 case STGM_SHARE_EXCLUSIVE:
8937 break;
8938 case 0:
8939 if (!(stgm & STGM_TRANSACTED))
8940 return E_FAIL;
8941 break;
8942 default:
8943 return E_FAIL;
8946 switch (create)
8948 case STGM_CREATE:
8949 case STGM_FAILIFTHERE:
8950 break;
8951 default:
8952 return E_FAIL;
8956 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8958 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8959 return E_FAIL;
8962 * STGM_CREATE | STGM_CONVERT
8963 * if both are false, STGM_FAILIFTHERE is set to TRUE
8965 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8966 return E_FAIL;
8969 * STGM_NOSCRATCH requires STGM_TRANSACTED
8971 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8972 return E_FAIL;
8975 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8976 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8978 if ( (stgm & STGM_NOSNAPSHOT) &&
8979 (!(stgm & STGM_TRANSACTED) ||
8980 share == STGM_SHARE_EXCLUSIVE ||
8981 share == STGM_SHARE_DENY_WRITE) )
8982 return E_FAIL;
8984 return S_OK;
8987 /****************************************************************************
8988 * GetShareModeFromSTGM
8990 * This method will return a share mode flag from a STGM value.
8991 * The STGM value is assumed valid.
8993 static DWORD GetShareModeFromSTGM(DWORD stgm)
8995 switch (STGM_SHARE_MODE(stgm))
8997 case 0:
8998 assert(stgm & STGM_TRANSACTED);
8999 /* fall-through */
9000 case STGM_SHARE_DENY_NONE:
9001 return FILE_SHARE_READ | FILE_SHARE_WRITE;
9002 case STGM_SHARE_DENY_READ:
9003 return FILE_SHARE_WRITE;
9004 case STGM_SHARE_DENY_WRITE:
9005 case STGM_SHARE_EXCLUSIVE:
9006 return FILE_SHARE_READ;
9008 ERR("Invalid share mode!\n");
9009 assert(0);
9010 return 0;
9013 /****************************************************************************
9014 * GetAccessModeFromSTGM
9016 * This method will return an access mode flag from a STGM value.
9017 * The STGM value is assumed valid.
9019 static DWORD GetAccessModeFromSTGM(DWORD stgm)
9021 switch (STGM_ACCESS_MODE(stgm))
9023 case STGM_READ:
9024 return GENERIC_READ;
9025 case STGM_WRITE:
9026 case STGM_READWRITE:
9027 return GENERIC_READ | GENERIC_WRITE;
9029 ERR("Invalid access mode!\n");
9030 assert(0);
9031 return 0;
9034 /****************************************************************************
9035 * GetCreationModeFromSTGM
9037 * This method will return a creation mode flag from a STGM value.
9038 * The STGM value is assumed valid.
9040 static DWORD GetCreationModeFromSTGM(DWORD stgm)
9042 switch(STGM_CREATE_MODE(stgm))
9044 case STGM_CREATE:
9045 return CREATE_ALWAYS;
9046 case STGM_CONVERT:
9047 FIXME("STGM_CONVERT not implemented!\n");
9048 return CREATE_NEW;
9049 case STGM_FAILIFTHERE:
9050 return CREATE_NEW;
9052 ERR("Invalid create mode!\n");
9053 assert(0);
9054 return 0;
9058 /*************************************************************************
9059 * OLECONVERT_LoadOLE10 [Internal]
9061 * Loads the OLE10 STREAM to memory
9063 * PARAMS
9064 * pOleStream [I] The OLESTREAM
9065 * pData [I] Data Structure for the OLESTREAM Data
9067 * RETURNS
9068 * Success: S_OK
9069 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9070 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9072 * NOTES
9073 * This function is used by OleConvertOLESTREAMToIStorage only.
9075 * Memory allocated for pData must be freed by the caller
9077 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
9079 DWORD dwSize;
9080 HRESULT hRes = S_OK;
9081 int nTryCnt=0;
9082 int max_try = 6;
9084 pData->pData = NULL;
9085 pData->pstrOleObjFileName = NULL;
9087 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
9089 /* Get the OleID */
9090 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9091 if(dwSize != sizeof(pData->dwOleID))
9093 hRes = CONVERT10_E_OLESTREAM_GET;
9095 else if(pData->dwOleID != OLESTREAM_ID)
9097 hRes = CONVERT10_E_OLESTREAM_FMT;
9099 else
9101 hRes = S_OK;
9102 break;
9106 if(hRes == S_OK)
9108 /* Get the TypeID... more info needed for this field */
9109 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9110 if(dwSize != sizeof(pData->dwTypeID))
9112 hRes = CONVERT10_E_OLESTREAM_GET;
9115 if(hRes == S_OK)
9117 if(pData->dwTypeID != 0)
9119 /* Get the length of the OleTypeName */
9120 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9121 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9123 hRes = CONVERT10_E_OLESTREAM_GET;
9126 if(hRes == S_OK)
9128 if(pData->dwOleTypeNameLength > 0)
9130 /* Get the OleTypeName */
9131 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9132 if(dwSize != pData->dwOleTypeNameLength)
9134 hRes = CONVERT10_E_OLESTREAM_GET;
9138 if(bStrem1)
9140 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
9141 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
9143 hRes = CONVERT10_E_OLESTREAM_GET;
9145 if(hRes == S_OK)
9147 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
9148 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
9149 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
9150 if(pData->pstrOleObjFileName)
9152 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
9153 if(dwSize != pData->dwOleObjFileNameLength)
9155 hRes = CONVERT10_E_OLESTREAM_GET;
9158 else
9159 hRes = CONVERT10_E_OLESTREAM_GET;
9162 else
9164 /* Get the Width of the Metafile */
9165 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9166 if(dwSize != sizeof(pData->dwMetaFileWidth))
9168 hRes = CONVERT10_E_OLESTREAM_GET;
9170 if(hRes == S_OK)
9172 /* Get the Height of the Metafile */
9173 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9174 if(dwSize != sizeof(pData->dwMetaFileHeight))
9176 hRes = CONVERT10_E_OLESTREAM_GET;
9180 if(hRes == S_OK)
9182 /* Get the Length of the Data */
9183 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9184 if(dwSize != sizeof(pData->dwDataLength))
9186 hRes = CONVERT10_E_OLESTREAM_GET;
9190 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
9192 if(!bStrem1) /* if it is a second OLE stream data */
9194 pData->dwDataLength -= 8;
9195 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
9196 if(dwSize != sizeof(pData->strUnknown))
9198 hRes = CONVERT10_E_OLESTREAM_GET;
9202 if(hRes == S_OK)
9204 if(pData->dwDataLength > 0)
9206 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
9208 /* Get Data (ex. IStorage, Metafile, or BMP) */
9209 if(pData->pData)
9211 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
9212 if(dwSize != pData->dwDataLength)
9214 hRes = CONVERT10_E_OLESTREAM_GET;
9217 else
9219 hRes = CONVERT10_E_OLESTREAM_GET;
9225 return hRes;
9228 /*************************************************************************
9229 * OLECONVERT_SaveOLE10 [Internal]
9231 * Saves the OLE10 STREAM From memory
9233 * PARAMS
9234 * pData [I] Data Structure for the OLESTREAM Data
9235 * pOleStream [I] The OLESTREAM to save
9237 * RETURNS
9238 * Success: S_OK
9239 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9241 * NOTES
9242 * This function is used by OleConvertIStorageToOLESTREAM only.
9245 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
9247 DWORD dwSize;
9248 HRESULT hRes = S_OK;
9251 /* Set the OleID */
9252 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9253 if(dwSize != sizeof(pData->dwOleID))
9255 hRes = CONVERT10_E_OLESTREAM_PUT;
9258 if(hRes == S_OK)
9260 /* Set the TypeID */
9261 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9262 if(dwSize != sizeof(pData->dwTypeID))
9264 hRes = CONVERT10_E_OLESTREAM_PUT;
9268 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
9270 /* Set the Length of the OleTypeName */
9271 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9272 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9274 hRes = CONVERT10_E_OLESTREAM_PUT;
9277 if(hRes == S_OK)
9279 if(pData->dwOleTypeNameLength > 0)
9281 /* Set the OleTypeName */
9282 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9283 if(dwSize != pData->dwOleTypeNameLength)
9285 hRes = CONVERT10_E_OLESTREAM_PUT;
9290 if(hRes == S_OK)
9292 /* Set the width of the Metafile */
9293 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9294 if(dwSize != sizeof(pData->dwMetaFileWidth))
9296 hRes = CONVERT10_E_OLESTREAM_PUT;
9300 if(hRes == S_OK)
9302 /* Set the height of the Metafile */
9303 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9304 if(dwSize != sizeof(pData->dwMetaFileHeight))
9306 hRes = CONVERT10_E_OLESTREAM_PUT;
9310 if(hRes == S_OK)
9312 /* Set the length of the Data */
9313 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9314 if(dwSize != sizeof(pData->dwDataLength))
9316 hRes = CONVERT10_E_OLESTREAM_PUT;
9320 if(hRes == S_OK)
9322 if(pData->dwDataLength > 0)
9324 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9325 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
9326 if(dwSize != pData->dwDataLength)
9328 hRes = CONVERT10_E_OLESTREAM_PUT;
9333 return hRes;
9336 /*************************************************************************
9337 * OLECONVERT_GetOLE20FromOLE10[Internal]
9339 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9340 * opens it, and copies the content to the dest IStorage for
9341 * OleConvertOLESTREAMToIStorage
9344 * PARAMS
9345 * pDestStorage [I] The IStorage to copy the data to
9346 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9347 * nBufferLength [I] The size of the buffer
9349 * RETURNS
9350 * Nothing
9352 * NOTES
9356 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
9358 HRESULT hRes;
9359 HANDLE hFile;
9360 IStorage *pTempStorage;
9361 DWORD dwNumOfBytesWritten;
9362 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9363 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9365 /* Create a temp File */
9366 GetTempPathW(MAX_PATH, wstrTempDir);
9367 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9368 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
9370 if(hFile != INVALID_HANDLE_VALUE)
9372 /* Write IStorage Data to File */
9373 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
9374 CloseHandle(hFile);
9376 /* Open and copy temp storage to the Dest Storage */
9377 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
9378 if(hRes == S_OK)
9380 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
9381 IStorage_Release(pTempStorage);
9383 DeleteFileW(wstrTempFile);
9388 /*************************************************************************
9389 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9391 * Saves the OLE10 STREAM From memory
9393 * PARAMS
9394 * pStorage [I] The Src IStorage to copy
9395 * pData [I] The Dest Memory to write to.
9397 * RETURNS
9398 * The size in bytes allocated for pData
9400 * NOTES
9401 * Memory allocated for pData must be freed by the caller
9403 * Used by OleConvertIStorageToOLESTREAM only.
9406 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
9408 HANDLE hFile;
9409 HRESULT hRes;
9410 DWORD nDataLength = 0;
9411 IStorage *pTempStorage;
9412 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9413 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9415 *pData = NULL;
9417 /* Create temp Storage */
9418 GetTempPathW(MAX_PATH, wstrTempDir);
9419 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9420 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
9422 if(hRes == S_OK)
9424 /* Copy Src Storage to the Temp Storage */
9425 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
9426 IStorage_Release(pTempStorage);
9428 /* Open Temp Storage as a file and copy to memory */
9429 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9430 if(hFile != INVALID_HANDLE_VALUE)
9432 nDataLength = GetFileSize(hFile, NULL);
9433 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
9434 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
9435 CloseHandle(hFile);
9437 DeleteFileW(wstrTempFile);
9439 return nDataLength;
9442 /*************************************************************************
9443 * STORAGE_CreateOleStream [Internal]
9445 * Creates the "\001OLE" stream in the IStorage if necessary.
9447 * PARAMS
9448 * storage [I] Dest storage to create the stream in
9449 * flags [I] flags to be set for newly created stream
9451 * RETURNS
9452 * HRESULT return value
9454 * NOTES
9456 * This stream is still unknown, MS Word seems to have extra data
9457 * but since the data is stored in the OLESTREAM there should be
9458 * no need to recreate the stream. If the stream is manually
9459 * deleted it will create it with this default data.
9462 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
9464 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9465 static const DWORD version_magic = 0x02000001;
9466 IStream *stream;
9467 HRESULT hr;
9469 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
9470 if (hr == S_OK)
9472 struct empty_1ole_stream {
9473 DWORD version_magic;
9474 DWORD flags;
9475 DWORD update_options;
9476 DWORD reserved;
9477 DWORD mon_stream_size;
9479 struct empty_1ole_stream stream_data;
9481 stream_data.version_magic = version_magic;
9482 stream_data.flags = flags;
9483 stream_data.update_options = 0;
9484 stream_data.reserved = 0;
9485 stream_data.mon_stream_size = 0;
9487 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
9488 IStream_Release(stream);
9491 return hr;
9494 /* write a string to a stream, preceded by its length */
9495 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
9497 HRESULT r;
9498 LPSTR str;
9499 DWORD len = 0;
9501 if( string )
9502 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
9503 r = IStream_Write( stm, &len, sizeof(len), NULL);
9504 if( FAILED( r ) )
9505 return r;
9506 if(len == 0)
9507 return r;
9508 str = CoTaskMemAlloc( len );
9509 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
9510 r = IStream_Write( stm, str, len, NULL);
9511 CoTaskMemFree( str );
9512 return r;
9515 /* read a string preceded by its length from a stream */
9516 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
9518 HRESULT r;
9519 DWORD len, count = 0;
9520 LPSTR str;
9521 LPWSTR wstr;
9523 r = IStream_Read( stm, &len, sizeof(len), &count );
9524 if( FAILED( r ) )
9525 return r;
9526 if( count != sizeof(len) )
9527 return E_OUTOFMEMORY;
9529 TRACE("%d bytes\n",len);
9531 str = CoTaskMemAlloc( len );
9532 if( !str )
9533 return E_OUTOFMEMORY;
9534 count = 0;
9535 r = IStream_Read( stm, str, len, &count );
9536 if( FAILED( r ) )
9537 return r;
9538 if( count != len )
9540 CoTaskMemFree( str );
9541 return E_OUTOFMEMORY;
9544 TRACE("Read string %s\n",debugstr_an(str,len));
9546 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
9547 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
9548 if( wstr )
9550 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
9551 wstr[len] = 0;
9553 CoTaskMemFree( str );
9555 *string = wstr;
9557 return r;
9561 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
9562 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
9564 IStream *pstm;
9565 HRESULT r = S_OK;
9566 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9568 static const BYTE unknown1[12] =
9569 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9570 0xFF, 0xFF, 0xFF, 0xFF};
9571 static const BYTE unknown2[16] =
9572 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9573 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9575 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
9576 debugstr_w(lpszUserType), debugstr_w(szClipName),
9577 debugstr_w(szProgIDName));
9579 /* Create a CompObj stream */
9580 r = IStorage_CreateStream(pstg, szwStreamName,
9581 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
9582 if( FAILED (r) )
9583 return r;
9585 /* Write CompObj Structure to stream */
9586 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
9588 if( SUCCEEDED( r ) )
9589 r = WriteClassStm( pstm, clsid );
9591 if( SUCCEEDED( r ) )
9592 r = STREAM_WriteString( pstm, lpszUserType );
9593 if( SUCCEEDED( r ) )
9594 r = STREAM_WriteString( pstm, szClipName );
9595 if( SUCCEEDED( r ) )
9596 r = STREAM_WriteString( pstm, szProgIDName );
9597 if( SUCCEEDED( r ) )
9598 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
9600 IStream_Release( pstm );
9602 return r;
9605 /***********************************************************************
9606 * WriteFmtUserTypeStg (OLE32.@)
9608 HRESULT WINAPI WriteFmtUserTypeStg(
9609 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
9611 STATSTG stat;
9612 HRESULT r;
9613 WCHAR szwClipName[0x40];
9614 CLSID clsid;
9615 LPWSTR wstrProgID = NULL;
9616 DWORD n;
9618 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
9620 /* get the clipboard format name */
9621 if( cf )
9623 n = GetClipboardFormatNameW( cf, szwClipName,
9624 sizeof(szwClipName)/sizeof(szwClipName[0]) );
9625 szwClipName[n]=0;
9628 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
9630 r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
9631 if(SUCCEEDED(r))
9632 clsid = stat.clsid;
9633 else
9634 clsid = CLSID_NULL;
9636 ProgIDFromCLSID(&clsid, &wstrProgID);
9638 TRACE("progid is %s\n",debugstr_w(wstrProgID));
9640 r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
9641 cf ? szwClipName : NULL, wstrProgID );
9643 CoTaskMemFree(wstrProgID);
9645 return r;
9649 /******************************************************************************
9650 * ReadFmtUserTypeStg [OLE32.@]
9652 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
9654 HRESULT r;
9655 IStream *stm = 0;
9656 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
9657 unsigned char unknown1[12];
9658 unsigned char unknown2[16];
9659 DWORD count;
9660 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
9661 CLSID clsid;
9663 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
9665 r = IStorage_OpenStream( pstg, szCompObj, NULL,
9666 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
9667 if( FAILED ( r ) )
9669 WARN("Failed to open stream r = %08x\n", r);
9670 return r;
9673 /* read the various parts of the structure */
9674 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
9675 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
9676 goto end;
9677 r = ReadClassStm( stm, &clsid );
9678 if( FAILED( r ) )
9679 goto end;
9681 r = STREAM_ReadString( stm, &szCLSIDName );
9682 if( FAILED( r ) )
9683 goto end;
9685 r = STREAM_ReadString( stm, &szOleTypeName );
9686 if( FAILED( r ) )
9687 goto end;
9689 r = STREAM_ReadString( stm, &szProgIDName );
9690 if( FAILED( r ) )
9691 goto end;
9693 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
9694 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
9695 goto end;
9697 /* ok, success... now we just need to store what we found */
9698 if( pcf )
9699 *pcf = RegisterClipboardFormatW( szOleTypeName );
9701 if( lplpszUserType )
9703 *lplpszUserType = szCLSIDName;
9704 szCLSIDName = NULL;
9707 end:
9708 CoTaskMemFree( szCLSIDName );
9709 CoTaskMemFree( szOleTypeName );
9710 CoTaskMemFree( szProgIDName );
9711 IStream_Release( stm );
9713 return r;
9717 /*************************************************************************
9718 * OLECONVERT_CreateCompObjStream [Internal]
9720 * Creates a "\001CompObj" is the destination IStorage if necessary.
9722 * PARAMS
9723 * pStorage [I] The dest IStorage to create the CompObj Stream
9724 * if necessary.
9725 * strOleTypeName [I] The ProgID
9727 * RETURNS
9728 * Success: S_OK
9729 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9731 * NOTES
9732 * This function is used by OleConvertOLESTREAMToIStorage only.
9734 * The stream data is stored in the OLESTREAM and there should be
9735 * no need to recreate the stream. If the stream is manually
9736 * deleted it will attempt to create it by querying the registry.
9740 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
9742 IStream *pStream;
9743 HRESULT hStorageRes, hRes = S_OK;
9744 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
9745 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9746 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
9748 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9749 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
9751 /* Initialize the CompObj structure */
9752 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
9753 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
9754 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
9757 /* Create a CompObj stream if it doesn't exist */
9758 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
9759 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9760 if(hStorageRes == S_OK)
9762 /* copy the OleTypeName to the compobj struct */
9763 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
9764 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
9766 /* copy the OleTypeName to the compobj struct */
9767 /* Note: in the test made, these were Identical */
9768 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
9769 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
9771 /* Get the CLSID */
9772 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
9773 bufferW, OLESTREAM_MAX_STR_LEN );
9774 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
9776 if(hRes == S_OK)
9778 HKEY hKey;
9779 LONG hErr;
9780 /* Get the CLSID Default Name from the Registry */
9781 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
9782 if(hErr == ERROR_SUCCESS)
9784 char strTemp[OLESTREAM_MAX_STR_LEN];
9785 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
9786 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
9787 if(hErr == ERROR_SUCCESS)
9789 strcpy(IStorageCompObj.strCLSIDName, strTemp);
9791 RegCloseKey(hKey);
9795 /* Write CompObj Structure to stream */
9796 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
9798 WriteClassStm(pStream,&(IStorageCompObj.clsid));
9800 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
9801 if(IStorageCompObj.dwCLSIDNameLength > 0)
9803 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
9805 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
9806 if(IStorageCompObj.dwOleTypeNameLength > 0)
9808 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
9810 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
9811 if(IStorageCompObj.dwProgIDNameLength > 0)
9813 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
9815 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
9816 IStream_Release(pStream);
9818 return hRes;
9822 /*************************************************************************
9823 * OLECONVERT_CreateOlePresStream[Internal]
9825 * Creates the "\002OlePres000" Stream with the Metafile data
9827 * PARAMS
9828 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
9829 * dwExtentX [I] Width of the Metafile
9830 * dwExtentY [I] Height of the Metafile
9831 * pData [I] Metafile data
9832 * dwDataLength [I] Size of the Metafile data
9834 * RETURNS
9835 * Success: S_OK
9836 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9838 * NOTES
9839 * This function is used by OleConvertOLESTREAMToIStorage only.
9842 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
9844 HRESULT hRes;
9845 IStream *pStream;
9846 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9847 BYTE pOlePresStreamHeader [] =
9849 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
9850 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9851 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9852 0x00, 0x00, 0x00, 0x00
9855 BYTE pOlePresStreamHeaderEmpty [] =
9857 0x00, 0x00, 0x00, 0x00,
9858 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9859 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9860 0x00, 0x00, 0x00, 0x00
9863 /* Create the OlePres000 Stream */
9864 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9865 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9867 if(hRes == S_OK)
9869 DWORD nHeaderSize;
9870 OLECONVERT_ISTORAGE_OLEPRES OlePres;
9872 memset(&OlePres, 0, sizeof(OlePres));
9873 /* Do we have any metafile data to save */
9874 if(dwDataLength > 0)
9876 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
9877 nHeaderSize = sizeof(pOlePresStreamHeader);
9879 else
9881 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
9882 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
9884 /* Set width and height of the metafile */
9885 OlePres.dwExtentX = dwExtentX;
9886 OlePres.dwExtentY = -dwExtentY;
9888 /* Set Data and Length */
9889 if(dwDataLength > sizeof(METAFILEPICT16))
9891 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
9892 OlePres.pData = &(pData[8]);
9894 /* Save OlePres000 Data to Stream */
9895 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
9896 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
9897 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
9898 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
9899 if(OlePres.dwSize > 0)
9901 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
9903 IStream_Release(pStream);
9907 /*************************************************************************
9908 * OLECONVERT_CreateOle10NativeStream [Internal]
9910 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9912 * PARAMS
9913 * pStorage [I] Dest storage to create the stream in
9914 * pData [I] Ole10 Native Data (ex. bmp)
9915 * dwDataLength [I] Size of the Ole10 Native Data
9917 * RETURNS
9918 * Nothing
9920 * NOTES
9921 * This function is used by OleConvertOLESTREAMToIStorage only.
9923 * Might need to verify the data and return appropriate error message
9926 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
9928 HRESULT hRes;
9929 IStream *pStream;
9930 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9932 /* Create the Ole10Native Stream */
9933 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9934 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9936 if(hRes == S_OK)
9938 /* Write info to stream */
9939 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9940 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9941 IStream_Release(pStream);
9946 /*************************************************************************
9947 * OLECONVERT_GetOLE10ProgID [Internal]
9949 * Finds the ProgID (or OleTypeID) from the IStorage
9951 * PARAMS
9952 * pStorage [I] The Src IStorage to get the ProgID
9953 * strProgID [I] the ProgID string to get
9954 * dwSize [I] the size of the string
9956 * RETURNS
9957 * Success: S_OK
9958 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9960 * NOTES
9961 * This function is used by OleConvertIStorageToOLESTREAM only.
9965 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9967 HRESULT hRes;
9968 IStream *pStream;
9969 LARGE_INTEGER iSeekPos;
9970 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9971 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9973 /* Open the CompObj Stream */
9974 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9975 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9976 if(hRes == S_OK)
9979 /*Get the OleType from the CompObj Stream */
9980 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9981 iSeekPos.u.HighPart = 0;
9983 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9984 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9985 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9986 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9987 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9988 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9989 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9991 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9992 if(*dwSize > 0)
9994 IStream_Read(pStream, strProgID, *dwSize, NULL);
9996 IStream_Release(pStream);
9998 else
10000 STATSTG stat;
10001 LPOLESTR wstrProgID;
10003 /* Get the OleType from the registry */
10004 REFCLSID clsid = &(stat.clsid);
10005 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
10006 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
10007 if(hRes == S_OK)
10009 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
10010 CoTaskMemFree(wstrProgID);
10014 return hRes;
10017 /*************************************************************************
10018 * OLECONVERT_GetOle10PresData [Internal]
10020 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10022 * PARAMS
10023 * pStorage [I] Src IStroage
10024 * pOleStream [I] Dest OleStream Mem Struct
10026 * RETURNS
10027 * Nothing
10029 * NOTES
10030 * This function is used by OleConvertIStorageToOLESTREAM only.
10032 * Memory allocated for pData must be freed by the caller
10036 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10039 HRESULT hRes;
10040 IStream *pStream;
10041 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10043 /* Initialize Default data for OLESTREAM */
10044 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10045 pOleStreamData[0].dwTypeID = 2;
10046 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10047 pOleStreamData[1].dwTypeID = 0;
10048 pOleStreamData[0].dwMetaFileWidth = 0;
10049 pOleStreamData[0].dwMetaFileHeight = 0;
10050 pOleStreamData[0].pData = NULL;
10051 pOleStreamData[1].pData = NULL;
10053 /* Open Ole10Native Stream */
10054 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10055 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10056 if(hRes == S_OK)
10059 /* Read Size and Data */
10060 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
10061 if(pOleStreamData->dwDataLength > 0)
10063 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
10064 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
10066 IStream_Release(pStream);
10072 /*************************************************************************
10073 * OLECONVERT_GetOle20PresData[Internal]
10075 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10077 * PARAMS
10078 * pStorage [I] Src IStroage
10079 * pOleStreamData [I] Dest OleStream Mem Struct
10081 * RETURNS
10082 * Nothing
10084 * NOTES
10085 * This function is used by OleConvertIStorageToOLESTREAM only.
10087 * Memory allocated for pData must be freed by the caller
10089 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10091 HRESULT hRes;
10092 IStream *pStream;
10093 OLECONVERT_ISTORAGE_OLEPRES olePress;
10094 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10096 /* Initialize Default data for OLESTREAM */
10097 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10098 pOleStreamData[0].dwTypeID = 2;
10099 pOleStreamData[0].dwMetaFileWidth = 0;
10100 pOleStreamData[0].dwMetaFileHeight = 0;
10101 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
10102 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10103 pOleStreamData[1].dwTypeID = 0;
10104 pOleStreamData[1].dwOleTypeNameLength = 0;
10105 pOleStreamData[1].strOleTypeName[0] = 0;
10106 pOleStreamData[1].dwMetaFileWidth = 0;
10107 pOleStreamData[1].dwMetaFileHeight = 0;
10108 pOleStreamData[1].pData = NULL;
10109 pOleStreamData[1].dwDataLength = 0;
10112 /* Open OlePress000 stream */
10113 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10114 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10115 if(hRes == S_OK)
10117 LARGE_INTEGER iSeekPos;
10118 METAFILEPICT16 MetaFilePict;
10119 static const char strMetafilePictName[] = "METAFILEPICT";
10121 /* Set the TypeID for a Metafile */
10122 pOleStreamData[1].dwTypeID = 5;
10124 /* Set the OleTypeName to Metafile */
10125 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
10126 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
10128 iSeekPos.u.HighPart = 0;
10129 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
10131 /* Get Presentation Data */
10132 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10133 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
10134 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
10135 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
10137 /*Set width and Height */
10138 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
10139 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
10140 if(olePress.dwSize > 0)
10142 /* Set Length */
10143 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
10145 /* Set MetaFilePict struct */
10146 MetaFilePict.mm = 8;
10147 MetaFilePict.xExt = olePress.dwExtentX;
10148 MetaFilePict.yExt = olePress.dwExtentY;
10149 MetaFilePict.hMF = 0;
10151 /* Get Metafile Data */
10152 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
10153 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
10154 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
10156 IStream_Release(pStream);
10160 /*************************************************************************
10161 * OleConvertOLESTREAMToIStorage [OLE32.@]
10163 * Read info on MSDN
10165 * TODO
10166 * DVTARGETDEVICE parameter is not handled
10167 * Still unsure of some mem fields for OLE 10 Stream
10168 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10169 * and "\001OLE" streams
10172 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
10173 LPOLESTREAM pOleStream,
10174 LPSTORAGE pstg,
10175 const DVTARGETDEVICE* ptd)
10177 int i;
10178 HRESULT hRes=S_OK;
10179 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10181 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
10183 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10185 if(ptd != NULL)
10187 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10190 if(pstg == NULL || pOleStream == NULL)
10192 hRes = E_INVALIDARG;
10195 if(hRes == S_OK)
10197 /* Load the OLESTREAM to Memory */
10198 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
10201 if(hRes == S_OK)
10203 /* Load the OLESTREAM to Memory (part 2)*/
10204 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
10207 if(hRes == S_OK)
10210 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
10212 /* Do we have the IStorage Data in the OLESTREAM */
10213 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
10215 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10216 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
10218 else
10220 /* It must be an original OLE 1.0 source */
10221 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10224 else
10226 /* It must be an original OLE 1.0 source */
10227 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10230 /* Create CompObj Stream if necessary */
10231 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
10232 if(hRes == S_OK)
10234 /*Create the Ole Stream if necessary */
10235 STORAGE_CreateOleStream(pstg, 0);
10240 /* Free allocated memory */
10241 for(i=0; i < 2; i++)
10243 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10244 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
10245 pOleStreamData[i].pstrOleObjFileName = NULL;
10247 return hRes;
10250 /*************************************************************************
10251 * OleConvertIStorageToOLESTREAM [OLE32.@]
10253 * Read info on MSDN
10255 * Read info on MSDN
10257 * TODO
10258 * Still unsure of some mem fields for OLE 10 Stream
10259 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10260 * and "\001OLE" streams.
10263 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
10264 LPSTORAGE pstg,
10265 LPOLESTREAM pOleStream)
10267 int i;
10268 HRESULT hRes = S_OK;
10269 IStream *pStream;
10270 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10271 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10273 TRACE("%p %p\n", pstg, pOleStream);
10275 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10277 if(pstg == NULL || pOleStream == NULL)
10279 hRes = E_INVALIDARG;
10281 if(hRes == S_OK)
10283 /* Get the ProgID */
10284 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
10285 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
10287 if(hRes == S_OK)
10289 /* Was it originally Ole10 */
10290 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
10291 if(hRes == S_OK)
10293 IStream_Release(pStream);
10294 /* Get Presentation Data for Ole10Native */
10295 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
10297 else
10299 /* Get Presentation Data (OLE20) */
10300 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
10303 /* Save OLESTREAM */
10304 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
10305 if(hRes == S_OK)
10307 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
10312 /* Free allocated memory */
10313 for(i=0; i < 2; i++)
10315 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10318 return hRes;
10321 enum stream_1ole_flags {
10322 OleStream_LinkedObject = 0x00000001,
10323 OleStream_Convert = 0x00000004
10326 /***********************************************************************
10327 * GetConvertStg (OLE32.@)
10329 HRESULT WINAPI GetConvertStg(IStorage *stg)
10331 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10332 static const DWORD version_magic = 0x02000001;
10333 DWORD header[2];
10334 IStream *stream;
10335 HRESULT hr;
10337 TRACE("%p\n", stg);
10339 if (!stg) return E_INVALIDARG;
10341 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
10342 if (FAILED(hr)) return hr;
10344 hr = IStream_Read(stream, header, sizeof(header), NULL);
10345 IStream_Release(stream);
10346 if (FAILED(hr)) return hr;
10348 if (header[0] != version_magic)
10350 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
10351 return E_FAIL;
10354 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
10357 /***********************************************************************
10358 * SetConvertStg (OLE32.@)
10360 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
10362 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10363 DWORD flags = convert ? OleStream_Convert : 0;
10364 IStream *stream;
10365 DWORD header[2];
10366 HRESULT hr;
10368 TRACE("(%p, %d)\n", storage, convert);
10370 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
10371 if (FAILED(hr))
10373 if (hr != STG_E_FILENOTFOUND)
10374 return hr;
10376 return STORAGE_CreateOleStream(storage, flags);
10379 hr = IStream_Read(stream, header, sizeof(header), NULL);
10380 if (FAILED(hr))
10382 IStream_Release(stream);
10383 return hr;
10386 /* update flag if differs */
10387 if ((header[1] ^ flags) & OleStream_Convert)
10389 LARGE_INTEGER pos = {{0}};
10391 if (header[1] & OleStream_Convert)
10392 flags = header[1] & ~OleStream_Convert;
10393 else
10394 flags = header[1] | OleStream_Convert;
10396 pos.QuadPart = sizeof(DWORD);
10397 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
10398 if (FAILED(hr))
10400 IStream_Release(stream);
10401 return hr;
10404 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
10407 IStream_Release(stream);
10408 return hr;
10411 /******************************************************************************
10412 * StgIsStorageFile [OLE32.@]
10413 * Verify if the file contains a storage object
10415 * PARAMS
10416 * fn [ I] Filename
10418 * RETURNS
10419 * S_OK if file has magic bytes as a storage object
10420 * S_FALSE if file is not storage
10422 HRESULT WINAPI
10423 StgIsStorageFile(LPCOLESTR fn)
10425 HANDLE hf;
10426 BYTE magic[8];
10427 DWORD bytes_read;
10429 TRACE("%s\n", debugstr_w(fn));
10430 hf = CreateFileW(fn, GENERIC_READ,
10431 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
10432 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
10434 if (hf == INVALID_HANDLE_VALUE)
10435 return STG_E_FILENOTFOUND;
10437 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
10439 WARN(" unable to read file\n");
10440 CloseHandle(hf);
10441 return S_FALSE;
10444 CloseHandle(hf);
10446 if (bytes_read != 8) {
10447 TRACE(" too short\n");
10448 return S_FALSE;
10451 if (!memcmp(magic,STORAGE_magic,8)) {
10452 TRACE(" -> YES\n");
10453 return S_OK;
10456 TRACE(" -> Invalid header.\n");
10457 return S_FALSE;
10460 /***********************************************************************
10461 * WriteClassStm (OLE32.@)
10463 * Writes a CLSID to a stream.
10465 * PARAMS
10466 * pStm [I] Stream to write to.
10467 * rclsid [I] CLSID to write.
10469 * RETURNS
10470 * Success: S_OK.
10471 * Failure: HRESULT code.
10473 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
10475 TRACE("(%p,%p)\n",pStm,rclsid);
10477 if (!pStm || !rclsid)
10478 return E_INVALIDARG;
10480 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
10483 /***********************************************************************
10484 * ReadClassStm (OLE32.@)
10486 * Reads a CLSID from a stream.
10488 * PARAMS
10489 * pStm [I] Stream to read from.
10490 * rclsid [O] CLSID to read.
10492 * RETURNS
10493 * Success: S_OK.
10494 * Failure: HRESULT code.
10496 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
10498 ULONG nbByte;
10499 HRESULT res;
10501 TRACE("(%p,%p)\n",pStm,pclsid);
10503 if (!pStm || !pclsid)
10504 return E_INVALIDARG;
10506 /* clear the output args */
10507 *pclsid = CLSID_NULL;
10509 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
10511 if (FAILED(res))
10512 return res;
10514 if (nbByte != sizeof(CLSID))
10515 return STG_E_READFAULT;
10516 else
10517 return S_OK;