msvcrt: Handle negative file->_cnt value in fwrite.
[wine.git] / dlls / ole32 / storage32.c
bloba880e2a59ce77b6c008ebade8a6fb5ca6ebc6bb1
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 ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
358 return (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.u.HighPart = 0;
1326 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1329 * add a block to the directory stream
1331 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1334 * memset the empty entry in order to initialize the unused newly
1335 * created entries
1337 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1340 * initialize them
1342 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1344 for(
1345 entryIndex = newEntryIndex + 1;
1346 entryIndex < lastEntry;
1347 entryIndex++)
1349 StorageImpl_WriteRawDirEntry(
1350 storage,
1351 entryIndex,
1352 emptyData);
1355 StorageImpl_SaveFileHeader(storage);
1358 UpdateRawDirEntry(currentData, newData);
1360 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1362 if (SUCCEEDED(hr))
1363 *index = newEntryIndex;
1365 return hr;
1368 /***************************************************************************
1370 * Internal Method
1372 * Mark a directory entry in the file as free.
1374 static HRESULT StorageImpl_DestroyDirEntry(
1375 StorageBaseImpl *base,
1376 DirRef index)
1378 BYTE emptyData[RAW_DIRENTRY_SIZE];
1379 StorageImpl *storage = (StorageImpl*)base;
1381 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1383 return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1387 /****************************************************************************
1389 * Internal Method
1391 * Case insensitive comparison of DirEntry.name by first considering
1392 * their size.
1394 * Returns <0 when name1 < name2
1395 * >0 when name1 > name2
1396 * 0 when name1 == name2
1398 static LONG entryNameCmp(
1399 const OLECHAR *name1,
1400 const OLECHAR *name2)
1402 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1404 while (diff == 0 && *name1 != 0)
1407 * We compare the string themselves only when they are of the same length
1409 diff = toupperW(*name1++) - toupperW(*name2++);
1412 return diff;
1415 /****************************************************************************
1417 * Internal Method
1419 * Add a directory entry to a storage
1421 static HRESULT insertIntoTree(
1422 StorageBaseImpl *This,
1423 DirRef parentStorageIndex,
1424 DirRef newEntryIndex)
1426 DirEntry currentEntry;
1427 DirEntry newEntry;
1430 * Read the inserted entry
1432 StorageBaseImpl_ReadDirEntry(This,
1433 newEntryIndex,
1434 &newEntry);
1437 * Read the storage entry
1439 StorageBaseImpl_ReadDirEntry(This,
1440 parentStorageIndex,
1441 &currentEntry);
1443 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1446 * The root storage contains some element, therefore, start the research
1447 * for the appropriate location.
1449 BOOL found = FALSE;
1450 DirRef current, next, previous, currentEntryId;
1453 * Keep a reference to the root of the storage's element tree
1455 currentEntryId = currentEntry.dirRootEntry;
1458 * Read
1460 StorageBaseImpl_ReadDirEntry(This,
1461 currentEntry.dirRootEntry,
1462 &currentEntry);
1464 previous = currentEntry.leftChild;
1465 next = currentEntry.rightChild;
1466 current = currentEntryId;
1468 while (!found)
1470 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1472 if (diff < 0)
1474 if (previous != DIRENTRY_NULL)
1476 StorageBaseImpl_ReadDirEntry(This,
1477 previous,
1478 &currentEntry);
1479 current = previous;
1481 else
1483 currentEntry.leftChild = newEntryIndex;
1484 StorageBaseImpl_WriteDirEntry(This,
1485 current,
1486 &currentEntry);
1487 found = TRUE;
1490 else if (diff > 0)
1492 if (next != DIRENTRY_NULL)
1494 StorageBaseImpl_ReadDirEntry(This,
1495 next,
1496 &currentEntry);
1497 current = next;
1499 else
1501 currentEntry.rightChild = newEntryIndex;
1502 StorageBaseImpl_WriteDirEntry(This,
1503 current,
1504 &currentEntry);
1505 found = TRUE;
1508 else
1511 * Trying to insert an item with the same name in the
1512 * subtree structure.
1514 return STG_E_FILEALREADYEXISTS;
1517 previous = currentEntry.leftChild;
1518 next = currentEntry.rightChild;
1521 else
1524 * The storage is empty, make the new entry the root of its element tree
1526 currentEntry.dirRootEntry = newEntryIndex;
1527 StorageBaseImpl_WriteDirEntry(This,
1528 parentStorageIndex,
1529 &currentEntry);
1532 return S_OK;
1535 /****************************************************************************
1537 * Internal Method
1539 * Find and read the element of a storage with the given name.
1541 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1542 const OLECHAR *name, DirEntry *data)
1544 DirRef currentEntry;
1546 /* Read the storage entry to find the root of the tree. */
1547 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1549 currentEntry = data->dirRootEntry;
1551 while (currentEntry != DIRENTRY_NULL)
1553 LONG cmp;
1555 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1557 cmp = entryNameCmp(name, data->name);
1559 if (cmp == 0)
1560 /* found it */
1561 break;
1563 else if (cmp < 0)
1564 currentEntry = data->leftChild;
1566 else if (cmp > 0)
1567 currentEntry = data->rightChild;
1570 return currentEntry;
1573 /****************************************************************************
1575 * Internal Method
1577 * Find and read the binary tree parent of the element with the given name.
1579 * If there is no such element, find a place where it could be inserted and
1580 * return STG_E_FILENOTFOUND.
1582 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1583 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1584 ULONG *relation)
1586 DirRef childEntry;
1587 DirEntry childData;
1589 /* Read the storage entry to find the root of the tree. */
1590 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1592 *parentEntry = storageEntry;
1593 *relation = DIRENTRY_RELATION_DIR;
1595 childEntry = parentData->dirRootEntry;
1597 while (childEntry != DIRENTRY_NULL)
1599 LONG cmp;
1601 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1603 cmp = entryNameCmp(childName, childData.name);
1605 if (cmp == 0)
1606 /* found it */
1607 break;
1609 else if (cmp < 0)
1611 *parentData = childData;
1612 *parentEntry = childEntry;
1613 *relation = DIRENTRY_RELATION_PREVIOUS;
1615 childEntry = parentData->leftChild;
1618 else if (cmp > 0)
1620 *parentData = childData;
1621 *parentEntry = childEntry;
1622 *relation = DIRENTRY_RELATION_NEXT;
1624 childEntry = parentData->rightChild;
1628 if (childEntry == DIRENTRY_NULL)
1629 return STG_E_FILENOTFOUND;
1630 else
1631 return S_OK;
1635 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1636 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1637 SNB snbExclude, IStorage *pstgDest);
1639 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
1640 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1641 SNB snbExclude, IStorage *pstgDest)
1643 DirEntry data;
1644 HRESULT hr;
1645 BOOL skip = FALSE;
1646 IStorage *pstgTmp;
1647 IStream *pstrChild, *pstrTmp;
1648 STATSTG strStat;
1650 if (srcEntry == DIRENTRY_NULL)
1651 return S_OK;
1653 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1655 if (FAILED(hr))
1656 return hr;
1658 if ( snbExclude )
1660 WCHAR **snb = snbExclude;
1662 while ( *snb != NULL && !skip )
1664 if ( lstrcmpW(data.name, *snb) == 0 )
1665 skip = TRUE;
1666 ++snb;
1670 if (!skip)
1672 if (data.stgType == STGTY_STORAGE && !skip_storage)
1675 * create a new storage in destination storage
1677 hr = IStorage_CreateStorage( pstgDest, data.name,
1678 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1679 0, 0,
1680 &pstgTmp );
1683 * if it already exist, don't create a new one use this one
1685 if (hr == STG_E_FILEALREADYEXISTS)
1687 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1688 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1689 NULL, 0, &pstgTmp );
1692 if (SUCCEEDED(hr))
1694 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1695 skip_stream, NULL, pstgTmp );
1697 IStorage_Release(pstgTmp);
1700 else if (data.stgType == STGTY_STREAM && !skip_stream)
1703 * create a new stream in destination storage. If the stream already
1704 * exist, it will be deleted and a new one will be created.
1706 hr = IStorage_CreateStream( pstgDest, data.name,
1707 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1708 0, 0, &pstrTmp );
1711 * open child stream storage. This operation must succeed even if the
1712 * stream is already open, so we use internal functions to do it.
1714 if (hr == S_OK)
1716 StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1718 if (streamimpl)
1720 pstrChild = &streamimpl->IStream_iface;
1721 if (pstrChild)
1722 IStream_AddRef(pstrChild);
1724 else
1726 pstrChild = NULL;
1727 hr = E_OUTOFMEMORY;
1731 if (hr == S_OK)
1734 * Get the size of the source stream
1736 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1739 * Set the size of the destination stream.
1741 IStream_SetSize(pstrTmp, strStat.cbSize);
1744 * do the copy
1746 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1747 NULL, NULL );
1749 IStream_Release( pstrChild );
1752 IStream_Release( pstrTmp );
1756 /* copy siblings */
1757 if (SUCCEEDED(hr))
1758 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1759 skip_stream, snbExclude, pstgDest );
1761 if (SUCCEEDED(hr))
1762 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1763 skip_stream, snbExclude, pstgDest );
1765 return hr;
1768 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1769 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1770 SNB snbExclude, IStorage *pstgDest)
1772 DirEntry data;
1773 HRESULT hr;
1775 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1777 if (SUCCEEDED(hr))
1778 hr = IStorage_SetClass( pstgDest, &data.clsid );
1780 if (SUCCEEDED(hr))
1781 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
1782 skip_stream, snbExclude, pstgDest );
1784 return hr;
1787 /*************************************************************************
1788 * CopyTo (IStorage)
1790 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1791 IStorage* iface,
1792 DWORD ciidExclude, /* [in] */
1793 const IID* rgiidExclude, /* [size_is][unique][in] */
1794 SNB snbExclude, /* [unique][in] */
1795 IStorage* pstgDest) /* [unique][in] */
1797 StorageBaseImpl *This = impl_from_IStorage(iface);
1799 BOOL skip_storage = FALSE, skip_stream = FALSE;
1800 DWORD i;
1802 TRACE("(%p, %d, %p, %p, %p)\n",
1803 iface, ciidExclude, rgiidExclude,
1804 snbExclude, pstgDest);
1806 if ( pstgDest == 0 )
1807 return STG_E_INVALIDPOINTER;
1809 for(i = 0; i < ciidExclude; ++i)
1811 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1812 skip_storage = TRUE;
1813 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1814 skip_stream = TRUE;
1815 else
1816 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1819 if (!skip_storage)
1821 /* Give up early if it looks like this would be infinitely recursive.
1822 * Oddly enough, this includes some cases that aren't really recursive, like
1823 * copying to a transacted child. */
1824 IStorage *pstgDestAncestor = pstgDest;
1825 IStorage *pstgDestAncestorChild = NULL;
1827 /* Go up the chain from the destination until we find the source storage. */
1828 while (pstgDestAncestor != iface) {
1829 pstgDestAncestorChild = pstgDest;
1831 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
1833 TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
1835 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
1837 else if (pstgDestAncestor->lpVtbl == &Storage32InternalImpl_Vtbl)
1839 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
1841 pstgDestAncestor = &internal->parentStorage->IStorage_iface;
1843 else
1844 break;
1847 if (pstgDestAncestor == iface)
1849 BOOL fail = TRUE;
1851 if (pstgDestAncestorChild && snbExclude)
1853 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
1854 DirEntry data;
1855 WCHAR **snb = snbExclude;
1857 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
1859 while ( *snb != NULL && fail )
1861 if ( lstrcmpW(data.name, *snb) == 0 )
1862 fail = FALSE;
1863 ++snb;
1867 if (fail)
1868 return STG_E_ACCESSDENIED;
1872 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
1873 skip_storage, skip_stream, snbExclude, pstgDest );
1876 /*************************************************************************
1877 * MoveElementTo (IStorage)
1879 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1880 IStorage* iface,
1881 const OLECHAR *pwcsName, /* [string][in] */
1882 IStorage *pstgDest, /* [unique][in] */
1883 const OLECHAR *pwcsNewName,/* [string][in] */
1884 DWORD grfFlags) /* [in] */
1886 FIXME("(%p %s %p %s %u): stub\n", iface,
1887 debugstr_w(pwcsName), pstgDest,
1888 debugstr_w(pwcsNewName), grfFlags);
1889 return E_NOTIMPL;
1892 /*************************************************************************
1893 * Commit (IStorage)
1895 * Ensures that any changes made to a storage object open in transacted mode
1896 * are reflected in the parent storage
1898 * In a non-transacted mode, this ensures all cached writes are completed.
1900 static HRESULT WINAPI StorageImpl_Commit(
1901 IStorage* iface,
1902 DWORD grfCommitFlags)/* [in] */
1904 StorageBaseImpl* This = impl_from_IStorage(iface);
1905 TRACE("(%p %d)\n", iface, grfCommitFlags);
1906 return StorageBaseImpl_Flush(This);
1909 /*************************************************************************
1910 * Revert (IStorage)
1912 * Discard all changes that have been made since the last commit operation
1914 static HRESULT WINAPI StorageImpl_Revert(
1915 IStorage* iface)
1917 TRACE("(%p)\n", iface);
1918 return S_OK;
1921 /*************************************************************************
1922 * DestroyElement (IStorage)
1924 * Strategy: This implementation is built this way for simplicity not for speed.
1925 * I always delete the topmost element of the enumeration and adjust
1926 * the deleted element pointer all the time. This takes longer to
1927 * do but allow to reinvoke DestroyElement whenever we encounter a
1928 * storage object. The optimisation resides in the usage of another
1929 * enumeration strategy that would give all the leaves of a storage
1930 * first. (postfix order)
1932 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1933 IStorage* iface,
1934 const OLECHAR *pwcsName)/* [string][in] */
1936 StorageBaseImpl *This = impl_from_IStorage(iface);
1938 HRESULT hr = S_OK;
1939 DirEntry entryToDelete;
1940 DirRef entryToDeleteRef;
1942 TRACE("(%p, %s)\n",
1943 iface, debugstr_w(pwcsName));
1945 if (pwcsName==NULL)
1946 return STG_E_INVALIDPOINTER;
1948 if (This->reverted)
1949 return STG_E_REVERTED;
1951 if ( !(This->openFlags & STGM_TRANSACTED) &&
1952 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1953 return STG_E_ACCESSDENIED;
1955 entryToDeleteRef = findElement(
1956 This,
1957 This->storageDirEntry,
1958 pwcsName,
1959 &entryToDelete);
1961 if ( entryToDeleteRef == DIRENTRY_NULL )
1963 return STG_E_FILENOTFOUND;
1966 if ( entryToDelete.stgType == STGTY_STORAGE )
1968 hr = deleteStorageContents(
1969 This,
1970 entryToDeleteRef,
1971 entryToDelete);
1973 else if ( entryToDelete.stgType == STGTY_STREAM )
1975 hr = deleteStreamContents(
1976 This,
1977 entryToDeleteRef,
1978 entryToDelete);
1981 if (hr!=S_OK)
1982 return hr;
1985 * Remove the entry from its parent storage
1987 hr = removeFromTree(
1988 This,
1989 This->storageDirEntry,
1990 entryToDeleteRef);
1993 * Invalidate the entry
1995 if (SUCCEEDED(hr))
1996 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1998 if (SUCCEEDED(hr))
1999 hr = StorageBaseImpl_Flush(This);
2001 return hr;
2005 /******************************************************************************
2006 * Internal stream list handlers
2009 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2011 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
2012 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
2015 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
2017 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
2018 list_remove(&(strm->StrmListEntry));
2021 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
2023 StgStreamImpl *strm;
2025 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
2027 if (strm->dirEntry == streamEntry)
2029 return TRUE;
2033 return FALSE;
2036 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
2038 StorageInternalImpl *childstg;
2040 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
2042 if (childstg->base.storageDirEntry == storageEntry)
2044 return TRUE;
2048 return FALSE;
2051 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2053 struct list *cur, *cur2;
2054 StgStreamImpl *strm=NULL;
2055 StorageInternalImpl *childstg=NULL;
2057 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2058 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2059 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2060 strm->parentStorage = NULL;
2061 list_remove(cur);
2064 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2065 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2066 StorageBaseImpl_Invalidate( &childstg->base );
2069 if (stg->transactedChild)
2071 StorageBaseImpl_Invalidate(stg->transactedChild);
2073 stg->transactedChild = NULL;
2078 /*********************************************************************
2080 * Internal Method
2082 * Delete the contents of a storage entry.
2085 static HRESULT deleteStorageContents(
2086 StorageBaseImpl *parentStorage,
2087 DirRef indexToDelete,
2088 DirEntry entryDataToDelete)
2090 IEnumSTATSTG *elements = 0;
2091 IStorage *childStorage = 0;
2092 STATSTG currentElement;
2093 HRESULT hr;
2094 HRESULT destroyHr = S_OK;
2095 StorageInternalImpl *stg, *stg2;
2097 /* Invalidate any open storage objects. */
2098 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2100 if (stg->base.storageDirEntry == indexToDelete)
2102 StorageBaseImpl_Invalidate(&stg->base);
2107 * Open the storage and enumerate it
2109 hr = IStorage_OpenStorage(
2110 &parentStorage->IStorage_iface,
2111 entryDataToDelete.name,
2113 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2116 &childStorage);
2118 if (hr != S_OK)
2120 return hr;
2124 * Enumerate the elements
2126 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2131 * Obtain the next element
2133 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2134 if (hr==S_OK)
2136 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2138 CoTaskMemFree(currentElement.pwcsName);
2142 * We need to Reset the enumeration every time because we delete elements
2143 * and the enumeration could be invalid
2145 IEnumSTATSTG_Reset(elements);
2147 } while ((hr == S_OK) && (destroyHr == S_OK));
2149 IStorage_Release(childStorage);
2150 IEnumSTATSTG_Release(elements);
2152 return destroyHr;
2155 /*********************************************************************
2157 * Internal Method
2159 * Perform the deletion of a stream's data
2162 static HRESULT deleteStreamContents(
2163 StorageBaseImpl *parentStorage,
2164 DirRef indexToDelete,
2165 DirEntry entryDataToDelete)
2167 IStream *pis;
2168 HRESULT hr;
2169 ULARGE_INTEGER size;
2170 StgStreamImpl *strm, *strm2;
2172 /* Invalidate any open stream objects. */
2173 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2175 if (strm->dirEntry == indexToDelete)
2177 TRACE("Stream deleted %p\n", strm);
2178 strm->parentStorage = NULL;
2179 list_remove(&strm->StrmListEntry);
2183 size.u.HighPart = 0;
2184 size.u.LowPart = 0;
2186 hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2187 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2189 if (hr!=S_OK)
2191 return(hr);
2195 * Zap the stream
2197 hr = IStream_SetSize(pis, size);
2199 if(hr != S_OK)
2201 return hr;
2205 * Release the stream object.
2207 IStream_Release(pis);
2209 return S_OK;
2212 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2214 switch (relation)
2216 case DIRENTRY_RELATION_PREVIOUS:
2217 entry->leftChild = new_target;
2218 break;
2219 case DIRENTRY_RELATION_NEXT:
2220 entry->rightChild = new_target;
2221 break;
2222 case DIRENTRY_RELATION_DIR:
2223 entry->dirRootEntry = new_target;
2224 break;
2225 default:
2226 assert(0);
2230 /*************************************************************************
2232 * Internal Method
2234 * This method removes a directory entry from its parent storage tree without
2235 * freeing any resources attached to it.
2237 static HRESULT removeFromTree(
2238 StorageBaseImpl *This,
2239 DirRef parentStorageIndex,
2240 DirRef deletedIndex)
2242 DirEntry entryToDelete;
2243 DirEntry parentEntry;
2244 DirRef parentEntryRef;
2245 ULONG typeOfRelation;
2246 HRESULT hr;
2248 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2250 if (hr != S_OK)
2251 return hr;
2254 * Find the element that links to the one we want to delete.
2256 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2257 &parentEntry, &parentEntryRef, &typeOfRelation);
2259 if (hr != S_OK)
2260 return hr;
2262 if (entryToDelete.leftChild != DIRENTRY_NULL)
2265 * Replace the deleted entry with its left child
2267 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2269 hr = StorageBaseImpl_WriteDirEntry(
2270 This,
2271 parentEntryRef,
2272 &parentEntry);
2273 if(FAILED(hr))
2275 return hr;
2278 if (entryToDelete.rightChild != DIRENTRY_NULL)
2281 * We need to reinsert the right child somewhere. We already know it and
2282 * its children are greater than everything in the left tree, so we
2283 * insert it at the rightmost point in the left tree.
2285 DirRef newRightChildParent = entryToDelete.leftChild;
2286 DirEntry newRightChildParentEntry;
2290 hr = StorageBaseImpl_ReadDirEntry(
2291 This,
2292 newRightChildParent,
2293 &newRightChildParentEntry);
2294 if (FAILED(hr))
2296 return hr;
2299 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2300 newRightChildParent = newRightChildParentEntry.rightChild;
2301 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2303 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2305 hr = StorageBaseImpl_WriteDirEntry(
2306 This,
2307 newRightChildParent,
2308 &newRightChildParentEntry);
2309 if (FAILED(hr))
2311 return hr;
2315 else
2318 * Replace the deleted entry with its right child
2320 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2322 hr = StorageBaseImpl_WriteDirEntry(
2323 This,
2324 parentEntryRef,
2325 &parentEntry);
2326 if(FAILED(hr))
2328 return hr;
2332 return hr;
2336 /******************************************************************************
2337 * SetElementTimes (IStorage)
2339 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2340 IStorage* iface,
2341 const OLECHAR *pwcsName,/* [string][in] */
2342 const FILETIME *pctime, /* [in] */
2343 const FILETIME *patime, /* [in] */
2344 const FILETIME *pmtime) /* [in] */
2346 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2347 return S_OK;
2350 /******************************************************************************
2351 * SetStateBits (IStorage)
2353 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2354 IStorage* iface,
2355 DWORD grfStateBits,/* [in] */
2356 DWORD grfMask) /* [in] */
2358 StorageBaseImpl *This = impl_from_IStorage(iface);
2360 if (This->reverted)
2361 return STG_E_REVERTED;
2363 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2364 return S_OK;
2367 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2368 DirRef index, const DirEntry *data)
2370 StorageImpl *This = (StorageImpl*)base;
2371 return StorageImpl_WriteDirEntry(This, index, data);
2374 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2375 DirRef index, DirEntry *data)
2377 StorageImpl *This = (StorageImpl*)base;
2378 return StorageImpl_ReadDirEntry(This, index, data);
2381 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2383 int i;
2385 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2387 if (!This->blockChainCache[i])
2389 return &This->blockChainCache[i];
2393 i = This->blockChainToEvict;
2395 BlockChainStream_Destroy(This->blockChainCache[i]);
2396 This->blockChainCache[i] = NULL;
2398 This->blockChainToEvict++;
2399 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2400 This->blockChainToEvict = 0;
2402 return &This->blockChainCache[i];
2405 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2406 DirRef index)
2408 int i, free_index=-1;
2410 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2412 if (!This->blockChainCache[i])
2414 if (free_index == -1) free_index = i;
2416 else if (This->blockChainCache[i]->ownerDirEntry == index)
2418 return &This->blockChainCache[i];
2422 if (free_index == -1)
2424 free_index = This->blockChainToEvict;
2426 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2427 This->blockChainCache[free_index] = NULL;
2429 This->blockChainToEvict++;
2430 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2431 This->blockChainToEvict = 0;
2434 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2435 return &This->blockChainCache[free_index];
2438 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2440 int i;
2442 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2444 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2446 BlockChainStream_Destroy(This->blockChainCache[i]);
2447 This->blockChainCache[i] = NULL;
2448 return;
2453 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2454 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2456 StorageImpl *This = (StorageImpl*)base;
2457 DirEntry data;
2458 HRESULT hr;
2459 ULONG bytesToRead;
2461 hr = StorageImpl_ReadDirEntry(This, index, &data);
2462 if (FAILED(hr)) return hr;
2464 if (data.size.QuadPart == 0)
2466 *bytesRead = 0;
2467 return S_OK;
2470 if (offset.QuadPart + size > data.size.QuadPart)
2472 bytesToRead = data.size.QuadPart - offset.QuadPart;
2474 else
2476 bytesToRead = size;
2479 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2481 SmallBlockChainStream *stream;
2483 stream = SmallBlockChainStream_Construct(This, NULL, index);
2484 if (!stream) return E_OUTOFMEMORY;
2486 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2488 SmallBlockChainStream_Destroy(stream);
2490 return hr;
2492 else
2494 BlockChainStream *stream = NULL;
2496 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2497 if (!stream) return E_OUTOFMEMORY;
2499 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2501 return hr;
2505 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2506 ULARGE_INTEGER newsize)
2508 StorageImpl *This = (StorageImpl*)base;
2509 DirEntry data;
2510 HRESULT hr;
2511 SmallBlockChainStream *smallblock=NULL;
2512 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2514 hr = StorageImpl_ReadDirEntry(This, index, &data);
2515 if (FAILED(hr)) return hr;
2517 /* In simple mode keep the stream size above the small block limit */
2518 if (This->base.openFlags & STGM_SIMPLE)
2519 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2521 if (data.size.QuadPart == newsize.QuadPart)
2522 return S_OK;
2524 /* Create a block chain object of the appropriate type */
2525 if (data.size.QuadPart == 0)
2527 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2529 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2530 if (!smallblock) return E_OUTOFMEMORY;
2532 else
2534 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2535 bigblock = *pbigblock;
2536 if (!bigblock) return E_OUTOFMEMORY;
2539 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2541 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2542 if (!smallblock) return E_OUTOFMEMORY;
2544 else
2546 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2547 bigblock = *pbigblock;
2548 if (!bigblock) return E_OUTOFMEMORY;
2551 /* Change the block chain type if necessary. */
2552 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2554 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2555 if (!bigblock)
2557 SmallBlockChainStream_Destroy(smallblock);
2558 return E_FAIL;
2561 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2562 *pbigblock = bigblock;
2564 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2566 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
2567 if (!smallblock)
2568 return E_FAIL;
2571 /* Set the size of the block chain. */
2572 if (smallblock)
2574 SmallBlockChainStream_SetSize(smallblock, newsize);
2575 SmallBlockChainStream_Destroy(smallblock);
2577 else
2579 BlockChainStream_SetSize(bigblock, newsize);
2582 /* Set the size in the directory entry. */
2583 hr = StorageImpl_ReadDirEntry(This, index, &data);
2584 if (SUCCEEDED(hr))
2586 data.size = newsize;
2588 hr = StorageImpl_WriteDirEntry(This, index, &data);
2590 return hr;
2593 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2594 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2596 StorageImpl *This = (StorageImpl*)base;
2597 DirEntry data;
2598 HRESULT hr;
2599 ULARGE_INTEGER newSize;
2601 hr = StorageImpl_ReadDirEntry(This, index, &data);
2602 if (FAILED(hr)) return hr;
2604 /* Grow the stream if necessary */
2605 newSize.QuadPart = 0;
2606 newSize.QuadPart = offset.QuadPart + size;
2608 if (newSize.QuadPart > data.size.QuadPart)
2610 hr = StorageImpl_StreamSetSize(base, index, newSize);
2611 if (FAILED(hr))
2612 return hr;
2614 hr = StorageImpl_ReadDirEntry(This, index, &data);
2615 if (FAILED(hr)) return hr;
2618 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2620 SmallBlockChainStream *stream;
2622 stream = SmallBlockChainStream_Construct(This, NULL, index);
2623 if (!stream) return E_OUTOFMEMORY;
2625 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2627 SmallBlockChainStream_Destroy(stream);
2629 return hr;
2631 else
2633 BlockChainStream *stream;
2635 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2636 if (!stream) return E_OUTOFMEMORY;
2638 return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2642 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2643 DirRef src)
2645 StorageImpl *This = (StorageImpl*)base;
2646 DirEntry dst_data, src_data;
2647 HRESULT hr;
2649 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2651 if (SUCCEEDED(hr))
2652 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2654 if (SUCCEEDED(hr))
2656 StorageImpl_DeleteCachedBlockChainStream(This, src);
2657 dst_data.startingBlock = src_data.startingBlock;
2658 dst_data.size = src_data.size;
2660 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2663 return hr;
2666 static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
2667 ULONG* result, BOOL refresh)
2669 StorageImpl *This = (StorageImpl*)base;
2670 HRESULT hr=S_OK;
2671 DWORD oldTransactionSig = This->transactionSig;
2673 if (refresh)
2675 ULARGE_INTEGER offset;
2676 ULONG bytes_read;
2677 BYTE data[4];
2679 offset.u.HighPart = 0;
2680 offset.u.LowPart = OFFSET_TRANSACTIONSIG;
2681 hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);
2683 if (SUCCEEDED(hr))
2685 StorageUtl_ReadDWord(data, 0, &This->transactionSig);
2687 if (oldTransactionSig != This->transactionSig)
2689 /* Someone else wrote to this, so toss all cached information. */
2690 TRACE("signature changed\n");
2692 hr = StorageImpl_Refresh(This, FALSE, FALSE);
2695 if (FAILED(hr))
2696 This->transactionSig = oldTransactionSig;
2700 *result = This->transactionSig;
2702 return hr;
2705 static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
2706 ULONG value)
2708 StorageImpl *This = (StorageImpl*)base;
2710 This->transactionSig = value;
2711 StorageImpl_SaveFileHeader(This);
2713 return S_OK;
2716 static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
2718 StorageImpl *This = (StorageImpl*)base;
2719 HRESULT hr;
2720 ULARGE_INTEGER offset, cb;
2722 if (write)
2724 /* Synchronous grab of second priority range, the commit lock, and the
2725 * lock-checking lock. */
2726 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
2727 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
2729 else
2731 offset.QuadPart = RANGELOCK_COMMIT;
2732 cb.QuadPart = 1;
2735 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
2737 if (hr == STG_E_INVALIDFUNCTION)
2738 hr = S_OK;
2740 return hr;
2743 static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
2745 StorageImpl *This = (StorageImpl*)base;
2746 HRESULT hr;
2747 ULARGE_INTEGER offset, cb;
2749 if (write)
2751 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
2752 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
2754 else
2756 offset.QuadPart = RANGELOCK_COMMIT;
2757 cb.QuadPart = 1;
2760 hr = ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2762 if (hr == STG_E_INVALIDFUNCTION)
2763 hr = S_OK;
2765 return hr;
2768 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2770 StorageImpl *This = (StorageImpl*) iface;
2771 STATSTG statstg;
2772 HRESULT hr;
2774 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2776 *result = statstg.pwcsName;
2778 return hr;
2781 static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
2783 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2784 return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
2787 static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
2789 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2790 return IStorage_AddRef(&This->IStorage_iface);
2793 static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
2795 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2796 return IStorage_Release(&This->IStorage_iface);
2799 static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
2801 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2802 FIXME("(%p)->(%d): stub\n", This, timeout);
2803 return E_NOTIMPL;
2806 static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
2808 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2809 FIXME("(%p): stub\n", This);
2810 return E_NOTIMPL;
2813 static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
2815 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2816 FIXME("(%p): stub\n", This);
2817 return E_NOTIMPL;
2820 static const IDirectWriterLockVtbl DirectWriterLockVtbl =
2822 directwriterlock_QueryInterface,
2823 directwriterlock_AddRef,
2824 directwriterlock_Release,
2825 directwriterlock_WaitForWriteAccess,
2826 directwriterlock_ReleaseWriteAccess,
2827 directwriterlock_HaveWriteAccess
2831 * Virtual function table for the IStorage32Impl class.
2833 static const IStorageVtbl Storage32Impl_Vtbl =
2835 StorageBaseImpl_QueryInterface,
2836 StorageBaseImpl_AddRef,
2837 StorageBaseImpl_Release,
2838 StorageBaseImpl_CreateStream,
2839 StorageBaseImpl_OpenStream,
2840 StorageBaseImpl_CreateStorage,
2841 StorageBaseImpl_OpenStorage,
2842 StorageBaseImpl_CopyTo,
2843 StorageBaseImpl_MoveElementTo,
2844 StorageImpl_Commit,
2845 StorageImpl_Revert,
2846 StorageBaseImpl_EnumElements,
2847 StorageBaseImpl_DestroyElement,
2848 StorageBaseImpl_RenameElement,
2849 StorageBaseImpl_SetElementTimes,
2850 StorageBaseImpl_SetClass,
2851 StorageBaseImpl_SetStateBits,
2852 StorageBaseImpl_Stat
2855 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2857 StorageImpl_Destroy,
2858 StorageImpl_Invalidate,
2859 StorageImpl_Flush,
2860 StorageImpl_GetFilename,
2861 StorageImpl_CreateDirEntry,
2862 StorageImpl_BaseWriteDirEntry,
2863 StorageImpl_BaseReadDirEntry,
2864 StorageImpl_DestroyDirEntry,
2865 StorageImpl_StreamReadAt,
2866 StorageImpl_StreamWriteAt,
2867 StorageImpl_StreamSetSize,
2868 StorageImpl_StreamLink,
2869 StorageImpl_GetTransactionSig,
2870 StorageImpl_SetTransactionSig,
2871 StorageImpl_LockTransaction,
2872 StorageImpl_UnlockTransaction
2875 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
2876 ULARGE_INTEGER cb, DWORD dwLockType)
2878 HRESULT hr;
2880 /* if it's a FileLockBytesImpl use LockFileEx in blocking mode */
2881 if (SUCCEEDED(FileLockBytesImpl_LockRegionSync(This->lockBytes, offset, cb)))
2882 return S_OK;
2884 /* otherwise we have to fake it based on an async lock */
2887 int delay=0;
2889 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
2891 if (hr == STG_E_ACCESSDENIED)
2893 Sleep(delay);
2894 if (delay < 150) delay++;
2896 } while (hr == STG_E_ACCESSDENIED);
2898 return hr;
2901 static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
2902 ULONG end, HRESULT fail_hr)
2904 HRESULT hr;
2905 ULARGE_INTEGER offset, cb;
2907 offset.QuadPart = start;
2908 cb.QuadPart = 1 + end - start;
2910 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2911 if (SUCCEEDED(hr)) ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2913 if (hr == STG_E_ACCESSDENIED)
2914 return fail_hr;
2915 else
2916 return S_OK;
2919 static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
2921 HRESULT hr=S_OK;
2922 int i, j;
2923 ULARGE_INTEGER offset, cb;
2925 cb.QuadPart = 1;
2927 for (i=start; i<=end; i++)
2929 offset.QuadPart = i;
2930 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2931 if (hr != STG_E_ACCESSDENIED)
2932 break;
2935 if (SUCCEEDED(hr))
2937 for (j=0; j<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); j++)
2939 if (This->locked_bytes[j] == 0)
2941 This->locked_bytes[j] = i;
2942 break;
2947 return hr;
2950 static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
2952 HRESULT hr;
2953 ULARGE_INTEGER offset;
2954 ULARGE_INTEGER cb;
2955 DWORD share_mode = STGM_SHARE_MODE(openFlags);
2957 if (openFlags & STGM_NOSNAPSHOT)
2959 /* STGM_NOSNAPSHOT implies deny write */
2960 if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
2961 else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
2964 /* Wrap all other locking inside a single lock so we can check ranges safely */
2965 offset.QuadPart = RANGELOCK_CHECKLOCKS;
2966 cb.QuadPart = 1;
2967 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
2969 /* If the ILockBytes doesn't support locking that's ok. */
2970 if (FAILED(hr)) return S_OK;
2972 hr = S_OK;
2974 /* First check for any conflicting locks. */
2975 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
2976 hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
2978 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
2979 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
2981 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
2982 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
2984 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
2985 hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
2987 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
2988 hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
2990 /* Then grab our locks. */
2991 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
2993 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
2994 if (SUCCEEDED(hr))
2995 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
2998 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
2999 hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
3001 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
3002 hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
3004 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
3005 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
3007 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
3008 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
3010 if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
3011 hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
3013 offset.QuadPart = RANGELOCK_CHECKLOCKS;
3014 cb.QuadPart = 1;
3015 ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
3017 return hr;
3020 static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
3022 HRESULT hr=S_OK;
3023 DirEntry currentEntry;
3024 DirRef currentEntryRef;
3025 BlockChainStream *blockChainStream;
3027 if (create)
3029 ULARGE_INTEGER size;
3030 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
3032 /* Discard any existing data. */
3033 size.QuadPart = 0;
3034 ILockBytes_SetSize(This->lockBytes, size);
3037 * Initialize all header variables:
3038 * - The big block depot consists of one block and it is at block 0
3039 * - The directory table starts at block 1
3040 * - There is no small block depot
3042 memset( This->bigBlockDepotStart,
3043 BLOCK_UNUSED,
3044 sizeof(This->bigBlockDepotStart));
3046 This->bigBlockDepotCount = 1;
3047 This->bigBlockDepotStart[0] = 0;
3048 This->rootStartBlock = 1;
3049 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
3050 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
3051 if (This->bigBlockSize == 4096)
3052 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
3053 else
3054 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
3055 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
3056 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
3057 This->extBigBlockDepotCount = 0;
3059 StorageImpl_SaveFileHeader(This);
3062 * Add one block for the big block depot and one block for the directory table
3064 size.u.HighPart = 0;
3065 size.u.LowPart = This->bigBlockSize * 3;
3066 ILockBytes_SetSize(This->lockBytes, size);
3069 * Initialize the big block depot
3071 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3072 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
3073 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
3074 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
3076 else
3079 * Load the header for the file.
3081 hr = StorageImpl_LoadFileHeader(This);
3083 if (FAILED(hr))
3085 return hr;
3090 * There is no block depot cached yet.
3092 This->indexBlockDepotCached = 0xFFFFFFFF;
3093 This->indexExtBlockDepotCached = 0xFFFFFFFF;
3096 * Start searching for free blocks with block 0.
3098 This->prevFreeBlock = 0;
3100 This->firstFreeSmallBlock = 0;
3102 /* Read the extended big block depot locations. */
3103 if (This->extBigBlockDepotCount != 0)
3105 ULONG current_block = This->extBigBlockDepotStart;
3106 ULONG cache_size = This->extBigBlockDepotCount * 2;
3107 ULONG i;
3109 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
3110 if (!This->extBigBlockDepotLocations)
3112 return E_OUTOFMEMORY;
3115 This->extBigBlockDepotLocationsSize = cache_size;
3117 for (i=0; i<This->extBigBlockDepotCount; i++)
3119 if (current_block == BLOCK_END_OF_CHAIN)
3121 WARN("File has too few extended big block depot blocks.\n");
3122 return STG_E_DOCFILECORRUPT;
3124 This->extBigBlockDepotLocations[i] = current_block;
3125 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
3128 else
3130 This->extBigBlockDepotLocations = NULL;
3131 This->extBigBlockDepotLocationsSize = 0;
3135 * Create the block chain abstractions.
3137 if(!(blockChainStream =
3138 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
3140 return STG_E_READFAULT;
3142 if (!new_object)
3143 BlockChainStream_Destroy(This->rootBlockChain);
3144 This->rootBlockChain = blockChainStream;
3146 if(!(blockChainStream =
3147 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
3148 DIRENTRY_NULL)))
3150 return STG_E_READFAULT;
3152 if (!new_object)
3153 BlockChainStream_Destroy(This->smallBlockDepotChain);
3154 This->smallBlockDepotChain = blockChainStream;
3157 * Write the root storage entry (memory only)
3159 if (create)
3161 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
3162 DirEntry rootEntry;
3164 * Initialize the directory table
3166 memset(&rootEntry, 0, sizeof(rootEntry));
3167 strcpyW(rootEntry.name, rootentryW);
3168 rootEntry.sizeOfNameString = sizeof(rootentryW);
3169 rootEntry.stgType = STGTY_ROOT;
3170 rootEntry.leftChild = DIRENTRY_NULL;
3171 rootEntry.rightChild = DIRENTRY_NULL;
3172 rootEntry.dirRootEntry = DIRENTRY_NULL;
3173 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
3174 rootEntry.size.u.HighPart = 0;
3175 rootEntry.size.u.LowPart = 0;
3177 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
3181 * Find the ID of the root storage.
3183 currentEntryRef = 0;
3187 hr = StorageImpl_ReadDirEntry(
3188 This,
3189 currentEntryRef,
3190 &currentEntry);
3192 if (SUCCEEDED(hr))
3194 if ( (currentEntry.sizeOfNameString != 0 ) &&
3195 (currentEntry.stgType == STGTY_ROOT) )
3197 This->base.storageDirEntry = currentEntryRef;
3201 currentEntryRef++;
3203 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
3205 if (FAILED(hr))
3207 return STG_E_READFAULT;
3211 * Create the block chain abstraction for the small block root chain.
3213 if(!(blockChainStream =
3214 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
3216 return STG_E_READFAULT;
3218 if (!new_object)
3219 BlockChainStream_Destroy(This->smallBlockRootChain);
3220 This->smallBlockRootChain = blockChainStream;
3222 if (!new_object)
3224 int i;
3225 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
3227 BlockChainStream_Destroy(This->blockChainCache[i]);
3228 This->blockChainCache[i] = NULL;
3232 return hr;
3235 static HRESULT StorageImpl_Construct(
3236 HANDLE hFile,
3237 LPCOLESTR pwcsName,
3238 ILockBytes* pLkbyt,
3239 DWORD openFlags,
3240 BOOL fileBased,
3241 BOOL create,
3242 ULONG sector_size,
3243 StorageImpl** result)
3245 StorageImpl* This;
3246 HRESULT hr = S_OK;
3248 if ( FAILED( validateSTGM(openFlags) ))
3249 return STG_E_INVALIDFLAG;
3251 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
3252 if (!This)
3253 return E_OUTOFMEMORY;
3255 memset(This, 0, sizeof(StorageImpl));
3257 list_init(&This->base.strmHead);
3259 list_init(&This->base.storageHead);
3261 This->base.IStorage_iface.lpVtbl = &Storage32Impl_Vtbl;
3262 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
3263 This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
3264 This->base.baseVtbl = &StorageImpl_BaseVtbl;
3265 This->base.openFlags = (openFlags & ~STGM_CREATE);
3266 This->base.ref = 1;
3267 This->base.create = create;
3269 if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
3270 This->base.lockingrole = SWMR_Writer;
3271 else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
3272 This->base.lockingrole = SWMR_Reader;
3273 else
3274 This->base.lockingrole = SWMR_None;
3276 This->base.reverted = FALSE;
3279 * Initialize the big block cache.
3281 This->bigBlockSize = sector_size;
3282 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
3283 if (hFile)
3284 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
3285 else
3287 This->lockBytes = pLkbyt;
3288 ILockBytes_AddRef(pLkbyt);
3291 if (SUCCEEDED(hr))
3292 hr = StorageImpl_GrabLocks(This, openFlags);
3294 if (SUCCEEDED(hr))
3295 hr = StorageImpl_Refresh(This, TRUE, create);
3297 if (FAILED(hr))
3299 IStorage_Release(&This->base.IStorage_iface);
3300 *result = NULL;
3302 else
3304 StorageImpl_Flush(&This->base);
3305 *result = This;
3308 return hr;
3311 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
3313 StorageImpl *This = (StorageImpl*) iface;
3315 StorageBaseImpl_DeleteAll(&This->base);
3317 This->base.reverted = TRUE;
3320 static void StorageImpl_Destroy(StorageBaseImpl* iface)
3322 StorageImpl *This = (StorageImpl*) iface;
3323 int i;
3324 TRACE("(%p)\n", This);
3326 StorageImpl_Flush(iface);
3328 StorageImpl_Invalidate(iface);
3330 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3332 BlockChainStream_Destroy(This->smallBlockRootChain);
3333 BlockChainStream_Destroy(This->rootBlockChain);
3334 BlockChainStream_Destroy(This->smallBlockDepotChain);
3336 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
3337 BlockChainStream_Destroy(This->blockChainCache[i]);
3339 for (i=0; i<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); i++)
3341 ULARGE_INTEGER offset, cb;
3342 cb.QuadPart = 1;
3343 if (This->locked_bytes[i] != 0)
3345 offset.QuadPart = This->locked_bytes[i];
3346 ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
3350 if (This->lockBytes)
3351 ILockBytes_Release(This->lockBytes);
3352 HeapFree(GetProcessHeap(), 0, This);
3355 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
3357 StorageImpl *This = (StorageImpl*)storage;
3358 int i;
3359 HRESULT hr;
3360 TRACE("(%p)\n", This);
3362 hr = BlockChainStream_Flush(This->smallBlockRootChain);
3364 if (SUCCEEDED(hr))
3365 hr = BlockChainStream_Flush(This->rootBlockChain);
3367 if (SUCCEEDED(hr))
3368 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
3370 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
3371 if (This->blockChainCache[i])
3372 hr = BlockChainStream_Flush(This->blockChainCache[i]);
3374 if (SUCCEEDED(hr))
3375 hr = ILockBytes_Flush(This->lockBytes);
3377 return hr;
3380 /******************************************************************************
3381 * Storage32Impl_GetNextFreeBigBlock
3383 * Returns the index of the next free big block.
3384 * If the big block depot is filled, this method will enlarge it.
3387 static ULONG StorageImpl_GetNextFreeBigBlock(
3388 StorageImpl* This)
3390 ULONG depotBlockIndexPos;
3391 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3392 ULONG depotBlockOffset;
3393 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3394 ULONG nextBlockIndex = BLOCK_SPECIAL;
3395 int depotIndex = 0;
3396 ULONG freeBlock = BLOCK_UNUSED;
3397 ULONG read;
3398 ULARGE_INTEGER neededSize;
3399 STATSTG statstg;
3401 depotIndex = This->prevFreeBlock / blocksPerDepot;
3402 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
3405 * Scan the entire big block depot until we find a block marked free
3407 while (nextBlockIndex != BLOCK_UNUSED)
3409 if (depotIndex < COUNT_BBDEPOTINHEADER)
3411 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
3414 * Grow the primary depot.
3416 if (depotBlockIndexPos == BLOCK_UNUSED)
3418 depotBlockIndexPos = depotIndex*blocksPerDepot;
3421 * Add a block depot.
3423 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
3424 This->bigBlockDepotCount++;
3425 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3428 * Flag it as a block depot.
3430 StorageImpl_SetNextBlockInChain(This,
3431 depotBlockIndexPos,
3432 BLOCK_SPECIAL);
3434 /* Save new header information.
3436 StorageImpl_SaveFileHeader(This);
3439 else
3441 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3443 if (depotBlockIndexPos == BLOCK_UNUSED)
3446 * Grow the extended depot.
3448 ULONG extIndex = BLOCK_UNUSED;
3449 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3450 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3452 if (extBlockOffset == 0)
3454 /* We need an extended block.
3456 extIndex = Storage32Impl_AddExtBlockDepot(This);
3457 This->extBigBlockDepotCount++;
3458 depotBlockIndexPos = extIndex + 1;
3460 else
3461 depotBlockIndexPos = depotIndex * blocksPerDepot;
3464 * Add a block depot and mark it in the extended block.
3466 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
3467 This->bigBlockDepotCount++;
3468 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3470 /* Flag the block depot.
3472 StorageImpl_SetNextBlockInChain(This,
3473 depotBlockIndexPos,
3474 BLOCK_SPECIAL);
3476 /* If necessary, flag the extended depot block.
3478 if (extIndex != BLOCK_UNUSED)
3479 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3481 /* Save header information.
3483 StorageImpl_SaveFileHeader(This);
3487 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
3489 if (read)
3491 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3492 ( nextBlockIndex != BLOCK_UNUSED))
3494 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3496 if (nextBlockIndex == BLOCK_UNUSED)
3498 freeBlock = (depotIndex * blocksPerDepot) +
3499 (depotBlockOffset/sizeof(ULONG));
3502 depotBlockOffset += sizeof(ULONG);
3506 depotIndex++;
3507 depotBlockOffset = 0;
3511 * make sure that the block physically exists before using it
3513 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3515 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3517 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3518 ILockBytes_SetSize(This->lockBytes, neededSize);
3520 This->prevFreeBlock = freeBlock;
3522 return freeBlock;
3525 /******************************************************************************
3526 * Storage32Impl_AddBlockDepot
3528 * This will create a depot block, essentially it is a block initialized
3529 * to BLOCK_UNUSEDs.
3531 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
3533 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3534 ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
3535 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3536 ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
3539 * Initialize blocks as free
3541 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3543 /* Reserve the range lock sector */
3544 if (depotIndex == rangeLockDepot)
3546 ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
3549 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3552 /******************************************************************************
3553 * Storage32Impl_GetExtDepotBlock
3555 * Returns the index of the block that corresponds to the specified depot
3556 * index. This method is only for depot indexes equal or greater than
3557 * COUNT_BBDEPOTINHEADER.
3559 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3561 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3562 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3563 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3564 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3565 ULONG blockIndex = BLOCK_UNUSED;
3566 ULONG extBlockIndex;
3567 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3568 int index, num_blocks;
3570 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3572 if (extBlockCount >= This->extBigBlockDepotCount)
3573 return BLOCK_UNUSED;
3575 if (This->indexExtBlockDepotCached != extBlockCount)
3577 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3579 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
3581 num_blocks = This->bigBlockSize / 4;
3583 for (index = 0; index < num_blocks; index++)
3585 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3586 This->extBlockDepotCached[index] = blockIndex;
3589 This->indexExtBlockDepotCached = extBlockCount;
3592 blockIndex = This->extBlockDepotCached[extBlockOffset];
3594 return blockIndex;
3597 /******************************************************************************
3598 * Storage32Impl_SetExtDepotBlock
3600 * Associates the specified block index to the specified depot index.
3601 * This method is only for depot indexes equal or greater than
3602 * COUNT_BBDEPOTINHEADER.
3604 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3606 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3607 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3608 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3609 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3610 ULONG extBlockIndex;
3612 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3614 assert(extBlockCount < This->extBigBlockDepotCount);
3616 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3618 if (extBlockIndex != BLOCK_UNUSED)
3620 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3621 extBlockOffset * sizeof(ULONG),
3622 blockIndex);
3625 if (This->indexExtBlockDepotCached == extBlockCount)
3627 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3631 /******************************************************************************
3632 * Storage32Impl_AddExtBlockDepot
3634 * Creates an extended depot block.
3636 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3638 ULONG numExtBlocks = This->extBigBlockDepotCount;
3639 ULONG nextExtBlock = This->extBigBlockDepotStart;
3640 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3641 ULONG index = BLOCK_UNUSED;
3642 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3643 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3644 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3646 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3647 blocksPerDepotBlock;
3649 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3652 * The first extended block.
3654 This->extBigBlockDepotStart = index;
3656 else
3659 * Find the last existing extended block.
3661 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3664 * Add the new extended block to the chain.
3666 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3667 index);
3671 * Initialize this block.
3673 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3674 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3676 /* Add the block to our cache. */
3677 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3679 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3680 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3682 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3683 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3685 This->extBigBlockDepotLocations = new_cache;
3686 This->extBigBlockDepotLocationsSize = new_cache_size;
3688 This->extBigBlockDepotLocations[numExtBlocks] = index;
3690 return index;
3693 /******************************************************************************
3694 * Storage32Impl_FreeBigBlock
3696 * This method will flag the specified block as free in the big block depot.
3698 static void StorageImpl_FreeBigBlock(
3699 StorageImpl* This,
3700 ULONG blockIndex)
3702 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3704 if (blockIndex < This->prevFreeBlock)
3705 This->prevFreeBlock = blockIndex;
3708 /************************************************************************
3709 * Storage32Impl_GetNextBlockInChain
3711 * This method will retrieve the block index of the next big block in
3712 * in the chain.
3714 * Params: This - Pointer to the Storage object.
3715 * blockIndex - Index of the block to retrieve the chain
3716 * for.
3717 * nextBlockIndex - receives the return value.
3719 * Returns: This method returns the index of the next block in the chain.
3720 * It will return the constants:
3721 * BLOCK_SPECIAL - If the block given was not part of a
3722 * chain.
3723 * BLOCK_END_OF_CHAIN - If the block given was the last in
3724 * a chain.
3725 * BLOCK_UNUSED - If the block given was not past of a chain
3726 * and is available.
3727 * BLOCK_EXTBBDEPOT - This block is part of the extended
3728 * big block depot.
3730 * See Windows documentation for more details on IStorage methods.
3732 static HRESULT StorageImpl_GetNextBlockInChain(
3733 StorageImpl* This,
3734 ULONG blockIndex,
3735 ULONG* nextBlockIndex)
3737 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3738 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3739 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3740 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3741 ULONG read;
3742 ULONG depotBlockIndexPos;
3743 int index, num_blocks;
3745 *nextBlockIndex = BLOCK_SPECIAL;
3747 if(depotBlockCount >= This->bigBlockDepotCount)
3749 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3750 This->bigBlockDepotCount);
3751 return STG_E_READFAULT;
3755 * Cache the currently accessed depot block.
3757 if (depotBlockCount != This->indexBlockDepotCached)
3759 This->indexBlockDepotCached = depotBlockCount;
3761 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3763 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3765 else
3768 * We have to look in the extended depot.
3770 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3773 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
3775 if (!read)
3776 return STG_E_READFAULT;
3778 num_blocks = This->bigBlockSize / 4;
3780 for (index = 0; index < num_blocks; index++)
3782 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3783 This->blockDepotCached[index] = *nextBlockIndex;
3787 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3789 return S_OK;
3792 /******************************************************************************
3793 * Storage32Impl_GetNextExtendedBlock
3795 * Given an extended block this method will return the next extended block.
3797 * NOTES:
3798 * The last ULONG of an extended block is the block index of the next
3799 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3800 * depot.
3802 * Return values:
3803 * - The index of the next extended block
3804 * - BLOCK_UNUSED: there is no next extended block.
3805 * - Any other return values denotes failure.
3807 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3809 ULONG nextBlockIndex = BLOCK_SPECIAL;
3810 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3812 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3813 &nextBlockIndex);
3815 return nextBlockIndex;
3818 /******************************************************************************
3819 * Storage32Impl_SetNextBlockInChain
3821 * This method will write the index of the specified block's next block
3822 * in the big block depot.
3824 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3825 * do the following
3827 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3828 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3829 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3832 static void StorageImpl_SetNextBlockInChain(
3833 StorageImpl* This,
3834 ULONG blockIndex,
3835 ULONG nextBlock)
3837 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3838 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3839 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3840 ULONG depotBlockIndexPos;
3842 assert(depotBlockCount < This->bigBlockDepotCount);
3843 assert(blockIndex != nextBlock);
3845 if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
3846 /* This should never happen (storage file format spec forbids it), but
3847 * older versions of Wine may have generated broken files. We don't want to
3848 * assert and potentially lose data, but we do want to know if this ever
3849 * happens in a newly-created file. */
3850 ERR("Using range lock page\n");
3852 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3854 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3856 else
3859 * We have to look in the extended depot.
3861 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3864 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3865 nextBlock);
3867 * Update the cached block depot, if necessary.
3869 if (depotBlockCount == This->indexBlockDepotCached)
3871 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3875 /******************************************************************************
3876 * Storage32Impl_LoadFileHeader
3878 * This method will read in the file header
3880 static HRESULT StorageImpl_LoadFileHeader(
3881 StorageImpl* This)
3883 HRESULT hr;
3884 BYTE headerBigBlock[HEADER_SIZE];
3885 int index;
3886 ULARGE_INTEGER offset;
3887 DWORD bytes_read;
3889 TRACE("\n");
3891 * Get a pointer to the big block of data containing the header.
3893 offset.u.HighPart = 0;
3894 offset.u.LowPart = 0;
3895 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3896 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3897 hr = STG_E_FILENOTFOUND;
3900 * Extract the information from the header.
3902 if (SUCCEEDED(hr))
3905 * Check for the "magic number" signature and return an error if it is not
3906 * found.
3908 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3910 return STG_E_OLDFORMAT;
3913 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3915 return STG_E_INVALIDHEADER;
3918 StorageUtl_ReadWord(
3919 headerBigBlock,
3920 OFFSET_BIGBLOCKSIZEBITS,
3921 &This->bigBlockSizeBits);
3923 StorageUtl_ReadWord(
3924 headerBigBlock,
3925 OFFSET_SMALLBLOCKSIZEBITS,
3926 &This->smallBlockSizeBits);
3928 StorageUtl_ReadDWord(
3929 headerBigBlock,
3930 OFFSET_BBDEPOTCOUNT,
3931 &This->bigBlockDepotCount);
3933 StorageUtl_ReadDWord(
3934 headerBigBlock,
3935 OFFSET_ROOTSTARTBLOCK,
3936 &This->rootStartBlock);
3938 StorageUtl_ReadDWord(
3939 headerBigBlock,
3940 OFFSET_TRANSACTIONSIG,
3941 &This->transactionSig);
3943 StorageUtl_ReadDWord(
3944 headerBigBlock,
3945 OFFSET_SMALLBLOCKLIMIT,
3946 &This->smallBlockLimit);
3948 StorageUtl_ReadDWord(
3949 headerBigBlock,
3950 OFFSET_SBDEPOTSTART,
3951 &This->smallBlockDepotStart);
3953 StorageUtl_ReadDWord(
3954 headerBigBlock,
3955 OFFSET_EXTBBDEPOTSTART,
3956 &This->extBigBlockDepotStart);
3958 StorageUtl_ReadDWord(
3959 headerBigBlock,
3960 OFFSET_EXTBBDEPOTCOUNT,
3961 &This->extBigBlockDepotCount);
3963 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3965 StorageUtl_ReadDWord(
3966 headerBigBlock,
3967 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3968 &(This->bigBlockDepotStart[index]));
3972 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3974 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3975 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3978 * Right now, the code is making some assumptions about the size of the
3979 * blocks, just make sure they are what we're expecting.
3981 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3982 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3983 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3985 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3986 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3987 hr = STG_E_INVALIDHEADER;
3989 else
3990 hr = S_OK;
3993 return hr;
3996 /******************************************************************************
3997 * Storage32Impl_SaveFileHeader
3999 * This method will save to the file the header
4001 static void StorageImpl_SaveFileHeader(
4002 StorageImpl* This)
4004 BYTE headerBigBlock[HEADER_SIZE];
4005 int index;
4006 HRESULT hr;
4007 ULARGE_INTEGER offset;
4008 DWORD bytes_read, bytes_written;
4009 DWORD major_version, dirsectorcount;
4012 * Get a pointer to the big block of data containing the header.
4014 offset.u.HighPart = 0;
4015 offset.u.LowPart = 0;
4016 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
4017 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
4018 hr = STG_E_FILENOTFOUND;
4020 if (This->bigBlockSizeBits == 0x9)
4021 major_version = 3;
4022 else if (This->bigBlockSizeBits == 0xc)
4023 major_version = 4;
4024 else
4026 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
4027 major_version = 4;
4031 * If the block read failed, the file is probably new.
4033 if (FAILED(hr))
4036 * Initialize for all unknown fields.
4038 memset(headerBigBlock, 0, HEADER_SIZE);
4041 * Initialize the magic number.
4043 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
4047 * Write the information to the header.
4049 StorageUtl_WriteWord(
4050 headerBigBlock,
4051 OFFSET_MINORVERSION,
4052 0x3e);
4054 StorageUtl_WriteWord(
4055 headerBigBlock,
4056 OFFSET_MAJORVERSION,
4057 major_version);
4059 StorageUtl_WriteWord(
4060 headerBigBlock,
4061 OFFSET_BYTEORDERMARKER,
4062 (WORD)-2);
4064 StorageUtl_WriteWord(
4065 headerBigBlock,
4066 OFFSET_BIGBLOCKSIZEBITS,
4067 This->bigBlockSizeBits);
4069 StorageUtl_WriteWord(
4070 headerBigBlock,
4071 OFFSET_SMALLBLOCKSIZEBITS,
4072 This->smallBlockSizeBits);
4074 if (major_version >= 4)
4076 if (This->rootBlockChain)
4077 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
4078 else
4079 /* This file is being created, and it will start out with one block. */
4080 dirsectorcount = 1;
4082 else
4083 /* This field must be 0 in versions older than 4 */
4084 dirsectorcount = 0;
4086 StorageUtl_WriteDWord(
4087 headerBigBlock,
4088 OFFSET_DIRSECTORCOUNT,
4089 dirsectorcount);
4091 StorageUtl_WriteDWord(
4092 headerBigBlock,
4093 OFFSET_BBDEPOTCOUNT,
4094 This->bigBlockDepotCount);
4096 StorageUtl_WriteDWord(
4097 headerBigBlock,
4098 OFFSET_ROOTSTARTBLOCK,
4099 This->rootStartBlock);
4101 StorageUtl_WriteDWord(
4102 headerBigBlock,
4103 OFFSET_TRANSACTIONSIG,
4104 This->transactionSig);
4106 StorageUtl_WriteDWord(
4107 headerBigBlock,
4108 OFFSET_SMALLBLOCKLIMIT,
4109 This->smallBlockLimit);
4111 StorageUtl_WriteDWord(
4112 headerBigBlock,
4113 OFFSET_SBDEPOTSTART,
4114 This->smallBlockDepotStart);
4116 StorageUtl_WriteDWord(
4117 headerBigBlock,
4118 OFFSET_SBDEPOTCOUNT,
4119 This->smallBlockDepotChain ?
4120 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
4122 StorageUtl_WriteDWord(
4123 headerBigBlock,
4124 OFFSET_EXTBBDEPOTSTART,
4125 This->extBigBlockDepotStart);
4127 StorageUtl_WriteDWord(
4128 headerBigBlock,
4129 OFFSET_EXTBBDEPOTCOUNT,
4130 This->extBigBlockDepotCount);
4132 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
4134 StorageUtl_WriteDWord(
4135 headerBigBlock,
4136 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
4137 (This->bigBlockDepotStart[index]));
4141 * Write the big block back to the file.
4143 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
4146 /******************************************************************************
4147 * StorageImpl_ReadRawDirEntry
4149 * This method will read the raw data from a directory entry in the file.
4151 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4153 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
4155 ULARGE_INTEGER offset;
4156 HRESULT hr;
4157 ULONG bytesRead;
4159 offset.u.HighPart = 0;
4160 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
4162 hr = BlockChainStream_ReadAt(
4163 This->rootBlockChain,
4164 offset,
4165 RAW_DIRENTRY_SIZE,
4166 buffer,
4167 &bytesRead);
4169 if (bytesRead != RAW_DIRENTRY_SIZE)
4170 return STG_E_READFAULT;
4172 return hr;
4175 /******************************************************************************
4176 * StorageImpl_WriteRawDirEntry
4178 * This method will write the raw data from a directory entry in the file.
4180 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4182 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
4184 ULARGE_INTEGER offset;
4185 ULONG bytesRead;
4187 offset.u.HighPart = 0;
4188 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
4190 return BlockChainStream_WriteAt(
4191 This->rootBlockChain,
4192 offset,
4193 RAW_DIRENTRY_SIZE,
4194 buffer,
4195 &bytesRead);
4198 /******************************************************************************
4199 * UpdateRawDirEntry
4201 * Update raw directory entry data from the fields in newData.
4203 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4205 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
4207 memset(buffer, 0, RAW_DIRENTRY_SIZE);
4209 memcpy(
4210 buffer + OFFSET_PS_NAME,
4211 newData->name,
4212 DIRENTRY_NAME_BUFFER_LEN );
4214 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
4216 StorageUtl_WriteWord(
4217 buffer,
4218 OFFSET_PS_NAMELENGTH,
4219 newData->sizeOfNameString);
4221 StorageUtl_WriteDWord(
4222 buffer,
4223 OFFSET_PS_LEFTCHILD,
4224 newData->leftChild);
4226 StorageUtl_WriteDWord(
4227 buffer,
4228 OFFSET_PS_RIGHTCHILD,
4229 newData->rightChild);
4231 StorageUtl_WriteDWord(
4232 buffer,
4233 OFFSET_PS_DIRROOT,
4234 newData->dirRootEntry);
4236 StorageUtl_WriteGUID(
4237 buffer,
4238 OFFSET_PS_GUID,
4239 &newData->clsid);
4241 StorageUtl_WriteDWord(
4242 buffer,
4243 OFFSET_PS_CTIMELOW,
4244 newData->ctime.dwLowDateTime);
4246 StorageUtl_WriteDWord(
4247 buffer,
4248 OFFSET_PS_CTIMEHIGH,
4249 newData->ctime.dwHighDateTime);
4251 StorageUtl_WriteDWord(
4252 buffer,
4253 OFFSET_PS_MTIMELOW,
4254 newData->mtime.dwLowDateTime);
4256 StorageUtl_WriteDWord(
4257 buffer,
4258 OFFSET_PS_MTIMEHIGH,
4259 newData->ctime.dwHighDateTime);
4261 StorageUtl_WriteDWord(
4262 buffer,
4263 OFFSET_PS_STARTBLOCK,
4264 newData->startingBlock);
4266 StorageUtl_WriteDWord(
4267 buffer,
4268 OFFSET_PS_SIZE,
4269 newData->size.u.LowPart);
4272 /******************************************************************************
4273 * Storage32Impl_ReadDirEntry
4275 * This method will read the specified directory entry.
4277 HRESULT StorageImpl_ReadDirEntry(
4278 StorageImpl* This,
4279 DirRef index,
4280 DirEntry* buffer)
4282 BYTE currentEntry[RAW_DIRENTRY_SIZE];
4283 HRESULT readRes;
4285 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
4287 if (SUCCEEDED(readRes))
4289 memset(buffer->name, 0, sizeof(buffer->name));
4290 memcpy(
4291 buffer->name,
4292 (WCHAR *)currentEntry+OFFSET_PS_NAME,
4293 DIRENTRY_NAME_BUFFER_LEN );
4294 TRACE("storage name: %s\n", debugstr_w(buffer->name));
4296 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
4298 StorageUtl_ReadWord(
4299 currentEntry,
4300 OFFSET_PS_NAMELENGTH,
4301 &buffer->sizeOfNameString);
4303 StorageUtl_ReadDWord(
4304 currentEntry,
4305 OFFSET_PS_LEFTCHILD,
4306 &buffer->leftChild);
4308 StorageUtl_ReadDWord(
4309 currentEntry,
4310 OFFSET_PS_RIGHTCHILD,
4311 &buffer->rightChild);
4313 StorageUtl_ReadDWord(
4314 currentEntry,
4315 OFFSET_PS_DIRROOT,
4316 &buffer->dirRootEntry);
4318 StorageUtl_ReadGUID(
4319 currentEntry,
4320 OFFSET_PS_GUID,
4321 &buffer->clsid);
4323 StorageUtl_ReadDWord(
4324 currentEntry,
4325 OFFSET_PS_CTIMELOW,
4326 &buffer->ctime.dwLowDateTime);
4328 StorageUtl_ReadDWord(
4329 currentEntry,
4330 OFFSET_PS_CTIMEHIGH,
4331 &buffer->ctime.dwHighDateTime);
4333 StorageUtl_ReadDWord(
4334 currentEntry,
4335 OFFSET_PS_MTIMELOW,
4336 &buffer->mtime.dwLowDateTime);
4338 StorageUtl_ReadDWord(
4339 currentEntry,
4340 OFFSET_PS_MTIMEHIGH,
4341 &buffer->mtime.dwHighDateTime);
4343 StorageUtl_ReadDWord(
4344 currentEntry,
4345 OFFSET_PS_STARTBLOCK,
4346 &buffer->startingBlock);
4348 StorageUtl_ReadDWord(
4349 currentEntry,
4350 OFFSET_PS_SIZE,
4351 &buffer->size.u.LowPart);
4353 buffer->size.u.HighPart = 0;
4356 return readRes;
4359 /*********************************************************************
4360 * Write the specified directory entry to the file
4362 HRESULT StorageImpl_WriteDirEntry(
4363 StorageImpl* This,
4364 DirRef index,
4365 const DirEntry* buffer)
4367 BYTE currentEntry[RAW_DIRENTRY_SIZE];
4369 UpdateRawDirEntry(currentEntry, buffer);
4371 return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
4374 static HRESULT StorageImpl_ReadBigBlock(
4375 StorageImpl* This,
4376 ULONG blockIndex,
4377 void* buffer,
4378 ULONG* out_read)
4380 ULARGE_INTEGER ulOffset;
4381 DWORD read=0;
4382 HRESULT hr;
4384 ulOffset.u.HighPart = 0;
4385 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4387 hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
4389 if (SUCCEEDED(hr) && read < This->bigBlockSize)
4391 /* File ends during this block; fill the rest with 0's. */
4392 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
4395 if (out_read) *out_read = read;
4397 return hr;
4400 static BOOL StorageImpl_ReadDWordFromBigBlock(
4401 StorageImpl* This,
4402 ULONG blockIndex,
4403 ULONG offset,
4404 DWORD* value)
4406 ULARGE_INTEGER ulOffset;
4407 DWORD read;
4408 DWORD tmp;
4410 ulOffset.u.HighPart = 0;
4411 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4412 ulOffset.u.LowPart += offset;
4414 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
4415 *value = lendian32toh(tmp);
4416 return (read == sizeof(DWORD));
4419 static BOOL StorageImpl_WriteBigBlock(
4420 StorageImpl* This,
4421 ULONG blockIndex,
4422 const void* buffer)
4424 ULARGE_INTEGER ulOffset;
4425 DWORD wrote;
4427 ulOffset.u.HighPart = 0;
4428 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4430 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
4431 return (wrote == This->bigBlockSize);
4434 static BOOL StorageImpl_WriteDWordToBigBlock(
4435 StorageImpl* This,
4436 ULONG blockIndex,
4437 ULONG offset,
4438 DWORD value)
4440 ULARGE_INTEGER ulOffset;
4441 DWORD wrote;
4443 ulOffset.u.HighPart = 0;
4444 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4445 ulOffset.u.LowPart += 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.u.LowPart / This->parentStorage->bigBlockSize;
7033 ULONG offsetInBlock = offset.u.LowPart % 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.u.HighPart = 0;
7081 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7082 offsetInBlock;
7084 StorageImpl_ReadAt(This->parentStorage,
7085 ulOffset,
7086 bufferWalker,
7087 bytesToReadInBuffer,
7088 &bytesReadAt);
7090 else
7092 if (!cachedBlock->read)
7094 ULONG read;
7095 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7096 return STG_E_READFAULT;
7098 cachedBlock->read = TRUE;
7101 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
7102 bytesReadAt = bytesToReadInBuffer;
7105 blockNoInSequence++;
7106 bufferWalker += bytesReadAt;
7107 size -= bytesReadAt;
7108 *bytesRead += bytesReadAt;
7109 offsetInBlock = 0; /* There is no offset on the next block */
7111 if (bytesToReadInBuffer != bytesReadAt)
7112 break;
7115 return S_OK;
7118 /******************************************************************************
7119 * BlockChainStream_WriteAt
7121 * Writes the specified number of bytes to this chain at the specified offset.
7122 * Will fail if not all specified number of bytes have been written.
7124 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
7125 ULARGE_INTEGER offset,
7126 ULONG size,
7127 const void* buffer,
7128 ULONG* bytesWritten)
7130 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
7131 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
7132 ULONG bytesToWrite;
7133 ULONG blockIndex;
7134 const BYTE* bufferWalker;
7135 HRESULT hr;
7136 BlockChainBlock *cachedBlock;
7138 *bytesWritten = 0;
7139 bufferWalker = buffer;
7141 while (size > 0)
7143 ULARGE_INTEGER ulOffset;
7144 DWORD bytesWrittenAt;
7147 * Calculate how many bytes we can copy to this big block.
7149 bytesToWrite =
7150 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
7152 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
7154 /* BlockChainStream_SetSize should have already been called to ensure we have
7155 * enough blocks in the chain to write into */
7156 if (FAILED(hr))
7158 ERR("not enough blocks in chain to write data\n");
7159 return hr;
7162 if (!cachedBlock)
7164 /* Not in cache, and we're going to write past the end of the block. */
7165 ulOffset.u.HighPart = 0;
7166 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
7167 offsetInBlock;
7169 StorageImpl_WriteAt(This->parentStorage,
7170 ulOffset,
7171 bufferWalker,
7172 bytesToWrite,
7173 &bytesWrittenAt);
7175 else
7177 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
7179 ULONG read;
7180 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
7181 return STG_E_READFAULT;
7184 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
7185 bytesWrittenAt = bytesToWrite;
7186 cachedBlock->read = TRUE;
7187 cachedBlock->dirty = TRUE;
7190 blockNoInSequence++;
7191 bufferWalker += bytesWrittenAt;
7192 size -= bytesWrittenAt;
7193 *bytesWritten += bytesWrittenAt;
7194 offsetInBlock = 0; /* There is no offset on the next block */
7196 if (bytesWrittenAt != bytesToWrite)
7197 break;
7200 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7203 /******************************************************************************
7204 * BlockChainStream_Shrink
7206 * Shrinks this chain in the big block depot.
7208 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
7209 ULARGE_INTEGER newSize)
7211 ULONG blockIndex;
7212 ULONG numBlocks;
7213 int i;
7216 * Figure out how many blocks are needed to contain the new size
7218 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
7220 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
7221 numBlocks++;
7223 if (numBlocks)
7226 * Go to the new end of chain
7228 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
7230 /* Mark the new end of chain */
7231 StorageImpl_SetNextBlockInChain(
7232 This->parentStorage,
7233 blockIndex,
7234 BLOCK_END_OF_CHAIN);
7236 This->tailIndex = blockIndex;
7238 else
7240 if (This->headOfStreamPlaceHolder != 0)
7242 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
7244 else
7246 DirEntry chainEntry;
7247 assert(This->ownerDirEntry != DIRENTRY_NULL);
7249 StorageImpl_ReadDirEntry(
7250 This->parentStorage,
7251 This->ownerDirEntry,
7252 &chainEntry);
7254 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7256 StorageImpl_WriteDirEntry(
7257 This->parentStorage,
7258 This->ownerDirEntry,
7259 &chainEntry);
7262 This->tailIndex = BLOCK_END_OF_CHAIN;
7265 This->numBlocks = numBlocks;
7268 * Mark the extra blocks as free
7270 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
7272 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
7273 StorageImpl_FreeBigBlock(This->parentStorage,
7274 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
7275 if (last_run->lastOffset == last_run->firstOffset)
7276 This->indexCacheLen--;
7277 else
7278 last_run->lastOffset--;
7282 * Reset the last accessed block cache.
7284 for (i=0; i<2; i++)
7286 if (This->cachedBlocks[i].index >= numBlocks)
7288 This->cachedBlocks[i].index = 0xffffffff;
7289 This->cachedBlocks[i].dirty = FALSE;
7293 return TRUE;
7296 /******************************************************************************
7297 * BlockChainStream_Enlarge
7299 * Grows this chain in the big block depot.
7301 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
7302 ULARGE_INTEGER newSize)
7304 ULONG blockIndex, currentBlock;
7305 ULONG newNumBlocks;
7306 ULONG oldNumBlocks = 0;
7308 blockIndex = BlockChainStream_GetHeadOfChain(This);
7311 * Empty chain. Create the head.
7313 if (blockIndex == BLOCK_END_OF_CHAIN)
7315 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7316 StorageImpl_SetNextBlockInChain(This->parentStorage,
7317 blockIndex,
7318 BLOCK_END_OF_CHAIN);
7320 if (This->headOfStreamPlaceHolder != 0)
7322 *(This->headOfStreamPlaceHolder) = blockIndex;
7324 else
7326 DirEntry chainEntry;
7327 assert(This->ownerDirEntry != DIRENTRY_NULL);
7329 StorageImpl_ReadDirEntry(
7330 This->parentStorage,
7331 This->ownerDirEntry,
7332 &chainEntry);
7334 chainEntry.startingBlock = blockIndex;
7336 StorageImpl_WriteDirEntry(
7337 This->parentStorage,
7338 This->ownerDirEntry,
7339 &chainEntry);
7342 This->tailIndex = blockIndex;
7343 This->numBlocks = 1;
7347 * Figure out how many blocks are needed to contain this stream
7349 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
7351 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
7352 newNumBlocks++;
7355 * Go to the current end of chain
7357 if (This->tailIndex == BLOCK_END_OF_CHAIN)
7359 currentBlock = blockIndex;
7361 while (blockIndex != BLOCK_END_OF_CHAIN)
7363 This->numBlocks++;
7364 currentBlock = blockIndex;
7366 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
7367 &blockIndex)))
7368 return FALSE;
7371 This->tailIndex = currentBlock;
7374 currentBlock = This->tailIndex;
7375 oldNumBlocks = This->numBlocks;
7378 * Add new blocks to the chain
7380 if (oldNumBlocks < newNumBlocks)
7382 while (oldNumBlocks < newNumBlocks)
7384 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
7386 StorageImpl_SetNextBlockInChain(
7387 This->parentStorage,
7388 currentBlock,
7389 blockIndex);
7391 StorageImpl_SetNextBlockInChain(
7392 This->parentStorage,
7393 blockIndex,
7394 BLOCK_END_OF_CHAIN);
7396 currentBlock = blockIndex;
7397 oldNumBlocks++;
7400 This->tailIndex = blockIndex;
7401 This->numBlocks = newNumBlocks;
7404 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
7405 return FALSE;
7407 return TRUE;
7410 /******************************************************************************
7411 * BlockChainStream_SetSize
7413 * Sets the size of this stream. The big block depot will be updated.
7414 * The file will grow if we grow the chain.
7416 * TODO: Free the actual blocks in the file when we shrink the chain.
7417 * Currently, the blocks are still in the file. So the file size
7418 * doesn't shrink even if we shrink streams.
7420 BOOL BlockChainStream_SetSize(
7421 BlockChainStream* This,
7422 ULARGE_INTEGER newSize)
7424 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
7426 if (newSize.u.LowPart == size.u.LowPart)
7427 return TRUE;
7429 if (newSize.u.LowPart < size.u.LowPart)
7431 BlockChainStream_Shrink(This, newSize);
7433 else
7435 BlockChainStream_Enlarge(This, newSize);
7438 return TRUE;
7441 /******************************************************************************
7442 * BlockChainStream_GetSize
7444 * Returns the size of this chain.
7445 * Will return the block count if this chain doesn't have a directory entry.
7447 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
7449 DirEntry chainEntry;
7451 if(This->headOfStreamPlaceHolder == NULL)
7454 * This chain has a directory entry so use the size value from there.
7456 StorageImpl_ReadDirEntry(
7457 This->parentStorage,
7458 This->ownerDirEntry,
7459 &chainEntry);
7461 return chainEntry.size;
7463 else
7466 * this chain is a chain that does not have a directory entry, figure out the
7467 * size by making the product number of used blocks times the
7468 * size of them
7470 ULARGE_INTEGER result;
7471 result.u.HighPart = 0;
7473 result.u.LowPart =
7474 BlockChainStream_GetCount(This) *
7475 This->parentStorage->bigBlockSize;
7477 return result;
7481 /******************************************************************************
7482 ** SmallBlockChainStream implementation
7485 SmallBlockChainStream* SmallBlockChainStream_Construct(
7486 StorageImpl* parentStorage,
7487 ULONG* headOfStreamPlaceHolder,
7488 DirRef dirEntry)
7490 SmallBlockChainStream* newStream;
7492 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
7494 newStream->parentStorage = parentStorage;
7495 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
7496 newStream->ownerDirEntry = dirEntry;
7498 return newStream;
7501 void SmallBlockChainStream_Destroy(
7502 SmallBlockChainStream* This)
7504 HeapFree(GetProcessHeap(), 0, This);
7507 /******************************************************************************
7508 * SmallBlockChainStream_GetHeadOfChain
7510 * Returns the head of this chain of small blocks.
7512 static ULONG SmallBlockChainStream_GetHeadOfChain(
7513 SmallBlockChainStream* This)
7515 DirEntry chainEntry;
7516 HRESULT hr;
7518 if (This->headOfStreamPlaceHolder != NULL)
7519 return *(This->headOfStreamPlaceHolder);
7521 if (This->ownerDirEntry)
7523 hr = StorageImpl_ReadDirEntry(
7524 This->parentStorage,
7525 This->ownerDirEntry,
7526 &chainEntry);
7528 if (SUCCEEDED(hr))
7530 return chainEntry.startingBlock;
7535 return BLOCK_END_OF_CHAIN;
7538 /******************************************************************************
7539 * SmallBlockChainStream_GetNextBlockInChain
7541 * Returns the index of the next small block in this chain.
7543 * Return Values:
7544 * - BLOCK_END_OF_CHAIN: end of this chain
7545 * - BLOCK_UNUSED: small block 'blockIndex' is free
7547 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
7548 SmallBlockChainStream* This,
7549 ULONG blockIndex,
7550 ULONG* nextBlockInChain)
7552 ULARGE_INTEGER offsetOfBlockInDepot;
7553 DWORD buffer;
7554 ULONG bytesRead;
7555 HRESULT res;
7557 *nextBlockInChain = BLOCK_END_OF_CHAIN;
7559 offsetOfBlockInDepot.u.HighPart = 0;
7560 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
7563 * Read those bytes in the buffer from the small block file.
7565 res = BlockChainStream_ReadAt(
7566 This->parentStorage->smallBlockDepotChain,
7567 offsetOfBlockInDepot,
7568 sizeof(DWORD),
7569 &buffer,
7570 &bytesRead);
7572 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
7573 res = STG_E_READFAULT;
7575 if (SUCCEEDED(res))
7577 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
7578 return S_OK;
7581 return res;
7584 /******************************************************************************
7585 * SmallBlockChainStream_SetNextBlockInChain
7587 * Writes the index of the next block of the specified block in the small
7588 * block depot.
7589 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7590 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7592 static void SmallBlockChainStream_SetNextBlockInChain(
7593 SmallBlockChainStream* This,
7594 ULONG blockIndex,
7595 ULONG nextBlock)
7597 ULARGE_INTEGER offsetOfBlockInDepot;
7598 DWORD buffer;
7599 ULONG bytesWritten;
7601 offsetOfBlockInDepot.u.HighPart = 0;
7602 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
7604 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
7607 * Read those bytes in the buffer from the small block file.
7609 BlockChainStream_WriteAt(
7610 This->parentStorage->smallBlockDepotChain,
7611 offsetOfBlockInDepot,
7612 sizeof(DWORD),
7613 &buffer,
7614 &bytesWritten);
7617 /******************************************************************************
7618 * SmallBlockChainStream_FreeBlock
7620 * Flag small block 'blockIndex' as free in the small block depot.
7622 static void SmallBlockChainStream_FreeBlock(
7623 SmallBlockChainStream* This,
7624 ULONG blockIndex)
7626 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
7629 /******************************************************************************
7630 * SmallBlockChainStream_GetNextFreeBlock
7632 * Returns the index of a free small block. The small block depot will be
7633 * enlarged if necessary. The small block chain will also be enlarged if
7634 * necessary.
7636 static ULONG SmallBlockChainStream_GetNextFreeBlock(
7637 SmallBlockChainStream* This)
7639 ULARGE_INTEGER offsetOfBlockInDepot;
7640 DWORD buffer;
7641 ULONG bytesRead;
7642 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
7643 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
7644 HRESULT res = S_OK;
7645 ULONG smallBlocksPerBigBlock;
7646 DirEntry rootEntry;
7647 ULONG blocksRequired;
7648 ULARGE_INTEGER old_size, size_required;
7650 offsetOfBlockInDepot.u.HighPart = 0;
7653 * Scan the small block depot for a free block
7655 while (nextBlockIndex != BLOCK_UNUSED)
7657 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
7659 res = BlockChainStream_ReadAt(
7660 This->parentStorage->smallBlockDepotChain,
7661 offsetOfBlockInDepot,
7662 sizeof(DWORD),
7663 &buffer,
7664 &bytesRead);
7667 * If we run out of space for the small block depot, enlarge it
7669 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
7671 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
7673 if (nextBlockIndex != BLOCK_UNUSED)
7674 blockIndex++;
7676 else
7678 ULONG count =
7679 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
7681 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
7682 ULARGE_INTEGER newSize, offset;
7683 ULONG bytesWritten;
7685 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
7686 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
7689 * Initialize all the small blocks to free
7691 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
7692 offset.QuadPart = count * This->parentStorage->bigBlockSize;
7693 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
7694 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
7696 StorageImpl_SaveFileHeader(This->parentStorage);
7700 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
7702 smallBlocksPerBigBlock =
7703 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
7706 * Verify if we have to allocate big blocks to contain small blocks
7708 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
7710 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
7712 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
7714 if (size_required.QuadPart > old_size.QuadPart)
7716 BlockChainStream_SetSize(
7717 This->parentStorage->smallBlockRootChain,
7718 size_required);
7720 StorageImpl_ReadDirEntry(
7721 This->parentStorage,
7722 This->parentStorage->base.storageDirEntry,
7723 &rootEntry);
7725 rootEntry.size = size_required;
7727 StorageImpl_WriteDirEntry(
7728 This->parentStorage,
7729 This->parentStorage->base.storageDirEntry,
7730 &rootEntry);
7733 return blockIndex;
7736 /******************************************************************************
7737 * SmallBlockChainStream_ReadAt
7739 * Reads a specified number of bytes from this chain at the specified offset.
7740 * bytesRead may be NULL.
7741 * Failure will be returned if the specified number of bytes has not been read.
7743 HRESULT SmallBlockChainStream_ReadAt(
7744 SmallBlockChainStream* This,
7745 ULARGE_INTEGER offset,
7746 ULONG size,
7747 void* buffer,
7748 ULONG* bytesRead)
7750 HRESULT rc = S_OK;
7751 ULARGE_INTEGER offsetInBigBlockFile;
7752 ULONG blockNoInSequence =
7753 offset.u.LowPart / This->parentStorage->smallBlockSize;
7755 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7756 ULONG bytesToReadInBuffer;
7757 ULONG blockIndex;
7758 ULONG bytesReadFromBigBlockFile;
7759 BYTE* bufferWalker;
7760 ULARGE_INTEGER stream_size;
7763 * This should never happen on a small block file.
7765 assert(offset.u.HighPart==0);
7767 *bytesRead = 0;
7769 stream_size = SmallBlockChainStream_GetSize(This);
7770 if (stream_size.QuadPart > offset.QuadPart)
7771 size = min(stream_size.QuadPart - offset.QuadPart, size);
7772 else
7773 return S_OK;
7776 * Find the first block in the stream that contains part of the buffer.
7778 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7780 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7782 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7783 if(FAILED(rc))
7784 return rc;
7785 blockNoInSequence--;
7789 * Start reading the buffer.
7791 bufferWalker = buffer;
7793 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7796 * Calculate how many bytes we can copy from this small block.
7798 bytesToReadInBuffer =
7799 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7802 * Calculate the offset of the small block in the small block file.
7804 offsetInBigBlockFile.u.HighPart = 0;
7805 offsetInBigBlockFile.u.LowPart =
7806 blockIndex * This->parentStorage->smallBlockSize;
7808 offsetInBigBlockFile.u.LowPart += offsetInBlock;
7811 * Read those bytes in the buffer from the small block file.
7812 * The small block has already been identified so it shouldn't fail
7813 * unless the file is corrupt.
7815 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
7816 offsetInBigBlockFile,
7817 bytesToReadInBuffer,
7818 bufferWalker,
7819 &bytesReadFromBigBlockFile);
7821 if (FAILED(rc))
7822 return rc;
7824 if (!bytesReadFromBigBlockFile)
7825 return STG_E_DOCFILECORRUPT;
7828 * Step to the next big block.
7830 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7831 if(FAILED(rc))
7832 return STG_E_DOCFILECORRUPT;
7834 bufferWalker += bytesReadFromBigBlockFile;
7835 size -= bytesReadFromBigBlockFile;
7836 *bytesRead += bytesReadFromBigBlockFile;
7837 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
7840 return S_OK;
7843 /******************************************************************************
7844 * SmallBlockChainStream_WriteAt
7846 * Writes the specified number of bytes to this chain at the specified offset.
7847 * Will fail if not all specified number of bytes have been written.
7849 HRESULT SmallBlockChainStream_WriteAt(
7850 SmallBlockChainStream* This,
7851 ULARGE_INTEGER offset,
7852 ULONG size,
7853 const void* buffer,
7854 ULONG* bytesWritten)
7856 ULARGE_INTEGER offsetInBigBlockFile;
7857 ULONG blockNoInSequence =
7858 offset.u.LowPart / This->parentStorage->smallBlockSize;
7860 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7861 ULONG bytesToWriteInBuffer;
7862 ULONG blockIndex;
7863 ULONG bytesWrittenToBigBlockFile;
7864 const BYTE* bufferWalker;
7865 HRESULT res;
7868 * This should never happen on a small block file.
7870 assert(offset.u.HighPart==0);
7873 * Find the first block in the stream that contains part of the buffer.
7875 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7877 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7879 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
7880 return STG_E_DOCFILECORRUPT;
7881 blockNoInSequence--;
7885 * Start writing the buffer.
7887 *bytesWritten = 0;
7888 bufferWalker = buffer;
7889 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7892 * Calculate how many bytes we can copy to this small block.
7894 bytesToWriteInBuffer =
7895 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7898 * Calculate the offset of the small block in the small block file.
7900 offsetInBigBlockFile.u.HighPart = 0;
7901 offsetInBigBlockFile.u.LowPart =
7902 blockIndex * This->parentStorage->smallBlockSize;
7904 offsetInBigBlockFile.u.LowPart += offsetInBlock;
7907 * Write those bytes in the buffer to the small block file.
7909 res = BlockChainStream_WriteAt(
7910 This->parentStorage->smallBlockRootChain,
7911 offsetInBigBlockFile,
7912 bytesToWriteInBuffer,
7913 bufferWalker,
7914 &bytesWrittenToBigBlockFile);
7915 if (FAILED(res))
7916 return res;
7919 * Step to the next big block.
7921 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7922 &blockIndex)))
7923 return FALSE;
7924 bufferWalker += bytesWrittenToBigBlockFile;
7925 size -= bytesWrittenToBigBlockFile;
7926 *bytesWritten += bytesWrittenToBigBlockFile;
7927 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7930 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7933 /******************************************************************************
7934 * SmallBlockChainStream_Shrink
7936 * Shrinks this chain in the small block depot.
7938 static BOOL SmallBlockChainStream_Shrink(
7939 SmallBlockChainStream* This,
7940 ULARGE_INTEGER newSize)
7942 ULONG blockIndex, extraBlock;
7943 ULONG numBlocks;
7944 ULONG count = 0;
7946 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7948 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7949 numBlocks++;
7951 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7954 * Go to the new end of chain
7956 while (count < numBlocks)
7958 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7959 &blockIndex)))
7960 return FALSE;
7961 count++;
7965 * If the count is 0, we have a special case, the head of the chain was
7966 * just freed.
7968 if (count == 0)
7970 DirEntry chainEntry;
7972 StorageImpl_ReadDirEntry(This->parentStorage,
7973 This->ownerDirEntry,
7974 &chainEntry);
7976 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7978 StorageImpl_WriteDirEntry(This->parentStorage,
7979 This->ownerDirEntry,
7980 &chainEntry);
7983 * We start freeing the chain at the head block.
7985 extraBlock = blockIndex;
7987 else
7989 /* Get the next block before marking the new end */
7990 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7991 &extraBlock)))
7992 return FALSE;
7994 /* Mark the new end of chain */
7995 SmallBlockChainStream_SetNextBlockInChain(
7996 This,
7997 blockIndex,
7998 BLOCK_END_OF_CHAIN);
8002 * Mark the extra blocks as free
8004 while (extraBlock != BLOCK_END_OF_CHAIN)
8006 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
8007 &blockIndex)))
8008 return FALSE;
8009 SmallBlockChainStream_FreeBlock(This, extraBlock);
8010 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
8011 extraBlock = blockIndex;
8014 return TRUE;
8017 /******************************************************************************
8018 * SmallBlockChainStream_Enlarge
8020 * Grows this chain in the small block depot.
8022 static BOOL SmallBlockChainStream_Enlarge(
8023 SmallBlockChainStream* This,
8024 ULARGE_INTEGER newSize)
8026 ULONG blockIndex, currentBlock;
8027 ULONG newNumBlocks;
8028 ULONG oldNumBlocks = 0;
8030 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8033 * Empty chain. Create the head.
8035 if (blockIndex == BLOCK_END_OF_CHAIN)
8037 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8038 SmallBlockChainStream_SetNextBlockInChain(
8039 This,
8040 blockIndex,
8041 BLOCK_END_OF_CHAIN);
8043 if (This->headOfStreamPlaceHolder != NULL)
8045 *(This->headOfStreamPlaceHolder) = blockIndex;
8047 else
8049 DirEntry chainEntry;
8051 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
8052 &chainEntry);
8054 chainEntry.startingBlock = blockIndex;
8056 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
8057 &chainEntry);
8061 currentBlock = blockIndex;
8064 * Figure out how many blocks are needed to contain this stream
8066 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
8068 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
8069 newNumBlocks++;
8072 * Go to the current end of chain
8074 while (blockIndex != BLOCK_END_OF_CHAIN)
8076 oldNumBlocks++;
8077 currentBlock = blockIndex;
8078 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
8079 return FALSE;
8083 * Add new blocks to the chain
8085 while (oldNumBlocks < newNumBlocks)
8087 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
8088 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
8090 SmallBlockChainStream_SetNextBlockInChain(
8091 This,
8092 blockIndex,
8093 BLOCK_END_OF_CHAIN);
8095 currentBlock = blockIndex;
8096 oldNumBlocks++;
8099 return TRUE;
8102 /******************************************************************************
8103 * SmallBlockChainStream_SetSize
8105 * Sets the size of this stream.
8106 * The file will grow if we grow the chain.
8108 * TODO: Free the actual blocks in the file when we shrink the chain.
8109 * Currently, the blocks are still in the file. So the file size
8110 * doesn't shrink even if we shrink streams.
8112 BOOL SmallBlockChainStream_SetSize(
8113 SmallBlockChainStream* This,
8114 ULARGE_INTEGER newSize)
8116 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
8118 if (newSize.u.LowPart == size.u.LowPart)
8119 return TRUE;
8121 if (newSize.u.LowPart < size.u.LowPart)
8123 SmallBlockChainStream_Shrink(This, newSize);
8125 else
8127 SmallBlockChainStream_Enlarge(This, newSize);
8130 return TRUE;
8133 /******************************************************************************
8134 * SmallBlockChainStream_GetCount
8136 * Returns the number of small blocks that comprises this chain.
8137 * This is not the size of the stream as the last block may not be full!
8140 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
8142 ULONG blockIndex;
8143 ULONG count = 0;
8145 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
8147 while(blockIndex != BLOCK_END_OF_CHAIN)
8149 count++;
8151 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
8152 blockIndex, &blockIndex)))
8153 return 0;
8156 return count;
8159 /******************************************************************************
8160 * SmallBlockChainStream_GetSize
8162 * Returns the size of this chain.
8164 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
8166 DirEntry chainEntry;
8168 if(This->headOfStreamPlaceHolder != NULL)
8170 ULARGE_INTEGER result;
8171 result.u.HighPart = 0;
8173 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
8174 This->parentStorage->smallBlockSize;
8176 return result;
8179 StorageImpl_ReadDirEntry(
8180 This->parentStorage,
8181 This->ownerDirEntry,
8182 &chainEntry);
8184 return chainEntry.size;
8187 static HRESULT create_storagefile(
8188 LPCOLESTR pwcsName,
8189 DWORD grfMode,
8190 DWORD grfAttrs,
8191 STGOPTIONS* pStgOptions,
8192 REFIID riid,
8193 void** ppstgOpen)
8195 StorageBaseImpl* newStorage = 0;
8196 HANDLE hFile = INVALID_HANDLE_VALUE;
8197 HRESULT hr = STG_E_INVALIDFLAG;
8198 DWORD shareMode;
8199 DWORD accessMode;
8200 DWORD creationMode;
8201 DWORD fileAttributes;
8202 WCHAR tempFileName[MAX_PATH];
8204 if (ppstgOpen == 0)
8205 return STG_E_INVALIDPOINTER;
8207 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
8208 return STG_E_INVALIDPARAMETER;
8210 /* if no share mode given then DENY_NONE is the default */
8211 if (STGM_SHARE_MODE(grfMode) == 0)
8212 grfMode |= STGM_SHARE_DENY_NONE;
8214 if ( FAILED( validateSTGM(grfMode) ))
8215 goto end;
8217 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8218 switch(STGM_ACCESS_MODE(grfMode))
8220 case STGM_WRITE:
8221 case STGM_READWRITE:
8222 break;
8223 default:
8224 goto end;
8227 /* in direct mode, can only use SHARE_EXCLUSIVE */
8228 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
8229 goto end;
8231 /* but in transacted mode, any share mode is valid */
8234 * Generate a unique name.
8236 if (pwcsName == 0)
8238 WCHAR tempPath[MAX_PATH];
8239 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
8241 memset(tempPath, 0, sizeof(tempPath));
8242 memset(tempFileName, 0, sizeof(tempFileName));
8244 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
8245 tempPath[0] = '.';
8247 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
8248 pwcsName = tempFileName;
8249 else
8251 hr = STG_E_INSUFFICIENTMEMORY;
8252 goto end;
8255 creationMode = TRUNCATE_EXISTING;
8257 else
8259 creationMode = GetCreationModeFromSTGM(grfMode);
8263 * Interpret the STGM value grfMode
8265 shareMode = GetShareModeFromSTGM(grfMode);
8266 accessMode = GetAccessModeFromSTGM(grfMode);
8268 if (grfMode & STGM_DELETEONRELEASE)
8269 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
8270 else
8271 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
8273 *ppstgOpen = 0;
8275 hFile = CreateFileW(pwcsName,
8276 accessMode,
8277 shareMode,
8278 NULL,
8279 creationMode,
8280 fileAttributes,
8283 if (hFile == INVALID_HANDLE_VALUE)
8285 if(GetLastError() == ERROR_FILE_EXISTS)
8286 hr = STG_E_FILEALREADYEXISTS;
8287 else
8288 hr = E_FAIL;
8289 goto end;
8293 * Allocate and initialize the new IStorage32object.
8295 hr = Storage_Construct(
8296 hFile,
8297 pwcsName,
8298 NULL,
8299 grfMode,
8300 TRUE,
8301 TRUE,
8302 pStgOptions->ulSectorSize,
8303 &newStorage);
8305 if (FAILED(hr))
8307 goto end;
8310 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
8311 IStorage_Release(&newStorage->IStorage_iface);
8313 end:
8314 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
8316 return hr;
8319 /******************************************************************************
8320 * StgCreateDocfile [OLE32.@]
8321 * Creates a new compound file storage object
8323 * PARAMS
8324 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8325 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8326 * reserved [ ?] unused?, usually 0
8327 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8329 * RETURNS
8330 * S_OK if the file was successfully created
8331 * some STG_E_ value if error
8332 * NOTES
8333 * if pwcsName is NULL, create file with new unique name
8334 * the function can returns
8335 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8336 * (unrealized now)
8338 HRESULT WINAPI StgCreateDocfile(
8339 LPCOLESTR pwcsName,
8340 DWORD grfMode,
8341 DWORD reserved,
8342 IStorage **ppstgOpen)
8344 STGOPTIONS stgoptions = {1, 0, 512};
8346 TRACE("(%s, %x, %d, %p)\n",
8347 debugstr_w(pwcsName), grfMode,
8348 reserved, ppstgOpen);
8350 if (ppstgOpen == 0)
8351 return STG_E_INVALIDPOINTER;
8352 if (reserved != 0)
8353 return STG_E_INVALIDPARAMETER;
8355 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
8358 /******************************************************************************
8359 * StgCreateStorageEx [OLE32.@]
8361 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8363 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8364 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8366 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
8368 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8369 return STG_E_INVALIDPARAMETER;
8372 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
8374 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8375 return STG_E_INVALIDPARAMETER;
8378 if (stgfmt == STGFMT_FILE)
8380 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8381 return STG_E_INVALIDPARAMETER;
8384 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
8386 STGOPTIONS defaultOptions = {1, 0, 512};
8388 if (!pStgOptions) pStgOptions = &defaultOptions;
8389 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
8393 ERR("Invalid stgfmt argument\n");
8394 return STG_E_INVALIDPARAMETER;
8397 /******************************************************************************
8398 * StgCreatePropSetStg [OLE32.@]
8400 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
8401 IPropertySetStorage **propset)
8403 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
8404 if (reserved)
8405 return STG_E_INVALIDPARAMETER;
8407 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
8410 /******************************************************************************
8411 * StgOpenStorageEx [OLE32.@]
8413 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
8415 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
8416 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
8418 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
8420 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8421 return STG_E_INVALIDPARAMETER;
8424 switch (stgfmt)
8426 case STGFMT_FILE:
8427 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8428 return STG_E_INVALIDPARAMETER;
8430 case STGFMT_STORAGE:
8431 break;
8433 case STGFMT_DOCFILE:
8434 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
8436 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8437 return STG_E_INVALIDPARAMETER;
8439 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8440 break;
8442 case STGFMT_ANY:
8443 WARN("STGFMT_ANY assuming storage\n");
8444 break;
8446 default:
8447 return STG_E_INVALIDPARAMETER;
8450 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
8454 /******************************************************************************
8455 * StgOpenStorage [OLE32.@]
8457 HRESULT WINAPI StgOpenStorage(
8458 const OLECHAR *pwcsName,
8459 IStorage *pstgPriority,
8460 DWORD grfMode,
8461 SNB snbExclude,
8462 DWORD reserved,
8463 IStorage **ppstgOpen)
8465 StorageBaseImpl* newStorage = 0;
8466 HRESULT hr = S_OK;
8467 HANDLE hFile = 0;
8468 DWORD shareMode;
8469 DWORD accessMode;
8470 LPWSTR temp_name = NULL;
8472 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8473 debugstr_w(pwcsName), pstgPriority, grfMode,
8474 snbExclude, reserved, ppstgOpen);
8476 if (pstgPriority)
8478 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8479 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
8480 if (FAILED(hr)) goto end;
8481 pwcsName = temp_name;
8482 TRACE("using filename %s\n", debugstr_w(temp_name));
8485 if (pwcsName == 0)
8487 hr = STG_E_INVALIDNAME;
8488 goto end;
8491 if (ppstgOpen == 0)
8493 hr = STG_E_INVALIDPOINTER;
8494 goto end;
8497 if (reserved)
8499 hr = STG_E_INVALIDPARAMETER;
8500 goto end;
8503 if (grfMode & STGM_PRIORITY)
8505 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
8506 return STG_E_INVALIDFLAG;
8507 if (grfMode & STGM_DELETEONRELEASE)
8508 return STG_E_INVALIDFUNCTION;
8509 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
8510 return STG_E_INVALIDFLAG;
8511 grfMode &= ~0xf0; /* remove the existing sharing mode */
8512 grfMode |= STGM_SHARE_DENY_NONE;
8516 * Validate the sharing mode
8518 if (grfMode & STGM_DIRECT_SWMR)
8520 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
8521 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
8523 hr = STG_E_INVALIDFLAG;
8524 goto end;
8527 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
8528 switch(STGM_SHARE_MODE(grfMode))
8530 case STGM_SHARE_EXCLUSIVE:
8531 case STGM_SHARE_DENY_WRITE:
8532 break;
8533 default:
8534 hr = STG_E_INVALIDFLAG;
8535 goto end;
8538 if ( FAILED( validateSTGM(grfMode) ) ||
8539 (grfMode&STGM_CREATE))
8541 hr = STG_E_INVALIDFLAG;
8542 goto end;
8545 /* shared reading requires transacted or single writer mode */
8546 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
8547 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
8548 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
8550 hr = STG_E_INVALIDFLAG;
8551 goto end;
8555 * Interpret the STGM value grfMode
8557 shareMode = GetShareModeFromSTGM(grfMode);
8558 accessMode = GetAccessModeFromSTGM(grfMode);
8560 *ppstgOpen = 0;
8562 hFile = CreateFileW( pwcsName,
8563 accessMode,
8564 shareMode,
8565 NULL,
8566 OPEN_EXISTING,
8567 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
8570 if (hFile==INVALID_HANDLE_VALUE)
8572 DWORD last_error = GetLastError();
8574 hr = E_FAIL;
8576 switch (last_error)
8578 case ERROR_FILE_NOT_FOUND:
8579 hr = STG_E_FILENOTFOUND;
8580 break;
8582 case ERROR_PATH_NOT_FOUND:
8583 hr = STG_E_PATHNOTFOUND;
8584 break;
8586 case ERROR_ACCESS_DENIED:
8587 case ERROR_WRITE_PROTECT:
8588 hr = STG_E_ACCESSDENIED;
8589 break;
8591 case ERROR_SHARING_VIOLATION:
8592 hr = STG_E_SHAREVIOLATION;
8593 break;
8595 default:
8596 hr = E_FAIL;
8599 goto end;
8603 * Refuse to open the file if it's too small to be a structured storage file
8604 * FIXME: verify the file when reading instead of here
8606 if (GetFileSize(hFile, NULL) < 0x100)
8608 CloseHandle(hFile);
8609 hr = STG_E_FILEALREADYEXISTS;
8610 goto end;
8614 * Allocate and initialize the new IStorage32object.
8616 hr = Storage_Construct(
8617 hFile,
8618 pwcsName,
8619 NULL,
8620 grfMode,
8621 TRUE,
8622 FALSE,
8623 512,
8624 &newStorage);
8626 if (FAILED(hr))
8629 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8631 if(hr == STG_E_INVALIDHEADER)
8632 hr = STG_E_FILEALREADYEXISTS;
8633 goto end;
8636 *ppstgOpen = &newStorage->IStorage_iface;
8638 end:
8639 CoTaskMemFree(temp_name);
8640 if (pstgPriority) IStorage_Release(pstgPriority);
8641 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
8642 return hr;
8645 /******************************************************************************
8646 * StgCreateDocfileOnILockBytes [OLE32.@]
8648 HRESULT WINAPI StgCreateDocfileOnILockBytes(
8649 ILockBytes *plkbyt,
8650 DWORD grfMode,
8651 DWORD reserved,
8652 IStorage** ppstgOpen)
8654 StorageBaseImpl* newStorage = 0;
8655 HRESULT hr = S_OK;
8657 if ((ppstgOpen == 0) || (plkbyt == 0))
8658 return STG_E_INVALIDPOINTER;
8661 * Allocate and initialize the new IStorage object.
8663 hr = Storage_Construct(
8666 plkbyt,
8667 grfMode,
8668 FALSE,
8669 TRUE,
8670 512,
8671 &newStorage);
8673 if (FAILED(hr))
8675 return hr;
8678 *ppstgOpen = &newStorage->IStorage_iface;
8680 return hr;
8683 /******************************************************************************
8684 * StgOpenStorageOnILockBytes [OLE32.@]
8686 HRESULT WINAPI StgOpenStorageOnILockBytes(
8687 ILockBytes *plkbyt,
8688 IStorage *pstgPriority,
8689 DWORD grfMode,
8690 SNB snbExclude,
8691 DWORD reserved,
8692 IStorage **ppstgOpen)
8694 StorageBaseImpl* newStorage = 0;
8695 HRESULT hr = S_OK;
8697 if ((plkbyt == 0) || (ppstgOpen == 0))
8698 return STG_E_INVALIDPOINTER;
8700 if ( FAILED( validateSTGM(grfMode) ))
8701 return STG_E_INVALIDFLAG;
8703 *ppstgOpen = 0;
8706 * Allocate and initialize the new IStorage object.
8708 hr = Storage_Construct(
8711 plkbyt,
8712 grfMode,
8713 FALSE,
8714 FALSE,
8715 512,
8716 &newStorage);
8718 if (FAILED(hr))
8720 return hr;
8723 *ppstgOpen = &newStorage->IStorage_iface;
8725 return hr;
8728 /******************************************************************************
8729 * StgSetTimes [ole32.@]
8730 * StgSetTimes [OLE32.@]
8734 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
8735 FILETIME const *patime, FILETIME const *pmtime)
8737 IStorage *stg = NULL;
8738 HRESULT r;
8740 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
8742 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
8743 0, 0, &stg);
8744 if( SUCCEEDED(r) )
8746 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
8747 IStorage_Release(stg);
8750 return r;
8753 /******************************************************************************
8754 * StgIsStorageILockBytes [OLE32.@]
8756 * Determines if the ILockBytes contains a storage object.
8758 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
8760 BYTE sig[sizeof(STORAGE_magic)];
8761 ULARGE_INTEGER offset;
8762 ULONG read = 0;
8764 offset.u.HighPart = 0;
8765 offset.u.LowPart = 0;
8767 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
8769 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
8770 return S_OK;
8772 return S_FALSE;
8775 /******************************************************************************
8776 * WriteClassStg [OLE32.@]
8778 * This method will store the specified CLSID in the specified storage object
8780 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
8782 if(!pStg)
8783 return E_INVALIDARG;
8785 if(!rclsid)
8786 return STG_E_INVALIDPOINTER;
8788 return IStorage_SetClass(pStg, rclsid);
8791 /***********************************************************************
8792 * ReadClassStg (OLE32.@)
8794 * This method reads the CLSID previously written to a storage object with
8795 * the WriteClassStg.
8797 * PARAMS
8798 * pstg [I] IStorage pointer
8799 * pclsid [O] Pointer to where the CLSID is written
8801 * RETURNS
8802 * Success: S_OK.
8803 * Failure: HRESULT code.
8805 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
8807 STATSTG pstatstg;
8808 HRESULT hRes;
8810 TRACE("(%p, %p)\n", pstg, pclsid);
8812 if(!pstg || !pclsid)
8813 return E_INVALIDARG;
8816 * read a STATSTG structure (contains the clsid) from the storage
8818 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
8820 if(SUCCEEDED(hRes))
8821 *pclsid=pstatstg.clsid;
8823 return hRes;
8826 /***********************************************************************
8827 * OleLoadFromStream (OLE32.@)
8829 * This function loads an object from stream
8831 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
8833 CLSID clsid;
8834 HRESULT res;
8835 LPPERSISTSTREAM xstm;
8837 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
8839 res=ReadClassStm(pStm,&clsid);
8840 if (FAILED(res))
8841 return res;
8842 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
8843 if (FAILED(res))
8844 return res;
8845 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
8846 if (FAILED(res)) {
8847 IUnknown_Release((IUnknown*)*ppvObj);
8848 return res;
8850 res=IPersistStream_Load(xstm,pStm);
8851 IPersistStream_Release(xstm);
8852 /* FIXME: all refcounts ok at this point? I think they should be:
8853 * pStm : unchanged
8854 * ppvObj : 1
8855 * xstm : 0 (released)
8857 return res;
8860 /***********************************************************************
8861 * OleSaveToStream (OLE32.@)
8863 * This function saves an object with the IPersistStream interface on it
8864 * to the specified stream.
8866 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
8869 CLSID clsid;
8870 HRESULT res;
8872 TRACE("(%p,%p)\n",pPStm,pStm);
8874 res=IPersistStream_GetClassID(pPStm,&clsid);
8876 if (SUCCEEDED(res)){
8878 res=WriteClassStm(pStm,&clsid);
8880 if (SUCCEEDED(res))
8882 res=IPersistStream_Save(pPStm,pStm,TRUE);
8885 TRACE("Finished Save\n");
8886 return res;
8889 /****************************************************************************
8890 * This method validate a STGM parameter that can contain the values below
8892 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8893 * The stgm values contained in 0xffff0000 are bitmasks.
8895 * STGM_DIRECT 0x00000000
8896 * STGM_TRANSACTED 0x00010000
8897 * STGM_SIMPLE 0x08000000
8899 * STGM_READ 0x00000000
8900 * STGM_WRITE 0x00000001
8901 * STGM_READWRITE 0x00000002
8903 * STGM_SHARE_DENY_NONE 0x00000040
8904 * STGM_SHARE_DENY_READ 0x00000030
8905 * STGM_SHARE_DENY_WRITE 0x00000020
8906 * STGM_SHARE_EXCLUSIVE 0x00000010
8908 * STGM_PRIORITY 0x00040000
8909 * STGM_DELETEONRELEASE 0x04000000
8911 * STGM_CREATE 0x00001000
8912 * STGM_CONVERT 0x00020000
8913 * STGM_FAILIFTHERE 0x00000000
8915 * STGM_NOSCRATCH 0x00100000
8916 * STGM_NOSNAPSHOT 0x00200000
8918 static HRESULT validateSTGM(DWORD stgm)
8920 DWORD access = STGM_ACCESS_MODE(stgm);
8921 DWORD share = STGM_SHARE_MODE(stgm);
8922 DWORD create = STGM_CREATE_MODE(stgm);
8924 if (stgm&~STGM_KNOWN_FLAGS)
8926 ERR("unknown flags %08x\n", stgm);
8927 return E_FAIL;
8930 switch (access)
8932 case STGM_READ:
8933 case STGM_WRITE:
8934 case STGM_READWRITE:
8935 break;
8936 default:
8937 return E_FAIL;
8940 switch (share)
8942 case STGM_SHARE_DENY_NONE:
8943 case STGM_SHARE_DENY_READ:
8944 case STGM_SHARE_DENY_WRITE:
8945 case STGM_SHARE_EXCLUSIVE:
8946 break;
8947 case 0:
8948 if (!(stgm & STGM_TRANSACTED))
8949 return E_FAIL;
8950 break;
8951 default:
8952 return E_FAIL;
8955 switch (create)
8957 case STGM_CREATE:
8958 case STGM_FAILIFTHERE:
8959 break;
8960 default:
8961 return E_FAIL;
8965 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8967 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8968 return E_FAIL;
8971 * STGM_CREATE | STGM_CONVERT
8972 * if both are false, STGM_FAILIFTHERE is set to TRUE
8974 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8975 return E_FAIL;
8978 * STGM_NOSCRATCH requires STGM_TRANSACTED
8980 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8981 return E_FAIL;
8984 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8985 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8987 if ( (stgm & STGM_NOSNAPSHOT) &&
8988 (!(stgm & STGM_TRANSACTED) ||
8989 share == STGM_SHARE_EXCLUSIVE ||
8990 share == STGM_SHARE_DENY_WRITE) )
8991 return E_FAIL;
8993 return S_OK;
8996 /****************************************************************************
8997 * GetShareModeFromSTGM
8999 * This method will return a share mode flag from a STGM value.
9000 * The STGM value is assumed valid.
9002 static DWORD GetShareModeFromSTGM(DWORD stgm)
9004 switch (STGM_SHARE_MODE(stgm))
9006 case 0:
9007 assert(stgm & STGM_TRANSACTED);
9008 /* fall-through */
9009 case STGM_SHARE_DENY_NONE:
9010 return FILE_SHARE_READ | FILE_SHARE_WRITE;
9011 case STGM_SHARE_DENY_READ:
9012 return FILE_SHARE_WRITE;
9013 case STGM_SHARE_DENY_WRITE:
9014 case STGM_SHARE_EXCLUSIVE:
9015 return FILE_SHARE_READ;
9017 ERR("Invalid share mode!\n");
9018 assert(0);
9019 return 0;
9022 /****************************************************************************
9023 * GetAccessModeFromSTGM
9025 * This method will return an access mode flag from a STGM value.
9026 * The STGM value is assumed valid.
9028 static DWORD GetAccessModeFromSTGM(DWORD stgm)
9030 switch (STGM_ACCESS_MODE(stgm))
9032 case STGM_READ:
9033 return GENERIC_READ;
9034 case STGM_WRITE:
9035 case STGM_READWRITE:
9036 return GENERIC_READ | GENERIC_WRITE;
9038 ERR("Invalid access mode!\n");
9039 assert(0);
9040 return 0;
9043 /****************************************************************************
9044 * GetCreationModeFromSTGM
9046 * This method will return a creation mode flag from a STGM value.
9047 * The STGM value is assumed valid.
9049 static DWORD GetCreationModeFromSTGM(DWORD stgm)
9051 switch(STGM_CREATE_MODE(stgm))
9053 case STGM_CREATE:
9054 return CREATE_ALWAYS;
9055 case STGM_CONVERT:
9056 FIXME("STGM_CONVERT not implemented!\n");
9057 return CREATE_NEW;
9058 case STGM_FAILIFTHERE:
9059 return CREATE_NEW;
9061 ERR("Invalid create mode!\n");
9062 assert(0);
9063 return 0;
9067 /*************************************************************************
9068 * OLECONVERT_LoadOLE10 [Internal]
9070 * Loads the OLE10 STREAM to memory
9072 * PARAMS
9073 * pOleStream [I] The OLESTREAM
9074 * pData [I] Data Structure for the OLESTREAM Data
9076 * RETURNS
9077 * Success: S_OK
9078 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9079 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9081 * NOTES
9082 * This function is used by OleConvertOLESTREAMToIStorage only.
9084 * Memory allocated for pData must be freed by the caller
9086 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
9088 DWORD dwSize;
9089 HRESULT hRes = S_OK;
9090 int nTryCnt=0;
9091 int max_try = 6;
9093 pData->pData = NULL;
9094 pData->pstrOleObjFileName = NULL;
9096 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
9098 /* Get the OleID */
9099 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9100 if(dwSize != sizeof(pData->dwOleID))
9102 hRes = CONVERT10_E_OLESTREAM_GET;
9104 else if(pData->dwOleID != OLESTREAM_ID)
9106 hRes = CONVERT10_E_OLESTREAM_FMT;
9108 else
9110 hRes = S_OK;
9111 break;
9115 if(hRes == S_OK)
9117 /* Get the TypeID... more info needed for this field */
9118 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9119 if(dwSize != sizeof(pData->dwTypeID))
9121 hRes = CONVERT10_E_OLESTREAM_GET;
9124 if(hRes == S_OK)
9126 if(pData->dwTypeID != 0)
9128 /* Get the length of the OleTypeName */
9129 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9130 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9132 hRes = CONVERT10_E_OLESTREAM_GET;
9135 if(hRes == S_OK)
9137 if(pData->dwOleTypeNameLength > 0)
9139 /* Get the OleTypeName */
9140 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9141 if(dwSize != pData->dwOleTypeNameLength)
9143 hRes = CONVERT10_E_OLESTREAM_GET;
9147 if(bStrem1)
9149 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
9150 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
9152 hRes = CONVERT10_E_OLESTREAM_GET;
9154 if(hRes == S_OK)
9156 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
9157 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
9158 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
9159 if(pData->pstrOleObjFileName)
9161 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
9162 if(dwSize != pData->dwOleObjFileNameLength)
9164 hRes = CONVERT10_E_OLESTREAM_GET;
9167 else
9168 hRes = CONVERT10_E_OLESTREAM_GET;
9171 else
9173 /* Get the Width of the Metafile */
9174 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9175 if(dwSize != sizeof(pData->dwMetaFileWidth))
9177 hRes = CONVERT10_E_OLESTREAM_GET;
9179 if(hRes == S_OK)
9181 /* Get the Height of the Metafile */
9182 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9183 if(dwSize != sizeof(pData->dwMetaFileHeight))
9185 hRes = CONVERT10_E_OLESTREAM_GET;
9189 if(hRes == S_OK)
9191 /* Get the Length of the Data */
9192 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9193 if(dwSize != sizeof(pData->dwDataLength))
9195 hRes = CONVERT10_E_OLESTREAM_GET;
9199 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
9201 if(!bStrem1) /* if it is a second OLE stream data */
9203 pData->dwDataLength -= 8;
9204 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
9205 if(dwSize != sizeof(pData->strUnknown))
9207 hRes = CONVERT10_E_OLESTREAM_GET;
9211 if(hRes == S_OK)
9213 if(pData->dwDataLength > 0)
9215 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
9217 /* Get Data (ex. IStorage, Metafile, or BMP) */
9218 if(pData->pData)
9220 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
9221 if(dwSize != pData->dwDataLength)
9223 hRes = CONVERT10_E_OLESTREAM_GET;
9226 else
9228 hRes = CONVERT10_E_OLESTREAM_GET;
9234 return hRes;
9237 /*************************************************************************
9238 * OLECONVERT_SaveOLE10 [Internal]
9240 * Saves the OLE10 STREAM From memory
9242 * PARAMS
9243 * pData [I] Data Structure for the OLESTREAM Data
9244 * pOleStream [I] The OLESTREAM to save
9246 * RETURNS
9247 * Success: S_OK
9248 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9250 * NOTES
9251 * This function is used by OleConvertIStorageToOLESTREAM only.
9254 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
9256 DWORD dwSize;
9257 HRESULT hRes = S_OK;
9260 /* Set the OleID */
9261 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
9262 if(dwSize != sizeof(pData->dwOleID))
9264 hRes = CONVERT10_E_OLESTREAM_PUT;
9267 if(hRes == S_OK)
9269 /* Set the TypeID */
9270 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
9271 if(dwSize != sizeof(pData->dwTypeID))
9273 hRes = CONVERT10_E_OLESTREAM_PUT;
9277 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
9279 /* Set the Length of the OleTypeName */
9280 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
9281 if(dwSize != sizeof(pData->dwOleTypeNameLength))
9283 hRes = CONVERT10_E_OLESTREAM_PUT;
9286 if(hRes == S_OK)
9288 if(pData->dwOleTypeNameLength > 0)
9290 /* Set the OleTypeName */
9291 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
9292 if(dwSize != pData->dwOleTypeNameLength)
9294 hRes = CONVERT10_E_OLESTREAM_PUT;
9299 if(hRes == S_OK)
9301 /* Set the width of the Metafile */
9302 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
9303 if(dwSize != sizeof(pData->dwMetaFileWidth))
9305 hRes = CONVERT10_E_OLESTREAM_PUT;
9309 if(hRes == S_OK)
9311 /* Set the height of the Metafile */
9312 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
9313 if(dwSize != sizeof(pData->dwMetaFileHeight))
9315 hRes = CONVERT10_E_OLESTREAM_PUT;
9319 if(hRes == S_OK)
9321 /* Set the length of the Data */
9322 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
9323 if(dwSize != sizeof(pData->dwDataLength))
9325 hRes = CONVERT10_E_OLESTREAM_PUT;
9329 if(hRes == S_OK)
9331 if(pData->dwDataLength > 0)
9333 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9334 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
9335 if(dwSize != pData->dwDataLength)
9337 hRes = CONVERT10_E_OLESTREAM_PUT;
9342 return hRes;
9345 /*************************************************************************
9346 * OLECONVERT_GetOLE20FromOLE10[Internal]
9348 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9349 * opens it, and copies the content to the dest IStorage for
9350 * OleConvertOLESTREAMToIStorage
9353 * PARAMS
9354 * pDestStorage [I] The IStorage to copy the data to
9355 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9356 * nBufferLength [I] The size of the buffer
9358 * RETURNS
9359 * Nothing
9361 * NOTES
9365 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
9367 HRESULT hRes;
9368 HANDLE hFile;
9369 IStorage *pTempStorage;
9370 DWORD dwNumOfBytesWritten;
9371 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9372 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9374 /* Create a temp File */
9375 GetTempPathW(MAX_PATH, wstrTempDir);
9376 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9377 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
9379 if(hFile != INVALID_HANDLE_VALUE)
9381 /* Write IStorage Data to File */
9382 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
9383 CloseHandle(hFile);
9385 /* Open and copy temp storage to the Dest Storage */
9386 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
9387 if(hRes == S_OK)
9389 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
9390 IStorage_Release(pTempStorage);
9392 DeleteFileW(wstrTempFile);
9397 /*************************************************************************
9398 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9400 * Saves the OLE10 STREAM From memory
9402 * PARAMS
9403 * pStorage [I] The Src IStorage to copy
9404 * pData [I] The Dest Memory to write to.
9406 * RETURNS
9407 * The size in bytes allocated for pData
9409 * NOTES
9410 * Memory allocated for pData must be freed by the caller
9412 * Used by OleConvertIStorageToOLESTREAM only.
9415 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
9417 HANDLE hFile;
9418 HRESULT hRes;
9419 DWORD nDataLength = 0;
9420 IStorage *pTempStorage;
9421 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
9422 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
9424 *pData = NULL;
9426 /* Create temp Storage */
9427 GetTempPathW(MAX_PATH, wstrTempDir);
9428 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
9429 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
9431 if(hRes == S_OK)
9433 /* Copy Src Storage to the Temp Storage */
9434 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
9435 IStorage_Release(pTempStorage);
9437 /* Open Temp Storage as a file and copy to memory */
9438 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9439 if(hFile != INVALID_HANDLE_VALUE)
9441 nDataLength = GetFileSize(hFile, NULL);
9442 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
9443 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
9444 CloseHandle(hFile);
9446 DeleteFileW(wstrTempFile);
9448 return nDataLength;
9451 /*************************************************************************
9452 * STORAGE_CreateOleStream [Internal]
9454 * Creates the "\001OLE" stream in the IStorage if necessary.
9456 * PARAMS
9457 * storage [I] Dest storage to create the stream in
9458 * flags [I] flags to be set for newly created stream
9460 * RETURNS
9461 * HRESULT return value
9463 * NOTES
9465 * This stream is still unknown, MS Word seems to have extra data
9466 * but since the data is stored in the OLESTREAM there should be
9467 * no need to recreate the stream. If the stream is manually
9468 * deleted it will create it with this default data.
9471 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
9473 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9474 static const DWORD version_magic = 0x02000001;
9475 IStream *stream;
9476 HRESULT hr;
9478 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
9479 if (hr == S_OK)
9481 struct empty_1ole_stream {
9482 DWORD version_magic;
9483 DWORD flags;
9484 DWORD update_options;
9485 DWORD reserved;
9486 DWORD mon_stream_size;
9488 struct empty_1ole_stream stream_data;
9490 stream_data.version_magic = version_magic;
9491 stream_data.flags = flags;
9492 stream_data.update_options = 0;
9493 stream_data.reserved = 0;
9494 stream_data.mon_stream_size = 0;
9496 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
9497 IStream_Release(stream);
9500 return hr;
9503 /* write a string to a stream, preceded by its length */
9504 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
9506 HRESULT r;
9507 LPSTR str;
9508 DWORD len = 0;
9510 if( string )
9511 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
9512 r = IStream_Write( stm, &len, sizeof(len), NULL);
9513 if( FAILED( r ) )
9514 return r;
9515 if(len == 0)
9516 return r;
9517 str = CoTaskMemAlloc( len );
9518 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
9519 r = IStream_Write( stm, str, len, NULL);
9520 CoTaskMemFree( str );
9521 return r;
9524 /* read a string preceded by its length from a stream */
9525 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
9527 HRESULT r;
9528 DWORD len, count = 0;
9529 LPSTR str;
9530 LPWSTR wstr;
9532 r = IStream_Read( stm, &len, sizeof(len), &count );
9533 if( FAILED( r ) )
9534 return r;
9535 if( count != sizeof(len) )
9536 return E_OUTOFMEMORY;
9538 TRACE("%d bytes\n",len);
9540 str = CoTaskMemAlloc( len );
9541 if( !str )
9542 return E_OUTOFMEMORY;
9543 count = 0;
9544 r = IStream_Read( stm, str, len, &count );
9545 if( FAILED( r ) )
9546 return r;
9547 if( count != len )
9549 CoTaskMemFree( str );
9550 return E_OUTOFMEMORY;
9553 TRACE("Read string %s\n",debugstr_an(str,len));
9555 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
9556 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
9557 if( wstr )
9559 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
9560 wstr[len] = 0;
9562 CoTaskMemFree( str );
9564 *string = wstr;
9566 return r;
9570 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
9571 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
9573 IStream *pstm;
9574 HRESULT r = S_OK;
9575 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9577 static const BYTE unknown1[12] =
9578 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9579 0xFF, 0xFF, 0xFF, 0xFF};
9580 static const BYTE unknown2[16] =
9581 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9582 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9584 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
9585 debugstr_w(lpszUserType), debugstr_w(szClipName),
9586 debugstr_w(szProgIDName));
9588 /* Create a CompObj stream */
9589 r = IStorage_CreateStream(pstg, szwStreamName,
9590 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
9591 if( FAILED (r) )
9592 return r;
9594 /* Write CompObj Structure to stream */
9595 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
9597 if( SUCCEEDED( r ) )
9598 r = WriteClassStm( pstm, clsid );
9600 if( SUCCEEDED( r ) )
9601 r = STREAM_WriteString( pstm, lpszUserType );
9602 if( SUCCEEDED( r ) )
9603 r = STREAM_WriteString( pstm, szClipName );
9604 if( SUCCEEDED( r ) )
9605 r = STREAM_WriteString( pstm, szProgIDName );
9606 if( SUCCEEDED( r ) )
9607 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
9609 IStream_Release( pstm );
9611 return r;
9614 /***********************************************************************
9615 * WriteFmtUserTypeStg (OLE32.@)
9617 HRESULT WINAPI WriteFmtUserTypeStg(
9618 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
9620 STATSTG stat;
9621 HRESULT r;
9622 WCHAR szwClipName[0x40];
9623 CLSID clsid;
9624 LPWSTR wstrProgID = NULL;
9625 DWORD n;
9627 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
9629 /* get the clipboard format name */
9630 if( cf )
9632 n = GetClipboardFormatNameW( cf, szwClipName,
9633 sizeof(szwClipName)/sizeof(szwClipName[0]) );
9634 szwClipName[n]=0;
9637 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
9639 r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
9640 if(SUCCEEDED(r))
9641 clsid = stat.clsid;
9642 else
9643 clsid = CLSID_NULL;
9645 ProgIDFromCLSID(&clsid, &wstrProgID);
9647 TRACE("progid is %s\n",debugstr_w(wstrProgID));
9649 r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
9650 cf ? szwClipName : NULL, wstrProgID );
9652 CoTaskMemFree(wstrProgID);
9654 return r;
9658 /******************************************************************************
9659 * ReadFmtUserTypeStg [OLE32.@]
9661 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
9663 HRESULT r;
9664 IStream *stm = 0;
9665 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
9666 unsigned char unknown1[12];
9667 unsigned char unknown2[16];
9668 DWORD count;
9669 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
9670 CLSID clsid;
9672 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
9674 r = IStorage_OpenStream( pstg, szCompObj, NULL,
9675 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
9676 if( FAILED ( r ) )
9678 WARN("Failed to open stream r = %08x\n", r);
9679 return r;
9682 /* read the various parts of the structure */
9683 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
9684 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
9685 goto end;
9686 r = ReadClassStm( stm, &clsid );
9687 if( FAILED( r ) )
9688 goto end;
9690 r = STREAM_ReadString( stm, &szCLSIDName );
9691 if( FAILED( r ) )
9692 goto end;
9694 r = STREAM_ReadString( stm, &szOleTypeName );
9695 if( FAILED( r ) )
9696 goto end;
9698 r = STREAM_ReadString( stm, &szProgIDName );
9699 if( FAILED( r ) )
9700 goto end;
9702 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
9703 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
9704 goto end;
9706 /* ok, success... now we just need to store what we found */
9707 if( pcf )
9708 *pcf = RegisterClipboardFormatW( szOleTypeName );
9710 if( lplpszUserType )
9712 *lplpszUserType = szCLSIDName;
9713 szCLSIDName = NULL;
9716 end:
9717 CoTaskMemFree( szCLSIDName );
9718 CoTaskMemFree( szOleTypeName );
9719 CoTaskMemFree( szProgIDName );
9720 IStream_Release( stm );
9722 return r;
9726 /*************************************************************************
9727 * OLECONVERT_CreateCompObjStream [Internal]
9729 * Creates a "\001CompObj" is the destination IStorage if necessary.
9731 * PARAMS
9732 * pStorage [I] The dest IStorage to create the CompObj Stream
9733 * if necessary.
9734 * strOleTypeName [I] The ProgID
9736 * RETURNS
9737 * Success: S_OK
9738 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9740 * NOTES
9741 * This function is used by OleConvertOLESTREAMToIStorage only.
9743 * The stream data is stored in the OLESTREAM and there should be
9744 * no need to recreate the stream. If the stream is manually
9745 * deleted it will attempt to create it by querying the registry.
9749 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
9751 IStream *pStream;
9752 HRESULT hStorageRes, hRes = S_OK;
9753 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
9754 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9755 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
9757 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9758 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
9760 /* Initialize the CompObj structure */
9761 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
9762 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
9763 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
9766 /* Create a CompObj stream if it doesn't exist */
9767 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
9768 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9769 if(hStorageRes == S_OK)
9771 /* copy the OleTypeName to the compobj struct */
9772 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
9773 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
9775 /* copy the OleTypeName to the compobj struct */
9776 /* Note: in the test made, these were Identical */
9777 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
9778 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
9780 /* Get the CLSID */
9781 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
9782 bufferW, OLESTREAM_MAX_STR_LEN );
9783 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
9785 if(hRes == S_OK)
9787 HKEY hKey;
9788 LONG hErr;
9789 /* Get the CLSID Default Name from the Registry */
9790 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
9791 if(hErr == ERROR_SUCCESS)
9793 char strTemp[OLESTREAM_MAX_STR_LEN];
9794 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
9795 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
9796 if(hErr == ERROR_SUCCESS)
9798 strcpy(IStorageCompObj.strCLSIDName, strTemp);
9800 RegCloseKey(hKey);
9804 /* Write CompObj Structure to stream */
9805 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
9807 WriteClassStm(pStream,&(IStorageCompObj.clsid));
9809 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
9810 if(IStorageCompObj.dwCLSIDNameLength > 0)
9812 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
9814 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
9815 if(IStorageCompObj.dwOleTypeNameLength > 0)
9817 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
9819 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
9820 if(IStorageCompObj.dwProgIDNameLength > 0)
9822 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
9824 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
9825 IStream_Release(pStream);
9827 return hRes;
9831 /*************************************************************************
9832 * OLECONVERT_CreateOlePresStream[Internal]
9834 * Creates the "\002OlePres000" Stream with the Metafile data
9836 * PARAMS
9837 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
9838 * dwExtentX [I] Width of the Metafile
9839 * dwExtentY [I] Height of the Metafile
9840 * pData [I] Metafile data
9841 * dwDataLength [I] Size of the Metafile data
9843 * RETURNS
9844 * Success: S_OK
9845 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9847 * NOTES
9848 * This function is used by OleConvertOLESTREAMToIStorage only.
9851 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
9853 HRESULT hRes;
9854 IStream *pStream;
9855 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9856 BYTE pOlePresStreamHeader [] =
9858 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 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 BYTE pOlePresStreamHeaderEmpty [] =
9866 0x00, 0x00, 0x00, 0x00,
9867 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9868 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9869 0x00, 0x00, 0x00, 0x00
9872 /* Create the OlePres000 Stream */
9873 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9874 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9876 if(hRes == S_OK)
9878 DWORD nHeaderSize;
9879 OLECONVERT_ISTORAGE_OLEPRES OlePres;
9881 memset(&OlePres, 0, sizeof(OlePres));
9882 /* Do we have any metafile data to save */
9883 if(dwDataLength > 0)
9885 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
9886 nHeaderSize = sizeof(pOlePresStreamHeader);
9888 else
9890 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
9891 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
9893 /* Set width and height of the metafile */
9894 OlePres.dwExtentX = dwExtentX;
9895 OlePres.dwExtentY = -dwExtentY;
9897 /* Set Data and Length */
9898 if(dwDataLength > sizeof(METAFILEPICT16))
9900 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
9901 OlePres.pData = &(pData[8]);
9903 /* Save OlePres000 Data to Stream */
9904 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
9905 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
9906 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
9907 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
9908 if(OlePres.dwSize > 0)
9910 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
9912 IStream_Release(pStream);
9916 /*************************************************************************
9917 * OLECONVERT_CreateOle10NativeStream [Internal]
9919 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9921 * PARAMS
9922 * pStorage [I] Dest storage to create the stream in
9923 * pData [I] Ole10 Native Data (ex. bmp)
9924 * dwDataLength [I] Size of the Ole10 Native Data
9926 * RETURNS
9927 * Nothing
9929 * NOTES
9930 * This function is used by OleConvertOLESTREAMToIStorage only.
9932 * Might need to verify the data and return appropriate error message
9935 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
9937 HRESULT hRes;
9938 IStream *pStream;
9939 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9941 /* Create the Ole10Native Stream */
9942 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9943 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9945 if(hRes == S_OK)
9947 /* Write info to stream */
9948 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9949 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9950 IStream_Release(pStream);
9955 /*************************************************************************
9956 * OLECONVERT_GetOLE10ProgID [Internal]
9958 * Finds the ProgID (or OleTypeID) from the IStorage
9960 * PARAMS
9961 * pStorage [I] The Src IStorage to get the ProgID
9962 * strProgID [I] the ProgID string to get
9963 * dwSize [I] the size of the string
9965 * RETURNS
9966 * Success: S_OK
9967 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9969 * NOTES
9970 * This function is used by OleConvertIStorageToOLESTREAM only.
9974 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9976 HRESULT hRes;
9977 IStream *pStream;
9978 LARGE_INTEGER iSeekPos;
9979 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9980 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9982 /* Open the CompObj Stream */
9983 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9984 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9985 if(hRes == S_OK)
9988 /*Get the OleType from the CompObj Stream */
9989 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9990 iSeekPos.u.HighPart = 0;
9992 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9993 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9994 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9995 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9996 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9997 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9998 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
10000 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
10001 if(*dwSize > 0)
10003 IStream_Read(pStream, strProgID, *dwSize, NULL);
10005 IStream_Release(pStream);
10007 else
10009 STATSTG stat;
10010 LPOLESTR wstrProgID;
10012 /* Get the OleType from the registry */
10013 REFCLSID clsid = &(stat.clsid);
10014 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
10015 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
10016 if(hRes == S_OK)
10018 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
10019 CoTaskMemFree(wstrProgID);
10023 return hRes;
10026 /*************************************************************************
10027 * OLECONVERT_GetOle10PresData [Internal]
10029 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10031 * PARAMS
10032 * pStorage [I] Src IStroage
10033 * pOleStream [I] Dest OleStream Mem Struct
10035 * RETURNS
10036 * Nothing
10038 * NOTES
10039 * This function is used by OleConvertIStorageToOLESTREAM only.
10041 * Memory allocated for pData must be freed by the caller
10045 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10048 HRESULT hRes;
10049 IStream *pStream;
10050 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10052 /* Initialize Default data for OLESTREAM */
10053 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10054 pOleStreamData[0].dwTypeID = 2;
10055 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10056 pOleStreamData[1].dwTypeID = 0;
10057 pOleStreamData[0].dwMetaFileWidth = 0;
10058 pOleStreamData[0].dwMetaFileHeight = 0;
10059 pOleStreamData[0].pData = NULL;
10060 pOleStreamData[1].pData = NULL;
10062 /* Open Ole10Native Stream */
10063 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10064 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10065 if(hRes == S_OK)
10068 /* Read Size and Data */
10069 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
10070 if(pOleStreamData->dwDataLength > 0)
10072 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
10073 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
10075 IStream_Release(pStream);
10081 /*************************************************************************
10082 * OLECONVERT_GetOle20PresData[Internal]
10084 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10086 * PARAMS
10087 * pStorage [I] Src IStroage
10088 * pOleStreamData [I] Dest OleStream Mem Struct
10090 * RETURNS
10091 * Nothing
10093 * NOTES
10094 * This function is used by OleConvertIStorageToOLESTREAM only.
10096 * Memory allocated for pData must be freed by the caller
10098 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
10100 HRESULT hRes;
10101 IStream *pStream;
10102 OLECONVERT_ISTORAGE_OLEPRES olePress;
10103 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10105 /* Initialize Default data for OLESTREAM */
10106 pOleStreamData[0].dwOleID = OLESTREAM_ID;
10107 pOleStreamData[0].dwTypeID = 2;
10108 pOleStreamData[0].dwMetaFileWidth = 0;
10109 pOleStreamData[0].dwMetaFileHeight = 0;
10110 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
10111 pOleStreamData[1].dwOleID = OLESTREAM_ID;
10112 pOleStreamData[1].dwTypeID = 0;
10113 pOleStreamData[1].dwOleTypeNameLength = 0;
10114 pOleStreamData[1].strOleTypeName[0] = 0;
10115 pOleStreamData[1].dwMetaFileWidth = 0;
10116 pOleStreamData[1].dwMetaFileHeight = 0;
10117 pOleStreamData[1].pData = NULL;
10118 pOleStreamData[1].dwDataLength = 0;
10121 /* Open OlePress000 stream */
10122 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
10123 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
10124 if(hRes == S_OK)
10126 LARGE_INTEGER iSeekPos;
10127 METAFILEPICT16 MetaFilePict;
10128 static const char strMetafilePictName[] = "METAFILEPICT";
10130 /* Set the TypeID for a Metafile */
10131 pOleStreamData[1].dwTypeID = 5;
10133 /* Set the OleTypeName to Metafile */
10134 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
10135 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
10137 iSeekPos.u.HighPart = 0;
10138 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
10140 /* Get Presentation Data */
10141 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
10142 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
10143 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
10144 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
10146 /*Set width and Height */
10147 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
10148 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
10149 if(olePress.dwSize > 0)
10151 /* Set Length */
10152 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
10154 /* Set MetaFilePict struct */
10155 MetaFilePict.mm = 8;
10156 MetaFilePict.xExt = olePress.dwExtentX;
10157 MetaFilePict.yExt = olePress.dwExtentY;
10158 MetaFilePict.hMF = 0;
10160 /* Get Metafile Data */
10161 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
10162 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
10163 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
10165 IStream_Release(pStream);
10169 /*************************************************************************
10170 * OleConvertOLESTREAMToIStorage [OLE32.@]
10172 * Read info on MSDN
10174 * TODO
10175 * DVTARGETDEVICE parameter is not handled
10176 * Still unsure of some mem fields for OLE 10 Stream
10177 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10178 * and "\001OLE" streams
10181 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
10182 LPOLESTREAM pOleStream,
10183 LPSTORAGE pstg,
10184 const DVTARGETDEVICE* ptd)
10186 int i;
10187 HRESULT hRes=S_OK;
10188 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10190 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
10192 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10194 if(ptd != NULL)
10196 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10199 if(pstg == NULL || pOleStream == NULL)
10201 hRes = E_INVALIDARG;
10204 if(hRes == S_OK)
10206 /* Load the OLESTREAM to Memory */
10207 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
10210 if(hRes == S_OK)
10212 /* Load the OLESTREAM to Memory (part 2)*/
10213 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
10216 if(hRes == S_OK)
10219 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
10221 /* Do we have the IStorage Data in the OLESTREAM */
10222 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
10224 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10225 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
10227 else
10229 /* It must be an original OLE 1.0 source */
10230 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10233 else
10235 /* It must be an original OLE 1.0 source */
10236 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
10239 /* Create CompObj Stream if necessary */
10240 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
10241 if(hRes == S_OK)
10243 /*Create the Ole Stream if necessary */
10244 STORAGE_CreateOleStream(pstg, 0);
10249 /* Free allocated memory */
10250 for(i=0; i < 2; i++)
10252 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10253 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
10254 pOleStreamData[i].pstrOleObjFileName = NULL;
10256 return hRes;
10259 /*************************************************************************
10260 * OleConvertIStorageToOLESTREAM [OLE32.@]
10262 * Read info on MSDN
10264 * Read info on MSDN
10266 * TODO
10267 * Still unsure of some mem fields for OLE 10 Stream
10268 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10269 * and "\001OLE" streams.
10272 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
10273 LPSTORAGE pstg,
10274 LPOLESTREAM pOleStream)
10276 int i;
10277 HRESULT hRes = S_OK;
10278 IStream *pStream;
10279 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
10280 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10282 TRACE("%p %p\n", pstg, pOleStream);
10284 memset(pOleStreamData, 0, sizeof(pOleStreamData));
10286 if(pstg == NULL || pOleStream == NULL)
10288 hRes = E_INVALIDARG;
10290 if(hRes == S_OK)
10292 /* Get the ProgID */
10293 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
10294 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
10296 if(hRes == S_OK)
10298 /* Was it originally Ole10 */
10299 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
10300 if(hRes == S_OK)
10302 IStream_Release(pStream);
10303 /* Get Presentation Data for Ole10Native */
10304 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
10306 else
10308 /* Get Presentation Data (OLE20) */
10309 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
10312 /* Save OLESTREAM */
10313 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
10314 if(hRes == S_OK)
10316 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
10321 /* Free allocated memory */
10322 for(i=0; i < 2; i++)
10324 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
10327 return hRes;
10330 enum stream_1ole_flags {
10331 OleStream_LinkedObject = 0x00000001,
10332 OleStream_Convert = 0x00000004
10335 /***********************************************************************
10336 * GetConvertStg (OLE32.@)
10338 HRESULT WINAPI GetConvertStg(IStorage *stg)
10340 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10341 static const DWORD version_magic = 0x02000001;
10342 DWORD header[2];
10343 IStream *stream;
10344 HRESULT hr;
10346 TRACE("%p\n", stg);
10348 if (!stg) return E_INVALIDARG;
10350 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
10351 if (FAILED(hr)) return hr;
10353 hr = IStream_Read(stream, header, sizeof(header), NULL);
10354 IStream_Release(stream);
10355 if (FAILED(hr)) return hr;
10357 if (header[0] != version_magic)
10359 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
10360 return E_FAIL;
10363 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
10366 /***********************************************************************
10367 * SetConvertStg (OLE32.@)
10369 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
10371 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
10372 DWORD flags = convert ? OleStream_Convert : 0;
10373 IStream *stream;
10374 DWORD header[2];
10375 HRESULT hr;
10377 TRACE("(%p, %d)\n", storage, convert);
10379 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
10380 if (FAILED(hr))
10382 if (hr != STG_E_FILENOTFOUND)
10383 return hr;
10385 return STORAGE_CreateOleStream(storage, flags);
10388 hr = IStream_Read(stream, header, sizeof(header), NULL);
10389 if (FAILED(hr))
10391 IStream_Release(stream);
10392 return hr;
10395 /* update flag if differs */
10396 if ((header[1] ^ flags) & OleStream_Convert)
10398 LARGE_INTEGER pos = {{0}};
10400 if (header[1] & OleStream_Convert)
10401 flags = header[1] & ~OleStream_Convert;
10402 else
10403 flags = header[1] | OleStream_Convert;
10405 pos.QuadPart = sizeof(DWORD);
10406 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
10407 if (FAILED(hr))
10409 IStream_Release(stream);
10410 return hr;
10413 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
10416 IStream_Release(stream);
10417 return hr;
10420 /******************************************************************************
10421 * StgIsStorageFile [OLE32.@]
10422 * Verify if the file contains a storage object
10424 * PARAMS
10425 * fn [ I] Filename
10427 * RETURNS
10428 * S_OK if file has magic bytes as a storage object
10429 * S_FALSE if file is not storage
10431 HRESULT WINAPI
10432 StgIsStorageFile(LPCOLESTR fn)
10434 HANDLE hf;
10435 BYTE magic[8];
10436 DWORD bytes_read;
10438 TRACE("%s\n", debugstr_w(fn));
10439 hf = CreateFileW(fn, GENERIC_READ,
10440 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
10441 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
10443 if (hf == INVALID_HANDLE_VALUE)
10444 return STG_E_FILENOTFOUND;
10446 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
10448 WARN(" unable to read file\n");
10449 CloseHandle(hf);
10450 return S_FALSE;
10453 CloseHandle(hf);
10455 if (bytes_read != 8) {
10456 TRACE(" too short\n");
10457 return S_FALSE;
10460 if (!memcmp(magic,STORAGE_magic,8)) {
10461 TRACE(" -> YES\n");
10462 return S_OK;
10465 TRACE(" -> Invalid header.\n");
10466 return S_FALSE;
10469 /***********************************************************************
10470 * WriteClassStm (OLE32.@)
10472 * Writes a CLSID to a stream.
10474 * PARAMS
10475 * pStm [I] Stream to write to.
10476 * rclsid [I] CLSID to write.
10478 * RETURNS
10479 * Success: S_OK.
10480 * Failure: HRESULT code.
10482 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
10484 TRACE("(%p,%p)\n",pStm,rclsid);
10486 if (!pStm || !rclsid)
10487 return E_INVALIDARG;
10489 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
10492 /***********************************************************************
10493 * ReadClassStm (OLE32.@)
10495 * Reads a CLSID from a stream.
10497 * PARAMS
10498 * pStm [I] Stream to read from.
10499 * rclsid [O] CLSID to read.
10501 * RETURNS
10502 * Success: S_OK.
10503 * Failure: HRESULT code.
10505 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
10507 ULONG nbByte;
10508 HRESULT res;
10510 TRACE("(%p,%p)\n",pStm,pclsid);
10512 if (!pStm || !pclsid)
10513 return E_INVALIDARG;
10515 /* clear the output args */
10516 *pclsid = CLSID_NULL;
10518 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
10520 if (FAILED(res))
10521 return res;
10523 if (nbByte != sizeof(CLSID))
10524 return STG_E_READFAULT;
10525 else
10526 return S_OK;