ole32: Support storage files larger than 4 GB.
[wine.git] / dlls / ole32 / storage32.c
blob69a2198b8e85fa76ce87acdc06c46240816cd5a5
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;
2878 /* if it's a FileLockBytesImpl use LockFileEx in blocking mode */
2879 if (SUCCEEDED(FileLockBytesImpl_LockRegionSync(This->lockBytes, offset, cb)))
2880 return S_OK;
2882 /* otherwise we have to fake it based on an async lock */
2885 int delay=0;
2887 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
2889 if (hr == STG_E_ACCESSDENIED)
2891 Sleep(delay);
2892 if (delay < 150) delay++;
2894 } while (hr == STG_E_ACCESSDENIED);
2896 return hr;
2899 static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
2900 ULONG end, HRESULT fail_hr)
2902 HRESULT hr;
2903 ULARGE_INTEGER offset, cb;
2905 offset.QuadPart = start;
2906 cb.QuadPart = 1 + end - start;
2908 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2909 if (SUCCEEDED(hr)) ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2911 if (hr == STG_E_ACCESSDENIED)
2912 return fail_hr;
2913 else
2914 return S_OK;
2917 static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
2919 HRESULT hr=S_OK;
2920 int i, j;
2921 ULARGE_INTEGER offset, cb;
2923 cb.QuadPart = 1;
2925 for (i=start; i<=end; i++)
2927 offset.QuadPart = i;
2928 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2929 if (hr != STG_E_ACCESSDENIED)
2930 break;
2933 if (SUCCEEDED(hr))
2935 for (j=0; j<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); j++)
2937 if (This->locked_bytes[j] == 0)
2939 This->locked_bytes[j] = i;
2940 break;
2945 return hr;
2948 static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
2950 HRESULT hr;
2951 ULARGE_INTEGER offset;
2952 ULARGE_INTEGER cb;
2953 DWORD share_mode = STGM_SHARE_MODE(openFlags);
2955 if (openFlags & STGM_NOSNAPSHOT)
2957 /* STGM_NOSNAPSHOT implies deny write */
2958 if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
2959 else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
2962 /* Wrap all other locking inside a single lock so we can check ranges safely */
2963 offset.QuadPart = RANGELOCK_CHECKLOCKS;
2964 cb.QuadPart = 1;
2965 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
2967 /* If the ILockBytes doesn't support locking that's ok. */
2968 if (FAILED(hr)) return S_OK;
2970 hr = S_OK;
2972 /* First check for any conflicting locks. */
2973 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
2974 hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
2976 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
2977 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
2979 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
2980 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
2982 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
2983 hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
2985 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
2986 hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
2988 /* Then grab our locks. */
2989 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
2991 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
2992 if (SUCCEEDED(hr))
2993 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
2996 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
2997 hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
2999 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
3000 hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
3002 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
3003 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
3005 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
3006 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
3008 if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
3009 hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
3011 offset.QuadPart = RANGELOCK_CHECKLOCKS;
3012 cb.QuadPart = 1;
3013 ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
3015 return hr;
3018 static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
3020 HRESULT hr=S_OK;
3021 DirEntry currentEntry;
3022 DirRef currentEntryRef;
3023 BlockChainStream *blockChainStream;
3025 if (create)
3027 ULARGE_INTEGER size;
3028 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
3030 /* Discard any existing data. */
3031 size.QuadPart = 0;
3032 ILockBytes_SetSize(This->lockBytes, size);
3035 * Initialize all header variables:
3036 * - The big block depot consists of one block and it is at block 0
3037 * - The directory table starts at block 1
3038 * - There is no small block depot
3040 memset( This->bigBlockDepotStart,
3041 BLOCK_UNUSED,
3042 sizeof(This->bigBlockDepotStart));
3044 This->bigBlockDepotCount = 1;
3045 This->bigBlockDepotStart[0] = 0;
3046 This->rootStartBlock = 1;
3047 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
3048 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
3049 if (This->bigBlockSize == 4096)
3050 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
3051 else
3052 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
3053 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
3054 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
3055 This->extBigBlockDepotCount = 0;
3057 StorageImpl_SaveFileHeader(This);
3060 * Add one block for the big block depot and one block for the directory table
3062 size.u.HighPart = 0;
3063 size.u.LowPart = This->bigBlockSize * 3;
3064 ILockBytes_SetSize(This->lockBytes, size);
3067 * Initialize the big block depot
3069 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3070 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
3071 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
3072 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
3074 else
3077 * Load the header for the file.
3079 hr = StorageImpl_LoadFileHeader(This);
3081 if (FAILED(hr))
3083 return hr;
3088 * There is no block depot cached yet.
3090 This->indexBlockDepotCached = 0xFFFFFFFF;
3091 This->indexExtBlockDepotCached = 0xFFFFFFFF;
3094 * Start searching for free blocks with block 0.
3096 This->prevFreeBlock = 0;
3098 This->firstFreeSmallBlock = 0;
3100 /* Read the extended big block depot locations. */
3101 if (This->extBigBlockDepotCount != 0)
3103 ULONG current_block = This->extBigBlockDepotStart;
3104 ULONG cache_size = This->extBigBlockDepotCount * 2;
3105 ULONG i;
3107 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
3108 if (!This->extBigBlockDepotLocations)
3110 return E_OUTOFMEMORY;
3113 This->extBigBlockDepotLocationsSize = cache_size;
3115 for (i=0; i<This->extBigBlockDepotCount; i++)
3117 if (current_block == BLOCK_END_OF_CHAIN)
3119 WARN("File has too few extended big block depot blocks.\n");
3120 return STG_E_DOCFILECORRUPT;
3122 This->extBigBlockDepotLocations[i] = current_block;
3123 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
3126 else
3128 This->extBigBlockDepotLocations = NULL;
3129 This->extBigBlockDepotLocationsSize = 0;
3133 * Create the block chain abstractions.
3135 if(!(blockChainStream =
3136 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
3138 return STG_E_READFAULT;
3140 if (!new_object)
3141 BlockChainStream_Destroy(This->rootBlockChain);
3142 This->rootBlockChain = blockChainStream;
3144 if(!(blockChainStream =
3145 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
3146 DIRENTRY_NULL)))
3148 return STG_E_READFAULT;
3150 if (!new_object)
3151 BlockChainStream_Destroy(This->smallBlockDepotChain);
3152 This->smallBlockDepotChain = blockChainStream;
3155 * Write the root storage entry (memory only)
3157 if (create)
3159 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
3160 DirEntry rootEntry;
3162 * Initialize the directory table
3164 memset(&rootEntry, 0, sizeof(rootEntry));
3165 strcpyW(rootEntry.name, rootentryW);
3166 rootEntry.sizeOfNameString = sizeof(rootentryW);
3167 rootEntry.stgType = STGTY_ROOT;
3168 rootEntry.leftChild = DIRENTRY_NULL;
3169 rootEntry.rightChild = DIRENTRY_NULL;
3170 rootEntry.dirRootEntry = DIRENTRY_NULL;
3171 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
3172 rootEntry.size.u.HighPart = 0;
3173 rootEntry.size.u.LowPart = 0;
3175 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
3179 * Find the ID of the root storage.
3181 currentEntryRef = 0;
3185 hr = StorageImpl_ReadDirEntry(
3186 This,
3187 currentEntryRef,
3188 &currentEntry);
3190 if (SUCCEEDED(hr))
3192 if ( (currentEntry.sizeOfNameString != 0 ) &&
3193 (currentEntry.stgType == STGTY_ROOT) )
3195 This->base.storageDirEntry = currentEntryRef;
3199 currentEntryRef++;
3201 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
3203 if (FAILED(hr))
3205 return STG_E_READFAULT;
3209 * Create the block chain abstraction for the small block root chain.
3211 if(!(blockChainStream =
3212 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
3214 return STG_E_READFAULT;
3216 if (!new_object)
3217 BlockChainStream_Destroy(This->smallBlockRootChain);
3218 This->smallBlockRootChain = blockChainStream;
3220 if (!new_object)
3222 int i;
3223 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
3225 BlockChainStream_Destroy(This->blockChainCache[i]);
3226 This->blockChainCache[i] = NULL;
3230 return hr;
3233 static HRESULT StorageImpl_Construct(
3234 HANDLE hFile,
3235 LPCOLESTR pwcsName,
3236 ILockBytes* pLkbyt,
3237 DWORD openFlags,
3238 BOOL fileBased,
3239 BOOL create,
3240 ULONG sector_size,
3241 StorageImpl** result)
3243 StorageImpl* This;
3244 HRESULT hr = S_OK;
3246 if ( FAILED( validateSTGM(openFlags) ))
3247 return STG_E_INVALIDFLAG;
3249 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
3250 if (!This)
3251 return E_OUTOFMEMORY;
3253 memset(This, 0, sizeof(StorageImpl));
3255 list_init(&This->base.strmHead);
3257 list_init(&This->base.storageHead);
3259 This->base.IStorage_iface.lpVtbl = &Storage32Impl_Vtbl;
3260 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
3261 This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
3262 This->base.baseVtbl = &StorageImpl_BaseVtbl;
3263 This->base.openFlags = (openFlags & ~STGM_CREATE);
3264 This->base.ref = 1;
3265 This->base.create = create;
3267 if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
3268 This->base.lockingrole = SWMR_Writer;
3269 else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
3270 This->base.lockingrole = SWMR_Reader;
3271 else
3272 This->base.lockingrole = SWMR_None;
3274 This->base.reverted = FALSE;
3277 * Initialize the big block cache.
3279 This->bigBlockSize = sector_size;
3280 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
3281 if (hFile)
3282 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
3283 else
3285 This->lockBytes = pLkbyt;
3286 ILockBytes_AddRef(pLkbyt);
3289 if (SUCCEEDED(hr))
3290 hr = StorageImpl_GrabLocks(This, openFlags);
3292 if (SUCCEEDED(hr))
3293 hr = StorageImpl_Refresh(This, TRUE, create);
3295 if (FAILED(hr))
3297 IStorage_Release(&This->base.IStorage_iface);
3298 *result = NULL;
3300 else
3302 StorageImpl_Flush(&This->base);
3303 *result = This;
3306 return hr;
3309 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
3311 StorageImpl *This = (StorageImpl*) iface;
3313 StorageBaseImpl_DeleteAll(&This->base);
3315 This->base.reverted = TRUE;
3318 static void StorageImpl_Destroy(StorageBaseImpl* iface)
3320 StorageImpl *This = (StorageImpl*) iface;
3321 int i;
3322 TRACE("(%p)\n", This);
3324 StorageImpl_Flush(iface);
3326 StorageImpl_Invalidate(iface);
3328 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3330 BlockChainStream_Destroy(This->smallBlockRootChain);
3331 BlockChainStream_Destroy(This->rootBlockChain);
3332 BlockChainStream_Destroy(This->smallBlockDepotChain);
3334 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
3335 BlockChainStream_Destroy(This->blockChainCache[i]);
3337 for (i=0; i<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); i++)
3339 ULARGE_INTEGER offset, cb;
3340 cb.QuadPart = 1;
3341 if (This->locked_bytes[i] != 0)
3343 offset.QuadPart = This->locked_bytes[i];
3344 ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
3348 if (This->lockBytes)
3349 ILockBytes_Release(This->lockBytes);
3350 HeapFree(GetProcessHeap(), 0, This);
3353 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
3355 StorageImpl *This = (StorageImpl*)storage;
3356 int i;
3357 HRESULT hr;
3358 TRACE("(%p)\n", This);
3360 hr = BlockChainStream_Flush(This->smallBlockRootChain);
3362 if (SUCCEEDED(hr))
3363 hr = BlockChainStream_Flush(This->rootBlockChain);
3365 if (SUCCEEDED(hr))
3366 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
3368 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
3369 if (This->blockChainCache[i])
3370 hr = BlockChainStream_Flush(This->blockChainCache[i]);
3372 if (SUCCEEDED(hr))
3373 hr = ILockBytes_Flush(This->lockBytes);
3375 return hr;
3378 /******************************************************************************
3379 * Storage32Impl_GetNextFreeBigBlock
3381 * Returns the index of the next free big block.
3382 * If the big block depot is filled, this method will enlarge it.
3385 static ULONG StorageImpl_GetNextFreeBigBlock(
3386 StorageImpl* This)
3388 ULONG depotBlockIndexPos;
3389 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3390 ULONG depotBlockOffset;
3391 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3392 ULONG nextBlockIndex = BLOCK_SPECIAL;
3393 int depotIndex = 0;
3394 ULONG freeBlock = BLOCK_UNUSED;
3395 ULONG read;
3396 ULARGE_INTEGER neededSize;
3397 STATSTG statstg;
3399 depotIndex = This->prevFreeBlock / blocksPerDepot;
3400 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
3403 * Scan the entire big block depot until we find a block marked free
3405 while (nextBlockIndex != BLOCK_UNUSED)
3407 if (depotIndex < COUNT_BBDEPOTINHEADER)
3409 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
3412 * Grow the primary depot.
3414 if (depotBlockIndexPos == BLOCK_UNUSED)
3416 depotBlockIndexPos = depotIndex*blocksPerDepot;
3419 * Add a block depot.
3421 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
3422 This->bigBlockDepotCount++;
3423 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3426 * Flag it as a block depot.
3428 StorageImpl_SetNextBlockInChain(This,
3429 depotBlockIndexPos,
3430 BLOCK_SPECIAL);
3432 /* Save new header information.
3434 StorageImpl_SaveFileHeader(This);
3437 else
3439 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3441 if (depotBlockIndexPos == BLOCK_UNUSED)
3444 * Grow the extended depot.
3446 ULONG extIndex = BLOCK_UNUSED;
3447 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3448 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3450 if (extBlockOffset == 0)
3452 /* We need an extended block.
3454 extIndex = Storage32Impl_AddExtBlockDepot(This);
3455 This->extBigBlockDepotCount++;
3456 depotBlockIndexPos = extIndex + 1;
3458 else
3459 depotBlockIndexPos = depotIndex * blocksPerDepot;
3462 * Add a block depot and mark it in the extended block.
3464 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
3465 This->bigBlockDepotCount++;
3466 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3468 /* Flag the block depot.
3470 StorageImpl_SetNextBlockInChain(This,
3471 depotBlockIndexPos,
3472 BLOCK_SPECIAL);
3474 /* If necessary, flag the extended depot block.
3476 if (extIndex != BLOCK_UNUSED)
3477 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3479 /* Save header information.
3481 StorageImpl_SaveFileHeader(This);
3485 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
3487 if (read)
3489 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3490 ( nextBlockIndex != BLOCK_UNUSED))
3492 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3494 if (nextBlockIndex == BLOCK_UNUSED)
3496 freeBlock = (depotIndex * blocksPerDepot) +
3497 (depotBlockOffset/sizeof(ULONG));
3500 depotBlockOffset += sizeof(ULONG);
3504 depotIndex++;
3505 depotBlockOffset = 0;
3509 * make sure that the block physically exists before using it
3511 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3513 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3515 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3516 ILockBytes_SetSize(This->lockBytes, neededSize);
3518 This->prevFreeBlock = freeBlock;
3520 return freeBlock;
3523 /******************************************************************************
3524 * Storage32Impl_AddBlockDepot
3526 * This will create a depot block, essentially it is a block initialized
3527 * to BLOCK_UNUSEDs.
3529 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
3531 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3532 ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
3533 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3534 ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
3537 * Initialize blocks as free
3539 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3541 /* Reserve the range lock sector */
3542 if (depotIndex == rangeLockDepot)
3544 ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
3547 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3550 /******************************************************************************
3551 * Storage32Impl_GetExtDepotBlock
3553 * Returns the index of the block that corresponds to the specified depot
3554 * index. This method is only for depot indexes equal or greater than
3555 * COUNT_BBDEPOTINHEADER.
3557 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3559 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3560 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3561 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3562 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3563 ULONG blockIndex = BLOCK_UNUSED;
3564 ULONG extBlockIndex;
3565 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3566 int index, num_blocks;
3568 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3570 if (extBlockCount >= This->extBigBlockDepotCount)
3571 return BLOCK_UNUSED;
3573 if (This->indexExtBlockDepotCached != extBlockCount)
3575 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3577 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
3579 num_blocks = This->bigBlockSize / 4;
3581 for (index = 0; index < num_blocks; index++)
3583 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3584 This->extBlockDepotCached[index] = blockIndex;
3587 This->indexExtBlockDepotCached = extBlockCount;
3590 blockIndex = This->extBlockDepotCached[extBlockOffset];
3592 return blockIndex;
3595 /******************************************************************************
3596 * Storage32Impl_SetExtDepotBlock
3598 * Associates the specified block index to the specified depot index.
3599 * This method is only for depot indexes equal or greater than
3600 * COUNT_BBDEPOTINHEADER.
3602 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3604 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3605 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3606 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3607 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3608 ULONG extBlockIndex;
3610 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3612 assert(extBlockCount < This->extBigBlockDepotCount);
3614 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3616 if (extBlockIndex != BLOCK_UNUSED)
3618 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3619 extBlockOffset * sizeof(ULONG),
3620 blockIndex);
3623 if (This->indexExtBlockDepotCached == extBlockCount)
3625 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3629 /******************************************************************************
3630 * Storage32Impl_AddExtBlockDepot
3632 * Creates an extended depot block.
3634 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3636 ULONG numExtBlocks = This->extBigBlockDepotCount;
3637 ULONG nextExtBlock = This->extBigBlockDepotStart;
3638 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3639 ULONG index = BLOCK_UNUSED;
3640 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3641 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3642 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3644 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3645 blocksPerDepotBlock;
3647 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3650 * The first extended block.
3652 This->extBigBlockDepotStart = index;
3654 else
3657 * Find the last existing extended block.
3659 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3662 * Add the new extended block to the chain.
3664 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3665 index);
3669 * Initialize this block.
3671 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3672 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3674 /* Add the block to our cache. */
3675 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3677 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3678 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3680 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3681 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3683 This->extBigBlockDepotLocations = new_cache;
3684 This->extBigBlockDepotLocationsSize = new_cache_size;
3686 This->extBigBlockDepotLocations[numExtBlocks] = index;
3688 return index;
3691 /******************************************************************************
3692 * Storage32Impl_FreeBigBlock
3694 * This method will flag the specified block as free in the big block depot.
3696 static void StorageImpl_FreeBigBlock(
3697 StorageImpl* This,
3698 ULONG blockIndex)
3700 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3702 if (blockIndex < This->prevFreeBlock)
3703 This->prevFreeBlock = blockIndex;
3706 /************************************************************************
3707 * Storage32Impl_GetNextBlockInChain
3709 * This method will retrieve the block index of the next big block in
3710 * in the chain.
3712 * Params: This - Pointer to the Storage object.
3713 * blockIndex - Index of the block to retrieve the chain
3714 * for.
3715 * nextBlockIndex - receives the return value.
3717 * Returns: This method returns the index of the next block in the chain.
3718 * It will return the constants:
3719 * BLOCK_SPECIAL - If the block given was not part of a
3720 * chain.
3721 * BLOCK_END_OF_CHAIN - If the block given was the last in
3722 * a chain.
3723 * BLOCK_UNUSED - If the block given was not past of a chain
3724 * and is available.
3725 * BLOCK_EXTBBDEPOT - This block is part of the extended
3726 * big block depot.
3728 * See Windows documentation for more details on IStorage methods.
3730 static HRESULT StorageImpl_GetNextBlockInChain(
3731 StorageImpl* This,
3732 ULONG blockIndex,
3733 ULONG* nextBlockIndex)
3735 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3736 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3737 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3738 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3739 ULONG read;
3740 ULONG depotBlockIndexPos;
3741 int index, num_blocks;
3743 *nextBlockIndex = BLOCK_SPECIAL;
3745 if(depotBlockCount >= This->bigBlockDepotCount)
3747 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3748 This->bigBlockDepotCount);
3749 return STG_E_READFAULT;
3753 * Cache the currently accessed depot block.
3755 if (depotBlockCount != This->indexBlockDepotCached)
3757 This->indexBlockDepotCached = depotBlockCount;
3759 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3761 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3763 else
3766 * We have to look in the extended depot.
3768 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3771 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
3773 if (!read)
3774 return STG_E_READFAULT;
3776 num_blocks = This->bigBlockSize / 4;
3778 for (index = 0; index < num_blocks; index++)
3780 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3781 This->blockDepotCached[index] = *nextBlockIndex;
3785 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3787 return S_OK;
3790 /******************************************************************************
3791 * Storage32Impl_GetNextExtendedBlock
3793 * Given an extended block this method will return the next extended block.
3795 * NOTES:
3796 * The last ULONG of an extended block is the block index of the next
3797 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3798 * depot.
3800 * Return values:
3801 * - The index of the next extended block
3802 * - BLOCK_UNUSED: there is no next extended block.
3803 * - Any other return values denotes failure.
3805 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3807 ULONG nextBlockIndex = BLOCK_SPECIAL;
3808 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3810 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3811 &nextBlockIndex);
3813 return nextBlockIndex;
3816 /******************************************************************************
3817 * Storage32Impl_SetNextBlockInChain
3819 * This method will write the index of the specified block's next block
3820 * in the big block depot.
3822 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3823 * do the following
3825 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3826 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3827 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3830 static void StorageImpl_SetNextBlockInChain(
3831 StorageImpl* This,
3832 ULONG blockIndex,
3833 ULONG nextBlock)
3835 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3836 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3837 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3838 ULONG depotBlockIndexPos;
3840 assert(depotBlockCount < This->bigBlockDepotCount);
3841 assert(blockIndex != nextBlock);
3843 if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
3844 /* This should never happen (storage file format spec forbids it), but
3845 * older versions of Wine may have generated broken files. We don't want to
3846 * assert and potentially lose data, but we do want to know if this ever
3847 * happens in a newly-created file. */
3848 ERR("Using range lock page\n");
3850 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3852 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3854 else
3857 * We have to look in the extended depot.
3859 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3862 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3863 nextBlock);
3865 * Update the cached block depot, if necessary.
3867 if (depotBlockCount == This->indexBlockDepotCached)
3869 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3873 /******************************************************************************
3874 * Storage32Impl_LoadFileHeader
3876 * This method will read in the file header
3878 static HRESULT StorageImpl_LoadFileHeader(
3879 StorageImpl* This)
3881 HRESULT hr;
3882 BYTE headerBigBlock[HEADER_SIZE];
3883 int index;
3884 ULARGE_INTEGER offset;
3885 DWORD bytes_read;
3887 TRACE("\n");
3889 * Get a pointer to the big block of data containing the header.
3891 offset.u.HighPart = 0;
3892 offset.u.LowPart = 0;
3893 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3894 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3895 hr = STG_E_FILENOTFOUND;
3898 * Extract the information from the header.
3900 if (SUCCEEDED(hr))
3903 * Check for the "magic number" signature and return an error if it is not
3904 * found.
3906 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3908 return STG_E_OLDFORMAT;
3911 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3913 return STG_E_INVALIDHEADER;
3916 StorageUtl_ReadWord(
3917 headerBigBlock,
3918 OFFSET_BIGBLOCKSIZEBITS,
3919 &This->bigBlockSizeBits);
3921 StorageUtl_ReadWord(
3922 headerBigBlock,
3923 OFFSET_SMALLBLOCKSIZEBITS,
3924 &This->smallBlockSizeBits);
3926 StorageUtl_ReadDWord(
3927 headerBigBlock,
3928 OFFSET_BBDEPOTCOUNT,
3929 &This->bigBlockDepotCount);
3931 StorageUtl_ReadDWord(
3932 headerBigBlock,
3933 OFFSET_ROOTSTARTBLOCK,
3934 &This->rootStartBlock);
3936 StorageUtl_ReadDWord(
3937 headerBigBlock,
3938 OFFSET_TRANSACTIONSIG,
3939 &This->transactionSig);
3941 StorageUtl_ReadDWord(
3942 headerBigBlock,
3943 OFFSET_SMALLBLOCKLIMIT,
3944 &This->smallBlockLimit);
3946 StorageUtl_ReadDWord(
3947 headerBigBlock,
3948 OFFSET_SBDEPOTSTART,
3949 &This->smallBlockDepotStart);
3951 StorageUtl_ReadDWord(
3952 headerBigBlock,
3953 OFFSET_EXTBBDEPOTSTART,
3954 &This->extBigBlockDepotStart);
3956 StorageUtl_ReadDWord(
3957 headerBigBlock,
3958 OFFSET_EXTBBDEPOTCOUNT,
3959 &This->extBigBlockDepotCount);
3961 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3963 StorageUtl_ReadDWord(
3964 headerBigBlock,
3965 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3966 &(This->bigBlockDepotStart[index]));
3970 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3972 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3973 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3976 * Right now, the code is making some assumptions about the size of the
3977 * blocks, just make sure they are what we're expecting.
3979 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3980 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3981 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3983 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3984 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3985 hr = STG_E_INVALIDHEADER;
3987 else
3988 hr = S_OK;
3991 return hr;
3994 /******************************************************************************
3995 * Storage32Impl_SaveFileHeader
3997 * This method will save to the file the header
3999 static void StorageImpl_SaveFileHeader(
4000 StorageImpl* This)
4002 BYTE headerBigBlock[HEADER_SIZE];
4003 int index;
4004 HRESULT hr;
4005 ULARGE_INTEGER offset;
4006 DWORD bytes_read, bytes_written;
4007 DWORD major_version, dirsectorcount;
4010 * Get a pointer to the big block of data containing the header.
4012 offset.u.HighPart = 0;
4013 offset.u.LowPart = 0;
4014 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
4015 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
4016 hr = STG_E_FILENOTFOUND;
4018 if (This->bigBlockSizeBits == 0x9)
4019 major_version = 3;
4020 else if (This->bigBlockSizeBits == 0xc)
4021 major_version = 4;
4022 else
4024 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
4025 major_version = 4;
4029 * If the block read failed, the file is probably new.
4031 if (FAILED(hr))
4034 * Initialize for all unknown fields.
4036 memset(headerBigBlock, 0, HEADER_SIZE);
4039 * Initialize the magic number.
4041 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
4045 * Write the information to the header.
4047 StorageUtl_WriteWord(
4048 headerBigBlock,
4049 OFFSET_MINORVERSION,
4050 0x3e);
4052 StorageUtl_WriteWord(
4053 headerBigBlock,
4054 OFFSET_MAJORVERSION,
4055 major_version);
4057 StorageUtl_WriteWord(
4058 headerBigBlock,
4059 OFFSET_BYTEORDERMARKER,
4060 (WORD)-2);
4062 StorageUtl_WriteWord(
4063 headerBigBlock,
4064 OFFSET_BIGBLOCKSIZEBITS,
4065 This->bigBlockSizeBits);
4067 StorageUtl_WriteWord(
4068 headerBigBlock,
4069 OFFSET_SMALLBLOCKSIZEBITS,
4070 This->smallBlockSizeBits);
4072 if (major_version >= 4)
4074 if (This->rootBlockChain)
4075 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
4076 else
4077 /* This file is being created, and it will start out with one block. */
4078 dirsectorcount = 1;
4080 else
4081 /* This field must be 0 in versions older than 4 */
4082 dirsectorcount = 0;
4084 StorageUtl_WriteDWord(
4085 headerBigBlock,
4086 OFFSET_DIRSECTORCOUNT,
4087 dirsectorcount);
4089 StorageUtl_WriteDWord(
4090 headerBigBlock,
4091 OFFSET_BBDEPOTCOUNT,
4092 This->bigBlockDepotCount);
4094 StorageUtl_WriteDWord(
4095 headerBigBlock,
4096 OFFSET_ROOTSTARTBLOCK,
4097 This->rootStartBlock);
4099 StorageUtl_WriteDWord(
4100 headerBigBlock,
4101 OFFSET_TRANSACTIONSIG,
4102 This->transactionSig);
4104 StorageUtl_WriteDWord(
4105 headerBigBlock,
4106 OFFSET_SMALLBLOCKLIMIT,
4107 This->smallBlockLimit);
4109 StorageUtl_WriteDWord(
4110 headerBigBlock,
4111 OFFSET_SBDEPOTSTART,
4112 This->smallBlockDepotStart);
4114 StorageUtl_WriteDWord(
4115 headerBigBlock,
4116 OFFSET_SBDEPOTCOUNT,
4117 This->smallBlockDepotChain ?
4118 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
4120 StorageUtl_WriteDWord(
4121 headerBigBlock,
4122 OFFSET_EXTBBDEPOTSTART,
4123 This->extBigBlockDepotStart);
4125 StorageUtl_WriteDWord(
4126 headerBigBlock,
4127 OFFSET_EXTBBDEPOTCOUNT,
4128 This->extBigBlockDepotCount);
4130 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
4132 StorageUtl_WriteDWord(
4133 headerBigBlock,
4134 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
4135 (This->bigBlockDepotStart[index]));
4139 * Write the big block back to the file.
4141 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
4144 /******************************************************************************
4145 * StorageImpl_ReadRawDirEntry
4147 * This method will read the raw data from a directory entry in the file.
4149 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4151 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
4153 ULARGE_INTEGER offset;
4154 HRESULT hr;
4155 ULONG bytesRead;
4157 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
4159 hr = BlockChainStream_ReadAt(
4160 This->rootBlockChain,
4161 offset,
4162 RAW_DIRENTRY_SIZE,
4163 buffer,
4164 &bytesRead);
4166 if (bytesRead != RAW_DIRENTRY_SIZE)
4167 return STG_E_READFAULT;
4169 return hr;
4172 /******************************************************************************
4173 * StorageImpl_WriteRawDirEntry
4175 * This method will write the raw data from a directory entry in the file.
4177 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4179 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
4181 ULARGE_INTEGER offset;
4182 ULONG bytesRead;
4184 offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
4186 return BlockChainStream_WriteAt(
4187 This->rootBlockChain,
4188 offset,
4189 RAW_DIRENTRY_SIZE,
4190 buffer,
4191 &bytesRead);
4194 /******************************************************************************
4195 * UpdateRawDirEntry
4197 * Update raw directory entry data from the fields in newData.
4199 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4201 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
4203 memset(buffer, 0, RAW_DIRENTRY_SIZE);
4205 memcpy(
4206 buffer + OFFSET_PS_NAME,
4207 newData->name,
4208 DIRENTRY_NAME_BUFFER_LEN );
4210 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
4212 StorageUtl_WriteWord(
4213 buffer,
4214 OFFSET_PS_NAMELENGTH,
4215 newData->sizeOfNameString);
4217 StorageUtl_WriteDWord(
4218 buffer,
4219 OFFSET_PS_LEFTCHILD,
4220 newData->leftChild);
4222 StorageUtl_WriteDWord(
4223 buffer,
4224 OFFSET_PS_RIGHTCHILD,
4225 newData->rightChild);
4227 StorageUtl_WriteDWord(
4228 buffer,
4229 OFFSET_PS_DIRROOT,
4230 newData->dirRootEntry);
4232 StorageUtl_WriteGUID(
4233 buffer,
4234 OFFSET_PS_GUID,
4235 &newData->clsid);
4237 StorageUtl_WriteDWord(
4238 buffer,
4239 OFFSET_PS_CTIMELOW,
4240 newData->ctime.dwLowDateTime);
4242 StorageUtl_WriteDWord(
4243 buffer,
4244 OFFSET_PS_CTIMEHIGH,
4245 newData->ctime.dwHighDateTime);
4247 StorageUtl_WriteDWord(
4248 buffer,
4249 OFFSET_PS_MTIMELOW,
4250 newData->mtime.dwLowDateTime);
4252 StorageUtl_WriteDWord(
4253 buffer,
4254 OFFSET_PS_MTIMEHIGH,
4255 newData->ctime.dwHighDateTime);
4257 StorageUtl_WriteDWord(
4258 buffer,
4259 OFFSET_PS_STARTBLOCK,
4260 newData->startingBlock);
4262 StorageUtl_WriteDWord(
4263 buffer,
4264 OFFSET_PS_SIZE,
4265 newData->size.u.LowPart);
4267 StorageUtl_WriteDWord(
4268 buffer,
4269 OFFSET_PS_SIZE_HIGH,
4270 newData->size.u.HighPart);
4273 /******************************************************************************
4274 * Storage32Impl_ReadDirEntry
4276 * This method will read the specified directory entry.
4278 HRESULT StorageImpl_ReadDirEntry(
4279 StorageImpl* This,
4280 DirRef index,
4281 DirEntry* buffer)
4283 BYTE currentEntry[RAW_DIRENTRY_SIZE];
4284 HRESULT readRes;
4286 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
4288 if (SUCCEEDED(readRes))
4290 memset(buffer->name, 0, sizeof(buffer->name));
4291 memcpy(
4292 buffer->name,
4293 (WCHAR *)currentEntry+OFFSET_PS_NAME,
4294 DIRENTRY_NAME_BUFFER_LEN );
4295 TRACE("storage name: %s\n", debugstr_w(buffer->name));
4297 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
4299 StorageUtl_ReadWord(
4300 currentEntry,
4301 OFFSET_PS_NAMELENGTH,
4302 &buffer->sizeOfNameString);
4304 StorageUtl_ReadDWord(
4305 currentEntry,
4306 OFFSET_PS_LEFTCHILD,
4307 &buffer->leftChild);
4309 StorageUtl_ReadDWord(
4310 currentEntry,
4311 OFFSET_PS_RIGHTCHILD,
4312 &buffer->rightChild);
4314 StorageUtl_ReadDWord(
4315 currentEntry,
4316 OFFSET_PS_DIRROOT,
4317 &buffer->dirRootEntry);
4319 StorageUtl_ReadGUID(
4320 currentEntry,
4321 OFFSET_PS_GUID,
4322 &buffer->clsid);
4324 StorageUtl_ReadDWord(
4325 currentEntry,
4326 OFFSET_PS_CTIMELOW,
4327 &buffer->ctime.dwLowDateTime);
4329 StorageUtl_ReadDWord(
4330 currentEntry,
4331 OFFSET_PS_CTIMEHIGH,
4332 &buffer->ctime.dwHighDateTime);
4334 StorageUtl_ReadDWord(
4335 currentEntry,
4336 OFFSET_PS_MTIMELOW,
4337 &buffer->mtime.dwLowDateTime);
4339 StorageUtl_ReadDWord(
4340 currentEntry,
4341 OFFSET_PS_MTIMEHIGH,
4342 &buffer->mtime.dwHighDateTime);
4344 StorageUtl_ReadDWord(
4345 currentEntry,
4346 OFFSET_PS_STARTBLOCK,
4347 &buffer->startingBlock);
4349 StorageUtl_ReadDWord(
4350 currentEntry,
4351 OFFSET_PS_SIZE,
4352 &buffer->size.u.LowPart);
4354 StorageUtl_ReadDWord(
4355 currentEntry,
4356 OFFSET_PS_SIZE_HIGH,
4357 &buffer->size.u.HighPart);
4360 return readRes;
4363 /*********************************************************************
4364 * Write the specified directory entry to the file
4366 HRESULT StorageImpl_WriteDirEntry(
4367 StorageImpl* This,
4368 DirRef index,
4369 const DirEntry* buffer)
4371 BYTE currentEntry[RAW_DIRENTRY_SIZE];
4373 UpdateRawDirEntry(currentEntry, buffer);
4375 return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
4378 static HRESULT StorageImpl_ReadBigBlock(
4379 StorageImpl* This,
4380 ULONG blockIndex,
4381 void* buffer,
4382 ULONG* out_read)
4384 ULARGE_INTEGER ulOffset;
4385 DWORD read=0;
4386 HRESULT hr;
4388 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4390 hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
4392 if (SUCCEEDED(hr) && read < This->bigBlockSize)
4394 /* File ends during this block; fill the rest with 0's. */
4395 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
4398 if (out_read) *out_read = read;
4400 return hr;
4403 static BOOL StorageImpl_ReadDWordFromBigBlock(
4404 StorageImpl* This,
4405 ULONG blockIndex,
4406 ULONG offset,
4407 DWORD* value)
4409 ULARGE_INTEGER ulOffset;
4410 DWORD read;
4411 DWORD tmp;
4413 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4414 ulOffset.QuadPart += offset;
4416 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
4417 *value = lendian32toh(tmp);
4418 return (read == sizeof(DWORD));
4421 static BOOL StorageImpl_WriteBigBlock(
4422 StorageImpl* This,
4423 ULONG blockIndex,
4424 const void* buffer)
4426 ULARGE_INTEGER ulOffset;
4427 DWORD wrote;
4429 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4431 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
4432 return (wrote == This->bigBlockSize);
4435 static BOOL StorageImpl_WriteDWordToBigBlock(
4436 StorageImpl* This,
4437 ULONG blockIndex,
4438 ULONG offset,
4439 DWORD value)
4441 ULARGE_INTEGER ulOffset;
4442 DWORD wrote;
4444 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4445 ulOffset.QuadPart += offset;
4447 value = htole32(value);
4448 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4449 return (wrote == sizeof(DWORD));
4452 /******************************************************************************
4453 * Storage32Impl_SmallBlocksToBigBlocks
4455 * This method will convert a small block chain to a big block chain.
4456 * The small block chain will be destroyed.
4458 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4459 StorageImpl* This,
4460 SmallBlockChainStream** ppsbChain)
4462 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4463 ULARGE_INTEGER size, offset;
4464 ULONG cbRead, cbWritten;
4465 ULARGE_INTEGER cbTotalRead;
4466 DirRef streamEntryRef;
4467 HRESULT resWrite = S_OK;
4468 HRESULT resRead;
4469 DirEntry streamEntry;
4470 BYTE *buffer;
4471 BlockChainStream *bbTempChain = NULL;
4472 BlockChainStream *bigBlockChain = NULL;
4475 * Create a temporary big block chain that doesn't have
4476 * an associated directory entry. This temporary chain will be
4477 * used to copy data from small blocks to big blocks.
4479 bbTempChain = BlockChainStream_Construct(This,
4480 &bbHeadOfChain,
4481 DIRENTRY_NULL);
4482 if(!bbTempChain) return NULL;
4484 * Grow the big block chain.
4486 size = SmallBlockChainStream_GetSize(*ppsbChain);
4487 BlockChainStream_SetSize(bbTempChain, size);
4490 * Copy the contents of the small block chain to the big block chain
4491 * by small block size increments.
4493 offset.u.LowPart = 0;
4494 offset.u.HighPart = 0;
4495 cbTotalRead.QuadPart = 0;
4497 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4500 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4501 offset,
4502 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4503 buffer,
4504 &cbRead);
4505 if (FAILED(resRead))
4506 break;
4508 if (cbRead > 0)
4510 cbTotalRead.QuadPart += cbRead;
4512 resWrite = BlockChainStream_WriteAt(bbTempChain,
4513 offset,
4514 cbRead,
4515 buffer,
4516 &cbWritten);
4518 if (FAILED(resWrite))
4519 break;
4521 offset.u.LowPart += cbRead;
4523 else
4525 resRead = STG_E_READFAULT;
4526 break;
4528 } while (cbTotalRead.QuadPart < size.QuadPart);
4529 HeapFree(GetProcessHeap(),0,buffer);
4531 size.u.HighPart = 0;
4532 size.u.LowPart = 0;
4534 if (FAILED(resRead) || FAILED(resWrite))
4536 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4537 BlockChainStream_SetSize(bbTempChain, size);
4538 BlockChainStream_Destroy(bbTempChain);
4539 return NULL;
4543 * Destroy the small block chain.
4545 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4546 SmallBlockChainStream_SetSize(*ppsbChain, size);
4547 SmallBlockChainStream_Destroy(*ppsbChain);
4548 *ppsbChain = 0;
4551 * Change the directory entry. This chain is now a big block chain
4552 * and it doesn't reside in the small blocks chain anymore.
4554 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4556 streamEntry.startingBlock = bbHeadOfChain;
4558 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4561 * Destroy the temporary entryless big block chain.
4562 * Create a new big block chain associated with this entry.
4564 BlockChainStream_Destroy(bbTempChain);
4565 bigBlockChain = BlockChainStream_Construct(This,
4566 NULL,
4567 streamEntryRef);
4569 return bigBlockChain;
4572 /******************************************************************************
4573 * Storage32Impl_BigBlocksToSmallBlocks
4575 * This method will convert a big block chain to a small block chain.
4576 * The big block chain will be destroyed on success.
4578 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4579 StorageImpl* This,
4580 BlockChainStream** ppbbChain,
4581 ULARGE_INTEGER newSize)
4583 ULARGE_INTEGER size, offset, cbTotalRead;
4584 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4585 DirRef streamEntryRef;
4586 HRESULT resWrite = S_OK, resRead = S_OK;
4587 DirEntry streamEntry;
4588 BYTE* buffer;
4589 SmallBlockChainStream* sbTempChain;
4591 TRACE("%p %p\n", This, ppbbChain);
4593 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4594 DIRENTRY_NULL);
4596 if(!sbTempChain)
4597 return NULL;
4599 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4600 size = BlockChainStream_GetSize(*ppbbChain);
4601 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4603 offset.u.HighPart = 0;
4604 offset.u.LowPart = 0;
4605 cbTotalRead.QuadPart = 0;
4606 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4607 while(cbTotalRead.QuadPart < size.QuadPart)
4609 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4610 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4611 buffer, &cbRead);
4613 if(FAILED(resRead))
4614 break;
4616 if(cbRead > 0)
4618 cbTotalRead.QuadPart += cbRead;
4620 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4621 cbRead, buffer, &cbWritten);
4623 if(FAILED(resWrite))
4624 break;
4626 offset.u.LowPart += cbRead;
4628 else
4630 resRead = STG_E_READFAULT;
4631 break;
4634 HeapFree(GetProcessHeap(), 0, buffer);
4636 size.u.HighPart = 0;
4637 size.u.LowPart = 0;
4639 if(FAILED(resRead) || FAILED(resWrite))
4641 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4642 SmallBlockChainStream_SetSize(sbTempChain, size);
4643 SmallBlockChainStream_Destroy(sbTempChain);
4644 return NULL;
4647 /* destroy the original big block chain */
4648 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4649 BlockChainStream_SetSize(*ppbbChain, size);
4650 BlockChainStream_Destroy(*ppbbChain);
4651 *ppbbChain = NULL;
4653 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4654 streamEntry.startingBlock = sbHeadOfChain;
4655 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4657 SmallBlockChainStream_Destroy(sbTempChain);
4658 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4661 static HRESULT StorageBaseImpl_CopyStream(
4662 StorageBaseImpl *dst, DirRef dst_entry,
4663 StorageBaseImpl *src, DirRef src_entry)
4665 HRESULT hr;
4666 BYTE data[4096];
4667 DirEntry srcdata;
4668 ULARGE_INTEGER bytes_copied;
4669 ULONG bytestocopy, bytesread, byteswritten;
4671 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4673 if (SUCCEEDED(hr))
4675 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4677 bytes_copied.QuadPart = 0;
4678 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4680 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4682 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4683 data, &bytesread);
4684 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4686 if (SUCCEEDED(hr))
4687 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4688 data, &byteswritten);
4689 if (SUCCEEDED(hr))
4691 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4692 bytes_copied.QuadPart += byteswritten;
4697 return hr;
4700 static HRESULT StorageBaseImpl_DupStorageTree(
4701 StorageBaseImpl *dst, DirRef *dst_entry,
4702 StorageBaseImpl *src, DirRef src_entry)
4704 HRESULT hr;
4705 DirEntry data;
4706 BOOL has_stream=FALSE;
4708 if (src_entry == DIRENTRY_NULL)
4710 *dst_entry = DIRENTRY_NULL;
4711 return S_OK;
4714 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
4715 if (SUCCEEDED(hr))
4717 has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0);
4718 data.startingBlock = BLOCK_END_OF_CHAIN;
4719 data.size.QuadPart = 0;
4721 hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild);
4724 if (SUCCEEDED(hr))
4725 hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
4727 if (SUCCEEDED(hr))
4728 hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);
4730 if (SUCCEEDED(hr))
4731 hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry);
4733 if (SUCCEEDED(hr) && has_stream)
4734 hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
4736 return hr;
4739 static HRESULT StorageBaseImpl_CopyStorageTree(
4740 StorageBaseImpl *dst, DirRef dst_entry,
4741 StorageBaseImpl *src, DirRef src_entry)
4743 HRESULT hr;
4744 DirEntry src_data, dst_data;
4745 DirRef new_root_entry;
4747 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
4749 if (SUCCEEDED(hr))
4751 hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
4754 if (SUCCEEDED(hr))
4756 hr = StorageBaseImpl_ReadDirEntry(dst, dst_entry, &dst_data);
4757 dst_data.clsid = src_data.clsid;
4758 dst_data.ctime = src_data.ctime;
4759 dst_data.mtime = src_data.mtime;
4760 dst_data.dirRootEntry = new_root_entry;
4763 if (SUCCEEDED(hr))
4764 hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
4766 return hr;
4769 static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
4771 HRESULT hr;
4772 DirEntry data;
4773 ULARGE_INTEGER zero;
4775 if (entry == DIRENTRY_NULL)
4776 return S_OK;
4778 zero.QuadPart = 0;
4780 hr = StorageBaseImpl_ReadDirEntry(This, entry, &data);
4782 if (SUCCEEDED(hr) && include_siblings)
4783 hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE);
4785 if (SUCCEEDED(hr) && include_siblings)
4786 hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE);
4788 if (SUCCEEDED(hr))
4789 hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
4791 if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
4792 hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
4794 if (SUCCEEDED(hr))
4795 hr = StorageBaseImpl_DestroyDirEntry(This, entry);
4797 return hr;
4800 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4802 DirRef result=This->firstFreeEntry;
4804 while (result < This->entries_size && This->entries[result].inuse)
4805 result++;
4807 if (result == This->entries_size)
4809 ULONG new_size = This->entries_size * 2;
4810 TransactedDirEntry *new_entries;
4812 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4813 if (!new_entries) return DIRENTRY_NULL;
4815 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4816 HeapFree(GetProcessHeap(), 0, This->entries);
4818 This->entries = new_entries;
4819 This->entries_size = new_size;
4822 This->entries[result].inuse = TRUE;
4824 This->firstFreeEntry = result+1;
4826 return result;
4829 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4830 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4832 DirRef stubEntryRef;
4833 TransactedDirEntry *entry;
4835 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4837 if (stubEntryRef != DIRENTRY_NULL)
4839 entry = &This->entries[stubEntryRef];
4841 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4843 entry->read = FALSE;
4846 return stubEntryRef;
4849 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4850 TransactedSnapshotImpl *This, DirRef entry)
4852 HRESULT hr=S_OK;
4853 DirEntry data;
4855 if (!This->entries[entry].read)
4857 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4858 This->entries[entry].transactedParentEntry,
4859 &data);
4861 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4863 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4865 if (data.leftChild == DIRENTRY_NULL)
4866 hr = E_OUTOFMEMORY;
4869 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4871 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4873 if (data.rightChild == DIRENTRY_NULL)
4874 hr = E_OUTOFMEMORY;
4877 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4879 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4881 if (data.dirRootEntry == DIRENTRY_NULL)
4882 hr = E_OUTOFMEMORY;
4885 if (SUCCEEDED(hr))
4887 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4888 This->entries[entry].read = TRUE;
4892 return hr;
4895 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4896 TransactedSnapshotImpl *This, DirRef entry)
4898 HRESULT hr = S_OK;
4900 if (!This->entries[entry].stream_dirty)
4902 DirEntry new_entrydata;
4904 memset(&new_entrydata, 0, sizeof(DirEntry));
4905 new_entrydata.name[0] = 'S';
4906 new_entrydata.sizeOfNameString = 1;
4907 new_entrydata.stgType = STGTY_STREAM;
4908 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4909 new_entrydata.leftChild = DIRENTRY_NULL;
4910 new_entrydata.rightChild = DIRENTRY_NULL;
4911 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4913 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4914 &This->entries[entry].stream_entry);
4916 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4918 hr = StorageBaseImpl_CopyStream(
4919 This->scratch, This->entries[entry].stream_entry,
4920 This->transactedParent, This->entries[entry].transactedParentEntry);
4922 if (FAILED(hr))
4923 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4926 if (SUCCEEDED(hr))
4927 This->entries[entry].stream_dirty = TRUE;
4929 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4931 /* Since this entry is modified, and we aren't using its stream data, we
4932 * no longer care about the original entry. */
4933 DirRef delete_ref;
4934 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4936 if (delete_ref != DIRENTRY_NULL)
4937 This->entries[delete_ref].deleted = TRUE;
4939 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4943 return hr;
4946 /* Find the first entry in a depth-first traversal. */
4947 static DirRef TransactedSnapshotImpl_FindFirstChild(
4948 TransactedSnapshotImpl* This, DirRef parent)
4950 DirRef cursor, prev;
4951 TransactedDirEntry *entry;
4953 cursor = parent;
4954 entry = &This->entries[cursor];
4955 while (entry->read)
4957 if (entry->data.leftChild != DIRENTRY_NULL)
4959 prev = cursor;
4960 cursor = entry->data.leftChild;
4961 entry = &This->entries[cursor];
4962 entry->parent = prev;
4964 else if (entry->data.rightChild != DIRENTRY_NULL)
4966 prev = cursor;
4967 cursor = entry->data.rightChild;
4968 entry = &This->entries[cursor];
4969 entry->parent = prev;
4971 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4973 prev = cursor;
4974 cursor = entry->data.dirRootEntry;
4975 entry = &This->entries[cursor];
4976 entry->parent = prev;
4978 else
4979 break;
4982 return cursor;
4985 /* Find the next entry in a depth-first traversal. */
4986 static DirRef TransactedSnapshotImpl_FindNextChild(
4987 TransactedSnapshotImpl* This, DirRef current)
4989 DirRef parent;
4990 TransactedDirEntry *parent_entry;
4992 parent = This->entries[current].parent;
4993 parent_entry = &This->entries[parent];
4995 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4997 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4999 This->entries[parent_entry->data.rightChild].parent = parent;
5000 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
5003 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
5005 This->entries[parent_entry->data.dirRootEntry].parent = parent;
5006 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
5010 return parent;
5013 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5014 static inline BOOL TransactedSnapshotImpl_MadeCopy(
5015 TransactedSnapshotImpl* This, DirRef entry)
5017 return entry != DIRENTRY_NULL &&
5018 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
5021 /* Destroy the entries created by CopyTree. */
5022 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5023 TransactedSnapshotImpl* This, DirRef stop)
5025 DirRef cursor;
5026 TransactedDirEntry *entry;
5027 ULARGE_INTEGER zero;
5029 zero.QuadPart = 0;
5031 if (!This->entries[This->base.storageDirEntry].read)
5032 return;
5034 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
5036 if (cursor == DIRENTRY_NULL)
5037 return;
5039 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
5041 while (cursor != DIRENTRY_NULL && cursor != stop)
5043 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
5045 entry = &This->entries[cursor];
5047 if (entry->stream_dirty)
5048 StorageBaseImpl_StreamSetSize(This->transactedParent,
5049 entry->newTransactedParentEntry, zero);
5051 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5052 entry->newTransactedParentEntry);
5054 entry->newTransactedParentEntry = entry->transactedParentEntry;
5057 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5061 /* Make a copy of our edited tree that we can use in the parent. */
5062 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
5064 DirRef cursor;
5065 TransactedDirEntry *entry;
5066 HRESULT hr = S_OK;
5068 cursor = This->base.storageDirEntry;
5069 entry = &This->entries[cursor];
5070 entry->parent = DIRENTRY_NULL;
5071 entry->newTransactedParentEntry = entry->transactedParentEntry;
5073 if (entry->data.dirRootEntry == DIRENTRY_NULL)
5074 return S_OK;
5076 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
5078 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
5079 entry = &This->entries[cursor];
5081 while (cursor != DIRENTRY_NULL)
5083 /* Make a copy of this entry in the transacted parent. */
5084 if (!entry->read ||
5085 (!entry->dirty && !entry->stream_dirty &&
5086 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
5087 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
5088 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
5089 entry->newTransactedParentEntry = entry->transactedParentEntry;
5090 else
5092 DirEntry newData;
5094 memcpy(&newData, &entry->data, sizeof(DirEntry));
5096 newData.size.QuadPart = 0;
5097 newData.startingBlock = BLOCK_END_OF_CHAIN;
5099 if (newData.leftChild != DIRENTRY_NULL)
5100 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
5102 if (newData.rightChild != DIRENTRY_NULL)
5103 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
5105 if (newData.dirRootEntry != DIRENTRY_NULL)
5106 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
5108 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
5109 &entry->newTransactedParentEntry);
5110 if (FAILED(hr))
5112 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5113 return hr;
5116 if (entry->stream_dirty)
5118 hr = StorageBaseImpl_CopyStream(
5119 This->transactedParent, entry->newTransactedParentEntry,
5120 This->scratch, entry->stream_entry);
5122 else if (entry->data.size.QuadPart)
5124 hr = StorageBaseImpl_StreamLink(
5125 This->transactedParent, entry->newTransactedParentEntry,
5126 entry->transactedParentEntry);
5129 if (FAILED(hr))
5131 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5132 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
5133 return hr;
5137 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
5138 entry = &This->entries[cursor];
5141 return hr;
5144 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
5145 IStorage* iface,
5146 DWORD grfCommitFlags) /* [in] */
5148 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
5149 TransactedDirEntry *root_entry;
5150 DirRef i, dir_root_ref;
5151 DirEntry data;
5152 ULARGE_INTEGER zero;
5153 HRESULT hr;
5154 ULONG transactionSig;
5156 zero.QuadPart = 0;
5158 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5160 /* Cannot commit a read-only transacted storage */
5161 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
5162 return STG_E_ACCESSDENIED;
5164 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
5165 if (hr == E_NOTIMPL) hr = S_OK;
5166 if (SUCCEEDED(hr))
5168 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
5169 if (SUCCEEDED(hr))
5171 if (transactionSig != This->lastTransactionSig)
5173 ERR("file was externally modified\n");
5174 hr = STG_E_NOTCURRENT;
5177 if (SUCCEEDED(hr))
5179 This->lastTransactionSig = transactionSig+1;
5180 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
5183 else if (hr == E_NOTIMPL)
5184 hr = S_OK;
5186 if (FAILED(hr)) goto end;
5188 /* To prevent data loss, we create the new structure in the file before we
5189 * delete the old one, so that in case of errors the old data is intact. We
5190 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5191 * needed in the rare situation where we have just enough free disk space to
5192 * overwrite the existing data. */
5194 root_entry = &This->entries[This->base.storageDirEntry];
5196 if (!root_entry->read)
5197 goto end;
5199 hr = TransactedSnapshotImpl_CopyTree(This);
5200 if (FAILED(hr)) goto end;
5202 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
5203 dir_root_ref = DIRENTRY_NULL;
5204 else
5205 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
5207 hr = StorageBaseImpl_Flush(This->transactedParent);
5209 /* Update the storage to use the new data in one step. */
5210 if (SUCCEEDED(hr))
5211 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5212 root_entry->transactedParentEntry, &data);
5214 if (SUCCEEDED(hr))
5216 data.dirRootEntry = dir_root_ref;
5217 data.clsid = root_entry->data.clsid;
5218 data.ctime = root_entry->data.ctime;
5219 data.mtime = root_entry->data.mtime;
5221 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
5222 root_entry->transactedParentEntry, &data);
5225 /* Try to flush after updating the root storage, but if the flush fails, keep
5226 * going, on the theory that it'll either succeed later or the subsequent
5227 * writes will fail. */
5228 StorageBaseImpl_Flush(This->transactedParent);
5230 if (SUCCEEDED(hr))
5232 /* Destroy the old now-orphaned data. */
5233 for (i=0; i<This->entries_size; i++)
5235 TransactedDirEntry *entry = &This->entries[i];
5236 if (entry->inuse)
5238 if (entry->deleted)
5240 StorageBaseImpl_StreamSetSize(This->transactedParent,
5241 entry->transactedParentEntry, zero);
5242 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5243 entry->transactedParentEntry);
5244 memset(entry, 0, sizeof(TransactedDirEntry));
5245 This->firstFreeEntry = min(i, This->firstFreeEntry);
5247 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
5249 if (entry->transactedParentEntry != DIRENTRY_NULL)
5250 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5251 entry->transactedParentEntry);
5252 if (entry->stream_dirty)
5254 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
5255 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
5256 entry->stream_dirty = FALSE;
5258 entry->dirty = FALSE;
5259 entry->transactedParentEntry = entry->newTransactedParentEntry;
5264 else
5266 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
5269 if (SUCCEEDED(hr))
5270 hr = StorageBaseImpl_Flush(This->transactedParent);
5271 end:
5272 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
5275 return hr;
5278 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
5279 IStorage* iface)
5281 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
5282 ULARGE_INTEGER zero;
5283 ULONG i;
5285 TRACE("(%p)\n", iface);
5287 /* Destroy the open objects. */
5288 StorageBaseImpl_DeleteAll(&This->base);
5290 /* Clear out the scratch file. */
5291 zero.QuadPart = 0;
5292 for (i=0; i<This->entries_size; i++)
5294 if (This->entries[i].stream_dirty)
5296 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
5297 zero);
5299 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
5303 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
5305 This->firstFreeEntry = 0;
5306 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
5308 return S_OK;
5311 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
5313 if (!This->reverted)
5315 TRACE("Storage invalidated (stg=%p)\n", This);
5317 This->reverted = TRUE;
5319 StorageBaseImpl_DeleteAll(This);
5323 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
5325 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
5327 IStorage_Revert(&This->base.IStorage_iface);
5328 IStorage_Release(&This->transactedParent->IStorage_iface);
5329 IStorage_Release(&This->scratch->IStorage_iface);
5330 HeapFree(GetProcessHeap(), 0, This->entries);
5331 HeapFree(GetProcessHeap(), 0, This);
5334 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
5336 /* We only need to flush when committing. */
5337 return S_OK;
5340 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5342 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
5344 return StorageBaseImpl_GetFilename(This->transactedParent, result);
5347 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
5348 const DirEntry *newData, DirRef *index)
5350 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5351 DirRef new_ref;
5352 TransactedDirEntry *new_entry;
5354 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
5355 if (new_ref == DIRENTRY_NULL)
5356 return E_OUTOFMEMORY;
5358 new_entry = &This->entries[new_ref];
5360 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
5361 new_entry->read = TRUE;
5362 new_entry->dirty = TRUE;
5363 memcpy(&new_entry->data, newData, sizeof(DirEntry));
5365 *index = new_ref;
5367 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
5369 return S_OK;
5372 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
5373 DirRef index, const DirEntry *data)
5375 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5376 HRESULT hr;
5378 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
5380 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5381 if (FAILED(hr)) return hr;
5383 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
5385 if (index != This->base.storageDirEntry)
5387 This->entries[index].dirty = TRUE;
5389 if (data->size.QuadPart == 0 &&
5390 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
5392 /* Since this entry is modified, and we aren't using its stream data, we
5393 * no longer care about the original entry. */
5394 DirRef delete_ref;
5395 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
5397 if (delete_ref != DIRENTRY_NULL)
5398 This->entries[delete_ref].deleted = TRUE;
5400 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5404 return S_OK;
5407 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
5408 DirRef index, DirEntry *data)
5410 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5411 HRESULT hr;
5413 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5414 if (FAILED(hr)) return hr;
5416 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
5418 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
5420 return S_OK;
5423 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
5424 DirRef index)
5426 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5428 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
5429 This->entries[index].data.size.QuadPart != 0)
5431 /* If we deleted this entry while it has stream data. We must have left the
5432 * data because some other entry is using it, and we need to leave the
5433 * original entry alone. */
5434 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
5435 This->firstFreeEntry = min(index, This->firstFreeEntry);
5437 else
5439 This->entries[index].deleted = TRUE;
5442 return S_OK;
5445 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
5446 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5448 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5450 if (This->entries[index].stream_dirty)
5452 return StorageBaseImpl_StreamReadAt(This->scratch,
5453 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
5455 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
5457 /* This stream doesn't live in the parent, and we haven't allocated storage
5458 * for it yet */
5459 *bytesRead = 0;
5460 return S_OK;
5462 else
5464 return StorageBaseImpl_StreamReadAt(This->transactedParent,
5465 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
5469 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
5470 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5472 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5473 HRESULT hr;
5475 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5476 if (FAILED(hr)) return hr;
5478 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5479 if (FAILED(hr)) return hr;
5481 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
5482 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
5484 if (SUCCEEDED(hr) && size != 0)
5485 This->entries[index].data.size.QuadPart = max(
5486 This->entries[index].data.size.QuadPart,
5487 offset.QuadPart + size);
5489 return hr;
5492 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
5493 DirRef index, ULARGE_INTEGER newsize)
5495 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5496 HRESULT hr;
5498 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5499 if (FAILED(hr)) return hr;
5501 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
5502 return S_OK;
5504 if (newsize.QuadPart == 0)
5506 /* Destroy any parent references or entries in the scratch file. */
5507 if (This->entries[index].stream_dirty)
5509 ULARGE_INTEGER zero;
5510 zero.QuadPart = 0;
5511 StorageBaseImpl_StreamSetSize(This->scratch,
5512 This->entries[index].stream_entry, zero);
5513 StorageBaseImpl_DestroyDirEntry(This->scratch,
5514 This->entries[index].stream_entry);
5515 This->entries[index].stream_dirty = FALSE;
5517 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
5519 DirRef delete_ref;
5520 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
5522 if (delete_ref != DIRENTRY_NULL)
5523 This->entries[delete_ref].deleted = TRUE;
5525 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5528 else
5530 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5531 if (FAILED(hr)) return hr;
5533 hr = StorageBaseImpl_StreamSetSize(This->scratch,
5534 This->entries[index].stream_entry, newsize);
5537 if (SUCCEEDED(hr))
5538 This->entries[index].data.size = newsize;
5540 return hr;
5543 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
5544 DirRef dst, DirRef src)
5546 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5547 HRESULT hr;
5548 TransactedDirEntry *dst_entry, *src_entry;
5550 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
5551 if (FAILED(hr)) return hr;
5553 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
5554 if (FAILED(hr)) return hr;
5556 dst_entry = &This->entries[dst];
5557 src_entry = &This->entries[src];
5559 dst_entry->stream_dirty = src_entry->stream_dirty;
5560 dst_entry->stream_entry = src_entry->stream_entry;
5561 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
5562 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
5563 dst_entry->data.size = src_entry->data.size;
5565 return S_OK;
5568 static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
5569 ULONG* result, BOOL refresh)
5571 return E_NOTIMPL;
5574 static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
5575 ULONG value)
5577 return E_NOTIMPL;
5580 static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
5582 return E_NOTIMPL;
5585 static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
5587 return E_NOTIMPL;
5590 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5592 StorageBaseImpl_QueryInterface,
5593 StorageBaseImpl_AddRef,
5594 StorageBaseImpl_Release,
5595 StorageBaseImpl_CreateStream,
5596 StorageBaseImpl_OpenStream,
5597 StorageBaseImpl_CreateStorage,
5598 StorageBaseImpl_OpenStorage,
5599 StorageBaseImpl_CopyTo,
5600 StorageBaseImpl_MoveElementTo,
5601 TransactedSnapshotImpl_Commit,
5602 TransactedSnapshotImpl_Revert,
5603 StorageBaseImpl_EnumElements,
5604 StorageBaseImpl_DestroyElement,
5605 StorageBaseImpl_RenameElement,
5606 StorageBaseImpl_SetElementTimes,
5607 StorageBaseImpl_SetClass,
5608 StorageBaseImpl_SetStateBits,
5609 StorageBaseImpl_Stat
5612 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5614 TransactedSnapshotImpl_Destroy,
5615 TransactedSnapshotImpl_Invalidate,
5616 TransactedSnapshotImpl_Flush,
5617 TransactedSnapshotImpl_GetFilename,
5618 TransactedSnapshotImpl_CreateDirEntry,
5619 TransactedSnapshotImpl_WriteDirEntry,
5620 TransactedSnapshotImpl_ReadDirEntry,
5621 TransactedSnapshotImpl_DestroyDirEntry,
5622 TransactedSnapshotImpl_StreamReadAt,
5623 TransactedSnapshotImpl_StreamWriteAt,
5624 TransactedSnapshotImpl_StreamSetSize,
5625 TransactedSnapshotImpl_StreamLink,
5626 TransactedSnapshotImpl_GetTransactionSig,
5627 TransactedSnapshotImpl_SetTransactionSig,
5628 TransactedSnapshotImpl_LockTransaction,
5629 TransactedSnapshotImpl_UnlockTransaction
5632 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5633 TransactedSnapshotImpl** result)
5635 HRESULT hr;
5637 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5638 if (*result)
5640 IStorage *scratch;
5642 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5644 /* This is OK because the property set storage functions use the IStorage functions. */
5645 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
5646 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5648 list_init(&(*result)->base.strmHead);
5650 list_init(&(*result)->base.storageHead);
5652 (*result)->base.ref = 1;
5654 (*result)->base.openFlags = parentStorage->openFlags;
5656 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5657 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
5659 /* Create a new temporary storage to act as the scratch file. */
5660 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
5661 0, &scratch);
5662 (*result)->scratch = impl_from_IStorage(scratch);
5664 if (SUCCEEDED(hr))
5666 ULONG num_entries = 20;
5668 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5669 (*result)->entries_size = num_entries;
5670 (*result)->firstFreeEntry = 0;
5672 if ((*result)->entries)
5674 /* parentStorage already has 1 reference, which we take over here. */
5675 (*result)->transactedParent = parentStorage;
5677 parentStorage->transactedChild = &(*result)->base;
5679 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5681 else
5683 IStorage_Release(scratch);
5685 hr = E_OUTOFMEMORY;
5689 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
5691 return hr;
5693 else
5694 return E_OUTOFMEMORY;
5697 static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
5699 if (!This->reverted)
5701 TRACE("Storage invalidated (stg=%p)\n", This);
5703 This->reverted = TRUE;
5705 StorageBaseImpl_DeleteAll(This);
5709 static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
5711 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
5713 TransactedSharedImpl_Invalidate(&This->base);
5714 IStorage_Release(&This->transactedParent->IStorage_iface);
5715 IStorage_Release(&This->scratch->base.IStorage_iface);
5716 HeapFree(GetProcessHeap(), 0, This);
5719 static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface)
5721 /* We only need to flush when committing. */
5722 return S_OK;
5725 static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5727 TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
5729 return StorageBaseImpl_GetFilename(This->transactedParent, result);
5732 static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
5733 const DirEntry *newData, DirRef *index)
5735 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5737 return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
5738 newData, index);
5741 static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
5742 DirRef index, const DirEntry *data)
5744 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5746 return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
5747 index, data);
5750 static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
5751 DirRef index, DirEntry *data)
5753 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5755 return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
5756 index, data);
5759 static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
5760 DirRef index)
5762 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5764 return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
5765 index);
5768 static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
5769 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5771 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5773 return StorageBaseImpl_StreamReadAt(&This->scratch->base,
5774 index, offset, size, buffer, bytesRead);
5777 static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
5778 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5780 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5782 return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
5783 index, offset, size, buffer, bytesWritten);
5786 static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
5787 DirRef index, ULARGE_INTEGER newsize)
5789 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5791 return StorageBaseImpl_StreamSetSize(&This->scratch->base,
5792 index, newsize);
5795 static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
5796 DirRef dst, DirRef src)
5798 TransactedSharedImpl* This = (TransactedSharedImpl*) base;
5800 return StorageBaseImpl_StreamLink(&This->scratch->base,
5801 dst, src);
5804 static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
5805 ULONG* result, BOOL refresh)
5807 return E_NOTIMPL;
5810 static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
5811 ULONG value)
5813 return E_NOTIMPL;
5816 static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
5818 return E_NOTIMPL;
5821 static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
5823 return E_NOTIMPL;
5826 static HRESULT WINAPI TransactedSharedImpl_Commit(
5827 IStorage* iface,
5828 DWORD grfCommitFlags) /* [in] */
5830 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
5831 DirRef new_storage_ref, prev_storage_ref;
5832 DirEntry src_data, dst_data;
5833 HRESULT hr;
5834 ULONG transactionSig;
5836 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5838 /* Cannot commit a read-only transacted storage */
5839 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
5840 return STG_E_ACCESSDENIED;
5842 hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
5843 if (hr == E_NOTIMPL) hr = S_OK;
5844 if (SUCCEEDED(hr))
5846 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
5847 if (SUCCEEDED(hr))
5849 if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
5850 hr = STG_E_NOTCURRENT;
5852 if (SUCCEEDED(hr))
5853 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
5855 else if (hr == E_NOTIMPL)
5856 hr = S_OK;
5858 if (SUCCEEDED(hr))
5859 hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data);
5861 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
5862 if (SUCCEEDED(hr))
5863 hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry);
5865 if (SUCCEEDED(hr))
5866 hr = StorageBaseImpl_Flush(This->transactedParent);
5868 if (SUCCEEDED(hr))
5869 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
5871 if (SUCCEEDED(hr))
5873 prev_storage_ref = dst_data.dirRootEntry;
5874 dst_data.dirRootEntry = new_storage_ref;
5875 dst_data.clsid = src_data.clsid;
5876 dst_data.ctime = src_data.ctime;
5877 dst_data.mtime = src_data.mtime;
5878 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
5881 if (SUCCEEDED(hr))
5883 /* Try to flush after updating the root storage, but if the flush fails, keep
5884 * going, on the theory that it'll either succeed later or the subsequent
5885 * writes will fail. */
5886 StorageBaseImpl_Flush(This->transactedParent);
5888 hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
5891 if (SUCCEEDED(hr))
5892 hr = StorageBaseImpl_Flush(This->transactedParent);
5894 StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
5896 if (SUCCEEDED(hr))
5897 hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT);
5899 if (SUCCEEDED(hr))
5901 This->lastTransactionSig = transactionSig+1;
5905 return hr;
5908 static HRESULT WINAPI TransactedSharedImpl_Revert(
5909 IStorage* iface)
5911 TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
5913 TRACE("(%p)\n", iface);
5915 /* Destroy the open objects. */
5916 StorageBaseImpl_DeleteAll(&This->base);
5918 return IStorage_Revert(&This->scratch->base.IStorage_iface);
5921 static const IStorageVtbl TransactedSharedImpl_Vtbl =
5923 StorageBaseImpl_QueryInterface,
5924 StorageBaseImpl_AddRef,
5925 StorageBaseImpl_Release,
5926 StorageBaseImpl_CreateStream,
5927 StorageBaseImpl_OpenStream,
5928 StorageBaseImpl_CreateStorage,
5929 StorageBaseImpl_OpenStorage,
5930 StorageBaseImpl_CopyTo,
5931 StorageBaseImpl_MoveElementTo,
5932 TransactedSharedImpl_Commit,
5933 TransactedSharedImpl_Revert,
5934 StorageBaseImpl_EnumElements,
5935 StorageBaseImpl_DestroyElement,
5936 StorageBaseImpl_RenameElement,
5937 StorageBaseImpl_SetElementTimes,
5938 StorageBaseImpl_SetClass,
5939 StorageBaseImpl_SetStateBits,
5940 StorageBaseImpl_Stat
5943 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl =
5945 TransactedSharedImpl_Destroy,
5946 TransactedSharedImpl_Invalidate,
5947 TransactedSharedImpl_Flush,
5948 TransactedSharedImpl_GetFilename,
5949 TransactedSharedImpl_CreateDirEntry,
5950 TransactedSharedImpl_WriteDirEntry,
5951 TransactedSharedImpl_ReadDirEntry,
5952 TransactedSharedImpl_DestroyDirEntry,
5953 TransactedSharedImpl_StreamReadAt,
5954 TransactedSharedImpl_StreamWriteAt,
5955 TransactedSharedImpl_StreamSetSize,
5956 TransactedSharedImpl_StreamLink,
5957 TransactedSharedImpl_GetTransactionSig,
5958 TransactedSharedImpl_SetTransactionSig,
5959 TransactedSharedImpl_LockTransaction,
5960 TransactedSharedImpl_UnlockTransaction
5963 static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
5964 TransactedSharedImpl** result)
5966 HRESULT hr;
5968 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
5969 if (*result)
5971 IStorage *scratch;
5973 (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
5975 /* This is OK because the property set storage functions use the IStorage functions. */
5976 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
5977 (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
5979 list_init(&(*result)->base.strmHead);
5981 list_init(&(*result)->base.storageHead);
5983 (*result)->base.ref = 1;
5985 (*result)->base.openFlags = parentStorage->openFlags;
5987 hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
5989 if (SUCCEEDED(hr))
5991 STGOPTIONS stgo;
5993 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5994 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
5996 stgo.usVersion = 1;
5997 stgo.reserved = 0;
5998 stgo.ulSectorSize = 4096;
5999 stgo.pwcsTemplateFile = NULL;
6001 /* Create a new temporary storage to act as the scratch file. */
6002 hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED,
6003 STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch);
6004 (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch);
6006 if (SUCCEEDED(hr))
6008 hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
6009 parentStorage, parentStorage->storageDirEntry);
6011 if (SUCCEEDED(hr))
6013 hr = IStorage_Commit(scratch, STGC_DEFAULT);
6015 (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry;
6016 (*result)->transactedParent = parentStorage;
6019 if (FAILED(hr))
6020 IStorage_Release(scratch);
6023 StorageBaseImpl_UnlockTransaction(parentStorage, FALSE);
6026 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
6028 return hr;
6030 else
6031 return E_OUTOFMEMORY;
6034 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
6035 BOOL toplevel, StorageBaseImpl** result)
6037 static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;
6039 if (parentStorage->openFlags & fixme_flags)
6041 fixme_flags &= ~parentStorage->openFlags;
6042 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
6045 if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) &&
6046 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE &&
6047 STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE)
6049 /* Need to create a temp file for the snapshot */
6050 return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result);
6053 return TransactedSnapshotImpl_Construct(parentStorage,
6054 (TransactedSnapshotImpl**)result);
6057 static HRESULT Storage_Construct(
6058 HANDLE hFile,
6059 LPCOLESTR pwcsName,
6060 ILockBytes* pLkbyt,
6061 DWORD openFlags,
6062 BOOL fileBased,
6063 BOOL create,
6064 ULONG sector_size,
6065 StorageBaseImpl** result)
6067 StorageImpl *newStorage;
6068 StorageBaseImpl *newTransactedStorage;
6069 HRESULT hr;
6071 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
6072 if (FAILED(hr)) goto end;
6074 if (openFlags & STGM_TRANSACTED)
6076 hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
6077 if (FAILED(hr))
6078 IStorage_Release(&newStorage->base.IStorage_iface);
6079 else
6080 *result = newTransactedStorage;
6082 else
6083 *result = &newStorage->base;
6085 end:
6086 return hr;
6089 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
6091 StorageInternalImpl* This = (StorageInternalImpl*) base;
6093 if (!This->base.reverted)
6095 TRACE("Storage invalidated (stg=%p)\n", This);
6097 This->base.reverted = TRUE;
6099 This->parentStorage = NULL;
6101 StorageBaseImpl_DeleteAll(&This->base);
6103 list_remove(&This->ParentListEntry);
6107 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
6109 StorageInternalImpl* This = (StorageInternalImpl*) iface;
6111 StorageInternalImpl_Invalidate(&This->base);
6113 HeapFree(GetProcessHeap(), 0, This);
6116 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
6118 StorageInternalImpl* This = (StorageInternalImpl*) iface;
6120 return StorageBaseImpl_Flush(This->parentStorage);
6123 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
6125 StorageInternalImpl* This = (StorageInternalImpl*) iface;
6127 return StorageBaseImpl_GetFilename(This->parentStorage, result);
6130 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
6131 const DirEntry *newData, DirRef *index)
6133 StorageInternalImpl* This = (StorageInternalImpl*) base;
6135 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
6136 newData, index);
6139 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
6140 DirRef index, const DirEntry *data)
6142 StorageInternalImpl* This = (StorageInternalImpl*) base;
6144 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
6145 index, data);
6148 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
6149 DirRef index, DirEntry *data)
6151 StorageInternalImpl* This = (StorageInternalImpl*) base;
6153 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
6154 index, data);
6157 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
6158 DirRef index)
6160 StorageInternalImpl* This = (StorageInternalImpl*) base;
6162 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
6163 index);
6166 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
6167 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
6169 StorageInternalImpl* This = (StorageInternalImpl*) base;
6171 return StorageBaseImpl_StreamReadAt(This->parentStorage,
6172 index, offset, size, buffer, bytesRead);
6175 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
6176 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
6178 StorageInternalImpl* This = (StorageInternalImpl*) base;
6180 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
6181 index, offset, size, buffer, bytesWritten);
6184 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
6185 DirRef index, ULARGE_INTEGER newsize)
6187 StorageInternalImpl* This = (StorageInternalImpl*) base;
6189 return StorageBaseImpl_StreamSetSize(This->parentStorage,
6190 index, newsize);
6193 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
6194 DirRef dst, DirRef src)
6196 StorageInternalImpl* This = (StorageInternalImpl*) base;
6198 return StorageBaseImpl_StreamLink(This->parentStorage,
6199 dst, src);
6202 static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
6203 ULONG* result, BOOL refresh)
6205 return E_NOTIMPL;
6208 static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
6209 ULONG value)
6211 return E_NOTIMPL;
6214 static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
6216 return E_NOTIMPL;
6219 static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
6221 return E_NOTIMPL;
6224 /******************************************************************************
6226 ** Storage32InternalImpl_Commit
6229 static HRESULT WINAPI StorageInternalImpl_Commit(
6230 IStorage* iface,
6231 DWORD grfCommitFlags) /* [in] */
6233 StorageBaseImpl* This = impl_from_IStorage(iface);
6234 TRACE("(%p,%x)\n", iface, grfCommitFlags);
6235 return StorageBaseImpl_Flush(This);
6238 /******************************************************************************
6240 ** Storage32InternalImpl_Revert
6243 static HRESULT WINAPI StorageInternalImpl_Revert(
6244 IStorage* iface)
6246 FIXME("(%p): stub\n", iface);
6247 return S_OK;
6250 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
6252 IStorage_Release(&This->parentStorage->IStorage_iface);
6253 HeapFree(GetProcessHeap(), 0, This);
6256 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
6257 IEnumSTATSTG* iface,
6258 REFIID riid,
6259 void** ppvObject)
6261 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6263 if (ppvObject==0)
6264 return E_INVALIDARG;
6266 *ppvObject = 0;
6268 if (IsEqualGUID(&IID_IUnknown, riid) ||
6269 IsEqualGUID(&IID_IEnumSTATSTG, riid))
6271 *ppvObject = This;
6272 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
6273 return S_OK;
6276 return E_NOINTERFACE;
6279 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
6280 IEnumSTATSTG* iface)
6282 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6283 return InterlockedIncrement(&This->ref);
6286 static ULONG WINAPI IEnumSTATSTGImpl_Release(
6287 IEnumSTATSTG* iface)
6289 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6291 ULONG newRef;
6293 newRef = InterlockedDecrement(&This->ref);
6295 if (newRef==0)
6297 IEnumSTATSTGImpl_Destroy(This);
6300 return newRef;
6303 static HRESULT IEnumSTATSTGImpl_GetNextRef(
6304 IEnumSTATSTGImpl* This,
6305 DirRef *ref)
6307 DirRef result = DIRENTRY_NULL;
6308 DirRef searchNode;
6309 DirEntry entry;
6310 HRESULT hr;
6311 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
6313 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
6314 This->parentStorage->storageDirEntry, &entry);
6315 searchNode = entry.dirRootEntry;
6317 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
6319 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
6321 if (SUCCEEDED(hr))
6323 LONG diff = entryNameCmp( entry.name, This->name);
6325 if (diff <= 0)
6327 searchNode = entry.rightChild;
6329 else
6331 result = searchNode;
6332 memcpy(result_name, entry.name, sizeof(result_name));
6333 searchNode = entry.leftChild;
6338 if (SUCCEEDED(hr))
6340 *ref = result;
6341 if (result != DIRENTRY_NULL)
6342 memcpy(This->name, result_name, sizeof(result_name));
6345 return hr;
6348 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
6349 IEnumSTATSTG* iface,
6350 ULONG celt,
6351 STATSTG* rgelt,
6352 ULONG* pceltFetched)
6354 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6356 DirEntry currentEntry;
6357 STATSTG* currentReturnStruct = rgelt;
6358 ULONG objectFetched = 0;
6359 DirRef currentSearchNode;
6360 HRESULT hr=S_OK;
6362 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
6363 return E_INVALIDARG;
6365 if (This->parentStorage->reverted)
6366 return STG_E_REVERTED;
6369 * To avoid the special case, get another pointer to a ULONG value if
6370 * the caller didn't supply one.
6372 if (pceltFetched==0)
6373 pceltFetched = &objectFetched;
6376 * Start the iteration, we will iterate until we hit the end of the
6377 * linked list or until we hit the number of items to iterate through
6379 *pceltFetched = 0;
6381 while ( *pceltFetched < celt )
6383 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
6385 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
6386 break;
6389 * Read the entry from the storage.
6391 StorageBaseImpl_ReadDirEntry(This->parentStorage,
6392 currentSearchNode,
6393 &currentEntry);
6396 * Copy the information to the return buffer.
6398 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
6399 currentReturnStruct,
6400 &currentEntry,
6401 STATFLAG_DEFAULT);
6404 * Step to the next item in the iteration
6406 (*pceltFetched)++;
6407 currentReturnStruct++;
6410 if (SUCCEEDED(hr) && *pceltFetched != celt)
6411 hr = S_FALSE;
6413 return hr;
6417 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
6418 IEnumSTATSTG* iface,
6419 ULONG celt)
6421 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6423 ULONG objectFetched = 0;
6424 DirRef currentSearchNode;
6425 HRESULT hr=S_OK;
6427 if (This->parentStorage->reverted)
6428 return STG_E_REVERTED;
6430 while ( (objectFetched < celt) )
6432 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
6434 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
6435 break;
6437 objectFetched++;
6440 if (SUCCEEDED(hr) && objectFetched != celt)
6441 return S_FALSE;
6443 return hr;
6446 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
6447 IEnumSTATSTG* iface)
6449 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6451 if (This->parentStorage->reverted)
6452 return STG_E_REVERTED;
6454 This->name[0] = 0;
6456 return S_OK;
6459 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
6460 IEnumSTATSTG* iface,
6461 IEnumSTATSTG** ppenum)
6463 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
6464 IEnumSTATSTGImpl* newClone;
6466 if (This->parentStorage->reverted)
6467 return STG_E_REVERTED;
6469 if (ppenum==0)
6470 return E_INVALIDARG;
6472 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
6473 This->storageDirEntry);
6474 if (!newClone)
6476 *ppenum = NULL;
6477 return E_OUTOFMEMORY;
6481 * The new clone enumeration must point to the same current node as
6482 * the old one.
6484 memcpy(newClone->name, This->name, sizeof(newClone->name));
6486 *ppenum = &newClone->IEnumSTATSTG_iface;
6488 return S_OK;
6492 * Virtual function table for the IEnumSTATSTGImpl class.
6494 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
6496 IEnumSTATSTGImpl_QueryInterface,
6497 IEnumSTATSTGImpl_AddRef,
6498 IEnumSTATSTGImpl_Release,
6499 IEnumSTATSTGImpl_Next,
6500 IEnumSTATSTGImpl_Skip,
6501 IEnumSTATSTGImpl_Reset,
6502 IEnumSTATSTGImpl_Clone
6505 /******************************************************************************
6506 ** IEnumSTATSTGImpl implementation
6509 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
6510 StorageBaseImpl* parentStorage,
6511 DirRef storageDirEntry)
6513 IEnumSTATSTGImpl* newEnumeration;
6515 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
6517 if (newEnumeration)
6519 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
6520 newEnumeration->ref = 1;
6521 newEnumeration->name[0] = 0;
6524 * We want to nail-down the reference to the storage in case the
6525 * enumeration out-lives the storage in the client application.
6527 newEnumeration->parentStorage = parentStorage;
6528 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
6530 newEnumeration->storageDirEntry = storageDirEntry;
6533 return newEnumeration;
6537 * Virtual function table for the Storage32InternalImpl class.
6539 static const IStorageVtbl Storage32InternalImpl_Vtbl =
6541 StorageBaseImpl_QueryInterface,
6542 StorageBaseImpl_AddRef,
6543 StorageBaseImpl_Release,
6544 StorageBaseImpl_CreateStream,
6545 StorageBaseImpl_OpenStream,
6546 StorageBaseImpl_CreateStorage,
6547 StorageBaseImpl_OpenStorage,
6548 StorageBaseImpl_CopyTo,
6549 StorageBaseImpl_MoveElementTo,
6550 StorageInternalImpl_Commit,
6551 StorageInternalImpl_Revert,
6552 StorageBaseImpl_EnumElements,
6553 StorageBaseImpl_DestroyElement,
6554 StorageBaseImpl_RenameElement,
6555 StorageBaseImpl_SetElementTimes,
6556 StorageBaseImpl_SetClass,
6557 StorageBaseImpl_SetStateBits,
6558 StorageBaseImpl_Stat
6561 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
6563 StorageInternalImpl_Destroy,
6564 StorageInternalImpl_Invalidate,
6565 StorageInternalImpl_Flush,
6566 StorageInternalImpl_GetFilename,
6567 StorageInternalImpl_CreateDirEntry,
6568 StorageInternalImpl_WriteDirEntry,
6569 StorageInternalImpl_ReadDirEntry,
6570 StorageInternalImpl_DestroyDirEntry,
6571 StorageInternalImpl_StreamReadAt,
6572 StorageInternalImpl_StreamWriteAt,
6573 StorageInternalImpl_StreamSetSize,
6574 StorageInternalImpl_StreamLink,
6575 StorageInternalImpl_GetTransactionSig,
6576 StorageInternalImpl_SetTransactionSig,
6577 StorageInternalImpl_LockTransaction,
6578 StorageInternalImpl_UnlockTransaction
6581 /******************************************************************************
6582 ** Storage32InternalImpl implementation
6585 static StorageInternalImpl* StorageInternalImpl_Construct(
6586 StorageBaseImpl* parentStorage,
6587 DWORD openFlags,
6588 DirRef storageDirEntry)
6590 StorageInternalImpl* newStorage;
6592 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
6594 if (newStorage!=0)
6596 list_init(&newStorage->base.strmHead);
6598 list_init(&newStorage->base.storageHead);
6601 * Initialize the virtual function table.
6603 newStorage->base.IStorage_iface.lpVtbl = &Storage32InternalImpl_Vtbl;
6604 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
6605 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
6606 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
6608 newStorage->base.reverted = FALSE;
6610 newStorage->base.ref = 1;
6612 newStorage->parentStorage = parentStorage;
6615 * Keep a reference to the directory entry of this storage
6617 newStorage->base.storageDirEntry = storageDirEntry;
6619 newStorage->base.create = FALSE;
6621 return newStorage;
6624 return 0;
6627 /******************************************************************************
6628 ** StorageUtl implementation
6631 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
6633 WORD tmp;
6635 memcpy(&tmp, buffer+offset, sizeof(WORD));
6636 *value = lendian16toh(tmp);
6639 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
6641 value = htole16(value);
6642 memcpy(buffer+offset, &value, sizeof(WORD));
6645 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
6647 DWORD tmp;
6649 memcpy(&tmp, buffer+offset, sizeof(DWORD));
6650 *value = lendian32toh(tmp);
6653 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
6655 value = htole32(value);
6656 memcpy(buffer+offset, &value, sizeof(DWORD));
6659 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
6660 ULARGE_INTEGER* value)
6662 #ifdef WORDS_BIGENDIAN
6663 ULARGE_INTEGER tmp;
6665 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
6666 value->u.LowPart = htole32(tmp.u.HighPart);
6667 value->u.HighPart = htole32(tmp.u.LowPart);
6668 #else
6669 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
6670 #endif
6673 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
6674 const ULARGE_INTEGER *value)
6676 #ifdef WORDS_BIGENDIAN
6677 ULARGE_INTEGER tmp;
6679 tmp.u.LowPart = htole32(value->u.HighPart);
6680 tmp.u.HighPart = htole32(value->u.LowPart);
6681 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
6682 #else
6683 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
6684 #endif
6687 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
6689 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
6690 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
6691 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
6693 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
6696 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
6698 StorageUtl_WriteDWord(buffer, offset, value->Data1);
6699 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
6700 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
6702 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
6705 void StorageUtl_CopyDirEntryToSTATSTG(
6706 StorageBaseImpl* storage,
6707 STATSTG* destination,
6708 const DirEntry* source,
6709 int statFlags)
6712 * The copy of the string occurs only when the flag is not set
6714 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
6716 /* Use the filename for the root storage. */
6717 destination->pwcsName = 0;
6718 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
6720 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
6721 (source->name[0] == 0) )
6723 destination->pwcsName = 0;
6725 else
6727 destination->pwcsName =
6728 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
6730 strcpyW(destination->pwcsName, source->name);
6733 switch (source->stgType)
6735 case STGTY_STORAGE:
6736 case STGTY_ROOT:
6737 destination->type = STGTY_STORAGE;
6738 break;
6739 case STGTY_STREAM:
6740 destination->type = STGTY_STREAM;
6741 break;
6742 default:
6743 destination->type = STGTY_STREAM;
6744 break;
6747 destination->cbSize = source->size;
6749 currentReturnStruct->mtime = {0}; TODO
6750 currentReturnStruct->ctime = {0};
6751 currentReturnStruct->atime = {0};
6753 destination->grfMode = 0;
6754 destination->grfLocksSupported = 0;
6755 destination->clsid = source->clsid;
6756 destination->grfStateBits = 0;
6757 destination->reserved = 0;
6760 /******************************************************************************
6761 ** BlockChainStream implementation
6764 /* Read and save the index of all blocks in this stream. */
6765 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
6767 ULONG next_sector, next_offset;
6768 HRESULT hr;
6769 struct BlockChainRun *last_run;
6771 if (This->indexCacheLen == 0)
6773 last_run = NULL;
6774 next_offset = 0;
6775 next_sector = BlockChainStream_GetHeadOfChain(This);
6777 else
6779 last_run = &This->indexCache[This->indexCacheLen-1];
6780 next_offset = last_run->lastOffset+1;
6781 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
6782 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
6783 &next_sector);
6784 if (FAILED(hr)) return hr;
6787 while (next_sector != BLOCK_END_OF_CHAIN)
6789 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
6791 /* Add the current block to the cache. */
6792 if (This->indexCacheSize == 0)
6794 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
6795 if (!This->indexCache) return E_OUTOFMEMORY;
6796 This->indexCacheSize = 16;
6798 else if (This->indexCacheSize == This->indexCacheLen)
6800 struct BlockChainRun *new_cache;
6801 ULONG new_size;
6803 new_size = This->indexCacheSize * 2;
6804 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
6805 if (!new_cache) return E_OUTOFMEMORY;
6806 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
6808 HeapFree(GetProcessHeap(), 0, This->indexCache);
6809 This->indexCache = new_cache;
6810 This->indexCacheSize = new_size;
6813 This->indexCacheLen++;
6814 last_run = &This->indexCache[This->indexCacheLen-1];
6815 last_run->firstSector = next_sector;
6816 last_run->firstOffset = next_offset;
6819 last_run->lastOffset = next_offset;
6821 /* Find the next block. */
6822 next_offset++;
6823 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
6824 if (FAILED(hr)) return hr;
6827 if (This->indexCacheLen)
6829 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
6830 This->numBlocks = last_run->lastOffset+1;
6832 else
6834 This->tailIndex = BLOCK_END_OF_CHAIN;
6835 This->numBlocks = 0;
6838 return S_OK;
6841 /* Locate the nth block in this stream. */
6842 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
6844 ULONG min_offset = 0, max_offset = This->numBlocks-1;
6845 ULONG min_run = 0, max_run = This->indexCacheLen-1;
6847 if (offset >= This->numBlocks)
6848 return BLOCK_END_OF_CHAIN;
6850 while (min_run < max_run)
6852 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
6853 if (offset < This->indexCache[run_to_check].firstOffset)
6855 max_offset = This->indexCache[run_to_check].firstOffset-1;
6856 max_run = run_to_check-1;
6858 else if (offset > This->indexCache[run_to_check].lastOffset)
6860 min_offset = This->indexCache[run_to_check].lastOffset+1;
6861 min_run = run_to_check+1;
6863 else
6864 /* Block is in this run. */
6865 min_run = max_run = run_to_check;
6868 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
6871 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
6872 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
6874 BlockChainBlock *result=NULL;
6875 int i;
6877 for (i=0; i<2; i++)
6878 if (This->cachedBlocks[i].index == index)
6880 *sector = This->cachedBlocks[i].sector;
6881 *block = &This->cachedBlocks[i];
6882 return S_OK;
6885 *sector = BlockChainStream_GetSectorOfOffset(This, index);
6886 if (*sector == BLOCK_END_OF_CHAIN)
6887 return STG_E_DOCFILECORRUPT;
6889 if (create)
6891 if (This->cachedBlocks[0].index == 0xffffffff)
6892 result = &This->cachedBlocks[0];
6893 else if (This->cachedBlocks[1].index == 0xffffffff)
6894 result = &This->cachedBlocks[1];
6895 else
6897 result = &This->cachedBlocks[This->blockToEvict++];
6898 if (This->blockToEvict == 2)
6899 This->blockToEvict = 0;
6902 if (result->dirty)
6904 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
6905 return STG_E_WRITEFAULT;
6906 result->dirty = FALSE;
6909 result->read = FALSE;
6910 result->index = index;
6911 result->sector = *sector;
6914 *block = result;
6915 return S_OK;
6918 BlockChainStream* BlockChainStream_Construct(
6919 StorageImpl* parentStorage,
6920 ULONG* headOfStreamPlaceHolder,
6921 DirRef dirEntry)
6923 BlockChainStream* newStream;
6925 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
6927 newStream->parentStorage = parentStorage;
6928 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6929 newStream->ownerDirEntry = dirEntry;
6930 newStream->indexCache = NULL;
6931 newStream->indexCacheLen = 0;
6932 newStream->indexCacheSize = 0;
6933 newStream->cachedBlocks[0].index = 0xffffffff;
6934 newStream->cachedBlocks[0].dirty = FALSE;
6935 newStream->cachedBlocks[1].index = 0xffffffff;
6936 newStream->cachedBlocks[1].dirty = FALSE;
6937 newStream->blockToEvict = 0;
6939 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
6941 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
6942 HeapFree(GetProcessHeap(), 0, newStream);
6943 return NULL;
6946 return newStream;
6949 HRESULT BlockChainStream_Flush(BlockChainStream* This)
6951 int i;
6952 if (!This) return S_OK;
6953 for (i=0; i<2; i++)
6955 if (This->cachedBlocks[i].dirty)
6957 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6958 This->cachedBlocks[i].dirty = FALSE;
6959 else
6960 return STG_E_WRITEFAULT;
6963 return S_OK;
6966 void BlockChainStream_Destroy(BlockChainStream* This)
6968 if (This)
6970 BlockChainStream_Flush(This);
6971 HeapFree(GetProcessHeap(), 0, This->indexCache);
6973 HeapFree(GetProcessHeap(), 0, This);
6976 /******************************************************************************
6977 * BlockChainStream_GetHeadOfChain
6979 * Returns the head of this stream chain.
6980 * Some special chains don't have directory entries, their heads are kept in
6981 * This->headOfStreamPlaceHolder.
6984 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6986 DirEntry chainEntry;
6987 HRESULT hr;
6989 if (This->headOfStreamPlaceHolder != 0)
6990 return *(This->headOfStreamPlaceHolder);
6992 if (This->ownerDirEntry != DIRENTRY_NULL)
6994 hr = StorageImpl_ReadDirEntry(
6995 This->parentStorage,
6996 This->ownerDirEntry,
6997 &chainEntry);
6999 if (SUCCEEDED(hr))
7001 return chainEntry.startingBlock;
7005 return BLOCK_END_OF_CHAIN;
7008 /******************************************************************************
7009 * BlockChainStream_GetCount
7011 * Returns the number of blocks that comprises this chain.
7012 * This is not the size of the stream as the last block may not be full!
7014 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
7016 return This->numBlocks;
7019 /******************************************************************************
7020 * BlockChainStream_ReadAt
7022 * Reads a specified number of bytes from this chain at the specified offset.
7023 * bytesRead may be NULL.
7024 * Failure will be returned if the specified number of bytes has not been read.
7026 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
7027 ULARGE_INTEGER offset,
7028 ULONG size,
7029 void* buffer,
7030 ULONG* bytesRead)
7032 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7033 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7034 ULONG bytesToReadInBuffer;
7035 ULONG blockIndex;
7036 BYTE* bufferWalker;
7037 ULARGE_INTEGER stream_size;
7038 HRESULT hr;
7039 BlockChainBlock *cachedBlock;
7041 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
7044 * Find the first block in the stream that contains part of the buffer.
7046 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
7048 *bytesRead = 0;
7050 stream_size = BlockChainStream_GetSize(This);
7051 if (stream_size.QuadPart > offset.QuadPart)
7052 size = min(stream_size.QuadPart - offset.QuadPart, size);
7053 else
7054 return S_OK;
7057 * Start reading the buffer.
7059 bufferWalker = buffer;
7061 while (size > 0)
7063 ULARGE_INTEGER ulOffset;
7064 DWORD bytesReadAt;
7067 * Calculate how many bytes we can copy from this big block.
7069 bytesToReadInBuffer =
7070 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7072 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
7074 if (FAILED(hr))
7075 return hr;
7077 if (!cachedBlock)
7079 /* Not in cache, and we're going to read past the end of the block. */
7080 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7081 offsetInBlock;
7083 StorageImpl_ReadAt(This->parentStorage,
7084 ulOffset,
7085 bufferWalker,
7086 bytesToReadInBuffer,
7087 &bytesReadAt);
7089 else
7091 if (!cachedBlock->read)
7093 ULONG read;
7094 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7095 return STG_E_READFAULT;
7097 cachedBlock->read = TRUE;
7100 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
7101 bytesReadAt = bytesToReadInBuffer;
7104 blockNoInSequence++;
7105 bufferWalker += bytesReadAt;
7106 size -= bytesReadAt;
7107 *bytesRead += bytesReadAt;
7108 offsetInBlock = 0; /* There is no offset on the next block */
7110 if (bytesToReadInBuffer != bytesReadAt)
7111 break;
7114 return S_OK;
7117 /******************************************************************************
7118 * BlockChainStream_WriteAt
7120 * Writes the specified number of bytes to this chain at the specified offset.
7121 * Will fail if not all specified number of bytes have been written.
7123 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
7124 ULARGE_INTEGER offset,
7125 ULONG size,
7126 const void* buffer,
7127 ULONG* bytesWritten)
7129 ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
7130 ULONG offsetInBlock = offset.QuadPart % This->parentStorage->bigBlockSize;
7131 ULONG bytesToWrite;
7132 ULONG blockIndex;
7133 const BYTE* bufferWalker;
7134 HRESULT hr;
7135 BlockChainBlock *cachedBlock;
7137 *bytesWritten = 0;
7138 bufferWalker = buffer;
7140 while (size > 0)
7142 ULARGE_INTEGER ulOffset;
7143 DWORD bytesWrittenAt;
7146 * Calculate how many bytes we can copy to this big block.
7148 bytesToWrite =
7149 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7151 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
7153 /* BlockChainStream_SetSize should have already been called to ensure we have
7154 * enough blocks in the chain to write into */
7155 if (FAILED(hr))
7157 ERR("not enough blocks in chain to write data\n");
7158 return hr;
7161 if (!cachedBlock)
7163 /* Not in cache, and we're going to write past the end of the block. */
7164 ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7165 offsetInBlock;
7167 StorageImpl_WriteAt(This->parentStorage,
7168 ulOffset,
7169 bufferWalker,
7170 bytesToWrite,
7171 &bytesWrittenAt);
7173 else
7175 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
7177 ULONG read;
7178 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7179 return STG_E_READFAULT;
7182 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
7183 bytesWrittenAt = bytesToWrite;
7184 cachedBlock->read = TRUE;
7185 cachedBlock->dirty = TRUE;
7188 blockNoInSequence++;
7189 bufferWalker += bytesWrittenAt;
7190 size -= bytesWrittenAt;
7191 *bytesWritten += bytesWrittenAt;
7192 offsetInBlock = 0; /* There is no offset on the next block */
7194 if (bytesWrittenAt != bytesToWrite)
7195 break;
7198 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7201 /******************************************************************************
7202 * BlockChainStream_Shrink
7204 * Shrinks this chain in the big block depot.
7206 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
7207 ULARGE_INTEGER newSize)
7209 ULONG blockIndex;
7210 ULONG numBlocks;
7211 int i;
7214 * Figure out how many blocks are needed to contain the new size
7216 numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7218 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7219 numBlocks++;
7221 if (numBlocks)
7224 * Go to the new end of chain
7226 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
7228 /* Mark the new end of chain */
7229 StorageImpl_SetNextBlockInChain(
7230 This->parentStorage,
7231 blockIndex,
7232 BLOCK_END_OF_CHAIN);
7234 This->tailIndex = blockIndex;
7236 else
7238 if (This->headOfStreamPlaceHolder != 0)
7240 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
7242 else
7244 DirEntry chainEntry;
7245 assert(This->ownerDirEntry != DIRENTRY_NULL);
7247 StorageImpl_ReadDirEntry(
7248 This->parentStorage,
7249 This->ownerDirEntry,
7250 &chainEntry);
7252 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7254 StorageImpl_WriteDirEntry(
7255 This->parentStorage,
7256 This->ownerDirEntry,
7257 &chainEntry);
7260 This->tailIndex = BLOCK_END_OF_CHAIN;
7263 This->numBlocks = numBlocks;
7266 * Mark the extra blocks as free
7268 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
7270 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
7271 StorageImpl_FreeBigBlock(This->parentStorage,
7272 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
7273 if (last_run->lastOffset == last_run->firstOffset)
7274 This->indexCacheLen--;
7275 else
7276 last_run->lastOffset--;
7280 * Reset the last accessed block cache.
7282 for (i=0; i<2; i++)
7284 if (This->cachedBlocks[i].index >= numBlocks)
7286 This->cachedBlocks[i].index = 0xffffffff;
7287 This->cachedBlocks[i].dirty = FALSE;
7291 return TRUE;
7294 /******************************************************************************
7295 * BlockChainStream_Enlarge
7297 * Grows this chain in the big block depot.
7299 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
7300 ULARGE_INTEGER newSize)
7302 ULONG blockIndex, currentBlock;
7303 ULONG newNumBlocks;
7304 ULONG oldNumBlocks = 0;
7306 blockIndex = BlockChainStream_GetHeadOfChain(This);
7309 * Empty chain. Create the head.
7311 if (blockIndex == BLOCK_END_OF_CHAIN)
7313 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7314 StorageImpl_SetNextBlockInChain(This->parentStorage,
7315 blockIndex,
7316 BLOCK_END_OF_CHAIN);
7318 if (This->headOfStreamPlaceHolder != 0)
7320 *(This->headOfStreamPlaceHolder) = blockIndex;
7322 else
7324 DirEntry chainEntry;
7325 assert(This->ownerDirEntry != DIRENTRY_NULL);
7327 StorageImpl_ReadDirEntry(
7328 This->parentStorage,
7329 This->ownerDirEntry,
7330 &chainEntry);
7332 chainEntry.startingBlock = blockIndex;
7334 StorageImpl_WriteDirEntry(
7335 This->parentStorage,
7336 This->ownerDirEntry,
7337 &chainEntry);
7340 This->tailIndex = blockIndex;
7341 This->numBlocks = 1;
7345 * Figure out how many blocks are needed to contain this stream
7347 newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
7349 if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
7350 newNumBlocks++;
7353 * Go to the current end of chain
7355 if (This->tailIndex == BLOCK_END_OF_CHAIN)
7357 currentBlock = blockIndex;
7359 while (blockIndex != BLOCK_END_OF_CHAIN)
7361 This->numBlocks++;
7362 currentBlock = blockIndex;
7364 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
7365 &blockIndex)))
7366 return FALSE;
7369 This->tailIndex = currentBlock;
7372 currentBlock = This->tailIndex;
7373 oldNumBlocks = This->numBlocks;
7376 * Add new blocks to the chain
7378 if (oldNumBlocks < newNumBlocks)
7380 while (oldNumBlocks < newNumBlocks)
7382 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7384 StorageImpl_SetNextBlockInChain(
7385 This->parentStorage,
7386 currentBlock,
7387 blockIndex);
7389 StorageImpl_SetNextBlockInChain(
7390 This->parentStorage,
7391 blockIndex,
7392 BLOCK_END_OF_CHAIN);
7394 currentBlock = blockIndex;
7395 oldNumBlocks++;
7398 This->tailIndex = blockIndex;
7399 This->numBlocks = newNumBlocks;
7402 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
7403 return FALSE;
7405 return TRUE;
7408 /******************************************************************************
7409 * BlockChainStream_SetSize
7411 * Sets the size of this stream. The big block depot will be updated.
7412 * The file will grow if we grow the chain.
7414 * TODO: Free the actual blocks in the file when we shrink the chain.
7415 * Currently, the blocks are still in the file. So the file size
7416 * doesn't shrink even if we shrink streams.
7418 BOOL BlockChainStream_SetSize(
7419 BlockChainStream* This,
7420 ULARGE_INTEGER newSize)
7422 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
7424 if (newSize.QuadPart == size.QuadPart)
7425 return TRUE;
7427 if (newSize.QuadPart < size.QuadPart)
7429 BlockChainStream_Shrink(This, newSize);
7431 else
7433 BlockChainStream_Enlarge(This, newSize);
7436 return TRUE;
7439 /******************************************************************************
7440 * BlockChainStream_GetSize
7442 * Returns the size of this chain.
7443 * Will return the block count if this chain doesn't have a directory entry.
7445 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
7447 DirEntry chainEntry;
7449 if(This->headOfStreamPlaceHolder == NULL)
7452 * This chain has a directory entry so use the size value from there.
7454 StorageImpl_ReadDirEntry(
7455 This->parentStorage,
7456 This->ownerDirEntry,
7457 &chainEntry);
7459 return chainEntry.size;
7461 else
7464 * this chain is a chain that does not have a directory entry, figure out the
7465 * size by making the product number of used blocks times the
7466 * size of them
7468 ULARGE_INTEGER result;
7469 result.QuadPart =
7470 (ULONGLONG)BlockChainStream_GetCount(This) *
7471 This->parentStorage->bigBlockSize;
7473 return result;
7477 /******************************************************************************
7478 ** SmallBlockChainStream implementation
7481 SmallBlockChainStream* SmallBlockChainStream_Construct(
7482 StorageImpl* parentStorage,
7483 ULONG* headOfStreamPlaceHolder,
7484 DirRef dirEntry)
7486 SmallBlockChainStream* newStream;
7488 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
7490 newStream->parentStorage = parentStorage;
7491 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7492 newStream->ownerDirEntry = dirEntry;
7494 return newStream;
7497 void SmallBlockChainStream_Destroy(
7498 SmallBlockChainStream* This)
7500 HeapFree(GetProcessHeap(), 0, This);
7503 /******************************************************************************
7504 * SmallBlockChainStream_GetHeadOfChain
7506 * Returns the head of this chain of small blocks.
7508 static ULONG SmallBlockChainStream_GetHeadOfChain(
7509 SmallBlockChainStream* This)
7511 DirEntry chainEntry;
7512 HRESULT hr;
7514 if (This->headOfStreamPlaceHolder != NULL)
7515 return *(This->headOfStreamPlaceHolder);
7517 if (This->ownerDirEntry)
7519 hr = StorageImpl_ReadDirEntry(
7520 This->parentStorage,
7521 This->ownerDirEntry,
7522 &chainEntry);
7524 if (SUCCEEDED(hr))
7526 return chainEntry.startingBlock;
7531 return BLOCK_END_OF_CHAIN;
7534 /******************************************************************************
7535 * SmallBlockChainStream_GetNextBlockInChain
7537 * Returns the index of the next small block in this chain.
7539 * Return Values:
7540 * - BLOCK_END_OF_CHAIN: end of this chain
7541 * - BLOCK_UNUSED: small block 'blockIndex' is free
7543 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
7544 SmallBlockChainStream* This,
7545 ULONG blockIndex,
7546 ULONG* nextBlockInChain)
7548 ULARGE_INTEGER offsetOfBlockInDepot;
7549 DWORD buffer;
7550 ULONG bytesRead;
7551 HRESULT res;
7553 *nextBlockInChain = BLOCK_END_OF_CHAIN;
7555 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7558 * Read those bytes in the buffer from the small block file.
7560 res = BlockChainStream_ReadAt(
7561 This->parentStorage->smallBlockDepotChain,
7562 offsetOfBlockInDepot,
7563 sizeof(DWORD),
7564 &buffer,
7565 &bytesRead);
7567 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
7568 res = STG_E_READFAULT;
7570 if (SUCCEEDED(res))
7572 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
7573 return S_OK;
7576 return res;
7579 /******************************************************************************
7580 * SmallBlockChainStream_SetNextBlockInChain
7582 * Writes the index of the next block of the specified block in the small
7583 * block depot.
7584 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7585 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7587 static void SmallBlockChainStream_SetNextBlockInChain(
7588 SmallBlockChainStream* This,
7589 ULONG blockIndex,
7590 ULONG nextBlock)
7592 ULARGE_INTEGER offsetOfBlockInDepot;
7593 DWORD buffer;
7594 ULONG bytesWritten;
7596 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7598 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
7601 * Read those bytes in the buffer from the small block file.
7603 BlockChainStream_WriteAt(
7604 This->parentStorage->smallBlockDepotChain,
7605 offsetOfBlockInDepot,
7606 sizeof(DWORD),
7607 &buffer,
7608 &bytesWritten);
7611 /******************************************************************************
7612 * SmallBlockChainStream_FreeBlock
7614 * Flag small block 'blockIndex' as free in the small block depot.
7616 static void SmallBlockChainStream_FreeBlock(
7617 SmallBlockChainStream* This,
7618 ULONG blockIndex)
7620 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
7623 /******************************************************************************
7624 * SmallBlockChainStream_GetNextFreeBlock
7626 * Returns the index of a free small block. The small block depot will be
7627 * enlarged if necessary. The small block chain will also be enlarged if
7628 * necessary.
7630 static ULONG SmallBlockChainStream_GetNextFreeBlock(
7631 SmallBlockChainStream* This)
7633 ULARGE_INTEGER offsetOfBlockInDepot;
7634 DWORD buffer;
7635 ULONG bytesRead;
7636 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
7637 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
7638 HRESULT res = S_OK;
7639 ULONG smallBlocksPerBigBlock;
7640 DirEntry rootEntry;
7641 ULONG blocksRequired;
7642 ULARGE_INTEGER old_size, size_required;
7644 offsetOfBlockInDepot.u.HighPart = 0;
7647 * Scan the small block depot for a free block
7649 while (nextBlockIndex != BLOCK_UNUSED)
7651 offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
7653 res = BlockChainStream_ReadAt(
7654 This->parentStorage->smallBlockDepotChain,
7655 offsetOfBlockInDepot,
7656 sizeof(DWORD),
7657 &buffer,
7658 &bytesRead);
7661 * If we run out of space for the small block depot, enlarge it
7663 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
7665 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
7667 if (nextBlockIndex != BLOCK_UNUSED)
7668 blockIndex++;
7670 else
7672 ULONG count =
7673 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
7675 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
7676 ULARGE_INTEGER newSize, offset;
7677 ULONG bytesWritten;
7679 newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize;
7680 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
7683 * Initialize all the small blocks to free
7685 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
7686 offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize;
7687 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
7688 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
7690 StorageImpl_SaveFileHeader(This->parentStorage);
7694 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
7696 smallBlocksPerBigBlock =
7697 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
7700 * Verify if we have to allocate big blocks to contain small blocks
7702 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
7704 size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize;
7706 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
7708 if (size_required.QuadPart > old_size.QuadPart)
7710 BlockChainStream_SetSize(
7711 This->parentStorage->smallBlockRootChain,
7712 size_required);
7714 StorageImpl_ReadDirEntry(
7715 This->parentStorage,
7716 This->parentStorage->base.storageDirEntry,
7717 &rootEntry);
7719 rootEntry.size = size_required;
7721 StorageImpl_WriteDirEntry(
7722 This->parentStorage,
7723 This->parentStorage->base.storageDirEntry,
7724 &rootEntry);
7727 return blockIndex;
7730 /******************************************************************************
7731 * SmallBlockChainStream_ReadAt
7733 * Reads a specified number of bytes from this chain at the specified offset.
7734 * bytesRead may be NULL.
7735 * Failure will be returned if the specified number of bytes has not been read.
7737 HRESULT SmallBlockChainStream_ReadAt(
7738 SmallBlockChainStream* This,
7739 ULARGE_INTEGER offset,
7740 ULONG size,
7741 void* buffer,
7742 ULONG* bytesRead)
7744 HRESULT rc = S_OK;
7745 ULARGE_INTEGER offsetInBigBlockFile;
7746 ULONG blockNoInSequence =
7747 offset.u.LowPart / This->parentStorage->smallBlockSize;
7749 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7750 ULONG bytesToReadInBuffer;
7751 ULONG blockIndex;
7752 ULONG bytesReadFromBigBlockFile;
7753 BYTE* bufferWalker;
7754 ULARGE_INTEGER stream_size;
7757 * This should never happen on a small block file.
7759 assert(offset.u.HighPart==0);
7761 *bytesRead = 0;
7763 stream_size = SmallBlockChainStream_GetSize(This);
7764 if (stream_size.QuadPart > offset.QuadPart)
7765 size = min(stream_size.QuadPart - offset.QuadPart, size);
7766 else
7767 return S_OK;
7770 * Find the first block in the stream that contains part of the buffer.
7772 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7774 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7776 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7777 if(FAILED(rc))
7778 return rc;
7779 blockNoInSequence--;
7783 * Start reading the buffer.
7785 bufferWalker = buffer;
7787 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7790 * Calculate how many bytes we can copy from this small block.
7792 bytesToReadInBuffer =
7793 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7796 * Calculate the offset of the small block in the small block file.
7798 offsetInBigBlockFile.QuadPart =
7799 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
7801 offsetInBigBlockFile.QuadPart += offsetInBlock;
7804 * Read those bytes in the buffer from the small block file.
7805 * The small block has already been identified so it shouldn't fail
7806 * unless the file is corrupt.
7808 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
7809 offsetInBigBlockFile,
7810 bytesToReadInBuffer,
7811 bufferWalker,
7812 &bytesReadFromBigBlockFile);
7814 if (FAILED(rc))
7815 return rc;
7817 if (!bytesReadFromBigBlockFile)
7818 return STG_E_DOCFILECORRUPT;
7821 * Step to the next big block.
7823 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7824 if(FAILED(rc))
7825 return STG_E_DOCFILECORRUPT;
7827 bufferWalker += bytesReadFromBigBlockFile;
7828 size -= bytesReadFromBigBlockFile;
7829 *bytesRead += bytesReadFromBigBlockFile;
7830 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
7833 return S_OK;
7836 /******************************************************************************
7837 * SmallBlockChainStream_WriteAt
7839 * Writes the specified number of bytes to this chain at the specified offset.
7840 * Will fail if not all specified number of bytes have been written.
7842 HRESULT SmallBlockChainStream_WriteAt(
7843 SmallBlockChainStream* This,
7844 ULARGE_INTEGER offset,
7845 ULONG size,
7846 const void* buffer,
7847 ULONG* bytesWritten)
7849 ULARGE_INTEGER offsetInBigBlockFile;
7850 ULONG blockNoInSequence =
7851 offset.u.LowPart / This->parentStorage->smallBlockSize;
7853 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7854 ULONG bytesToWriteInBuffer;
7855 ULONG blockIndex;
7856 ULONG bytesWrittenToBigBlockFile;
7857 const BYTE* bufferWalker;
7858 HRESULT res;
7861 * This should never happen on a small block file.
7863 assert(offset.u.HighPart==0);
7866 * Find the first block in the stream that contains part of the buffer.
7868 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7870 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7872 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
7873 return STG_E_DOCFILECORRUPT;
7874 blockNoInSequence--;
7878 * Start writing the buffer.
7880 *bytesWritten = 0;
7881 bufferWalker = buffer;
7882 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7885 * Calculate how many bytes we can copy to this small block.
7887 bytesToWriteInBuffer =
7888 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7891 * Calculate the offset of the small block in the small block file.
7893 offsetInBigBlockFile.QuadPart =
7894 (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
7896 offsetInBigBlockFile.QuadPart += offsetInBlock;
7899 * Write those bytes in the buffer to the small block file.
7901 res = BlockChainStream_WriteAt(
7902 This->parentStorage->smallBlockRootChain,
7903 offsetInBigBlockFile,
7904 bytesToWriteInBuffer,
7905 bufferWalker,
7906 &bytesWrittenToBigBlockFile);
7907 if (FAILED(res))
7908 return res;
7911 * Step to the next big block.
7913 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7914 &blockIndex)))
7915 return FALSE;
7916 bufferWalker += bytesWrittenToBigBlockFile;
7917 size -= bytesWrittenToBigBlockFile;
7918 *bytesWritten += bytesWrittenToBigBlockFile;
7919 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7922 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7925 /******************************************************************************
7926 * SmallBlockChainStream_Shrink
7928 * Shrinks this chain in the small block depot.
7930 static BOOL SmallBlockChainStream_Shrink(
7931 SmallBlockChainStream* This,
7932 ULARGE_INTEGER newSize)
7934 ULONG blockIndex, extraBlock;
7935 ULONG numBlocks;
7936 ULONG count = 0;
7938 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7940 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7941 numBlocks++;
7943 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7946 * Go to the new end of chain
7948 while (count < numBlocks)
7950 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7951 &blockIndex)))
7952 return FALSE;
7953 count++;
7957 * If the count is 0, we have a special case, the head of the chain was
7958 * just freed.
7960 if (count == 0)
7962 DirEntry chainEntry;
7964 StorageImpl_ReadDirEntry(This->parentStorage,
7965 This->ownerDirEntry,
7966 &chainEntry);
7968 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7970 StorageImpl_WriteDirEntry(This->parentStorage,
7971 This->ownerDirEntry,
7972 &chainEntry);
7975 * We start freeing the chain at the head block.
7977 extraBlock = blockIndex;
7979 else
7981 /* Get the next block before marking the new end */
7982 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7983 &extraBlock)))
7984 return FALSE;
7986 /* Mark the new end of chain */
7987 SmallBlockChainStream_SetNextBlockInChain(
7988 This,
7989 blockIndex,
7990 BLOCK_END_OF_CHAIN);
7994 * Mark the extra blocks as free
7996 while (extraBlock != BLOCK_END_OF_CHAIN)
7998 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7999 &blockIndex)))
8000 return FALSE;
8001 SmallBlockChainStream_FreeBlock(This, extraBlock);
8002 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
8003 extraBlock = blockIndex;
8006 return TRUE;
8009 /******************************************************************************
8010 * SmallBlockChainStream_Enlarge
8012 * Grows this chain in the small block depot.
8014 static BOOL SmallBlockChainStream_Enlarge(
8015 SmallBlockChainStream* This,
8016 ULARGE_INTEGER newSize)
8018 ULONG blockIndex, currentBlock;
8019 ULONG newNumBlocks;
8020 ULONG oldNumBlocks = 0;
8022 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8025 * Empty chain. Create the head.
8027 if (blockIndex == BLOCK_END_OF_CHAIN)
8029 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8030 SmallBlockChainStream_SetNextBlockInChain(
8031 This,
8032 blockIndex,
8033 BLOCK_END_OF_CHAIN);
8035 if (This->headOfStreamPlaceHolder != NULL)
8037 *(This->headOfStreamPlaceHolder) = blockIndex;
8039 else
8041 DirEntry chainEntry;
8043 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
8044 &chainEntry);
8046 chainEntry.startingBlock = blockIndex;
8048 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
8049 &chainEntry);
8053 currentBlock = blockIndex;
8056 * Figure out how many blocks are needed to contain this stream
8058 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8060 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8061 newNumBlocks++;
8064 * Go to the current end of chain
8066 while (blockIndex != BLOCK_END_OF_CHAIN)
8068 oldNumBlocks++;
8069 currentBlock = blockIndex;
8070 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
8071 return FALSE;
8075 * Add new blocks to the chain
8077 while (oldNumBlocks < newNumBlocks)
8079 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8080 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
8082 SmallBlockChainStream_SetNextBlockInChain(
8083 This,
8084 blockIndex,
8085 BLOCK_END_OF_CHAIN);
8087 currentBlock = blockIndex;
8088 oldNumBlocks++;
8091 return TRUE;
8094 /******************************************************************************
8095 * SmallBlockChainStream_SetSize
8097 * Sets the size of this stream.
8098 * The file will grow if we grow the chain.
8100 * TODO: Free the actual blocks in the file when we shrink the chain.
8101 * Currently, the blocks are still in the file. So the file size
8102 * doesn't shrink even if we shrink streams.
8104 BOOL SmallBlockChainStream_SetSize(
8105 SmallBlockChainStream* This,
8106 ULARGE_INTEGER newSize)
8108 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
8110 if (newSize.u.LowPart == size.u.LowPart)
8111 return TRUE;
8113 if (newSize.u.LowPart < size.u.LowPart)
8115 SmallBlockChainStream_Shrink(This, newSize);
8117 else
8119 SmallBlockChainStream_Enlarge(This, newSize);
8122 return TRUE;
8125 /******************************************************************************
8126 * SmallBlockChainStream_GetCount
8128 * Returns the number of small blocks that comprises this chain.
8129 * This is not the size of the stream as the last block may not be full!
8132 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
8134 ULONG blockIndex;
8135 ULONG count = 0;
8137 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8139 while(blockIndex != BLOCK_END_OF_CHAIN)
8141 count++;
8143 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
8144 blockIndex, &blockIndex)))
8145 return 0;
8148 return count;
8151 /******************************************************************************
8152 * SmallBlockChainStream_GetSize
8154 * Returns the size of this chain.
8156 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
8158 DirEntry chainEntry;
8160 if(This->headOfStreamPlaceHolder != NULL)
8162 ULARGE_INTEGER result;
8163 result.u.HighPart = 0;
8165 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
8166 This->parentStorage->smallBlockSize;
8168 return result;
8171 StorageImpl_ReadDirEntry(
8172 This->parentStorage,
8173 This->ownerDirEntry,
8174 &chainEntry);
8176 return chainEntry.size;
8179 static HRESULT create_storagefile(
8180 LPCOLESTR pwcsName,
8181 DWORD grfMode,
8182 DWORD grfAttrs,
8183 STGOPTIONS* pStgOptions,
8184 REFIID riid,
8185 void** ppstgOpen)
8187 StorageBaseImpl* newStorage = 0;
8188 HANDLE hFile = INVALID_HANDLE_VALUE;
8189 HRESULT hr = STG_E_INVALIDFLAG;
8190 DWORD shareMode;
8191 DWORD accessMode;
8192 DWORD creationMode;
8193 DWORD fileAttributes;
8194 WCHAR tempFileName[MAX_PATH];
8196 if (ppstgOpen == 0)
8197 return STG_E_INVALIDPOINTER;
8199 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
8200 return STG_E_INVALIDPARAMETER;
8202 /* if no share mode given then DENY_NONE is the default */
8203 if (STGM_SHARE_MODE(grfMode) == 0)
8204 grfMode |= STGM_SHARE_DENY_NONE;
8206 if ( FAILED( validateSTGM(grfMode) ))
8207 goto end;
8209 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8210 switch(STGM_ACCESS_MODE(grfMode))
8212 case STGM_WRITE:
8213 case STGM_READWRITE:
8214 break;
8215 default:
8216 goto end;
8219 /* in direct mode, can only use SHARE_EXCLUSIVE */
8220 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
8221 goto end;
8223 /* but in transacted mode, any share mode is valid */
8226 * Generate a unique name.
8228 if (pwcsName == 0)
8230 WCHAR tempPath[MAX_PATH];
8231 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
8233 memset(tempPath, 0, sizeof(tempPath));
8234 memset(tempFileName, 0, sizeof(tempFileName));
8236 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
8237 tempPath[0] = '.';
8239 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
8240 pwcsName = tempFileName;
8241 else
8243 hr = STG_E_INSUFFICIENTMEMORY;
8244 goto end;
8247 creationMode = TRUNCATE_EXISTING;
8249 else
8251 creationMode = GetCreationModeFromSTGM(grfMode);
8255 * Interpret the STGM value grfMode
8257 shareMode = GetShareModeFromSTGM(grfMode);
8258 accessMode = GetAccessModeFromSTGM(grfMode);
8260 if (grfMode & STGM_DELETEONRELEASE)
8261 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
8262 else
8263 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
8265 *ppstgOpen = 0;
8267 hFile = CreateFileW(pwcsName,
8268 accessMode,
8269 shareMode,
8270 NULL,
8271 creationMode,
8272 fileAttributes,
8275 if (hFile == INVALID_HANDLE_VALUE)
8277 if(GetLastError() == ERROR_FILE_EXISTS)
8278 hr = STG_E_FILEALREADYEXISTS;
8279 else
8280 hr = E_FAIL;
8281 goto end;
8285 * Allocate and initialize the new IStorage32object.
8287 hr = Storage_Construct(
8288 hFile,
8289 pwcsName,
8290 NULL,
8291 grfMode,
8292 TRUE,
8293 TRUE,
8294 pStgOptions->ulSectorSize,
8295 &newStorage);
8297 if (FAILED(hr))
8299 goto end;
8302 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
8303 IStorage_Release(&newStorage->IStorage_iface);
8305 end:
8306 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
8308 return hr;
8311 /******************************************************************************
8312 * StgCreateDocfile [OLE32.@]
8313 * Creates a new compound file storage object
8315 * PARAMS
8316 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8317 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8318 * reserved [ ?] unused?, usually 0
8319 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8321 * RETURNS
8322 * S_OK if the file was successfully created
8323 * some STG_E_ value if error
8324 * NOTES
8325 * if pwcsName is NULL, create file with new unique name
8326 * the function can returns
8327 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8328 * (unrealized now)
8330 HRESULT WINAPI StgCreateDocfile(
8331 LPCOLESTR pwcsName,
8332 DWORD grfMode,
8333 DWORD reserved,
8334 IStorage **ppstgOpen)
8336 STGOPTIONS stgoptions = {1, 0, 512};
8338 TRACE("(%s, %x, %d, %p)\n",
8339 debugstr_w(pwcsName), grfMode,
8340 reserved, ppstgOpen);
8342 if (ppstgOpen == 0)
8343 return STG_E_INVALIDPOINTER;
8344 if (reserved != 0)
8345 return STG_E_INVALIDPARAMETER;
8347 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
8350 /******************************************************************************
8351 * StgCreateStorageEx [OLE32.@]
8353 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8355 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8356 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8358 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
8360 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8361 return STG_E_INVALIDPARAMETER;
8364 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
8366 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8367 return STG_E_INVALIDPARAMETER;
8370 if (stgfmt == STGFMT_FILE)
8372 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8373 return STG_E_INVALIDPARAMETER;
8376 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
8378 STGOPTIONS defaultOptions = {1, 0, 512};
8380 if (!pStgOptions) pStgOptions = &defaultOptions;
8381 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
8385 ERR("Invalid stgfmt argument\n");
8386 return STG_E_INVALIDPARAMETER;
8389 /******************************************************************************
8390 * StgCreatePropSetStg [OLE32.@]
8392 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
8393 IPropertySetStorage **propset)
8395 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
8396 if (reserved)
8397 return STG_E_INVALIDPARAMETER;
8399 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
8402 /******************************************************************************
8403 * StgOpenStorageEx [OLE32.@]
8405 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8407 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8408 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8410 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
8412 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8413 return STG_E_INVALIDPARAMETER;
8416 switch (stgfmt)
8418 case STGFMT_FILE:
8419 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8420 return STG_E_INVALIDPARAMETER;
8422 case STGFMT_STORAGE:
8423 break;
8425 case STGFMT_DOCFILE:
8426 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
8428 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8429 return STG_E_INVALIDPARAMETER;
8431 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8432 break;
8434 case STGFMT_ANY:
8435 WARN("STGFMT_ANY assuming storage\n");
8436 break;
8438 default:
8439 return STG_E_INVALIDPARAMETER;
8442 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
8446 /******************************************************************************
8447 * StgOpenStorage [OLE32.@]
8449 HRESULT WINAPI StgOpenStorage(
8450 const OLECHAR *pwcsName,
8451 IStorage *pstgPriority,
8452 DWORD grfMode,
8453 SNB snbExclude,
8454 DWORD reserved,
8455 IStorage **ppstgOpen)
8457 StorageBaseImpl* newStorage = 0;
8458 HRESULT hr = S_OK;
8459 HANDLE hFile = 0;
8460 DWORD shareMode;
8461 DWORD accessMode;
8462 LPWSTR temp_name = NULL;
8464 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8465 debugstr_w(pwcsName), pstgPriority, grfMode,
8466 snbExclude, reserved, ppstgOpen);
8468 if (pstgPriority)
8470 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8471 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
8472 if (FAILED(hr)) goto end;
8473 pwcsName = temp_name;
8474 TRACE("using filename %s\n", debugstr_w(temp_name));
8477 if (pwcsName == 0)
8479 hr = STG_E_INVALIDNAME;
8480 goto end;
8483 if (ppstgOpen == 0)
8485 hr = STG_E_INVALIDPOINTER;
8486 goto end;
8489 if (reserved)
8491 hr = STG_E_INVALIDPARAMETER;
8492 goto end;
8495 if (grfMode & STGM_PRIORITY)
8497 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
8498 return STG_E_INVALIDFLAG;
8499 if (grfMode & STGM_DELETEONRELEASE)
8500 return STG_E_INVALIDFUNCTION;
8501 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
8502 return STG_E_INVALIDFLAG;
8503 grfMode &= ~0xf0; /* remove the existing sharing mode */
8504 grfMode |= STGM_SHARE_DENY_NONE;
8508 * Validate the sharing mode
8510 if (grfMode & STGM_DIRECT_SWMR)
8512 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
8513 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
8515 hr = STG_E_INVALIDFLAG;
8516 goto end;
8519 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
8520 switch(STGM_SHARE_MODE(grfMode))
8522 case STGM_SHARE_EXCLUSIVE:
8523 case STGM_SHARE_DENY_WRITE:
8524 break;
8525 default:
8526 hr = STG_E_INVALIDFLAG;
8527 goto end;
8530 if ( FAILED( validateSTGM(grfMode) ) ||
8531 (grfMode&STGM_CREATE))
8533 hr = STG_E_INVALIDFLAG;
8534 goto end;
8537 /* shared reading requires transacted or single writer mode */
8538 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
8539 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
8540 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
8542 hr = STG_E_INVALIDFLAG;
8543 goto end;
8547 * Interpret the STGM value grfMode
8549 shareMode = GetShareModeFromSTGM(grfMode);
8550 accessMode = GetAccessModeFromSTGM(grfMode);
8552 *ppstgOpen = 0;
8554 hFile = CreateFileW( pwcsName,
8555 accessMode,
8556 shareMode,
8557 NULL,
8558 OPEN_EXISTING,
8559 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
8562 if (hFile==INVALID_HANDLE_VALUE)
8564 DWORD last_error = GetLastError();
8566 hr = E_FAIL;
8568 switch (last_error)
8570 case ERROR_FILE_NOT_FOUND:
8571 hr = STG_E_FILENOTFOUND;
8572 break;
8574 case ERROR_PATH_NOT_FOUND:
8575 hr = STG_E_PATHNOTFOUND;
8576 break;
8578 case ERROR_ACCESS_DENIED:
8579 case ERROR_WRITE_PROTECT:
8580 hr = STG_E_ACCESSDENIED;
8581 break;
8583 case ERROR_SHARING_VIOLATION:
8584 hr = STG_E_SHAREVIOLATION;
8585 break;
8587 default:
8588 hr = E_FAIL;
8591 goto end;
8595 * Refuse to open the file if it's too small to be a structured storage file
8596 * FIXME: verify the file when reading instead of here
8598 if (GetFileSize(hFile, NULL) < 0x100)
8600 CloseHandle(hFile);
8601 hr = STG_E_FILEALREADYEXISTS;
8602 goto end;
8606 * Allocate and initialize the new IStorage32object.
8608 hr = Storage_Construct(
8609 hFile,
8610 pwcsName,
8611 NULL,
8612 grfMode,
8613 TRUE,
8614 FALSE,
8615 512,
8616 &newStorage);
8618 if (FAILED(hr))
8621 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8623 if(hr == STG_E_INVALIDHEADER)
8624 hr = STG_E_FILEALREADYEXISTS;
8625 goto end;
8628 *ppstgOpen = &newStorage->IStorage_iface;
8630 end:
8631 CoTaskMemFree(temp_name);
8632 if (pstgPriority) IStorage_Release(pstgPriority);
8633 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
8634 return hr;
8637 /******************************************************************************
8638 * StgCreateDocfileOnILockBytes [OLE32.@]
8640 HRESULT WINAPI StgCreateDocfileOnILockBytes(
8641 ILockBytes *plkbyt,
8642 DWORD grfMode,
8643 DWORD reserved,
8644 IStorage** ppstgOpen)
8646 StorageBaseImpl* newStorage = 0;
8647 HRESULT hr = S_OK;
8649 if ((ppstgOpen == 0) || (plkbyt == 0))
8650 return STG_E_INVALIDPOINTER;
8653 * Allocate and initialize the new IStorage object.
8655 hr = Storage_Construct(
8658 plkbyt,
8659 grfMode,
8660 FALSE,
8661 TRUE,
8662 512,
8663 &newStorage);
8665 if (FAILED(hr))
8667 return hr;
8670 *ppstgOpen = &newStorage->IStorage_iface;
8672 return hr;
8675 /******************************************************************************
8676 * StgOpenStorageOnILockBytes [OLE32.@]
8678 HRESULT WINAPI StgOpenStorageOnILockBytes(
8679 ILockBytes *plkbyt,
8680 IStorage *pstgPriority,
8681 DWORD grfMode,
8682 SNB snbExclude,
8683 DWORD reserved,
8684 IStorage **ppstgOpen)
8686 StorageBaseImpl* newStorage = 0;
8687 HRESULT hr = S_OK;
8689 if ((plkbyt == 0) || (ppstgOpen == 0))
8690 return STG_E_INVALIDPOINTER;
8692 if ( FAILED( validateSTGM(grfMode) ))
8693 return STG_E_INVALIDFLAG;
8695 *ppstgOpen = 0;
8698 * Allocate and initialize the new IStorage object.
8700 hr = Storage_Construct(
8703 plkbyt,
8704 grfMode,
8705 FALSE,
8706 FALSE,
8707 512,
8708 &newStorage);
8710 if (FAILED(hr))
8712 return hr;
8715 *ppstgOpen = &newStorage->IStorage_iface;
8717 return hr;
8720 /******************************************************************************
8721 * StgSetTimes [ole32.@]
8722 * StgSetTimes [OLE32.@]
8726 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
8727 FILETIME const *patime, FILETIME const *pmtime)
8729 IStorage *stg = NULL;
8730 HRESULT r;
8732 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
8734 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
8735 0, 0, &stg);
8736 if( SUCCEEDED(r) )
8738 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
8739 IStorage_Release(stg);
8742 return r;
8745 /******************************************************************************
8746 * StgIsStorageILockBytes [OLE32.@]
8748 * Determines if the ILockBytes contains a storage object.
8750 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
8752 BYTE sig[sizeof(STORAGE_magic)];
8753 ULARGE_INTEGER offset;
8754 ULONG read = 0;
8756 offset.u.HighPart = 0;
8757 offset.u.LowPart = 0;
8759 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
8761 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
8762 return S_OK;
8764 return S_FALSE;
8767 /******************************************************************************
8768 * WriteClassStg [OLE32.@]
8770 * This method will store the specified CLSID in the specified storage object
8772 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
8774 if(!pStg)
8775 return E_INVALIDARG;
8777 if(!rclsid)
8778 return STG_E_INVALIDPOINTER;
8780 return IStorage_SetClass(pStg, rclsid);
8783 /***********************************************************************
8784 * ReadClassStg (OLE32.@)
8786 * This method reads the CLSID previously written to a storage object with
8787 * the WriteClassStg.
8789 * PARAMS
8790 * pstg [I] IStorage pointer
8791 * pclsid [O] Pointer to where the CLSID is written
8793 * RETURNS
8794 * Success: S_OK.
8795 * Failure: HRESULT code.
8797 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
8799 STATSTG pstatstg;
8800 HRESULT hRes;
8802 TRACE("(%p, %p)\n", pstg, pclsid);
8804 if(!pstg || !pclsid)
8805 return E_INVALIDARG;
8808 * read a STATSTG structure (contains the clsid) from the storage
8810 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
8812 if(SUCCEEDED(hRes))
8813 *pclsid=pstatstg.clsid;
8815 return hRes;
8818 /***********************************************************************
8819 * OleLoadFromStream (OLE32.@)
8821 * This function loads an object from stream
8823 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
8825 CLSID clsid;
8826 HRESULT res;
8827 LPPERSISTSTREAM xstm;
8829 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
8831 res=ReadClassStm(pStm,&clsid);
8832 if (FAILED(res))
8833 return res;
8834 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
8835 if (FAILED(res))
8836 return res;
8837 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
8838 if (FAILED(res)) {
8839 IUnknown_Release((IUnknown*)*ppvObj);
8840 return res;
8842 res=IPersistStream_Load(xstm,pStm);
8843 IPersistStream_Release(xstm);
8844 /* FIXME: all refcounts ok at this point? I think they should be:
8845 * pStm : unchanged
8846 * ppvObj : 1
8847 * xstm : 0 (released)
8849 return res;
8852 /***********************************************************************
8853 * OleSaveToStream (OLE32.@)
8855 * This function saves an object with the IPersistStream interface on it
8856 * to the specified stream.
8858 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
8861 CLSID clsid;
8862 HRESULT res;
8864 TRACE("(%p,%p)\n",pPStm,pStm);
8866 res=IPersistStream_GetClassID(pPStm,&clsid);
8868 if (SUCCEEDED(res)){
8870 res=WriteClassStm(pStm,&clsid);
8872 if (SUCCEEDED(res))
8874 res=IPersistStream_Save(pPStm,pStm,TRUE);
8877 TRACE("Finished Save\n");
8878 return res;
8881 /****************************************************************************
8882 * This method validate a STGM parameter that can contain the values below
8884 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8885 * The stgm values contained in 0xffff0000 are bitmasks.
8887 * STGM_DIRECT 0x00000000
8888 * STGM_TRANSACTED 0x00010000
8889 * STGM_SIMPLE 0x08000000
8891 * STGM_READ 0x00000000
8892 * STGM_WRITE 0x00000001
8893 * STGM_READWRITE 0x00000002
8895 * STGM_SHARE_DENY_NONE 0x00000040
8896 * STGM_SHARE_DENY_READ 0x00000030
8897 * STGM_SHARE_DENY_WRITE 0x00000020
8898 * STGM_SHARE_EXCLUSIVE 0x00000010
8900 * STGM_PRIORITY 0x00040000
8901 * STGM_DELETEONRELEASE 0x04000000
8903 * STGM_CREATE 0x00001000
8904 * STGM_CONVERT 0x00020000
8905 * STGM_FAILIFTHERE 0x00000000
8907 * STGM_NOSCRATCH 0x00100000
8908 * STGM_NOSNAPSHOT 0x00200000
8910 static HRESULT validateSTGM(DWORD stgm)
8912 DWORD access = STGM_ACCESS_MODE(stgm);
8913 DWORD share = STGM_SHARE_MODE(stgm);
8914 DWORD create = STGM_CREATE_MODE(stgm);
8916 if (stgm&~STGM_KNOWN_FLAGS)
8918 ERR("unknown flags %08x\n", stgm);
8919 return E_FAIL;
8922 switch (access)
8924 case STGM_READ:
8925 case STGM_WRITE:
8926 case STGM_READWRITE:
8927 break;
8928 default:
8929 return E_FAIL;
8932 switch (share)
8934 case STGM_SHARE_DENY_NONE:
8935 case STGM_SHARE_DENY_READ:
8936 case STGM_SHARE_DENY_WRITE:
8937 case STGM_SHARE_EXCLUSIVE:
8938 break;
8939 case 0:
8940 if (!(stgm & STGM_TRANSACTED))
8941 return E_FAIL;
8942 break;
8943 default:
8944 return E_FAIL;
8947 switch (create)
8949 case STGM_CREATE:
8950 case STGM_FAILIFTHERE:
8951 break;
8952 default:
8953 return E_FAIL;
8957 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8959 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8960 return E_FAIL;
8963 * STGM_CREATE | STGM_CONVERT
8964 * if both are false, STGM_FAILIFTHERE is set to TRUE
8966 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8967 return E_FAIL;
8970 * STGM_NOSCRATCH requires STGM_TRANSACTED
8972 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8973 return E_FAIL;
8976 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8977 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8979 if ( (stgm & STGM_NOSNAPSHOT) &&
8980 (!(stgm & STGM_TRANSACTED) ||
8981 share == STGM_SHARE_EXCLUSIVE ||
8982 share == STGM_SHARE_DENY_WRITE) )
8983 return E_FAIL;
8985 return S_OK;
8988 /****************************************************************************
8989 * GetShareModeFromSTGM
8991 * This method will return a share mode flag from a STGM value.
8992 * The STGM value is assumed valid.
8994 static DWORD GetShareModeFromSTGM(DWORD stgm)
8996 switch (STGM_SHARE_MODE(stgm))
8998 case 0:
8999 assert(stgm & STGM_TRANSACTED);
9000 /* fall-through */
9001 case STGM_SHARE_DENY_NONE:
9002 return FILE_SHARE_READ | FILE_SHARE_WRITE;
9003 case STGM_SHARE_DENY_READ:
9004 return FILE_SHARE_WRITE;
9005 case STGM_SHARE_DENY_WRITE:
9006 case STGM_SHARE_EXCLUSIVE:
9007 return FILE_SHARE_READ;
9009 ERR("Invalid share mode!\n");
9010 assert(0);
9011 return 0;
9014 /****************************************************************************
9015 * GetAccessModeFromSTGM
9017 * This method will return an access mode flag from a STGM value.
9018 * The STGM value is assumed valid.
9020 static DWORD GetAccessModeFromSTGM(DWORD stgm)
9022 switch (STGM_ACCESS_MODE(stgm))
9024 case STGM_READ:
9025 return GENERIC_READ;
9026 case STGM_WRITE:
9027 case STGM_READWRITE:
9028 return GENERIC_READ | GENERIC_WRITE;
9030 ERR("Invalid access mode!\n");
9031 assert(0);
9032 return 0;
9035 /****************************************************************************
9036 * GetCreationModeFromSTGM
9038 * This method will return a creation mode flag from a STGM value.
9039 * The STGM value is assumed valid.
9041 static DWORD GetCreationModeFromSTGM(DWORD stgm)
9043 switch(STGM_CREATE_MODE(stgm))
9045 case STGM_CREATE:
9046 return CREATE_ALWAYS;
9047 case STGM_CONVERT:
9048 FIXME("STGM_CONVERT not implemented!\n");
9049 return CREATE_NEW;
9050 case STGM_FAILIFTHERE:
9051 return CREATE_NEW;
9053 ERR("Invalid create mode!\n");
9054 assert(0);
9055 return 0;
9059 /*************************************************************************
9060 * OLECONVERT_LoadOLE10 [Internal]
9062 * Loads the OLE10 STREAM to memory
9064 * PARAMS
9065 * pOleStream [I] The OLESTREAM
9066 * pData [I] Data Structure for the OLESTREAM Data
9068 * RETURNS
9069 * Success: S_OK
9070 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9071 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9073 * NOTES
9074 * This function is used by OleConvertOLESTREAMToIStorage only.
9076 * Memory allocated for pData must be freed by the caller
9078 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
9080 DWORD dwSize;
9081 HRESULT hRes = S_OK;
9082 int nTryCnt=0;
9083 int max_try = 6;
9085 pData->pData = NULL;
9086 pData->pstrOleObjFileName = NULL;
9088 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
9090 /* Get the OleID */
9091 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9092 if(dwSize != sizeof(pData->dwOleID))
9094 hRes = CONVERT10_E_OLESTREAM_GET;
9096 else if(pData->dwOleID != OLESTREAM_ID)
9098 hRes = CONVERT10_E_OLESTREAM_FMT;
9100 else
9102 hRes = S_OK;
9103 break;
9107 if(hRes == S_OK)
9109 /* Get the TypeID... more info needed for this field */
9110 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9111 if(dwSize != sizeof(pData->dwTypeID))
9113 hRes = CONVERT10_E_OLESTREAM_GET;
9116 if(hRes == S_OK)
9118 if(pData->dwTypeID != 0)
9120 /* Get the length of the OleTypeName */
9121 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9122 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9124 hRes = CONVERT10_E_OLESTREAM_GET;
9127 if(hRes == S_OK)
9129 if(pData->dwOleTypeNameLength > 0)
9131 /* Get the OleTypeName */
9132 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9133 if(dwSize != pData->dwOleTypeNameLength)
9135 hRes = CONVERT10_E_OLESTREAM_GET;
9139 if(bStrem1)
9141 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
9142 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
9144 hRes = CONVERT10_E_OLESTREAM_GET;
9146 if(hRes == S_OK)
9148 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
9149 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
9150 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
9151 if(pData->pstrOleObjFileName)
9153 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
9154 if(dwSize != pData->dwOleObjFileNameLength)
9156 hRes = CONVERT10_E_OLESTREAM_GET;
9159 else
9160 hRes = CONVERT10_E_OLESTREAM_GET;
9163 else
9165 /* Get the Width of the Metafile */
9166 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9167 if(dwSize != sizeof(pData->dwMetaFileWidth))
9169 hRes = CONVERT10_E_OLESTREAM_GET;
9171 if(hRes == S_OK)
9173 /* Get the Height of the Metafile */
9174 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9175 if(dwSize != sizeof(pData->dwMetaFileHeight))
9177 hRes = CONVERT10_E_OLESTREAM_GET;
9181 if(hRes == S_OK)
9183 /* Get the Length of the Data */
9184 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9185 if(dwSize != sizeof(pData->dwDataLength))
9187 hRes = CONVERT10_E_OLESTREAM_GET;
9191 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
9193 if(!bStrem1) /* if it is a second OLE stream data */
9195 pData->dwDataLength -= 8;
9196 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
9197 if(dwSize != sizeof(pData->strUnknown))
9199 hRes = CONVERT10_E_OLESTREAM_GET;
9203 if(hRes == S_OK)
9205 if(pData->dwDataLength > 0)
9207 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
9209 /* Get Data (ex. IStorage, Metafile, or BMP) */
9210 if(pData->pData)
9212 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
9213 if(dwSize != pData->dwDataLength)
9215 hRes = CONVERT10_E_OLESTREAM_GET;
9218 else
9220 hRes = CONVERT10_E_OLESTREAM_GET;
9226 return hRes;
9229 /*************************************************************************
9230 * OLECONVERT_SaveOLE10 [Internal]
9232 * Saves the OLE10 STREAM From memory
9234 * PARAMS
9235 * pData [I] Data Structure for the OLESTREAM Data
9236 * pOleStream [I] The OLESTREAM to save
9238 * RETURNS
9239 * Success: S_OK
9240 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9242 * NOTES
9243 * This function is used by OleConvertIStorageToOLESTREAM only.
9246 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
9248 DWORD dwSize;
9249 HRESULT hRes = S_OK;
9252 /* Set the OleID */
9253 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9254 if(dwSize != sizeof(pData->dwOleID))
9256 hRes = CONVERT10_E_OLESTREAM_PUT;
9259 if(hRes == S_OK)
9261 /* Set the TypeID */
9262 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9263 if(dwSize != sizeof(pData->dwTypeID))
9265 hRes = CONVERT10_E_OLESTREAM_PUT;
9269 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
9271 /* Set the Length of the OleTypeName */
9272 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9273 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9275 hRes = CONVERT10_E_OLESTREAM_PUT;
9278 if(hRes == S_OK)
9280 if(pData->dwOleTypeNameLength > 0)
9282 /* Set the OleTypeName */
9283 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9284 if(dwSize != pData->dwOleTypeNameLength)
9286 hRes = CONVERT10_E_OLESTREAM_PUT;
9291 if(hRes == S_OK)
9293 /* Set the width of the Metafile */
9294 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9295 if(dwSize != sizeof(pData->dwMetaFileWidth))
9297 hRes = CONVERT10_E_OLESTREAM_PUT;
9301 if(hRes == S_OK)
9303 /* Set the height of the Metafile */
9304 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9305 if(dwSize != sizeof(pData->dwMetaFileHeight))
9307 hRes = CONVERT10_E_OLESTREAM_PUT;
9311 if(hRes == S_OK)
9313 /* Set the length of the Data */
9314 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9315 if(dwSize != sizeof(pData->dwDataLength))
9317 hRes = CONVERT10_E_OLESTREAM_PUT;
9321 if(hRes == S_OK)
9323 if(pData->dwDataLength > 0)
9325 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9326 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
9327 if(dwSize != pData->dwDataLength)
9329 hRes = CONVERT10_E_OLESTREAM_PUT;
9334 return hRes;
9337 /*************************************************************************
9338 * OLECONVERT_GetOLE20FromOLE10[Internal]
9340 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9341 * opens it, and copies the content to the dest IStorage for
9342 * OleConvertOLESTREAMToIStorage
9345 * PARAMS
9346 * pDestStorage [I] The IStorage to copy the data to
9347 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9348 * nBufferLength [I] The size of the buffer
9350 * RETURNS
9351 * Nothing
9353 * NOTES
9357 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
9359 HRESULT hRes;
9360 HANDLE hFile;
9361 IStorage *pTempStorage;
9362 DWORD dwNumOfBytesWritten;
9363 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9364 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9366 /* Create a temp File */
9367 GetTempPathW(MAX_PATH, wstrTempDir);
9368 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9369 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
9371 if(hFile != INVALID_HANDLE_VALUE)
9373 /* Write IStorage Data to File */
9374 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
9375 CloseHandle(hFile);
9377 /* Open and copy temp storage to the Dest Storage */
9378 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
9379 if(hRes == S_OK)
9381 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
9382 IStorage_Release(pTempStorage);
9384 DeleteFileW(wstrTempFile);
9389 /*************************************************************************
9390 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9392 * Saves the OLE10 STREAM From memory
9394 * PARAMS
9395 * pStorage [I] The Src IStorage to copy
9396 * pData [I] The Dest Memory to write to.
9398 * RETURNS
9399 * The size in bytes allocated for pData
9401 * NOTES
9402 * Memory allocated for pData must be freed by the caller
9404 * Used by OleConvertIStorageToOLESTREAM only.
9407 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
9409 HANDLE hFile;
9410 HRESULT hRes;
9411 DWORD nDataLength = 0;
9412 IStorage *pTempStorage;
9413 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9414 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9416 *pData = NULL;
9418 /* Create temp Storage */
9419 GetTempPathW(MAX_PATH, wstrTempDir);
9420 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9421 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
9423 if(hRes == S_OK)
9425 /* Copy Src Storage to the Temp Storage */
9426 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
9427 IStorage_Release(pTempStorage);
9429 /* Open Temp Storage as a file and copy to memory */
9430 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9431 if(hFile != INVALID_HANDLE_VALUE)
9433 nDataLength = GetFileSize(hFile, NULL);
9434 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
9435 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
9436 CloseHandle(hFile);
9438 DeleteFileW(wstrTempFile);
9440 return nDataLength;
9443 /*************************************************************************
9444 * STORAGE_CreateOleStream [Internal]
9446 * Creates the "\001OLE" stream in the IStorage if necessary.
9448 * PARAMS
9449 * storage [I] Dest storage to create the stream in
9450 * flags [I] flags to be set for newly created stream
9452 * RETURNS
9453 * HRESULT return value
9455 * NOTES
9457 * This stream is still unknown, MS Word seems to have extra data
9458 * but since the data is stored in the OLESTREAM there should be
9459 * no need to recreate the stream. If the stream is manually
9460 * deleted it will create it with this default data.
9463 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
9465 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9466 static const DWORD version_magic = 0x02000001;
9467 IStream *stream;
9468 HRESULT hr;
9470 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
9471 if (hr == S_OK)
9473 struct empty_1ole_stream {
9474 DWORD version_magic;
9475 DWORD flags;
9476 DWORD update_options;
9477 DWORD reserved;
9478 DWORD mon_stream_size;
9480 struct empty_1ole_stream stream_data;
9482 stream_data.version_magic = version_magic;
9483 stream_data.flags = flags;
9484 stream_data.update_options = 0;
9485 stream_data.reserved = 0;
9486 stream_data.mon_stream_size = 0;
9488 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
9489 IStream_Release(stream);
9492 return hr;
9495 /* write a string to a stream, preceded by its length */
9496 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
9498 HRESULT r;
9499 LPSTR str;
9500 DWORD len = 0;
9502 if( string )
9503 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
9504 r = IStream_Write( stm, &len, sizeof(len), NULL);
9505 if( FAILED( r ) )
9506 return r;
9507 if(len == 0)
9508 return r;
9509 str = CoTaskMemAlloc( len );
9510 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
9511 r = IStream_Write( stm, str, len, NULL);
9512 CoTaskMemFree( str );
9513 return r;
9516 /* read a string preceded by its length from a stream */
9517 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
9519 HRESULT r;
9520 DWORD len, count = 0;
9521 LPSTR str;
9522 LPWSTR wstr;
9524 r = IStream_Read( stm, &len, sizeof(len), &count );
9525 if( FAILED( r ) )
9526 return r;
9527 if( count != sizeof(len) )
9528 return E_OUTOFMEMORY;
9530 TRACE("%d bytes\n",len);
9532 str = CoTaskMemAlloc( len );
9533 if( !str )
9534 return E_OUTOFMEMORY;
9535 count = 0;
9536 r = IStream_Read( stm, str, len, &count );
9537 if( FAILED( r ) )
9538 return r;
9539 if( count != len )
9541 CoTaskMemFree( str );
9542 return E_OUTOFMEMORY;
9545 TRACE("Read string %s\n",debugstr_an(str,len));
9547 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
9548 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
9549 if( wstr )
9551 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
9552 wstr[len] = 0;
9554 CoTaskMemFree( str );
9556 *string = wstr;
9558 return r;
9562 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
9563 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
9565 IStream *pstm;
9566 HRESULT r = S_OK;
9567 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9569 static const BYTE unknown1[12] =
9570 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9571 0xFF, 0xFF, 0xFF, 0xFF};
9572 static const BYTE unknown2[16] =
9573 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9574 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9576 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
9577 debugstr_w(lpszUserType), debugstr_w(szClipName),
9578 debugstr_w(szProgIDName));
9580 /* Create a CompObj stream */
9581 r = IStorage_CreateStream(pstg, szwStreamName,
9582 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
9583 if( FAILED (r) )
9584 return r;
9586 /* Write CompObj Structure to stream */
9587 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
9589 if( SUCCEEDED( r ) )
9590 r = WriteClassStm( pstm, clsid );
9592 if( SUCCEEDED( r ) )
9593 r = STREAM_WriteString( pstm, lpszUserType );
9594 if( SUCCEEDED( r ) )
9595 r = STREAM_WriteString( pstm, szClipName );
9596 if( SUCCEEDED( r ) )
9597 r = STREAM_WriteString( pstm, szProgIDName );
9598 if( SUCCEEDED( r ) )
9599 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
9601 IStream_Release( pstm );
9603 return r;
9606 /***********************************************************************
9607 * WriteFmtUserTypeStg (OLE32.@)
9609 HRESULT WINAPI WriteFmtUserTypeStg(
9610 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
9612 STATSTG stat;
9613 HRESULT r;
9614 WCHAR szwClipName[0x40];
9615 CLSID clsid;
9616 LPWSTR wstrProgID = NULL;
9617 DWORD n;
9619 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
9621 /* get the clipboard format name */
9622 if( cf )
9624 n = GetClipboardFormatNameW( cf, szwClipName,
9625 sizeof(szwClipName)/sizeof(szwClipName[0]) );
9626 szwClipName[n]=0;
9629 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
9631 r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
9632 if(SUCCEEDED(r))
9633 clsid = stat.clsid;
9634 else
9635 clsid = CLSID_NULL;
9637 ProgIDFromCLSID(&clsid, &wstrProgID);
9639 TRACE("progid is %s\n",debugstr_w(wstrProgID));
9641 r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
9642 cf ? szwClipName : NULL, wstrProgID );
9644 CoTaskMemFree(wstrProgID);
9646 return r;
9650 /******************************************************************************
9651 * ReadFmtUserTypeStg [OLE32.@]
9653 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
9655 HRESULT r;
9656 IStream *stm = 0;
9657 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
9658 unsigned char unknown1[12];
9659 unsigned char unknown2[16];
9660 DWORD count;
9661 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
9662 CLSID clsid;
9664 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
9666 r = IStorage_OpenStream( pstg, szCompObj, NULL,
9667 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
9668 if( FAILED ( r ) )
9670 WARN("Failed to open stream r = %08x\n", r);
9671 return r;
9674 /* read the various parts of the structure */
9675 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
9676 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
9677 goto end;
9678 r = ReadClassStm( stm, &clsid );
9679 if( FAILED( r ) )
9680 goto end;
9682 r = STREAM_ReadString( stm, &szCLSIDName );
9683 if( FAILED( r ) )
9684 goto end;
9686 r = STREAM_ReadString( stm, &szOleTypeName );
9687 if( FAILED( r ) )
9688 goto end;
9690 r = STREAM_ReadString( stm, &szProgIDName );
9691 if( FAILED( r ) )
9692 goto end;
9694 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
9695 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
9696 goto end;
9698 /* ok, success... now we just need to store what we found */
9699 if( pcf )
9700 *pcf = RegisterClipboardFormatW( szOleTypeName );
9702 if( lplpszUserType )
9704 *lplpszUserType = szCLSIDName;
9705 szCLSIDName = NULL;
9708 end:
9709 CoTaskMemFree( szCLSIDName );
9710 CoTaskMemFree( szOleTypeName );
9711 CoTaskMemFree( szProgIDName );
9712 IStream_Release( stm );
9714 return r;
9718 /*************************************************************************
9719 * OLECONVERT_CreateCompObjStream [Internal]
9721 * Creates a "\001CompObj" is the destination IStorage if necessary.
9723 * PARAMS
9724 * pStorage [I] The dest IStorage to create the CompObj Stream
9725 * if necessary.
9726 * strOleTypeName [I] The ProgID
9728 * RETURNS
9729 * Success: S_OK
9730 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9732 * NOTES
9733 * This function is used by OleConvertOLESTREAMToIStorage only.
9735 * The stream data is stored in the OLESTREAM and there should be
9736 * no need to recreate the stream. If the stream is manually
9737 * deleted it will attempt to create it by querying the registry.
9741 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
9743 IStream *pStream;
9744 HRESULT hStorageRes, hRes = S_OK;
9745 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
9746 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9747 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
9749 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9750 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
9752 /* Initialize the CompObj structure */
9753 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
9754 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
9755 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
9758 /* Create a CompObj stream if it doesn't exist */
9759 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
9760 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9761 if(hStorageRes == S_OK)
9763 /* copy the OleTypeName to the compobj struct */
9764 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
9765 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
9767 /* copy the OleTypeName to the compobj struct */
9768 /* Note: in the test made, these were Identical */
9769 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
9770 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
9772 /* Get the CLSID */
9773 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
9774 bufferW, OLESTREAM_MAX_STR_LEN );
9775 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
9777 if(hRes == S_OK)
9779 HKEY hKey;
9780 LONG hErr;
9781 /* Get the CLSID Default Name from the Registry */
9782 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
9783 if(hErr == ERROR_SUCCESS)
9785 char strTemp[OLESTREAM_MAX_STR_LEN];
9786 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
9787 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
9788 if(hErr == ERROR_SUCCESS)
9790 strcpy(IStorageCompObj.strCLSIDName, strTemp);
9792 RegCloseKey(hKey);
9796 /* Write CompObj Structure to stream */
9797 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
9799 WriteClassStm(pStream,&(IStorageCompObj.clsid));
9801 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
9802 if(IStorageCompObj.dwCLSIDNameLength > 0)
9804 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
9806 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
9807 if(IStorageCompObj.dwOleTypeNameLength > 0)
9809 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
9811 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
9812 if(IStorageCompObj.dwProgIDNameLength > 0)
9814 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
9816 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
9817 IStream_Release(pStream);
9819 return hRes;
9823 /*************************************************************************
9824 * OLECONVERT_CreateOlePresStream[Internal]
9826 * Creates the "\002OlePres000" Stream with the Metafile data
9828 * PARAMS
9829 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
9830 * dwExtentX [I] Width of the Metafile
9831 * dwExtentY [I] Height of the Metafile
9832 * pData [I] Metafile data
9833 * dwDataLength [I] Size of the Metafile data
9835 * RETURNS
9836 * Success: S_OK
9837 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9839 * NOTES
9840 * This function is used by OleConvertOLESTREAMToIStorage only.
9843 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
9845 HRESULT hRes;
9846 IStream *pStream;
9847 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9848 BYTE pOlePresStreamHeader [] =
9850 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
9851 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9852 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9853 0x00, 0x00, 0x00, 0x00
9856 BYTE pOlePresStreamHeaderEmpty [] =
9858 0x00, 0x00, 0x00, 0x00,
9859 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9860 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9861 0x00, 0x00, 0x00, 0x00
9864 /* Create the OlePres000 Stream */
9865 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9866 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9868 if(hRes == S_OK)
9870 DWORD nHeaderSize;
9871 OLECONVERT_ISTORAGE_OLEPRES OlePres;
9873 memset(&OlePres, 0, sizeof(OlePres));
9874 /* Do we have any metafile data to save */
9875 if(dwDataLength > 0)
9877 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
9878 nHeaderSize = sizeof(pOlePresStreamHeader);
9880 else
9882 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
9883 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
9885 /* Set width and height of the metafile */
9886 OlePres.dwExtentX = dwExtentX;
9887 OlePres.dwExtentY = -dwExtentY;
9889 /* Set Data and Length */
9890 if(dwDataLength > sizeof(METAFILEPICT16))
9892 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
9893 OlePres.pData = &(pData[8]);
9895 /* Save OlePres000 Data to Stream */
9896 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
9897 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
9898 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
9899 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
9900 if(OlePres.dwSize > 0)
9902 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
9904 IStream_Release(pStream);
9908 /*************************************************************************
9909 * OLECONVERT_CreateOle10NativeStream [Internal]
9911 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9913 * PARAMS
9914 * pStorage [I] Dest storage to create the stream in
9915 * pData [I] Ole10 Native Data (ex. bmp)
9916 * dwDataLength [I] Size of the Ole10 Native Data
9918 * RETURNS
9919 * Nothing
9921 * NOTES
9922 * This function is used by OleConvertOLESTREAMToIStorage only.
9924 * Might need to verify the data and return appropriate error message
9927 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
9929 HRESULT hRes;
9930 IStream *pStream;
9931 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9933 /* Create the Ole10Native Stream */
9934 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9935 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9937 if(hRes == S_OK)
9939 /* Write info to stream */
9940 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9941 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9942 IStream_Release(pStream);
9947 /*************************************************************************
9948 * OLECONVERT_GetOLE10ProgID [Internal]
9950 * Finds the ProgID (or OleTypeID) from the IStorage
9952 * PARAMS
9953 * pStorage [I] The Src IStorage to get the ProgID
9954 * strProgID [I] the ProgID string to get
9955 * dwSize [I] the size of the string
9957 * RETURNS
9958 * Success: S_OK
9959 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9961 * NOTES
9962 * This function is used by OleConvertIStorageToOLESTREAM only.
9966 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9968 HRESULT hRes;
9969 IStream *pStream;
9970 LARGE_INTEGER iSeekPos;
9971 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9972 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9974 /* Open the CompObj Stream */
9975 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9976 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9977 if(hRes == S_OK)
9980 /*Get the OleType from the CompObj Stream */
9981 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9982 iSeekPos.u.HighPart = 0;
9984 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9985 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9986 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9987 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9988 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9989 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9990 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9992 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9993 if(*dwSize > 0)
9995 IStream_Read(pStream, strProgID, *dwSize, NULL);
9997 IStream_Release(pStream);
9999 else
10001 STATSTG stat;
10002 LPOLESTR wstrProgID;
10004 /* Get the OleType from the registry */
10005 REFCLSID clsid = &(stat.clsid);
10006 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
10007 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
10008 if(hRes == S_OK)
10010 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
10011 CoTaskMemFree(wstrProgID);
10015 return hRes;
10018 /*************************************************************************
10019 * OLECONVERT_GetOle10PresData [Internal]
10021 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10023 * PARAMS
10024 * pStorage [I] Src IStroage
10025 * pOleStream [I] Dest OleStream Mem Struct
10027 * RETURNS
10028 * Nothing
10030 * NOTES
10031 * This function is used by OleConvertIStorageToOLESTREAM only.
10033 * Memory allocated for pData must be freed by the caller
10037 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10040 HRESULT hRes;
10041 IStream *pStream;
10042 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10044 /* Initialize Default data for OLESTREAM */
10045 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10046 pOleStreamData[0].dwTypeID = 2;
10047 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10048 pOleStreamData[1].dwTypeID = 0;
10049 pOleStreamData[0].dwMetaFileWidth = 0;
10050 pOleStreamData[0].dwMetaFileHeight = 0;
10051 pOleStreamData[0].pData = NULL;
10052 pOleStreamData[1].pData = NULL;
10054 /* Open Ole10Native Stream */
10055 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10056 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10057 if(hRes == S_OK)
10060 /* Read Size and Data */
10061 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
10062 if(pOleStreamData->dwDataLength > 0)
10064 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
10065 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
10067 IStream_Release(pStream);
10073 /*************************************************************************
10074 * OLECONVERT_GetOle20PresData[Internal]
10076 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10078 * PARAMS
10079 * pStorage [I] Src IStroage
10080 * pOleStreamData [I] Dest OleStream Mem Struct
10082 * RETURNS
10083 * Nothing
10085 * NOTES
10086 * This function is used by OleConvertIStorageToOLESTREAM only.
10088 * Memory allocated for pData must be freed by the caller
10090 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10092 HRESULT hRes;
10093 IStream *pStream;
10094 OLECONVERT_ISTORAGE_OLEPRES olePress;
10095 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10097 /* Initialize Default data for OLESTREAM */
10098 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10099 pOleStreamData[0].dwTypeID = 2;
10100 pOleStreamData[0].dwMetaFileWidth = 0;
10101 pOleStreamData[0].dwMetaFileHeight = 0;
10102 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
10103 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10104 pOleStreamData[1].dwTypeID = 0;
10105 pOleStreamData[1].dwOleTypeNameLength = 0;
10106 pOleStreamData[1].strOleTypeName[0] = 0;
10107 pOleStreamData[1].dwMetaFileWidth = 0;
10108 pOleStreamData[1].dwMetaFileHeight = 0;
10109 pOleStreamData[1].pData = NULL;
10110 pOleStreamData[1].dwDataLength = 0;
10113 /* Open OlePress000 stream */
10114 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10115 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10116 if(hRes == S_OK)
10118 LARGE_INTEGER iSeekPos;
10119 METAFILEPICT16 MetaFilePict;
10120 static const char strMetafilePictName[] = "METAFILEPICT";
10122 /* Set the TypeID for a Metafile */
10123 pOleStreamData[1].dwTypeID = 5;
10125 /* Set the OleTypeName to Metafile */
10126 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
10127 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
10129 iSeekPos.u.HighPart = 0;
10130 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
10132 /* Get Presentation Data */
10133 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10134 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
10135 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
10136 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
10138 /*Set width and Height */
10139 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
10140 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
10141 if(olePress.dwSize > 0)
10143 /* Set Length */
10144 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
10146 /* Set MetaFilePict struct */
10147 MetaFilePict.mm = 8;
10148 MetaFilePict.xExt = olePress.dwExtentX;
10149 MetaFilePict.yExt = olePress.dwExtentY;
10150 MetaFilePict.hMF = 0;
10152 /* Get Metafile Data */
10153 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
10154 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
10155 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
10157 IStream_Release(pStream);
10161 /*************************************************************************
10162 * OleConvertOLESTREAMToIStorage [OLE32.@]
10164 * Read info on MSDN
10166 * TODO
10167 * DVTARGETDEVICE parameter is not handled
10168 * Still unsure of some mem fields for OLE 10 Stream
10169 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10170 * and "\001OLE" streams
10173 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
10174 LPOLESTREAM pOleStream,
10175 LPSTORAGE pstg,
10176 const DVTARGETDEVICE* ptd)
10178 int i;
10179 HRESULT hRes=S_OK;
10180 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10182 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
10184 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10186 if(ptd != NULL)
10188 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10191 if(pstg == NULL || pOleStream == NULL)
10193 hRes = E_INVALIDARG;
10196 if(hRes == S_OK)
10198 /* Load the OLESTREAM to Memory */
10199 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
10202 if(hRes == S_OK)
10204 /* Load the OLESTREAM to Memory (part 2)*/
10205 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
10208 if(hRes == S_OK)
10211 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
10213 /* Do we have the IStorage Data in the OLESTREAM */
10214 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
10216 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10217 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
10219 else
10221 /* It must be an original OLE 1.0 source */
10222 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10225 else
10227 /* It must be an original OLE 1.0 source */
10228 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10231 /* Create CompObj Stream if necessary */
10232 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
10233 if(hRes == S_OK)
10235 /*Create the Ole Stream if necessary */
10236 STORAGE_CreateOleStream(pstg, 0);
10241 /* Free allocated memory */
10242 for(i=0; i < 2; i++)
10244 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10245 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
10246 pOleStreamData[i].pstrOleObjFileName = NULL;
10248 return hRes;
10251 /*************************************************************************
10252 * OleConvertIStorageToOLESTREAM [OLE32.@]
10254 * Read info on MSDN
10256 * Read info on MSDN
10258 * TODO
10259 * Still unsure of some mem fields for OLE 10 Stream
10260 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10261 * and "\001OLE" streams.
10264 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
10265 LPSTORAGE pstg,
10266 LPOLESTREAM pOleStream)
10268 int i;
10269 HRESULT hRes = S_OK;
10270 IStream *pStream;
10271 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10272 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10274 TRACE("%p %p\n", pstg, pOleStream);
10276 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10278 if(pstg == NULL || pOleStream == NULL)
10280 hRes = E_INVALIDARG;
10282 if(hRes == S_OK)
10284 /* Get the ProgID */
10285 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
10286 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
10288 if(hRes == S_OK)
10290 /* Was it originally Ole10 */
10291 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
10292 if(hRes == S_OK)
10294 IStream_Release(pStream);
10295 /* Get Presentation Data for Ole10Native */
10296 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
10298 else
10300 /* Get Presentation Data (OLE20) */
10301 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
10304 /* Save OLESTREAM */
10305 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
10306 if(hRes == S_OK)
10308 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
10313 /* Free allocated memory */
10314 for(i=0; i < 2; i++)
10316 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10319 return hRes;
10322 enum stream_1ole_flags {
10323 OleStream_LinkedObject = 0x00000001,
10324 OleStream_Convert = 0x00000004
10327 /***********************************************************************
10328 * GetConvertStg (OLE32.@)
10330 HRESULT WINAPI GetConvertStg(IStorage *stg)
10332 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10333 static const DWORD version_magic = 0x02000001;
10334 DWORD header[2];
10335 IStream *stream;
10336 HRESULT hr;
10338 TRACE("%p\n", stg);
10340 if (!stg) return E_INVALIDARG;
10342 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
10343 if (FAILED(hr)) return hr;
10345 hr = IStream_Read(stream, header, sizeof(header), NULL);
10346 IStream_Release(stream);
10347 if (FAILED(hr)) return hr;
10349 if (header[0] != version_magic)
10351 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
10352 return E_FAIL;
10355 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
10358 /***********************************************************************
10359 * SetConvertStg (OLE32.@)
10361 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
10363 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10364 DWORD flags = convert ? OleStream_Convert : 0;
10365 IStream *stream;
10366 DWORD header[2];
10367 HRESULT hr;
10369 TRACE("(%p, %d)\n", storage, convert);
10371 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
10372 if (FAILED(hr))
10374 if (hr != STG_E_FILENOTFOUND)
10375 return hr;
10377 return STORAGE_CreateOleStream(storage, flags);
10380 hr = IStream_Read(stream, header, sizeof(header), NULL);
10381 if (FAILED(hr))
10383 IStream_Release(stream);
10384 return hr;
10387 /* update flag if differs */
10388 if ((header[1] ^ flags) & OleStream_Convert)
10390 LARGE_INTEGER pos = {{0}};
10392 if (header[1] & OleStream_Convert)
10393 flags = header[1] & ~OleStream_Convert;
10394 else
10395 flags = header[1] | OleStream_Convert;
10397 pos.QuadPart = sizeof(DWORD);
10398 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
10399 if (FAILED(hr))
10401 IStream_Release(stream);
10402 return hr;
10405 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
10408 IStream_Release(stream);
10409 return hr;
10412 /******************************************************************************
10413 * StgIsStorageFile [OLE32.@]
10414 * Verify if the file contains a storage object
10416 * PARAMS
10417 * fn [ I] Filename
10419 * RETURNS
10420 * S_OK if file has magic bytes as a storage object
10421 * S_FALSE if file is not storage
10423 HRESULT WINAPI
10424 StgIsStorageFile(LPCOLESTR fn)
10426 HANDLE hf;
10427 BYTE magic[8];
10428 DWORD bytes_read;
10430 TRACE("%s\n", debugstr_w(fn));
10431 hf = CreateFileW(fn, GENERIC_READ,
10432 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
10433 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
10435 if (hf == INVALID_HANDLE_VALUE)
10436 return STG_E_FILENOTFOUND;
10438 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
10440 WARN(" unable to read file\n");
10441 CloseHandle(hf);
10442 return S_FALSE;
10445 CloseHandle(hf);
10447 if (bytes_read != 8) {
10448 TRACE(" too short\n");
10449 return S_FALSE;
10452 if (!memcmp(magic,STORAGE_magic,8)) {
10453 TRACE(" -> YES\n");
10454 return S_OK;
10457 TRACE(" -> Invalid header.\n");
10458 return S_FALSE;
10461 /***********************************************************************
10462 * WriteClassStm (OLE32.@)
10464 * Writes a CLSID to a stream.
10466 * PARAMS
10467 * pStm [I] Stream to write to.
10468 * rclsid [I] CLSID to write.
10470 * RETURNS
10471 * Success: S_OK.
10472 * Failure: HRESULT code.
10474 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
10476 TRACE("(%p,%p)\n",pStm,rclsid);
10478 if (!pStm || !rclsid)
10479 return E_INVALIDARG;
10481 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
10484 /***********************************************************************
10485 * ReadClassStm (OLE32.@)
10487 * Reads a CLSID from a stream.
10489 * PARAMS
10490 * pStm [I] Stream to read from.
10491 * rclsid [O] CLSID to read.
10493 * RETURNS
10494 * Success: S_OK.
10495 * Failure: HRESULT code.
10497 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
10499 ULONG nbByte;
10500 HRESULT res;
10502 TRACE("(%p,%p)\n",pStm,pclsid);
10504 if (!pStm || !pclsid)
10505 return E_INVALIDARG;
10507 /* clear the output args */
10508 *pclsid = CLSID_NULL;
10510 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
10512 if (FAILED(res))
10513 return res;
10515 if (nbByte != sizeof(CLSID))
10516 return STG_E_READFAULT;
10517 else
10518 return S_OK;