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
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.
40 #define NONAMELESSUNION
46 #include "wine/unicode.h"
47 #include "wine/debug.h"
49 #include "storage32.h"
50 #include "ole2.h" /* For Write/ReadClassStm */
53 #include "wine/wingdi16.h"
54 #include "compobj_private.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static inline StorageBaseImpl
*impl_from_IStorage( IStorage
*iface
)
70 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IStorage_iface
);
73 static inline StorageBaseImpl
*impl_from_IDirectWriterLock( IDirectWriterLock
*iface
)
75 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IDirectWriterLock_iface
);
78 /****************************************************************************
79 * Storage32InternalImpl definitions.
81 * Definition of the implementation structure for the IStorage32 interface.
82 * This one implements the IStorage32 interface for storage that are
83 * inside another storage.
85 struct StorageInternalImpl
87 struct StorageBaseImpl base
;
90 * Entry in the parent's stream tracking list
92 struct list ParentListEntry
;
94 StorageBaseImpl
*parentStorage
;
96 typedef struct StorageInternalImpl StorageInternalImpl
;
98 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
;
99 static const IStorageVtbl Storage32InternalImpl_Vtbl
;
101 /* Method definitions for the Storage32InternalImpl class. */
102 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageBaseImpl
* parentStorage
,
103 DWORD openFlags
, DirRef storageDirEntry
);
104 static HRESULT
StorageImpl_Refresh(StorageImpl
*This
, BOOL new_object
, BOOL create
);
105 static void StorageImpl_Destroy(StorageBaseImpl
* iface
);
106 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
);
107 static HRESULT
StorageImpl_Flush(StorageBaseImpl
* iface
);
108 static HRESULT
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
, ULONG
*read
);
109 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
110 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
111 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
112 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
113 static HRESULT
StorageImpl_LockRegionSync(StorageImpl
*This
, ULARGE_INTEGER offset
, ULARGE_INTEGER cb
, DWORD dwLockType
);
115 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
, ULONG depotIndex
);
116 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
117 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
118 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
119 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
121 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
122 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
123 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
125 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
126 static ULONG
SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream
* This
);
127 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
128 ULONG blockIndex
, ULONG offset
, DWORD value
);
129 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
130 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
132 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
);
133 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
);
135 typedef struct TransactedDirEntry
137 /* If applicable, a reference to the original DirEntry in the transacted
138 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
139 DirRef transactedParentEntry
;
141 /* True if this entry is being used. */
144 /* True if data is up to date. */
147 /* True if this entry has been modified. */
150 /* True if this entry's stream has been modified. */
153 /* True if this entry has been deleted in the transacted storage, but the
154 * delete has not yet been committed. */
157 /* If this entry's stream has been modified, a reference to where the stream
158 * is stored in the snapshot file. */
161 /* This directory entry's data, including any changes that have been made. */
164 /* A reference to the parent of this node. This is only valid while we are
165 * committing changes. */
168 /* A reference to a newly-created entry in the transacted parent. This is
169 * always equal to transactedParentEntry except when committing changes. */
170 DirRef newTransactedParentEntry
;
171 } TransactedDirEntry
;
173 /****************************************************************************
174 * Transacted storage object.
176 typedef struct TransactedSnapshotImpl
178 struct StorageBaseImpl base
;
181 * Modified streams are temporarily saved to the scratch file.
183 StorageBaseImpl
*scratch
;
185 /* The directory structure is kept here, so that we can track how these
186 * entries relate to those in the parent storage. */
187 TransactedDirEntry
*entries
;
189 ULONG firstFreeEntry
;
192 * Changes are committed to the transacted parent.
194 StorageBaseImpl
*transactedParent
;
196 /* The transaction signature from when we last committed */
197 ULONG lastTransactionSig
;
198 } TransactedSnapshotImpl
;
200 typedef struct TransactedSharedImpl
202 struct StorageBaseImpl base
;
205 * Snapshot and uncommitted changes go here.
207 TransactedSnapshotImpl
*scratch
;
210 * Changes are committed to the transacted parent.
212 StorageBaseImpl
*transactedParent
;
214 /* The transaction signature from when we last committed */
215 ULONG lastTransactionSig
;
216 } TransactedSharedImpl
;
218 /* Generic function to create a transacted wrapper for a direct storage object. */
219 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
* parent
, BOOL toplevel
, StorageBaseImpl
** result
);
221 /* OLESTREAM memory structure to use for Get and Put Routines */
222 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
227 DWORD dwOleTypeNameLength
;
228 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
229 CHAR
*pstrOleObjFileName
;
230 DWORD dwOleObjFileNameLength
;
231 DWORD dwMetaFileWidth
;
232 DWORD dwMetaFileHeight
;
233 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
236 }OLECONVERT_OLESTREAM_DATA
;
238 /* CompObj Stream structure */
239 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
244 DWORD dwCLSIDNameLength
;
245 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
246 DWORD dwOleTypeNameLength
;
247 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
248 DWORD dwProgIDNameLength
;
249 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
251 }OLECONVERT_ISTORAGE_COMPOBJ
;
254 /* Ole Presentation Stream structure */
255 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
263 }OLECONVERT_ISTORAGE_OLEPRES
;
267 /***********************************************************************
268 * Forward declaration of internal functions used by the method DestroyElement
270 static HRESULT
deleteStorageContents(
271 StorageBaseImpl
*parentStorage
,
272 DirRef indexToDelete
,
273 DirEntry entryDataToDelete
);
275 static HRESULT
deleteStreamContents(
276 StorageBaseImpl
*parentStorage
,
277 DirRef indexToDelete
,
278 DirEntry entryDataToDelete
);
280 static HRESULT
removeFromTree(
281 StorageBaseImpl
*This
,
282 DirRef parentStorageIndex
,
283 DirRef deletedIndex
);
285 /***********************************************************************
286 * Declaration of the functions used to manipulate DirEntry
289 static HRESULT
insertIntoTree(
290 StorageBaseImpl
*This
,
291 DirRef parentStorageIndex
,
292 DirRef newEntryIndex
);
294 static LONG
entryNameCmp(
295 const OLECHAR
*name1
,
296 const OLECHAR
*name2
);
298 static DirRef
findElement(
299 StorageBaseImpl
*storage
,
304 static HRESULT
findTreeParent(
305 StorageBaseImpl
*storage
,
307 const OLECHAR
*childName
,
308 DirEntry
*parentData
,
312 /***********************************************************************
313 * Declaration of miscellaneous functions...
315 static HRESULT
validateSTGM(DWORD stgmValue
);
317 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
318 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
319 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
321 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
324 /****************************************************************************
325 * IEnumSTATSTGImpl definitions.
327 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
328 * This class allows iterating through the content of a storage and to find
329 * specific items inside it.
331 struct IEnumSTATSTGImpl
333 IEnumSTATSTG IEnumSTATSTG_iface
;
335 LONG ref
; /* Reference count */
336 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
337 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
339 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
342 static inline IEnumSTATSTGImpl
*impl_from_IEnumSTATSTG(IEnumSTATSTG
*iface
)
344 return CONTAINING_RECORD(iface
, IEnumSTATSTGImpl
, IEnumSTATSTG_iface
);
348 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
* This
, DirRef storageDirEntry
);
349 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
351 /************************************************************************
355 static ULONGLONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
357 return (ULONGLONG
)(index
+1) * This
->bigBlockSize
;
360 /************************************************************************
361 ** Storage32BaseImpl implementation
363 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
364 ULARGE_INTEGER offset
,
369 return ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
372 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
373 ULARGE_INTEGER offset
,
378 return ILockBytes_WriteAt(This
->lockBytes
,offset
,buffer
,size
,bytesWritten
);
381 /************************************************************************
382 * Storage32BaseImpl_QueryInterface (IUnknown)
384 * This method implements the common QueryInterface for all IStorage32
385 * implementations contained in this file.
387 * See Windows documentation for more details on IUnknown methods.
389 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
394 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
401 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
402 IsEqualGUID(&IID_IStorage
, riid
))
404 *ppvObject
= &This
->IStorage_iface
;
406 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
408 *ppvObject
= &This
->IPropertySetStorage_iface
;
410 /* locking interface is reported for writer only */
411 else if (IsEqualGUID(&IID_IDirectWriterLock
, riid
) && This
->lockingrole
== SWMR_Writer
)
413 *ppvObject
= &This
->IDirectWriterLock_iface
;
416 return E_NOINTERFACE
;
418 IStorage_AddRef(iface
);
423 /************************************************************************
424 * Storage32BaseImpl_AddRef (IUnknown)
426 * This method implements the common AddRef for all IStorage32
427 * implementations contained in this file.
429 * See Windows documentation for more details on IUnknown methods.
431 static ULONG WINAPI
StorageBaseImpl_AddRef(
434 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
435 ULONG ref
= InterlockedIncrement(&This
->ref
);
437 TRACE("(%p) AddRef to %d\n", This
, ref
);
442 /************************************************************************
443 * Storage32BaseImpl_Release (IUnknown)
445 * This method implements the common Release for all IStorage32
446 * implementations contained in this file.
448 * See Windows documentation for more details on IUnknown methods.
450 static ULONG WINAPI
StorageBaseImpl_Release(
453 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
455 ULONG ref
= InterlockedDecrement(&This
->ref
);
457 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
462 * Since we are using a system of base-classes, we want to call the
463 * destructor of the appropriate derived class. To do this, we are
464 * using virtual functions to implement the destructor.
466 StorageBaseImpl_Destroy(This
);
472 /************************************************************************
473 * Storage32BaseImpl_OpenStream (IStorage)
475 * This method will open the specified stream object from the current storage.
477 * See Windows documentation for more details on IStorage methods.
479 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
481 const OLECHAR
* pwcsName
, /* [string][in] */
482 void* reserved1
, /* [unique][in] */
483 DWORD grfMode
, /* [in] */
484 DWORD reserved2
, /* [in] */
485 IStream
** ppstm
) /* [out] */
487 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
488 StgStreamImpl
* newStream
;
489 DirEntry currentEntry
;
490 DirRef streamEntryRef
;
491 HRESULT res
= STG_E_UNKNOWN
;
493 TRACE("(%p, %s, %p, %x, %d, %p)\n",
494 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
496 if ( (pwcsName
==NULL
) || (ppstm
==0) )
504 if ( FAILED( validateSTGM(grfMode
) ) ||
505 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
507 res
= STG_E_INVALIDFLAG
;
514 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
516 res
= STG_E_INVALIDFUNCTION
;
522 res
= STG_E_REVERTED
;
527 * Check that we're compatible with the parent's storage mode, but
528 * only if we are not in transacted mode
530 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
531 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
533 res
= STG_E_INVALIDFLAG
;
539 * Search for the element with the given name
541 streamEntryRef
= findElement(
543 This
->storageDirEntry
,
548 * If it was found, construct the stream object and return a pointer to it.
550 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
551 (currentEntry
.stgType
==STGTY_STREAM
) )
553 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
555 /* A single stream cannot be opened a second time. */
556 res
= STG_E_ACCESSDENIED
;
560 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
564 newStream
->grfMode
= grfMode
;
565 *ppstm
= &newStream
->IStream_iface
;
567 IStream_AddRef(*ppstm
);
577 res
= STG_E_FILENOTFOUND
;
581 TRACE("<-- IStream %p\n", *ppstm
);
582 TRACE("<-- %08x\n", res
);
586 /************************************************************************
587 * Storage32BaseImpl_OpenStorage (IStorage)
589 * This method will open a new storage object from the current storage.
591 * See Windows documentation for more details on IStorage methods.
593 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
595 const OLECHAR
* pwcsName
, /* [string][unique][in] */
596 IStorage
* pstgPriority
, /* [unique][in] */
597 DWORD grfMode
, /* [in] */
598 SNB snbExclude
, /* [unique][in] */
599 DWORD reserved
, /* [in] */
600 IStorage
** ppstg
) /* [out] */
602 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
603 StorageInternalImpl
* newStorage
;
604 StorageBaseImpl
* newTransactedStorage
;
605 DirEntry currentEntry
;
606 DirRef storageEntryRef
;
607 HRESULT res
= STG_E_UNKNOWN
;
609 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
610 iface
, debugstr_w(pwcsName
), pstgPriority
,
611 grfMode
, snbExclude
, reserved
, ppstg
);
613 if ((pwcsName
==NULL
) || (ppstg
==0) )
619 if (This
->openFlags
& STGM_SIMPLE
)
621 res
= STG_E_INVALIDFUNCTION
;
626 if (snbExclude
!= NULL
)
628 res
= STG_E_INVALIDPARAMETER
;
632 if ( FAILED( validateSTGM(grfMode
) ))
634 res
= STG_E_INVALIDFLAG
;
641 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
642 (grfMode
& STGM_DELETEONRELEASE
) ||
643 (grfMode
& STGM_PRIORITY
) )
645 res
= STG_E_INVALIDFUNCTION
;
650 return STG_E_REVERTED
;
653 * Check that we're compatible with the parent's storage mode,
654 * but only if we are not transacted
656 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
657 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
659 res
= STG_E_ACCESSDENIED
;
666 storageEntryRef
= findElement(
668 This
->storageDirEntry
,
672 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
673 (currentEntry
.stgType
==STGTY_STORAGE
) )
675 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
677 /* A single storage cannot be opened a second time. */
678 res
= STG_E_ACCESSDENIED
;
682 newStorage
= StorageInternalImpl_Construct(
689 if (grfMode
& STGM_TRANSACTED
)
691 res
= Storage_ConstructTransacted(&newStorage
->base
, FALSE
, &newTransactedStorage
);
695 HeapFree(GetProcessHeap(), 0, newStorage
);
699 *ppstg
= &newTransactedStorage
->IStorage_iface
;
703 *ppstg
= &newStorage
->base
.IStorage_iface
;
706 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
712 res
= STG_E_INSUFFICIENTMEMORY
;
716 res
= STG_E_FILENOTFOUND
;
719 TRACE("<-- %08x\n", res
);
723 /************************************************************************
724 * Storage32BaseImpl_EnumElements (IStorage)
726 * This method will create an enumerator object that can be used to
727 * retrieve information about all the elements in the storage object.
729 * See Windows documentation for more details on IStorage methods.
731 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
733 DWORD reserved1
, /* [in] */
734 void* reserved2
, /* [size_is][unique][in] */
735 DWORD reserved3
, /* [in] */
736 IEnumSTATSTG
** ppenum
) /* [out] */
738 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
739 IEnumSTATSTGImpl
* newEnum
;
741 TRACE("(%p, %d, %p, %d, %p)\n",
742 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
748 return STG_E_REVERTED
;
750 newEnum
= IEnumSTATSTGImpl_Construct(
752 This
->storageDirEntry
);
756 *ppenum
= &newEnum
->IEnumSTATSTG_iface
;
760 return E_OUTOFMEMORY
;
763 /************************************************************************
764 * Storage32BaseImpl_Stat (IStorage)
766 * This method will retrieve information about this storage object.
768 * See Windows documentation for more details on IStorage methods.
770 static HRESULT WINAPI
StorageBaseImpl_Stat(
772 STATSTG
* pstatstg
, /* [out] */
773 DWORD grfStatFlag
) /* [in] */
775 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
776 DirEntry currentEntry
;
777 HRESULT res
= STG_E_UNKNOWN
;
779 TRACE("(%p, %p, %x)\n",
780 iface
, pstatstg
, grfStatFlag
);
790 res
= STG_E_REVERTED
;
794 res
= StorageBaseImpl_ReadDirEntry(
796 This
->storageDirEntry
,
801 StorageUtl_CopyDirEntryToSTATSTG(
807 pstatstg
->grfMode
= This
->openFlags
;
808 pstatstg
->grfStateBits
= This
->stateBits
;
814 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg
->pwcsName
), pstatstg
->type
, pstatstg
->cbSize
.u
.LowPart
, pstatstg
->cbSize
.u
.HighPart
, pstatstg
->grfMode
, pstatstg
->grfLocksSupported
, pstatstg
->grfStateBits
);
816 TRACE("<-- %08x\n", res
);
820 /************************************************************************
821 * Storage32BaseImpl_RenameElement (IStorage)
823 * This method will rename the specified element.
825 * See Windows documentation for more details on IStorage methods.
827 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
829 const OLECHAR
* pwcsOldName
, /* [in] */
830 const OLECHAR
* pwcsNewName
) /* [in] */
832 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
833 DirEntry currentEntry
;
834 DirRef currentEntryRef
;
836 TRACE("(%p, %s, %s)\n",
837 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
840 return STG_E_REVERTED
;
842 currentEntryRef
= findElement(This
,
843 This
->storageDirEntry
,
847 if (currentEntryRef
!= DIRENTRY_NULL
)
850 * There is already an element with the new name
852 return STG_E_FILEALREADYEXISTS
;
856 * Search for the old element name
858 currentEntryRef
= findElement(This
,
859 This
->storageDirEntry
,
863 if (currentEntryRef
!= DIRENTRY_NULL
)
865 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
866 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
868 WARN("Element is already open; cannot rename.\n");
869 return STG_E_ACCESSDENIED
;
872 /* Remove the element from its current position in the tree */
873 removeFromTree(This
, This
->storageDirEntry
,
876 /* Change the name of the element */
877 strcpyW(currentEntry
.name
, pwcsNewName
);
879 /* Delete any sibling links */
880 currentEntry
.leftChild
= DIRENTRY_NULL
;
881 currentEntry
.rightChild
= DIRENTRY_NULL
;
883 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
886 /* Insert the element in a new position in the tree */
887 insertIntoTree(This
, This
->storageDirEntry
,
893 * There is no element with the old name
895 return STG_E_FILENOTFOUND
;
898 return StorageBaseImpl_Flush(This
);
901 /************************************************************************
902 * Storage32BaseImpl_CreateStream (IStorage)
904 * This method will create a stream object within this storage
906 * See Windows documentation for more details on IStorage methods.
908 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
910 const OLECHAR
* pwcsName
, /* [string][in] */
911 DWORD grfMode
, /* [in] */
912 DWORD reserved1
, /* [in] */
913 DWORD reserved2
, /* [in] */
914 IStream
** ppstm
) /* [out] */
916 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
917 StgStreamImpl
* newStream
;
918 DirEntry currentEntry
, newStreamEntry
;
919 DirRef currentEntryRef
, newStreamEntryRef
;
922 TRACE("(%p, %s, %x, %d, %d, %p)\n",
923 iface
, debugstr_w(pwcsName
), grfMode
,
924 reserved1
, reserved2
, ppstm
);
927 return STG_E_INVALIDPOINTER
;
930 return STG_E_INVALIDNAME
;
932 if (reserved1
|| reserved2
)
933 return STG_E_INVALIDPARAMETER
;
935 if ( FAILED( validateSTGM(grfMode
) ))
936 return STG_E_INVALIDFLAG
;
938 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
939 return STG_E_INVALIDFLAG
;
942 return STG_E_REVERTED
;
947 if ((grfMode
& STGM_DELETEONRELEASE
) ||
948 (grfMode
& STGM_TRANSACTED
))
949 return STG_E_INVALIDFUNCTION
;
952 * Don't worry about permissions in transacted mode, as we can always write
953 * changes; we just can't always commit them.
955 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
956 /* Can't create a stream on read-only storage */
957 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
958 return STG_E_ACCESSDENIED
;
960 /* Can't create a stream with greater access than the parent. */
961 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
962 return STG_E_ACCESSDENIED
;
965 if(This
->openFlags
& STGM_SIMPLE
)
966 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
970 currentEntryRef
= findElement(This
,
971 This
->storageDirEntry
,
975 if (currentEntryRef
!= DIRENTRY_NULL
)
978 * An element with this name already exists
980 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
982 IStorage_DestroyElement(iface
, pwcsName
);
985 return STG_E_FILEALREADYEXISTS
;
989 * memset the empty entry
991 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
993 newStreamEntry
.sizeOfNameString
=
994 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
996 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
997 return STG_E_INVALIDNAME
;
999 strcpyW(newStreamEntry
.name
, pwcsName
);
1001 newStreamEntry
.stgType
= STGTY_STREAM
;
1002 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1003 newStreamEntry
.size
.u
.LowPart
= 0;
1004 newStreamEntry
.size
.u
.HighPart
= 0;
1006 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
1007 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
1008 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
1010 /* call CoFileTime to get the current time
1011 newStreamEntry.ctime
1012 newStreamEntry.mtime
1015 /* newStreamEntry.clsid */
1018 * Create an entry with the new data
1020 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
1025 * Insert the new entry in the parent storage's tree.
1027 hr
= insertIntoTree(
1029 This
->storageDirEntry
,
1033 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
1038 * Open the stream to return it.
1040 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
1044 *ppstm
= &newStream
->IStream_iface
;
1045 IStream_AddRef(*ppstm
);
1049 return STG_E_INSUFFICIENTMEMORY
;
1052 return StorageBaseImpl_Flush(This
);
1055 /************************************************************************
1056 * Storage32BaseImpl_SetClass (IStorage)
1058 * This method will write the specified CLSID in the directory entry of this
1061 * See Windows documentation for more details on IStorage methods.
1063 static HRESULT WINAPI
StorageBaseImpl_SetClass(
1065 REFCLSID clsid
) /* [in] */
1067 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1069 DirEntry currentEntry
;
1071 TRACE("(%p, %p)\n", iface
, clsid
);
1074 return STG_E_REVERTED
;
1076 hRes
= StorageBaseImpl_ReadDirEntry(This
,
1077 This
->storageDirEntry
,
1079 if (SUCCEEDED(hRes
))
1081 currentEntry
.clsid
= *clsid
;
1083 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1084 This
->storageDirEntry
,
1088 if (SUCCEEDED(hRes
))
1089 hRes
= StorageBaseImpl_Flush(This
);
1094 /************************************************************************
1095 ** Storage32Impl implementation
1098 /************************************************************************
1099 * Storage32BaseImpl_CreateStorage (IStorage)
1101 * This method will create the storage object within the provided storage.
1103 * See Windows documentation for more details on IStorage methods.
1105 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1107 const OLECHAR
*pwcsName
, /* [string][in] */
1108 DWORD grfMode
, /* [in] */
1109 DWORD reserved1
, /* [in] */
1110 DWORD reserved2
, /* [in] */
1111 IStorage
**ppstg
) /* [out] */
1113 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
1115 DirEntry currentEntry
;
1117 DirRef currentEntryRef
;
1121 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1122 iface
, debugstr_w(pwcsName
), grfMode
,
1123 reserved1
, reserved2
, ppstg
);
1126 return STG_E_INVALIDPOINTER
;
1128 if (This
->openFlags
& STGM_SIMPLE
)
1130 return STG_E_INVALIDFUNCTION
;
1134 return STG_E_INVALIDNAME
;
1138 if ( FAILED( validateSTGM(grfMode
) ) ||
1139 (grfMode
& STGM_DELETEONRELEASE
) )
1141 WARN("bad grfMode: 0x%x\n", grfMode
);
1142 return STG_E_INVALIDFLAG
;
1146 return STG_E_REVERTED
;
1149 * Check that we're compatible with the parent's storage mode
1151 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1152 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1154 WARN("access denied\n");
1155 return STG_E_ACCESSDENIED
;
1158 currentEntryRef
= findElement(This
,
1159 This
->storageDirEntry
,
1163 if (currentEntryRef
!= DIRENTRY_NULL
)
1166 * An element with this name already exists
1168 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1169 ((This
->openFlags
& STGM_TRANSACTED
) ||
1170 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
1172 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1178 WARN("file already exists\n");
1179 return STG_E_FILEALREADYEXISTS
;
1182 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
1183 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1185 WARN("read-only storage\n");
1186 return STG_E_ACCESSDENIED
;
1189 memset(&newEntry
, 0, sizeof(DirEntry
));
1191 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1193 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1195 FIXME("name too long\n");
1196 return STG_E_INVALIDNAME
;
1199 strcpyW(newEntry
.name
, pwcsName
);
1201 newEntry
.stgType
= STGTY_STORAGE
;
1202 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1203 newEntry
.size
.u
.LowPart
= 0;
1204 newEntry
.size
.u
.HighPart
= 0;
1206 newEntry
.leftChild
= DIRENTRY_NULL
;
1207 newEntry
.rightChild
= DIRENTRY_NULL
;
1208 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1210 /* call CoFileTime to get the current time
1215 /* newEntry.clsid */
1218 * Create a new directory entry for the storage
1220 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1225 * Insert the new directory entry into the parent storage's tree
1227 hr
= insertIntoTree(
1229 This
->storageDirEntry
,
1233 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
1238 * Open it to get a pointer to return.
1240 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1242 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1248 hr
= StorageBaseImpl_Flush(This
);
1254 /***************************************************************************
1258 * Reserve a directory entry in the file and initialize it.
1260 static HRESULT
StorageImpl_CreateDirEntry(
1261 StorageBaseImpl
*base
,
1262 const DirEntry
*newData
,
1265 StorageImpl
*storage
= (StorageImpl
*)base
;
1266 ULONG currentEntryIndex
= 0;
1267 ULONG newEntryIndex
= DIRENTRY_NULL
;
1269 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1270 WORD sizeOfNameString
;
1274 hr
= StorageImpl_ReadRawDirEntry(storage
,
1280 StorageUtl_ReadWord(
1282 OFFSET_PS_NAMELENGTH
,
1285 if (sizeOfNameString
== 0)
1288 * The entry exists and is available, we found it.
1290 newEntryIndex
= currentEntryIndex
;
1296 * We exhausted the directory entries, we will create more space below
1298 newEntryIndex
= currentEntryIndex
;
1300 currentEntryIndex
++;
1302 } while (newEntryIndex
== DIRENTRY_NULL
);
1305 * grow the directory stream
1309 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1310 ULARGE_INTEGER newSize
;
1312 ULONG lastEntry
= 0;
1313 ULONG blockCount
= 0;
1316 * obtain the new count of blocks in the directory stream
1318 blockCount
= BlockChainStream_GetCount(
1319 storage
->rootBlockChain
)+1;
1322 * initialize the size used by the directory stream
1324 newSize
.QuadPart
= (ULONGLONG
)storage
->bigBlockSize
* blockCount
;
1327 * add a block to the directory stream
1329 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1332 * memset the empty entry in order to initialize the unused newly
1335 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1340 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1343 entryIndex
= newEntryIndex
+ 1;
1344 entryIndex
< lastEntry
;
1347 StorageImpl_WriteRawDirEntry(
1353 StorageImpl_SaveFileHeader(storage
);
1356 UpdateRawDirEntry(currentData
, newData
);
1358 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1361 *index
= newEntryIndex
;
1366 /***************************************************************************
1370 * Mark a directory entry in the file as free.
1372 static HRESULT
StorageImpl_DestroyDirEntry(
1373 StorageBaseImpl
*base
,
1376 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1377 StorageImpl
*storage
= (StorageImpl
*)base
;
1379 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1381 return StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1385 /****************************************************************************
1389 * Case insensitive comparison of DirEntry.name by first considering
1392 * Returns <0 when name1 < name2
1393 * >0 when name1 > name2
1394 * 0 when name1 == name2
1396 static LONG
entryNameCmp(
1397 const OLECHAR
*name1
,
1398 const OLECHAR
*name2
)
1400 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1402 while (diff
== 0 && *name1
!= 0)
1405 * We compare the string themselves only when they are of the same length
1407 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1413 /****************************************************************************
1417 * Add a directory entry to a storage
1419 static HRESULT
insertIntoTree(
1420 StorageBaseImpl
*This
,
1421 DirRef parentStorageIndex
,
1422 DirRef newEntryIndex
)
1424 DirEntry currentEntry
;
1428 * Read the inserted entry
1430 StorageBaseImpl_ReadDirEntry(This
,
1435 * Read the storage entry
1437 StorageBaseImpl_ReadDirEntry(This
,
1441 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1444 * The root storage contains some element, therefore, start the research
1445 * for the appropriate location.
1448 DirRef current
, next
, previous
, currentEntryId
;
1451 * Keep a reference to the root of the storage's element tree
1453 currentEntryId
= currentEntry
.dirRootEntry
;
1458 StorageBaseImpl_ReadDirEntry(This
,
1459 currentEntry
.dirRootEntry
,
1462 previous
= currentEntry
.leftChild
;
1463 next
= currentEntry
.rightChild
;
1464 current
= currentEntryId
;
1468 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1472 if (previous
!= DIRENTRY_NULL
)
1474 StorageBaseImpl_ReadDirEntry(This
,
1481 currentEntry
.leftChild
= newEntryIndex
;
1482 StorageBaseImpl_WriteDirEntry(This
,
1490 if (next
!= DIRENTRY_NULL
)
1492 StorageBaseImpl_ReadDirEntry(This
,
1499 currentEntry
.rightChild
= newEntryIndex
;
1500 StorageBaseImpl_WriteDirEntry(This
,
1509 * Trying to insert an item with the same name in the
1510 * subtree structure.
1512 return STG_E_FILEALREADYEXISTS
;
1515 previous
= currentEntry
.leftChild
;
1516 next
= currentEntry
.rightChild
;
1522 * The storage is empty, make the new entry the root of its element tree
1524 currentEntry
.dirRootEntry
= newEntryIndex
;
1525 StorageBaseImpl_WriteDirEntry(This
,
1533 /****************************************************************************
1537 * Find and read the element of a storage with the given name.
1539 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1540 const OLECHAR
*name
, DirEntry
*data
)
1542 DirRef currentEntry
;
1544 /* Read the storage entry to find the root of the tree. */
1545 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1547 currentEntry
= data
->dirRootEntry
;
1549 while (currentEntry
!= DIRENTRY_NULL
)
1553 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1555 cmp
= entryNameCmp(name
, data
->name
);
1562 currentEntry
= data
->leftChild
;
1565 currentEntry
= data
->rightChild
;
1568 return currentEntry
;
1571 /****************************************************************************
1575 * Find and read the binary tree parent of the element with the given name.
1577 * If there is no such element, find a place where it could be inserted and
1578 * return STG_E_FILENOTFOUND.
1580 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1581 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1587 /* Read the storage entry to find the root of the tree. */
1588 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1590 *parentEntry
= storageEntry
;
1591 *relation
= DIRENTRY_RELATION_DIR
;
1593 childEntry
= parentData
->dirRootEntry
;
1595 while (childEntry
!= DIRENTRY_NULL
)
1599 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1601 cmp
= entryNameCmp(childName
, childData
.name
);
1609 *parentData
= childData
;
1610 *parentEntry
= childEntry
;
1611 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1613 childEntry
= parentData
->leftChild
;
1618 *parentData
= childData
;
1619 *parentEntry
= childEntry
;
1620 *relation
= DIRENTRY_RELATION_NEXT
;
1622 childEntry
= parentData
->rightChild
;
1626 if (childEntry
== DIRENTRY_NULL
)
1627 return STG_E_FILENOTFOUND
;
1633 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1634 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1635 SNB snbExclude
, IStorage
*pstgDest
);
1637 static HRESULT
StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl
*This
,
1638 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1639 SNB snbExclude
, IStorage
*pstgDest
)
1645 IStream
*pstrChild
, *pstrTmp
;
1648 if (srcEntry
== DIRENTRY_NULL
)
1651 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1658 WCHAR
**snb
= snbExclude
;
1660 while ( *snb
!= NULL
&& !skip
)
1662 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1670 if (data
.stgType
== STGTY_STORAGE
&& !skip_storage
)
1673 * create a new storage in destination storage
1675 hr
= IStorage_CreateStorage( pstgDest
, data
.name
,
1676 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1681 * if it already exist, don't create a new one use this one
1683 if (hr
== STG_E_FILEALREADYEXISTS
)
1685 hr
= IStorage_OpenStorage( pstgDest
, data
.name
, NULL
,
1686 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1687 NULL
, 0, &pstgTmp
);
1692 hr
= StorageBaseImpl_CopyStorageEntryTo( This
, srcEntry
, skip_storage
,
1693 skip_stream
, NULL
, pstgTmp
);
1695 IStorage_Release(pstgTmp
);
1698 else if (data
.stgType
== STGTY_STREAM
&& !skip_stream
)
1701 * create a new stream in destination storage. If the stream already
1702 * exist, it will be deleted and a new one will be created.
1704 hr
= IStorage_CreateStream( pstgDest
, data
.name
,
1705 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1709 * open child stream storage. This operation must succeed even if the
1710 * stream is already open, so we use internal functions to do it.
1714 StgStreamImpl
*streamimpl
= StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntry
);
1718 pstrChild
= &streamimpl
->IStream_iface
;
1720 IStream_AddRef(pstrChild
);
1732 * Get the size of the source stream
1734 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1737 * Set the size of the destination stream.
1739 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1744 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1747 IStream_Release( pstrChild
);
1750 IStream_Release( pstrTmp
);
1756 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.leftChild
, skip_storage
,
1757 skip_stream
, snbExclude
, pstgDest
);
1760 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.rightChild
, skip_storage
,
1761 skip_stream
, snbExclude
, pstgDest
);
1766 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1767 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1768 SNB snbExclude
, IStorage
*pstgDest
)
1773 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1776 hr
= IStorage_SetClass( pstgDest
, &data
.clsid
);
1779 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.dirRootEntry
, skip_storage
,
1780 skip_stream
, snbExclude
, pstgDest
);
1785 /*************************************************************************
1788 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1790 DWORD ciidExclude
, /* [in] */
1791 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1792 SNB snbExclude
, /* [unique][in] */
1793 IStorage
* pstgDest
) /* [unique][in] */
1795 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1797 BOOL skip_storage
= FALSE
, skip_stream
= FALSE
;
1800 TRACE("(%p, %d, %p, %p, %p)\n",
1801 iface
, ciidExclude
, rgiidExclude
,
1802 snbExclude
, pstgDest
);
1804 if ( pstgDest
== 0 )
1805 return STG_E_INVALIDPOINTER
;
1807 for(i
= 0; i
< ciidExclude
; ++i
)
1809 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1810 skip_storage
= TRUE
;
1811 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1814 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1819 /* Give up early if it looks like this would be infinitely recursive.
1820 * Oddly enough, this includes some cases that aren't really recursive, like
1821 * copying to a transacted child. */
1822 IStorage
*pstgDestAncestor
= pstgDest
;
1823 IStorage
*pstgDestAncestorChild
= NULL
;
1825 /* Go up the chain from the destination until we find the source storage. */
1826 while (pstgDestAncestor
!= iface
) {
1827 pstgDestAncestorChild
= pstgDest
;
1829 if (pstgDestAncestor
->lpVtbl
== &TransactedSnapshotImpl_Vtbl
)
1831 TransactedSnapshotImpl
*snapshot
= (TransactedSnapshotImpl
*) pstgDestAncestor
;
1833 pstgDestAncestor
= &snapshot
->transactedParent
->IStorage_iface
;
1835 else if (pstgDestAncestor
->lpVtbl
== &Storage32InternalImpl_Vtbl
)
1837 StorageInternalImpl
*internal
= (StorageInternalImpl
*) pstgDestAncestor
;
1839 pstgDestAncestor
= &internal
->parentStorage
->IStorage_iface
;
1845 if (pstgDestAncestor
== iface
)
1849 if (pstgDestAncestorChild
&& snbExclude
)
1851 StorageBaseImpl
*ancestorChildBase
= (StorageBaseImpl
*)pstgDestAncestorChild
;
1853 WCHAR
**snb
= snbExclude
;
1855 StorageBaseImpl_ReadDirEntry(ancestorChildBase
, ancestorChildBase
->storageDirEntry
, &data
);
1857 while ( *snb
!= NULL
&& fail
)
1859 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1866 return STG_E_ACCESSDENIED
;
1870 return StorageBaseImpl_CopyStorageEntryTo( This
, This
->storageDirEntry
,
1871 skip_storage
, skip_stream
, snbExclude
, pstgDest
);
1874 /*************************************************************************
1875 * MoveElementTo (IStorage)
1877 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1879 const OLECHAR
*pwcsName
, /* [string][in] */
1880 IStorage
*pstgDest
, /* [unique][in] */
1881 const OLECHAR
*pwcsNewName
,/* [string][in] */
1882 DWORD grfFlags
) /* [in] */
1884 FIXME("(%p %s %p %s %u): stub\n", iface
,
1885 debugstr_w(pwcsName
), pstgDest
,
1886 debugstr_w(pwcsNewName
), grfFlags
);
1890 /*************************************************************************
1893 * Ensures that any changes made to a storage object open in transacted mode
1894 * are reflected in the parent storage
1896 * In a non-transacted mode, this ensures all cached writes are completed.
1898 static HRESULT WINAPI
StorageImpl_Commit(
1900 DWORD grfCommitFlags
)/* [in] */
1902 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
1903 TRACE("(%p %d)\n", iface
, grfCommitFlags
);
1904 return StorageBaseImpl_Flush(This
);
1907 /*************************************************************************
1910 * Discard all changes that have been made since the last commit operation
1912 static HRESULT WINAPI
StorageImpl_Revert(
1915 TRACE("(%p)\n", iface
);
1919 /*************************************************************************
1920 * DestroyElement (IStorage)
1922 * Strategy: This implementation is built this way for simplicity not for speed.
1923 * I always delete the topmost element of the enumeration and adjust
1924 * the deleted element pointer all the time. This takes longer to
1925 * do but allow to reinvoke DestroyElement whenever we encounter a
1926 * storage object. The optimisation resides in the usage of another
1927 * enumeration strategy that would give all the leaves of a storage
1928 * first. (postfix order)
1930 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1932 const OLECHAR
*pwcsName
)/* [string][in] */
1934 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1937 DirEntry entryToDelete
;
1938 DirRef entryToDeleteRef
;
1941 iface
, debugstr_w(pwcsName
));
1944 return STG_E_INVALIDPOINTER
;
1947 return STG_E_REVERTED
;
1949 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1950 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1951 return STG_E_ACCESSDENIED
;
1953 entryToDeleteRef
= findElement(
1955 This
->storageDirEntry
,
1959 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1961 return STG_E_FILENOTFOUND
;
1964 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1966 hr
= deleteStorageContents(
1971 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1973 hr
= deleteStreamContents(
1983 * Remove the entry from its parent storage
1985 hr
= removeFromTree(
1987 This
->storageDirEntry
,
1991 * Invalidate the entry
1994 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1997 hr
= StorageBaseImpl_Flush(This
);
2003 /******************************************************************************
2004 * Internal stream list handlers
2007 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2009 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
2010 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
2013 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2015 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
2016 list_remove(&(strm
->StrmListEntry
));
2019 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
2021 StgStreamImpl
*strm
;
2023 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
2025 if (strm
->dirEntry
== streamEntry
)
2034 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
2036 StorageInternalImpl
*childstg
;
2038 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2040 if (childstg
->base
.storageDirEntry
== storageEntry
)
2049 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
2051 struct list
*cur
, *cur2
;
2052 StgStreamImpl
*strm
=NULL
;
2053 StorageInternalImpl
*childstg
=NULL
;
2055 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
2056 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
2057 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
2058 strm
->parentStorage
= NULL
;
2062 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
2063 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
2064 StorageBaseImpl_Invalidate( &childstg
->base
);
2067 if (stg
->transactedChild
)
2069 StorageBaseImpl_Invalidate(stg
->transactedChild
);
2071 stg
->transactedChild
= NULL
;
2076 /*********************************************************************
2080 * Delete the contents of a storage entry.
2083 static HRESULT
deleteStorageContents(
2084 StorageBaseImpl
*parentStorage
,
2085 DirRef indexToDelete
,
2086 DirEntry entryDataToDelete
)
2088 IEnumSTATSTG
*elements
= 0;
2089 IStorage
*childStorage
= 0;
2090 STATSTG currentElement
;
2092 HRESULT destroyHr
= S_OK
;
2093 StorageInternalImpl
*stg
, *stg2
;
2095 /* Invalidate any open storage objects. */
2096 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2098 if (stg
->base
.storageDirEntry
== indexToDelete
)
2100 StorageBaseImpl_Invalidate(&stg
->base
);
2105 * Open the storage and enumerate it
2107 hr
= IStorage_OpenStorage(
2108 &parentStorage
->IStorage_iface
,
2109 entryDataToDelete
.name
,
2111 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2122 * Enumerate the elements
2124 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2129 * Obtain the next element
2131 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2134 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2136 CoTaskMemFree(currentElement
.pwcsName
);
2140 * We need to Reset the enumeration every time because we delete elements
2141 * and the enumeration could be invalid
2143 IEnumSTATSTG_Reset(elements
);
2145 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2147 IStorage_Release(childStorage
);
2148 IEnumSTATSTG_Release(elements
);
2153 /*********************************************************************
2157 * Perform the deletion of a stream's data
2160 static HRESULT
deleteStreamContents(
2161 StorageBaseImpl
*parentStorage
,
2162 DirRef indexToDelete
,
2163 DirEntry entryDataToDelete
)
2167 ULARGE_INTEGER size
;
2168 StgStreamImpl
*strm
, *strm2
;
2170 /* Invalidate any open stream objects. */
2171 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2173 if (strm
->dirEntry
== indexToDelete
)
2175 TRACE("Stream deleted %p\n", strm
);
2176 strm
->parentStorage
= NULL
;
2177 list_remove(&strm
->StrmListEntry
);
2181 size
.u
.HighPart
= 0;
2184 hr
= IStorage_OpenStream(&parentStorage
->IStorage_iface
,
2185 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2195 hr
= IStream_SetSize(pis
, size
);
2203 * Release the stream object.
2205 IStream_Release(pis
);
2210 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2214 case DIRENTRY_RELATION_PREVIOUS
:
2215 entry
->leftChild
= new_target
;
2217 case DIRENTRY_RELATION_NEXT
:
2218 entry
->rightChild
= new_target
;
2220 case DIRENTRY_RELATION_DIR
:
2221 entry
->dirRootEntry
= new_target
;
2228 /*************************************************************************
2232 * This method removes a directory entry from its parent storage tree without
2233 * freeing any resources attached to it.
2235 static HRESULT
removeFromTree(
2236 StorageBaseImpl
*This
,
2237 DirRef parentStorageIndex
,
2238 DirRef deletedIndex
)
2240 DirEntry entryToDelete
;
2241 DirEntry parentEntry
;
2242 DirRef parentEntryRef
;
2243 ULONG typeOfRelation
;
2246 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2252 * Find the element that links to the one we want to delete.
2254 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2255 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2260 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2263 * Replace the deleted entry with its left child
2265 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2267 hr
= StorageBaseImpl_WriteDirEntry(
2276 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2279 * We need to reinsert the right child somewhere. We already know it and
2280 * its children are greater than everything in the left tree, so we
2281 * insert it at the rightmost point in the left tree.
2283 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2284 DirEntry newRightChildParentEntry
;
2288 hr
= StorageBaseImpl_ReadDirEntry(
2290 newRightChildParent
,
2291 &newRightChildParentEntry
);
2297 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2298 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2299 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2301 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2303 hr
= StorageBaseImpl_WriteDirEntry(
2305 newRightChildParent
,
2306 &newRightChildParentEntry
);
2316 * Replace the deleted entry with its right child
2318 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2320 hr
= StorageBaseImpl_WriteDirEntry(
2334 /******************************************************************************
2335 * SetElementTimes (IStorage)
2337 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2339 const OLECHAR
*pwcsName
,/* [string][in] */
2340 const FILETIME
*pctime
, /* [in] */
2341 const FILETIME
*patime
, /* [in] */
2342 const FILETIME
*pmtime
) /* [in] */
2344 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2348 /******************************************************************************
2349 * SetStateBits (IStorage)
2351 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2353 DWORD grfStateBits
,/* [in] */
2354 DWORD grfMask
) /* [in] */
2356 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2359 return STG_E_REVERTED
;
2361 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2365 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2366 DirRef index
, const DirEntry
*data
)
2368 StorageImpl
*This
= (StorageImpl
*)base
;
2369 return StorageImpl_WriteDirEntry(This
, index
, data
);
2372 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2373 DirRef index
, DirEntry
*data
)
2375 StorageImpl
*This
= (StorageImpl
*)base
;
2376 return StorageImpl_ReadDirEntry(This
, index
, data
);
2379 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2383 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2385 if (!This
->blockChainCache
[i
])
2387 return &This
->blockChainCache
[i
];
2391 i
= This
->blockChainToEvict
;
2393 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2394 This
->blockChainCache
[i
] = NULL
;
2396 This
->blockChainToEvict
++;
2397 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2398 This
->blockChainToEvict
= 0;
2400 return &This
->blockChainCache
[i
];
2403 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2406 int i
, free_index
=-1;
2408 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2410 if (!This
->blockChainCache
[i
])
2412 if (free_index
== -1) free_index
= i
;
2414 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2416 return &This
->blockChainCache
[i
];
2420 if (free_index
== -1)
2422 free_index
= This
->blockChainToEvict
;
2424 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2425 This
->blockChainCache
[free_index
] = NULL
;
2427 This
->blockChainToEvict
++;
2428 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2429 This
->blockChainToEvict
= 0;
2432 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2433 return &This
->blockChainCache
[free_index
];
2436 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
2440 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2442 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2444 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2445 This
->blockChainCache
[i
] = NULL
;
2451 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2452 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2454 StorageImpl
*This
= (StorageImpl
*)base
;
2459 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2460 if (FAILED(hr
)) return hr
;
2462 if (data
.size
.QuadPart
== 0)
2468 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2470 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2477 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2479 SmallBlockChainStream
*stream
;
2481 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2482 if (!stream
) return E_OUTOFMEMORY
;
2484 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2486 SmallBlockChainStream_Destroy(stream
);
2492 BlockChainStream
*stream
= NULL
;
2494 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2495 if (!stream
) return E_OUTOFMEMORY
;
2497 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2503 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2504 ULARGE_INTEGER newsize
)
2506 StorageImpl
*This
= (StorageImpl
*)base
;
2509 SmallBlockChainStream
*smallblock
=NULL
;
2510 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2512 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2513 if (FAILED(hr
)) return hr
;
2515 /* In simple mode keep the stream size above the small block limit */
2516 if (This
->base
.openFlags
& STGM_SIMPLE
)
2517 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2519 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2522 /* Create a block chain object of the appropriate type */
2523 if (data
.size
.QuadPart
== 0)
2525 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2527 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2528 if (!smallblock
) return E_OUTOFMEMORY
;
2532 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2533 bigblock
= *pbigblock
;
2534 if (!bigblock
) return E_OUTOFMEMORY
;
2537 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2539 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2540 if (!smallblock
) return E_OUTOFMEMORY
;
2544 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2545 bigblock
= *pbigblock
;
2546 if (!bigblock
) return E_OUTOFMEMORY
;
2549 /* Change the block chain type if necessary. */
2550 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2552 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2555 SmallBlockChainStream_Destroy(smallblock
);
2559 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2560 *pbigblock
= bigblock
;
2562 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2564 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
2569 /* Set the size of the block chain. */
2572 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2573 SmallBlockChainStream_Destroy(smallblock
);
2577 BlockChainStream_SetSize(bigblock
, newsize
);
2580 /* Set the size in the directory entry. */
2581 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2584 data
.size
= newsize
;
2586 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2591 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2592 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2594 StorageImpl
*This
= (StorageImpl
*)base
;
2597 ULARGE_INTEGER newSize
;
2599 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2600 if (FAILED(hr
)) return hr
;
2602 /* Grow the stream if necessary */
2603 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2605 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2607 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2611 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2612 if (FAILED(hr
)) return hr
;
2615 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2617 SmallBlockChainStream
*stream
;
2619 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2620 if (!stream
) return E_OUTOFMEMORY
;
2622 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2624 SmallBlockChainStream_Destroy(stream
);
2630 BlockChainStream
*stream
;
2632 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2633 if (!stream
) return E_OUTOFMEMORY
;
2635 return BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2639 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
2642 StorageImpl
*This
= (StorageImpl
*)base
;
2643 DirEntry dst_data
, src_data
;
2646 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
2649 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
2653 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
2654 dst_data
.startingBlock
= src_data
.startingBlock
;
2655 dst_data
.size
= src_data
.size
;
2657 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
2663 static HRESULT
StorageImpl_GetTransactionSig(StorageBaseImpl
*base
,
2664 ULONG
* result
, BOOL refresh
)
2666 StorageImpl
*This
= (StorageImpl
*)base
;
2668 DWORD oldTransactionSig
= This
->transactionSig
;
2672 ULARGE_INTEGER offset
;
2676 offset
.u
.HighPart
= 0;
2677 offset
.u
.LowPart
= OFFSET_TRANSACTIONSIG
;
2678 hr
= StorageImpl_ReadAt(This
, offset
, data
, 4, &bytes_read
);
2682 StorageUtl_ReadDWord(data
, 0, &This
->transactionSig
);
2684 if (oldTransactionSig
!= This
->transactionSig
)
2686 /* Someone else wrote to this, so toss all cached information. */
2687 TRACE("signature changed\n");
2689 hr
= StorageImpl_Refresh(This
, FALSE
, FALSE
);
2693 This
->transactionSig
= oldTransactionSig
;
2697 *result
= This
->transactionSig
;
2702 static HRESULT
StorageImpl_SetTransactionSig(StorageBaseImpl
*base
,
2705 StorageImpl
*This
= (StorageImpl
*)base
;
2707 This
->transactionSig
= value
;
2708 StorageImpl_SaveFileHeader(This
);
2713 static HRESULT
StorageImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
2715 StorageImpl
*This
= (StorageImpl
*)base
;
2717 ULARGE_INTEGER offset
, cb
;
2721 /* Synchronous grab of second priority range, the commit lock, and the
2722 * lock-checking lock. */
2723 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
2724 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
2728 offset
.QuadPart
= RANGELOCK_COMMIT
;
2732 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
);
2734 if (hr
== STG_E_INVALIDFUNCTION
)
2740 static HRESULT
StorageImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
2742 StorageImpl
*This
= (StorageImpl
*)base
;
2744 ULARGE_INTEGER offset
, cb
;
2748 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
2749 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
2753 offset
.QuadPart
= RANGELOCK_COMMIT
;
2757 hr
= ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2759 if (hr
== STG_E_INVALIDFUNCTION
)
2765 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
2767 StorageImpl
*This
= (StorageImpl
*) iface
;
2771 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
2773 *result
= statstg
.pwcsName
;
2778 static HRESULT WINAPI
directwriterlock_QueryInterface(IDirectWriterLock
*iface
, REFIID riid
, void **obj
)
2780 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2781 return IStorage_QueryInterface(&This
->IStorage_iface
, riid
, obj
);
2784 static ULONG WINAPI
directwriterlock_AddRef(IDirectWriterLock
*iface
)
2786 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2787 return IStorage_AddRef(&This
->IStorage_iface
);
2790 static ULONG WINAPI
directwriterlock_Release(IDirectWriterLock
*iface
)
2792 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2793 return IStorage_Release(&This
->IStorage_iface
);
2796 static HRESULT WINAPI
directwriterlock_WaitForWriteAccess(IDirectWriterLock
*iface
, DWORD timeout
)
2798 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2799 FIXME("(%p)->(%d): stub\n", This
, timeout
);
2803 static HRESULT WINAPI
directwriterlock_ReleaseWriteAccess(IDirectWriterLock
*iface
)
2805 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2806 FIXME("(%p): stub\n", This
);
2810 static HRESULT WINAPI
directwriterlock_HaveWriteAccess(IDirectWriterLock
*iface
)
2812 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2813 FIXME("(%p): stub\n", This
);
2817 static const IDirectWriterLockVtbl DirectWriterLockVtbl
=
2819 directwriterlock_QueryInterface
,
2820 directwriterlock_AddRef
,
2821 directwriterlock_Release
,
2822 directwriterlock_WaitForWriteAccess
,
2823 directwriterlock_ReleaseWriteAccess
,
2824 directwriterlock_HaveWriteAccess
2828 * Virtual function table for the IStorage32Impl class.
2830 static const IStorageVtbl Storage32Impl_Vtbl
=
2832 StorageBaseImpl_QueryInterface
,
2833 StorageBaseImpl_AddRef
,
2834 StorageBaseImpl_Release
,
2835 StorageBaseImpl_CreateStream
,
2836 StorageBaseImpl_OpenStream
,
2837 StorageBaseImpl_CreateStorage
,
2838 StorageBaseImpl_OpenStorage
,
2839 StorageBaseImpl_CopyTo
,
2840 StorageBaseImpl_MoveElementTo
,
2843 StorageBaseImpl_EnumElements
,
2844 StorageBaseImpl_DestroyElement
,
2845 StorageBaseImpl_RenameElement
,
2846 StorageBaseImpl_SetElementTimes
,
2847 StorageBaseImpl_SetClass
,
2848 StorageBaseImpl_SetStateBits
,
2849 StorageBaseImpl_Stat
2852 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2854 StorageImpl_Destroy
,
2855 StorageImpl_Invalidate
,
2857 StorageImpl_GetFilename
,
2858 StorageImpl_CreateDirEntry
,
2859 StorageImpl_BaseWriteDirEntry
,
2860 StorageImpl_BaseReadDirEntry
,
2861 StorageImpl_DestroyDirEntry
,
2862 StorageImpl_StreamReadAt
,
2863 StorageImpl_StreamWriteAt
,
2864 StorageImpl_StreamSetSize
,
2865 StorageImpl_StreamLink
,
2866 StorageImpl_GetTransactionSig
,
2867 StorageImpl_SetTransactionSig
,
2868 StorageImpl_LockTransaction
,
2869 StorageImpl_UnlockTransaction
2872 static HRESULT
StorageImpl_LockRegionSync(StorageImpl
*This
, ULARGE_INTEGER offset
,
2873 ULARGE_INTEGER cb
, DWORD dwLockType
)
2877 DWORD start_time
= GetTickCount();
2878 DWORD last_sanity_check
= start_time
;
2879 ULARGE_INTEGER sanity_offset
, sanity_cb
;
2881 sanity_offset
.QuadPart
= RANGELOCK_UNK1_FIRST
;
2882 sanity_cb
.QuadPart
= RANGELOCK_UNK1_LAST
- RANGELOCK_UNK1_FIRST
+ 1;
2886 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
2888 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
2890 DWORD current_time
= GetTickCount();
2891 if (current_time
- start_time
>= 20000)
2896 if (current_time
- last_sanity_check
>= 500)
2898 /* Any storage implementation with the file open in a
2899 * shared mode should not lock these bytes for writing. However,
2900 * some programs (LibreOffice Writer) will keep ALL bytes locked
2901 * when opening in exclusive mode. We can use a read lock to
2902 * detect this case early, and not hang a full 20 seconds.
2904 * This can collide with another attempt to open the file in
2905 * exclusive mode, but it's unlikely, and someone would fail anyway. */
2906 hr
= ILockBytes_LockRegion(This
->lockBytes
, sanity_offset
, sanity_cb
, 0);
2907 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
2909 if (hr
== STG_E_INVALIDFUNCTION
)
2911 /* ignore this, lockbytes might support dwLockType but not 0 */
2912 hr
= STG_E_ACCESSDENIED
;
2916 ILockBytes_UnlockRegion(This
->lockBytes
, sanity_offset
, sanity_cb
, 0);
2917 hr
= STG_E_ACCESSDENIED
;
2920 last_sanity_check
= current_time
;
2923 if (delay
< 150) delay
++;
2925 } while (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
);
2930 static HRESULT
StorageImpl_CheckLockRange(StorageImpl
*This
, ULONG start
,
2931 ULONG end
, HRESULT fail_hr
)
2934 ULARGE_INTEGER offset
, cb
;
2936 offset
.QuadPart
= start
;
2937 cb
.QuadPart
= 1 + end
- start
;
2939 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2940 if (SUCCEEDED(hr
)) ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2942 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
2948 static HRESULT
StorageImpl_LockOne(StorageImpl
*This
, ULONG start
, ULONG end
)
2952 ULARGE_INTEGER offset
, cb
;
2956 for (i
=start
; i
<=end
; i
++)
2958 offset
.QuadPart
= i
;
2959 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2960 if (hr
!= STG_E_ACCESSDENIED
&& hr
!= STG_E_LOCKVIOLATION
)
2966 for (j
=0; j
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); j
++)
2968 if (This
->locked_bytes
[j
] == 0)
2970 This
->locked_bytes
[j
] = i
;
2979 static HRESULT
StorageImpl_GrabLocks(StorageImpl
*This
, DWORD openFlags
)
2982 ULARGE_INTEGER offset
;
2984 DWORD share_mode
= STGM_SHARE_MODE(openFlags
);
2986 if (openFlags
& STGM_NOSNAPSHOT
)
2988 /* STGM_NOSNAPSHOT implies deny write */
2989 if (share_mode
== STGM_SHARE_DENY_READ
) share_mode
= STGM_SHARE_EXCLUSIVE
;
2990 else if (share_mode
!= STGM_SHARE_EXCLUSIVE
) share_mode
= STGM_SHARE_DENY_WRITE
;
2993 /* Wrap all other locking inside a single lock so we can check ranges safely */
2994 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
2996 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
);
2998 /* If the ILockBytes doesn't support locking that's ok. */
2999 if (hr
== STG_E_INVALIDFUNCTION
) return S_OK
;
3000 else if (FAILED(hr
)) return hr
;
3004 /* First check for any conflicting locks. */
3005 if ((openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
3006 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_COMMIT
, RANGELOCK_COMMIT
, STG_E_LOCKVIOLATION
);
3008 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
3009 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
, STG_E_SHAREVIOLATION
);
3011 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
3012 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
, STG_E_SHAREVIOLATION
);
3014 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
3015 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
, STG_E_LOCKVIOLATION
);
3017 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
3018 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
, STG_E_LOCKVIOLATION
);
3020 /* Then grab our locks. */
3021 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
3023 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY1_FIRST
, RANGELOCK_PRIORITY1_LAST
);
3025 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY2_FIRST
, RANGELOCK_PRIORITY2_LAST
);
3028 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
3029 hr
= StorageImpl_LockOne(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
);
3031 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
3032 hr
= StorageImpl_LockOne(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
);
3034 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
3035 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
);
3037 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
3038 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
);
3040 if (SUCCEEDED(hr
) && (openFlags
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
)
3041 hr
= StorageImpl_LockOne(This
, RANGELOCK_NOSNAPSHOT_FIRST
, RANGELOCK_NOSNAPSHOT_LAST
);
3043 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
3045 ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
3050 static HRESULT
StorageImpl_Refresh(StorageImpl
*This
, BOOL new_object
, BOOL create
)
3053 DirEntry currentEntry
;
3054 DirRef currentEntryRef
;
3055 BlockChainStream
*blockChainStream
;
3059 ULARGE_INTEGER size
;
3060 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
3062 /* Discard any existing data. */
3064 ILockBytes_SetSize(This
->lockBytes
, size
);
3067 * Initialize all header variables:
3068 * - The big block depot consists of one block and it is at block 0
3069 * - The directory table starts at block 1
3070 * - There is no small block depot
3072 memset( This
->bigBlockDepotStart
,
3074 sizeof(This
->bigBlockDepotStart
));
3076 This
->bigBlockDepotCount
= 1;
3077 This
->bigBlockDepotStart
[0] = 0;
3078 This
->rootStartBlock
= 1;
3079 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
3080 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
3081 if (This
->bigBlockSize
== 4096)
3082 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
3084 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
3085 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
3086 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
3087 This
->extBigBlockDepotCount
= 0;
3089 StorageImpl_SaveFileHeader(This
);
3092 * Add one block for the big block depot and one block for the directory table
3094 size
.u
.HighPart
= 0;
3095 size
.u
.LowPart
= This
->bigBlockSize
* 3;
3096 ILockBytes_SetSize(This
->lockBytes
, size
);
3099 * Initialize the big block depot
3101 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3102 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
3103 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
3104 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
3109 * Load the header for the file.
3111 hr
= StorageImpl_LoadFileHeader(This
);
3120 * There is no block depot cached yet.
3122 This
->indexBlockDepotCached
= 0xFFFFFFFF;
3123 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
3126 * Start searching for free blocks with block 0.
3128 This
->prevFreeBlock
= 0;
3130 This
->firstFreeSmallBlock
= 0;
3132 /* Read the extended big block depot locations. */
3133 if (This
->extBigBlockDepotCount
!= 0)
3135 ULONG current_block
= This
->extBigBlockDepotStart
;
3136 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
3139 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
3140 if (!This
->extBigBlockDepotLocations
)
3142 return E_OUTOFMEMORY
;
3145 This
->extBigBlockDepotLocationsSize
= cache_size
;
3147 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
3149 if (current_block
== BLOCK_END_OF_CHAIN
)
3151 WARN("File has too few extended big block depot blocks.\n");
3152 return STG_E_DOCFILECORRUPT
;
3154 This
->extBigBlockDepotLocations
[i
] = current_block
;
3155 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
3160 This
->extBigBlockDepotLocations
= NULL
;
3161 This
->extBigBlockDepotLocationsSize
= 0;
3165 * Create the block chain abstractions.
3167 if(!(blockChainStream
=
3168 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
3170 return STG_E_READFAULT
;
3173 BlockChainStream_Destroy(This
->rootBlockChain
);
3174 This
->rootBlockChain
= blockChainStream
;
3176 if(!(blockChainStream
=
3177 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
3180 return STG_E_READFAULT
;
3183 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
3184 This
->smallBlockDepotChain
= blockChainStream
;
3187 * Write the root storage entry (memory only)
3191 static const WCHAR rootentryW
[] = {'R','o','o','t',' ','E','n','t','r','y',0};
3194 * Initialize the directory table
3196 memset(&rootEntry
, 0, sizeof(rootEntry
));
3197 strcpyW(rootEntry
.name
, rootentryW
);
3198 rootEntry
.sizeOfNameString
= sizeof(rootentryW
);
3199 rootEntry
.stgType
= STGTY_ROOT
;
3200 rootEntry
.leftChild
= DIRENTRY_NULL
;
3201 rootEntry
.rightChild
= DIRENTRY_NULL
;
3202 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
3203 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
3204 rootEntry
.size
.u
.HighPart
= 0;
3205 rootEntry
.size
.u
.LowPart
= 0;
3207 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
3211 * Find the ID of the root storage.
3213 currentEntryRef
= 0;
3217 hr
= StorageImpl_ReadDirEntry(
3224 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
3225 (currentEntry
.stgType
== STGTY_ROOT
) )
3227 This
->base
.storageDirEntry
= currentEntryRef
;
3233 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
3237 return STG_E_READFAULT
;
3241 * Create the block chain abstraction for the small block root chain.
3243 if(!(blockChainStream
=
3244 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
3246 return STG_E_READFAULT
;
3249 BlockChainStream_Destroy(This
->smallBlockRootChain
);
3250 This
->smallBlockRootChain
= blockChainStream
;
3255 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3257 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
3258 This
->blockChainCache
[i
] = NULL
;
3265 static HRESULT
StorageImpl_Construct(
3273 StorageImpl
** result
)
3278 if ( FAILED( validateSTGM(openFlags
) ))
3279 return STG_E_INVALIDFLAG
;
3281 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
3283 return E_OUTOFMEMORY
;
3285 memset(This
, 0, sizeof(StorageImpl
));
3287 list_init(&This
->base
.strmHead
);
3289 list_init(&This
->base
.storageHead
);
3291 This
->base
.IStorage_iface
.lpVtbl
= &Storage32Impl_Vtbl
;
3292 This
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
3293 This
->base
.IDirectWriterLock_iface
.lpVtbl
= &DirectWriterLockVtbl
;
3294 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
3295 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
3297 This
->base
.create
= create
;
3299 if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
))
3300 This
->base
.lockingrole
= SWMR_Writer
;
3301 else if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READ
|STGM_SHARE_DENY_NONE
))
3302 This
->base
.lockingrole
= SWMR_Reader
;
3304 This
->base
.lockingrole
= SWMR_None
;
3306 This
->base
.reverted
= FALSE
;
3309 * Initialize the big block cache.
3311 This
->bigBlockSize
= sector_size
;
3312 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
3314 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
3317 This
->lockBytes
= pLkbyt
;
3318 ILockBytes_AddRef(pLkbyt
);
3322 hr
= StorageImpl_GrabLocks(This
, openFlags
);
3325 hr
= StorageImpl_Refresh(This
, TRUE
, create
);
3329 IStorage_Release(&This
->base
.IStorage_iface
);
3334 StorageImpl_Flush(&This
->base
);
3341 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
3343 StorageImpl
*This
= (StorageImpl
*) iface
;
3345 StorageBaseImpl_DeleteAll(&This
->base
);
3347 This
->base
.reverted
= TRUE
;
3350 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
3352 StorageImpl
*This
= (StorageImpl
*) iface
;
3354 TRACE("(%p)\n", This
);
3356 StorageImpl_Flush(iface
);
3358 StorageImpl_Invalidate(iface
);
3360 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3362 BlockChainStream_Destroy(This
->smallBlockRootChain
);
3363 BlockChainStream_Destroy(This
->rootBlockChain
);
3364 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
3366 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3367 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
3369 for (i
=0; i
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); i
++)
3371 ULARGE_INTEGER offset
, cb
;
3373 if (This
->locked_bytes
[i
] != 0)
3375 offset
.QuadPart
= This
->locked_bytes
[i
];
3376 ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
3380 if (This
->lockBytes
)
3381 ILockBytes_Release(This
->lockBytes
);
3382 HeapFree(GetProcessHeap(), 0, This
);
3385 static HRESULT
StorageImpl_Flush(StorageBaseImpl
*storage
)
3387 StorageImpl
*This
= (StorageImpl
*)storage
;
3390 TRACE("(%p)\n", This
);
3392 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
3395 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
3398 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
3400 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3401 if (This
->blockChainCache
[i
])
3402 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
3405 hr
= ILockBytes_Flush(This
->lockBytes
);
3410 /******************************************************************************
3411 * Storage32Impl_GetNextFreeBigBlock
3413 * Returns the index of the next free big block.
3414 * If the big block depot is filled, this method will enlarge it.
3417 static ULONG
StorageImpl_GetNextFreeBigBlock(
3420 ULONG depotBlockIndexPos
;
3421 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3422 ULONG depotBlockOffset
;
3423 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3424 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3426 ULONG freeBlock
= BLOCK_UNUSED
;
3428 ULARGE_INTEGER neededSize
;
3431 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
3432 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
3435 * Scan the entire big block depot until we find a block marked free
3437 while (nextBlockIndex
!= BLOCK_UNUSED
)
3439 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
3441 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
3444 * Grow the primary depot.
3446 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3448 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
3451 * Add a block depot.
3453 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
3454 This
->bigBlockDepotCount
++;
3455 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
3458 * Flag it as a block depot.
3460 StorageImpl_SetNextBlockInChain(This
,
3464 /* Save new header information.
3466 StorageImpl_SaveFileHeader(This
);
3471 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
3473 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3476 * Grow the extended depot.
3478 ULONG extIndex
= BLOCK_UNUSED
;
3479 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3480 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
3482 if (extBlockOffset
== 0)
3484 /* We need an extended block.
3486 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
3487 This
->extBigBlockDepotCount
++;
3488 depotBlockIndexPos
= extIndex
+ 1;
3491 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
3494 * Add a block depot and mark it in the extended block.
3496 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
3497 This
->bigBlockDepotCount
++;
3498 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
3500 /* Flag the block depot.
3502 StorageImpl_SetNextBlockInChain(This
,
3506 /* If necessary, flag the extended depot block.
3508 if (extIndex
!= BLOCK_UNUSED
)
3509 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
3511 /* Save header information.
3513 StorageImpl_SaveFileHeader(This
);
3517 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
3521 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
3522 ( nextBlockIndex
!= BLOCK_UNUSED
))
3524 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
3526 if (nextBlockIndex
== BLOCK_UNUSED
)
3528 freeBlock
= (depotIndex
* blocksPerDepot
) +
3529 (depotBlockOffset
/sizeof(ULONG
));
3532 depotBlockOffset
+= sizeof(ULONG
);
3537 depotBlockOffset
= 0;
3541 * make sure that the block physically exists before using it
3543 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
3545 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
3547 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
3548 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
3550 This
->prevFreeBlock
= freeBlock
;
3555 /******************************************************************************
3556 * Storage32Impl_AddBlockDepot
3558 * This will create a depot block, essentially it is a block initialized
3561 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
, ULONG depotIndex
)
3563 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3564 ULONG rangeLockIndex
= RANGELOCK_FIRST
/ This
->bigBlockSize
- 1;
3565 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3566 ULONG rangeLockDepot
= rangeLockIndex
/ blocksPerDepot
;
3569 * Initialize blocks as free
3571 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3573 /* Reserve the range lock sector */
3574 if (depotIndex
== rangeLockDepot
)
3576 ((ULONG
*)blockBuffer
)[rangeLockIndex
% blocksPerDepot
] = BLOCK_END_OF_CHAIN
;
3579 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3582 /******************************************************************************
3583 * Storage32Impl_GetExtDepotBlock
3585 * Returns the index of the block that corresponds to the specified depot
3586 * index. This method is only for depot indexes equal or greater than
3587 * COUNT_BBDEPOTINHEADER.
3589 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3591 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3592 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3593 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3594 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3595 ULONG blockIndex
= BLOCK_UNUSED
;
3596 ULONG extBlockIndex
;
3597 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3598 int index
, num_blocks
;
3600 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3602 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3603 return BLOCK_UNUSED
;
3605 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3607 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3609 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
, NULL
);
3611 num_blocks
= This
->bigBlockSize
/ 4;
3613 for (index
= 0; index
< num_blocks
; index
++)
3615 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3616 This
->extBlockDepotCached
[index
] = blockIndex
;
3619 This
->indexExtBlockDepotCached
= extBlockCount
;
3622 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3627 /******************************************************************************
3628 * Storage32Impl_SetExtDepotBlock
3630 * Associates the specified block index to the specified depot index.
3631 * This method is only for depot indexes equal or greater than
3632 * COUNT_BBDEPOTINHEADER.
3634 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3636 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3637 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3638 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3639 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3640 ULONG extBlockIndex
;
3642 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3644 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3646 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3648 if (extBlockIndex
!= BLOCK_UNUSED
)
3650 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3651 extBlockOffset
* sizeof(ULONG
),
3655 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3657 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3661 /******************************************************************************
3662 * Storage32Impl_AddExtBlockDepot
3664 * Creates an extended depot block.
3666 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3668 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3669 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3670 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3671 ULONG index
= BLOCK_UNUSED
;
3672 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3673 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3674 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3676 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3677 blocksPerDepotBlock
;
3679 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3682 * The first extended block.
3684 This
->extBigBlockDepotStart
= index
;
3689 * Find the last existing extended block.
3691 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3694 * Add the new extended block to the chain.
3696 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3701 * Initialize this block.
3703 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3704 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3706 /* Add the block to our cache. */
3707 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3709 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3710 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3712 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3713 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3715 This
->extBigBlockDepotLocations
= new_cache
;
3716 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3718 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3723 /******************************************************************************
3724 * Storage32Impl_FreeBigBlock
3726 * This method will flag the specified block as free in the big block depot.
3728 static void StorageImpl_FreeBigBlock(
3732 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3734 if (blockIndex
< This
->prevFreeBlock
)
3735 This
->prevFreeBlock
= blockIndex
;
3738 /************************************************************************
3739 * Storage32Impl_GetNextBlockInChain
3741 * This method will retrieve the block index of the next big block in
3744 * Params: This - Pointer to the Storage object.
3745 * blockIndex - Index of the block to retrieve the chain
3747 * nextBlockIndex - receives the return value.
3749 * Returns: This method returns the index of the next block in the chain.
3750 * It will return the constants:
3751 * BLOCK_SPECIAL - If the block given was not part of a
3753 * BLOCK_END_OF_CHAIN - If the block given was the last in
3755 * BLOCK_UNUSED - If the block given was not past of a chain
3757 * BLOCK_EXTBBDEPOT - This block is part of the extended
3760 * See Windows documentation for more details on IStorage methods.
3762 static HRESULT
StorageImpl_GetNextBlockInChain(
3765 ULONG
* nextBlockIndex
)
3767 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3768 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3769 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3770 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3772 ULONG depotBlockIndexPos
;
3773 int index
, num_blocks
;
3775 *nextBlockIndex
= BLOCK_SPECIAL
;
3777 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3779 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3780 This
->bigBlockDepotCount
);
3781 return STG_E_READFAULT
;
3785 * Cache the currently accessed depot block.
3787 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3789 This
->indexBlockDepotCached
= depotBlockCount
;
3791 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3793 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3798 * We have to look in the extended depot.
3800 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3803 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
3806 return STG_E_READFAULT
;
3808 num_blocks
= This
->bigBlockSize
/ 4;
3810 for (index
= 0; index
< num_blocks
; index
++)
3812 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3813 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3817 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3822 /******************************************************************************
3823 * Storage32Impl_GetNextExtendedBlock
3825 * Given an extended block this method will return the next extended block.
3828 * The last ULONG of an extended block is the block index of the next
3829 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3833 * - The index of the next extended block
3834 * - BLOCK_UNUSED: there is no next extended block.
3835 * - Any other return values denotes failure.
3837 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3839 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3840 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3842 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3845 return nextBlockIndex
;
3848 /******************************************************************************
3849 * Storage32Impl_SetNextBlockInChain
3851 * This method will write the index of the specified block's next block
3852 * in the big block depot.
3854 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3857 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3858 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3859 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3862 static void StorageImpl_SetNextBlockInChain(
3867 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3868 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3869 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3870 ULONG depotBlockIndexPos
;
3872 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3873 assert(blockIndex
!= nextBlock
);
3875 if (blockIndex
== (RANGELOCK_FIRST
/ This
->bigBlockSize
) - 1)
3876 /* This should never happen (storage file format spec forbids it), but
3877 * older versions of Wine may have generated broken files. We don't want to
3878 * assert and potentially lose data, but we do want to know if this ever
3879 * happens in a newly-created file. */
3880 ERR("Using range lock page\n");
3882 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3884 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3889 * We have to look in the extended depot.
3891 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3894 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3897 * Update the cached block depot, if necessary.
3899 if (depotBlockCount
== This
->indexBlockDepotCached
)
3901 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3905 /******************************************************************************
3906 * Storage32Impl_LoadFileHeader
3908 * This method will read in the file header
3910 static HRESULT
StorageImpl_LoadFileHeader(
3914 BYTE headerBigBlock
[HEADER_SIZE
];
3916 ULARGE_INTEGER offset
;
3921 * Get a pointer to the big block of data containing the header.
3923 offset
.u
.HighPart
= 0;
3924 offset
.u
.LowPart
= 0;
3925 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3926 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3927 hr
= STG_E_FILENOTFOUND
;
3930 * Extract the information from the header.
3935 * Check for the "magic number" signature and return an error if it is not
3938 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3940 return STG_E_OLDFORMAT
;
3943 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3945 return STG_E_INVALIDHEADER
;
3948 StorageUtl_ReadWord(
3950 OFFSET_BIGBLOCKSIZEBITS
,
3951 &This
->bigBlockSizeBits
);
3953 StorageUtl_ReadWord(
3955 OFFSET_SMALLBLOCKSIZEBITS
,
3956 &This
->smallBlockSizeBits
);
3958 StorageUtl_ReadDWord(
3960 OFFSET_BBDEPOTCOUNT
,
3961 &This
->bigBlockDepotCount
);
3963 StorageUtl_ReadDWord(
3965 OFFSET_ROOTSTARTBLOCK
,
3966 &This
->rootStartBlock
);
3968 StorageUtl_ReadDWord(
3970 OFFSET_TRANSACTIONSIG
,
3971 &This
->transactionSig
);
3973 StorageUtl_ReadDWord(
3975 OFFSET_SMALLBLOCKLIMIT
,
3976 &This
->smallBlockLimit
);
3978 StorageUtl_ReadDWord(
3980 OFFSET_SBDEPOTSTART
,
3981 &This
->smallBlockDepotStart
);
3983 StorageUtl_ReadDWord(
3985 OFFSET_EXTBBDEPOTSTART
,
3986 &This
->extBigBlockDepotStart
);
3988 StorageUtl_ReadDWord(
3990 OFFSET_EXTBBDEPOTCOUNT
,
3991 &This
->extBigBlockDepotCount
);
3993 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3995 StorageUtl_ReadDWord(
3997 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3998 &(This
->bigBlockDepotStart
[index
]));
4002 * Make the bitwise arithmetic to get the size of the blocks in bytes.
4004 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
4005 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
4008 * Right now, the code is making some assumptions about the size of the
4009 * blocks, just make sure they are what we're expecting.
4011 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
4012 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
4013 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
4015 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
4016 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
4017 hr
= STG_E_INVALIDHEADER
;
4026 /******************************************************************************
4027 * Storage32Impl_SaveFileHeader
4029 * This method will save to the file the header
4031 static void StorageImpl_SaveFileHeader(
4034 BYTE headerBigBlock
[HEADER_SIZE
];
4037 ULARGE_INTEGER offset
;
4038 DWORD bytes_read
, bytes_written
;
4039 DWORD major_version
, dirsectorcount
;
4042 * Get a pointer to the big block of data containing the header.
4044 offset
.u
.HighPart
= 0;
4045 offset
.u
.LowPart
= 0;
4046 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
4047 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
4048 hr
= STG_E_FILENOTFOUND
;
4050 if (This
->bigBlockSizeBits
== 0x9)
4052 else if (This
->bigBlockSizeBits
== 0xc)
4056 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
4061 * If the block read failed, the file is probably new.
4066 * Initialize for all unknown fields.
4068 memset(headerBigBlock
, 0, HEADER_SIZE
);
4071 * Initialize the magic number.
4073 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
4077 * Write the information to the header.
4079 StorageUtl_WriteWord(
4081 OFFSET_MINORVERSION
,
4084 StorageUtl_WriteWord(
4086 OFFSET_MAJORVERSION
,
4089 StorageUtl_WriteWord(
4091 OFFSET_BYTEORDERMARKER
,
4094 StorageUtl_WriteWord(
4096 OFFSET_BIGBLOCKSIZEBITS
,
4097 This
->bigBlockSizeBits
);
4099 StorageUtl_WriteWord(
4101 OFFSET_SMALLBLOCKSIZEBITS
,
4102 This
->smallBlockSizeBits
);
4104 if (major_version
>= 4)
4106 if (This
->rootBlockChain
)
4107 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
4109 /* This file is being created, and it will start out with one block. */
4113 /* This field must be 0 in versions older than 4 */
4116 StorageUtl_WriteDWord(
4118 OFFSET_DIRSECTORCOUNT
,
4121 StorageUtl_WriteDWord(
4123 OFFSET_BBDEPOTCOUNT
,
4124 This
->bigBlockDepotCount
);
4126 StorageUtl_WriteDWord(
4128 OFFSET_ROOTSTARTBLOCK
,
4129 This
->rootStartBlock
);
4131 StorageUtl_WriteDWord(
4133 OFFSET_TRANSACTIONSIG
,
4134 This
->transactionSig
);
4136 StorageUtl_WriteDWord(
4138 OFFSET_SMALLBLOCKLIMIT
,
4139 This
->smallBlockLimit
);
4141 StorageUtl_WriteDWord(
4143 OFFSET_SBDEPOTSTART
,
4144 This
->smallBlockDepotStart
);
4146 StorageUtl_WriteDWord(
4148 OFFSET_SBDEPOTCOUNT
,
4149 This
->smallBlockDepotChain
?
4150 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
4152 StorageUtl_WriteDWord(
4154 OFFSET_EXTBBDEPOTSTART
,
4155 This
->extBigBlockDepotStart
);
4157 StorageUtl_WriteDWord(
4159 OFFSET_EXTBBDEPOTCOUNT
,
4160 This
->extBigBlockDepotCount
);
4162 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
4164 StorageUtl_WriteDWord(
4166 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
4167 (This
->bigBlockDepotStart
[index
]));
4171 * Write the big block back to the file.
4173 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
4176 /******************************************************************************
4177 * StorageImpl_ReadRawDirEntry
4179 * This method will read the raw data from a directory entry in the file.
4181 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4183 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
4185 ULARGE_INTEGER offset
;
4189 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
4191 hr
= BlockChainStream_ReadAt(
4192 This
->rootBlockChain
,
4198 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
4199 return STG_E_READFAULT
;
4204 /******************************************************************************
4205 * StorageImpl_WriteRawDirEntry
4207 * This method will write the raw data from a directory entry in the file.
4209 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4211 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
4213 ULARGE_INTEGER offset
;
4216 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
4218 return BlockChainStream_WriteAt(
4219 This
->rootBlockChain
,
4226 /******************************************************************************
4229 * Update raw directory entry data from the fields in newData.
4231 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4233 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
4235 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
4238 buffer
+ OFFSET_PS_NAME
,
4240 DIRENTRY_NAME_BUFFER_LEN
);
4242 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
4244 StorageUtl_WriteWord(
4246 OFFSET_PS_NAMELENGTH
,
4247 newData
->sizeOfNameString
);
4249 StorageUtl_WriteDWord(
4251 OFFSET_PS_LEFTCHILD
,
4252 newData
->leftChild
);
4254 StorageUtl_WriteDWord(
4256 OFFSET_PS_RIGHTCHILD
,
4257 newData
->rightChild
);
4259 StorageUtl_WriteDWord(
4262 newData
->dirRootEntry
);
4264 StorageUtl_WriteGUID(
4269 StorageUtl_WriteDWord(
4272 newData
->ctime
.dwLowDateTime
);
4274 StorageUtl_WriteDWord(
4276 OFFSET_PS_CTIMEHIGH
,
4277 newData
->ctime
.dwHighDateTime
);
4279 StorageUtl_WriteDWord(
4282 newData
->mtime
.dwLowDateTime
);
4284 StorageUtl_WriteDWord(
4286 OFFSET_PS_MTIMEHIGH
,
4287 newData
->ctime
.dwHighDateTime
);
4289 StorageUtl_WriteDWord(
4291 OFFSET_PS_STARTBLOCK
,
4292 newData
->startingBlock
);
4294 StorageUtl_WriteDWord(
4297 newData
->size
.u
.LowPart
);
4299 StorageUtl_WriteDWord(
4301 OFFSET_PS_SIZE_HIGH
,
4302 newData
->size
.u
.HighPart
);
4305 /******************************************************************************
4306 * Storage32Impl_ReadDirEntry
4308 * This method will read the specified directory entry.
4310 HRESULT
StorageImpl_ReadDirEntry(
4315 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
4318 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
4320 if (SUCCEEDED(readRes
))
4322 memset(buffer
->name
, 0, sizeof(buffer
->name
));
4325 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
4326 DIRENTRY_NAME_BUFFER_LEN
);
4327 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
4329 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
4331 StorageUtl_ReadWord(
4333 OFFSET_PS_NAMELENGTH
,
4334 &buffer
->sizeOfNameString
);
4336 StorageUtl_ReadDWord(
4338 OFFSET_PS_LEFTCHILD
,
4339 &buffer
->leftChild
);
4341 StorageUtl_ReadDWord(
4343 OFFSET_PS_RIGHTCHILD
,
4344 &buffer
->rightChild
);
4346 StorageUtl_ReadDWord(
4349 &buffer
->dirRootEntry
);
4351 StorageUtl_ReadGUID(
4356 StorageUtl_ReadDWord(
4359 &buffer
->ctime
.dwLowDateTime
);
4361 StorageUtl_ReadDWord(
4363 OFFSET_PS_CTIMEHIGH
,
4364 &buffer
->ctime
.dwHighDateTime
);
4366 StorageUtl_ReadDWord(
4369 &buffer
->mtime
.dwLowDateTime
);
4371 StorageUtl_ReadDWord(
4373 OFFSET_PS_MTIMEHIGH
,
4374 &buffer
->mtime
.dwHighDateTime
);
4376 StorageUtl_ReadDWord(
4378 OFFSET_PS_STARTBLOCK
,
4379 &buffer
->startingBlock
);
4381 StorageUtl_ReadDWord(
4384 &buffer
->size
.u
.LowPart
);
4386 StorageUtl_ReadDWord(
4388 OFFSET_PS_SIZE_HIGH
,
4389 &buffer
->size
.u
.HighPart
);
4395 /*********************************************************************
4396 * Write the specified directory entry to the file
4398 HRESULT
StorageImpl_WriteDirEntry(
4401 const DirEntry
* buffer
)
4403 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
4405 UpdateRawDirEntry(currentEntry
, buffer
);
4407 return StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
4410 static HRESULT
StorageImpl_ReadBigBlock(
4416 ULARGE_INTEGER ulOffset
;
4420 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4422 hr
= StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
4424 if (SUCCEEDED(hr
) && read
< This
->bigBlockSize
)
4426 /* File ends during this block; fill the rest with 0's. */
4427 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
4430 if (out_read
) *out_read
= read
;
4435 static BOOL
StorageImpl_ReadDWordFromBigBlock(
4441 ULARGE_INTEGER ulOffset
;
4445 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4446 ulOffset
.QuadPart
+= offset
;
4448 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
4449 *value
= lendian32toh(tmp
);
4450 return (read
== sizeof(DWORD
));
4453 static BOOL
StorageImpl_WriteBigBlock(
4458 ULARGE_INTEGER ulOffset
;
4461 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4463 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
4464 return (wrote
== This
->bigBlockSize
);
4467 static BOOL
StorageImpl_WriteDWordToBigBlock(
4473 ULARGE_INTEGER ulOffset
;
4476 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4477 ulOffset
.QuadPart
+= offset
;
4479 value
= htole32(value
);
4480 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
4481 return (wrote
== sizeof(DWORD
));
4484 /******************************************************************************
4485 * Storage32Impl_SmallBlocksToBigBlocks
4487 * This method will convert a small block chain to a big block chain.
4488 * The small block chain will be destroyed.
4490 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
4492 SmallBlockChainStream
** ppsbChain
)
4494 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4495 ULARGE_INTEGER size
, offset
;
4496 ULONG cbRead
, cbWritten
;
4497 ULARGE_INTEGER cbTotalRead
;
4498 DirRef streamEntryRef
;
4499 HRESULT resWrite
= S_OK
;
4501 DirEntry streamEntry
;
4503 BlockChainStream
*bbTempChain
= NULL
;
4504 BlockChainStream
*bigBlockChain
= NULL
;
4507 * Create a temporary big block chain that doesn't have
4508 * an associated directory entry. This temporary chain will be
4509 * used to copy data from small blocks to big blocks.
4511 bbTempChain
= BlockChainStream_Construct(This
,
4514 if(!bbTempChain
) return NULL
;
4516 * Grow the big block chain.
4518 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
4519 BlockChainStream_SetSize(bbTempChain
, size
);
4522 * Copy the contents of the small block chain to the big block chain
4523 * by small block size increments.
4525 offset
.u
.LowPart
= 0;
4526 offset
.u
.HighPart
= 0;
4527 cbTotalRead
.QuadPart
= 0;
4529 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
4532 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
4534 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4537 if (FAILED(resRead
))
4542 cbTotalRead
.QuadPart
+= cbRead
;
4544 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
4550 if (FAILED(resWrite
))
4553 offset
.u
.LowPart
+= cbRead
;
4557 resRead
= STG_E_READFAULT
;
4560 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
4561 HeapFree(GetProcessHeap(),0,buffer
);
4563 size
.u
.HighPart
= 0;
4566 if (FAILED(resRead
) || FAILED(resWrite
))
4568 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4569 BlockChainStream_SetSize(bbTempChain
, size
);
4570 BlockChainStream_Destroy(bbTempChain
);
4575 * Destroy the small block chain.
4577 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
4578 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
4579 SmallBlockChainStream_Destroy(*ppsbChain
);
4583 * Change the directory entry. This chain is now a big block chain
4584 * and it doesn't reside in the small blocks chain anymore.
4586 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4588 streamEntry
.startingBlock
= bbHeadOfChain
;
4590 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4593 * Destroy the temporary entryless big block chain.
4594 * Create a new big block chain associated with this entry.
4596 BlockChainStream_Destroy(bbTempChain
);
4597 bigBlockChain
= BlockChainStream_Construct(This
,
4601 return bigBlockChain
;
4604 /******************************************************************************
4605 * Storage32Impl_BigBlocksToSmallBlocks
4607 * This method will convert a big block chain to a small block chain.
4608 * The big block chain will be destroyed on success.
4610 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
4612 BlockChainStream
** ppbbChain
,
4613 ULARGE_INTEGER newSize
)
4615 ULARGE_INTEGER size
, offset
, cbTotalRead
;
4616 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4617 DirRef streamEntryRef
;
4618 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
4619 DirEntry streamEntry
;
4621 SmallBlockChainStream
* sbTempChain
;
4623 TRACE("%p %p\n", This
, ppbbChain
);
4625 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
4631 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
4632 size
= BlockChainStream_GetSize(*ppbbChain
);
4633 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
4635 offset
.u
.HighPart
= 0;
4636 offset
.u
.LowPart
= 0;
4637 cbTotalRead
.QuadPart
= 0;
4638 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
4639 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
4641 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
4642 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4650 cbTotalRead
.QuadPart
+= cbRead
;
4652 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
4653 cbRead
, buffer
, &cbWritten
);
4655 if(FAILED(resWrite
))
4658 offset
.u
.LowPart
+= cbRead
;
4662 resRead
= STG_E_READFAULT
;
4666 HeapFree(GetProcessHeap(), 0, buffer
);
4668 size
.u
.HighPart
= 0;
4671 if(FAILED(resRead
) || FAILED(resWrite
))
4673 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4674 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4675 SmallBlockChainStream_Destroy(sbTempChain
);
4679 /* destroy the original big block chain */
4680 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4681 BlockChainStream_SetSize(*ppbbChain
, size
);
4682 BlockChainStream_Destroy(*ppbbChain
);
4685 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4686 streamEntry
.startingBlock
= sbHeadOfChain
;
4687 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4689 SmallBlockChainStream_Destroy(sbTempChain
);
4690 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4693 static HRESULT
StorageBaseImpl_CopyStream(
4694 StorageBaseImpl
*dst
, DirRef dst_entry
,
4695 StorageBaseImpl
*src
, DirRef src_entry
)
4700 ULARGE_INTEGER bytes_copied
;
4701 ULONG bytestocopy
, bytesread
, byteswritten
;
4703 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
4707 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
4709 bytes_copied
.QuadPart
= 0;
4710 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
4712 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
4714 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
4716 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
4719 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
4720 data
, &byteswritten
);
4723 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
4724 bytes_copied
.QuadPart
+= byteswritten
;
4732 static HRESULT
StorageBaseImpl_DupStorageTree(
4733 StorageBaseImpl
*dst
, DirRef
*dst_entry
,
4734 StorageBaseImpl
*src
, DirRef src_entry
)
4738 BOOL has_stream
=FALSE
;
4740 if (src_entry
== DIRENTRY_NULL
)
4742 *dst_entry
= DIRENTRY_NULL
;
4746 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &data
);
4749 has_stream
= (data
.stgType
== STGTY_STREAM
&& data
.size
.QuadPart
!= 0);
4750 data
.startingBlock
= BLOCK_END_OF_CHAIN
;
4751 data
.size
.QuadPart
= 0;
4753 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.leftChild
, src
, data
.leftChild
);
4757 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.rightChild
, src
, data
.rightChild
);
4760 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.dirRootEntry
, src
, data
.dirRootEntry
);
4763 hr
= StorageBaseImpl_CreateDirEntry(dst
, &data
, dst_entry
);
4765 if (SUCCEEDED(hr
) && has_stream
)
4766 hr
= StorageBaseImpl_CopyStream(dst
, *dst_entry
, src
, src_entry
);
4771 static HRESULT
StorageBaseImpl_CopyStorageTree(
4772 StorageBaseImpl
*dst
, DirRef dst_entry
,
4773 StorageBaseImpl
*src
, DirRef src_entry
)
4776 DirEntry src_data
, dst_data
;
4777 DirRef new_root_entry
;
4779 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &src_data
);
4783 hr
= StorageBaseImpl_DupStorageTree(dst
, &new_root_entry
, src
, src_data
.dirRootEntry
);
4788 hr
= StorageBaseImpl_ReadDirEntry(dst
, dst_entry
, &dst_data
);
4789 dst_data
.clsid
= src_data
.clsid
;
4790 dst_data
.ctime
= src_data
.ctime
;
4791 dst_data
.mtime
= src_data
.mtime
;
4792 dst_data
.dirRootEntry
= new_root_entry
;
4796 hr
= StorageBaseImpl_WriteDirEntry(dst
, dst_entry
, &dst_data
);
4801 static HRESULT
StorageBaseImpl_DeleteStorageTree(StorageBaseImpl
*This
, DirRef entry
, BOOL include_siblings
)
4805 ULARGE_INTEGER zero
;
4807 if (entry
== DIRENTRY_NULL
)
4812 hr
= StorageBaseImpl_ReadDirEntry(This
, entry
, &data
);
4814 if (SUCCEEDED(hr
) && include_siblings
)
4815 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.leftChild
, TRUE
);
4817 if (SUCCEEDED(hr
) && include_siblings
)
4818 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.rightChild
, TRUE
);
4821 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.dirRootEntry
, TRUE
);
4823 if (SUCCEEDED(hr
) && data
.stgType
== STGTY_STREAM
)
4824 hr
= StorageBaseImpl_StreamSetSize(This
, entry
, zero
);
4827 hr
= StorageBaseImpl_DestroyDirEntry(This
, entry
);
4832 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
4834 DirRef result
=This
->firstFreeEntry
;
4836 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
4839 if (result
== This
->entries_size
)
4841 ULONG new_size
= This
->entries_size
* 2;
4842 TransactedDirEntry
*new_entries
;
4844 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
4845 if (!new_entries
) return DIRENTRY_NULL
;
4847 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
4848 HeapFree(GetProcessHeap(), 0, This
->entries
);
4850 This
->entries
= new_entries
;
4851 This
->entries_size
= new_size
;
4854 This
->entries
[result
].inuse
= TRUE
;
4856 This
->firstFreeEntry
= result
+1;
4861 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
4862 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
4864 DirRef stubEntryRef
;
4865 TransactedDirEntry
*entry
;
4867 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
4869 if (stubEntryRef
!= DIRENTRY_NULL
)
4871 entry
= &This
->entries
[stubEntryRef
];
4873 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
4875 entry
->read
= FALSE
;
4878 return stubEntryRef
;
4881 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
4882 TransactedSnapshotImpl
*This
, DirRef entry
)
4887 if (!This
->entries
[entry
].read
)
4889 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4890 This
->entries
[entry
].transactedParentEntry
,
4893 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
4895 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
4897 if (data
.leftChild
== DIRENTRY_NULL
)
4901 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
4903 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
4905 if (data
.rightChild
== DIRENTRY_NULL
)
4909 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
4911 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
4913 if (data
.dirRootEntry
== DIRENTRY_NULL
)
4919 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
4920 This
->entries
[entry
].read
= TRUE
;
4927 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
4928 TransactedSnapshotImpl
*This
, DirRef entry
)
4932 if (!This
->entries
[entry
].stream_dirty
)
4934 DirEntry new_entrydata
;
4936 memset(&new_entrydata
, 0, sizeof(DirEntry
));
4937 new_entrydata
.name
[0] = 'S';
4938 new_entrydata
.sizeOfNameString
= 1;
4939 new_entrydata
.stgType
= STGTY_STREAM
;
4940 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
4941 new_entrydata
.leftChild
= DIRENTRY_NULL
;
4942 new_entrydata
.rightChild
= DIRENTRY_NULL
;
4943 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
4945 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
4946 &This
->entries
[entry
].stream_entry
);
4948 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4950 hr
= StorageBaseImpl_CopyStream(
4951 This
->scratch
, This
->entries
[entry
].stream_entry
,
4952 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
4955 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
4959 This
->entries
[entry
].stream_dirty
= TRUE
;
4961 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4963 /* Since this entry is modified, and we aren't using its stream data, we
4964 * no longer care about the original entry. */
4966 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
4968 if (delete_ref
!= DIRENTRY_NULL
)
4969 This
->entries
[delete_ref
].deleted
= TRUE
;
4971 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
4978 /* Find the first entry in a depth-first traversal. */
4979 static DirRef
TransactedSnapshotImpl_FindFirstChild(
4980 TransactedSnapshotImpl
* This
, DirRef parent
)
4982 DirRef cursor
, prev
;
4983 TransactedDirEntry
*entry
;
4986 entry
= &This
->entries
[cursor
];
4989 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
4992 cursor
= entry
->data
.leftChild
;
4993 entry
= &This
->entries
[cursor
];
4994 entry
->parent
= prev
;
4996 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
4999 cursor
= entry
->data
.rightChild
;
5000 entry
= &This
->entries
[cursor
];
5001 entry
->parent
= prev
;
5003 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5006 cursor
= entry
->data
.dirRootEntry
;
5007 entry
= &This
->entries
[cursor
];
5008 entry
->parent
= prev
;
5017 /* Find the next entry in a depth-first traversal. */
5018 static DirRef
TransactedSnapshotImpl_FindNextChild(
5019 TransactedSnapshotImpl
* This
, DirRef current
)
5022 TransactedDirEntry
*parent_entry
;
5024 parent
= This
->entries
[current
].parent
;
5025 parent_entry
= &This
->entries
[parent
];
5027 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
5029 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
5031 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
5032 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
5035 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5037 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
5038 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
5045 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5046 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
5047 TransactedSnapshotImpl
* This
, DirRef entry
)
5049 return entry
!= DIRENTRY_NULL
&&
5050 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
5053 /* Destroy the entries created by CopyTree. */
5054 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5055 TransactedSnapshotImpl
* This
, DirRef stop
)
5058 TransactedDirEntry
*entry
;
5059 ULARGE_INTEGER zero
;
5063 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
5066 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
5068 if (cursor
== DIRENTRY_NULL
)
5071 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
5073 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
5075 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
5077 entry
= &This
->entries
[cursor
];
5079 if (entry
->stream_dirty
)
5080 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5081 entry
->newTransactedParentEntry
, zero
);
5083 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5084 entry
->newTransactedParentEntry
);
5086 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5089 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5093 /* Make a copy of our edited tree that we can use in the parent. */
5094 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
5097 TransactedDirEntry
*entry
;
5100 cursor
= This
->base
.storageDirEntry
;
5101 entry
= &This
->entries
[cursor
];
5102 entry
->parent
= DIRENTRY_NULL
;
5103 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5105 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5108 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
5110 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
5111 entry
= &This
->entries
[cursor
];
5113 while (cursor
!= DIRENTRY_NULL
)
5115 /* Make a copy of this entry in the transacted parent. */
5117 (!entry
->dirty
&& !entry
->stream_dirty
&&
5118 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
5119 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
5120 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
5121 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5126 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
5128 newData
.size
.QuadPart
= 0;
5129 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
5131 if (newData
.leftChild
!= DIRENTRY_NULL
)
5132 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
5134 if (newData
.rightChild
!= DIRENTRY_NULL
)
5135 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
5137 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
5138 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
5140 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
5141 &entry
->newTransactedParentEntry
);
5144 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5148 if (entry
->stream_dirty
)
5150 hr
= StorageBaseImpl_CopyStream(
5151 This
->transactedParent
, entry
->newTransactedParentEntry
,
5152 This
->scratch
, entry
->stream_entry
);
5154 else if (entry
->data
.size
.QuadPart
)
5156 hr
= StorageBaseImpl_StreamLink(
5157 This
->transactedParent
, entry
->newTransactedParentEntry
,
5158 entry
->transactedParentEntry
);
5163 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5164 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5169 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5170 entry
= &This
->entries
[cursor
];
5176 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
5178 DWORD grfCommitFlags
) /* [in] */
5180 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5181 TransactedDirEntry
*root_entry
;
5182 DirRef i
, dir_root_ref
;
5184 ULARGE_INTEGER zero
;
5186 ULONG transactionSig
;
5190 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5192 /* Cannot commit a read-only transacted storage */
5193 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5194 return STG_E_ACCESSDENIED
;
5196 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5197 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5200 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5203 if (transactionSig
!= This
->lastTransactionSig
)
5205 ERR("file was externally modified\n");
5206 hr
= STG_E_NOTCURRENT
;
5211 This
->lastTransactionSig
= transactionSig
+1;
5212 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, This
->lastTransactionSig
);
5215 else if (hr
== E_NOTIMPL
)
5218 if (FAILED(hr
)) goto end
;
5220 /* To prevent data loss, we create the new structure in the file before we
5221 * delete the old one, so that in case of errors the old data is intact. We
5222 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5223 * needed in the rare situation where we have just enough free disk space to
5224 * overwrite the existing data. */
5226 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
5228 if (!root_entry
->read
)
5231 hr
= TransactedSnapshotImpl_CopyTree(This
);
5232 if (FAILED(hr
)) goto end
;
5234 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5235 dir_root_ref
= DIRENTRY_NULL
;
5237 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
5239 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5241 /* Update the storage to use the new data in one step. */
5243 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5244 root_entry
->transactedParentEntry
, &data
);
5248 data
.dirRootEntry
= dir_root_ref
;
5249 data
.clsid
= root_entry
->data
.clsid
;
5250 data
.ctime
= root_entry
->data
.ctime
;
5251 data
.mtime
= root_entry
->data
.mtime
;
5253 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
5254 root_entry
->transactedParentEntry
, &data
);
5257 /* Try to flush after updating the root storage, but if the flush fails, keep
5258 * going, on the theory that it'll either succeed later or the subsequent
5259 * writes will fail. */
5260 StorageBaseImpl_Flush(This
->transactedParent
);
5264 /* Destroy the old now-orphaned data. */
5265 for (i
=0; i
<This
->entries_size
; i
++)
5267 TransactedDirEntry
*entry
= &This
->entries
[i
];
5272 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5273 entry
->transactedParentEntry
, zero
);
5274 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5275 entry
->transactedParentEntry
);
5276 memset(entry
, 0, sizeof(TransactedDirEntry
));
5277 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
5279 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
5281 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
5282 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5283 entry
->transactedParentEntry
);
5284 if (entry
->stream_dirty
)
5286 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
5287 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
5288 entry
->stream_dirty
= FALSE
;
5290 entry
->dirty
= FALSE
;
5291 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
5298 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
5302 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5304 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
5310 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
5313 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5314 ULARGE_INTEGER zero
;
5317 TRACE("(%p)\n", iface
);
5319 /* Destroy the open objects. */
5320 StorageBaseImpl_DeleteAll(&This
->base
);
5322 /* Clear out the scratch file. */
5324 for (i
=0; i
<This
->entries_size
; i
++)
5326 if (This
->entries
[i
].stream_dirty
)
5328 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
5331 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
5335 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
5337 This
->firstFreeEntry
= 0;
5338 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
5343 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
5345 if (!This
->reverted
)
5347 TRACE("Storage invalidated (stg=%p)\n", This
);
5349 This
->reverted
= TRUE
;
5351 StorageBaseImpl_DeleteAll(This
);
5355 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
5357 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
5359 IStorage_Revert(&This
->base
.IStorage_iface
);
5360 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
5361 IStorage_Release(&This
->scratch
->IStorage_iface
);
5362 HeapFree(GetProcessHeap(), 0, This
->entries
);
5363 HeapFree(GetProcessHeap(), 0, This
);
5366 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
5368 /* We only need to flush when committing. */
5372 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5374 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
5376 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
5379 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
5380 const DirEntry
*newData
, DirRef
*index
)
5382 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5384 TransactedDirEntry
*new_entry
;
5386 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
5387 if (new_ref
== DIRENTRY_NULL
)
5388 return E_OUTOFMEMORY
;
5390 new_entry
= &This
->entries
[new_ref
];
5392 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
5393 new_entry
->read
= TRUE
;
5394 new_entry
->dirty
= TRUE
;
5395 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
5399 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
5404 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
5405 DirRef index
, const DirEntry
*data
)
5407 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5410 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
5412 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5413 if (FAILED(hr
)) return hr
;
5415 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
5417 if (index
!= This
->base
.storageDirEntry
)
5419 This
->entries
[index
].dirty
= TRUE
;
5421 if (data
->size
.QuadPart
== 0 &&
5422 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
5424 /* Since this entry is modified, and we aren't using its stream data, we
5425 * no longer care about the original entry. */
5427 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
5429 if (delete_ref
!= DIRENTRY_NULL
)
5430 This
->entries
[delete_ref
].deleted
= TRUE
;
5432 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5439 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
5440 DirRef index
, DirEntry
*data
)
5442 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5445 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5446 if (FAILED(hr
)) return hr
;
5448 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
5450 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
5455 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5458 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5460 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
5461 This
->entries
[index
].data
.size
.QuadPart
!= 0)
5463 /* If we deleted this entry while it has stream data. We must have left the
5464 * data because some other entry is using it, and we need to leave the
5465 * original entry alone. */
5466 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
5467 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
5471 This
->entries
[index
].deleted
= TRUE
;
5477 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
5478 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5480 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5482 if (This
->entries
[index
].stream_dirty
)
5484 return StorageBaseImpl_StreamReadAt(This
->scratch
,
5485 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
5487 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
5489 /* This stream doesn't live in the parent, and we haven't allocated storage
5496 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
5497 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
5501 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
5502 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5504 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5507 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5508 if (FAILED(hr
)) return hr
;
5510 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5511 if (FAILED(hr
)) return hr
;
5513 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
5514 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
5516 if (SUCCEEDED(hr
) && size
!= 0)
5517 This
->entries
[index
].data
.size
.QuadPart
= max(
5518 This
->entries
[index
].data
.size
.QuadPart
,
5519 offset
.QuadPart
+ size
);
5524 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
5525 DirRef index
, ULARGE_INTEGER newsize
)
5527 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5530 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5531 if (FAILED(hr
)) return hr
;
5533 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
5536 if (newsize
.QuadPart
== 0)
5538 /* Destroy any parent references or entries in the scratch file. */
5539 if (This
->entries
[index
].stream_dirty
)
5541 ULARGE_INTEGER zero
;
5543 StorageBaseImpl_StreamSetSize(This
->scratch
,
5544 This
->entries
[index
].stream_entry
, zero
);
5545 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
5546 This
->entries
[index
].stream_entry
);
5547 This
->entries
[index
].stream_dirty
= FALSE
;
5549 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
5552 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
5554 if (delete_ref
!= DIRENTRY_NULL
)
5555 This
->entries
[delete_ref
].deleted
= TRUE
;
5557 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5562 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5563 if (FAILED(hr
)) return hr
;
5565 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
5566 This
->entries
[index
].stream_entry
, newsize
);
5570 This
->entries
[index
].data
.size
= newsize
;
5575 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
5576 DirRef dst
, DirRef src
)
5578 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5580 TransactedDirEntry
*dst_entry
, *src_entry
;
5582 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
5583 if (FAILED(hr
)) return hr
;
5585 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
5586 if (FAILED(hr
)) return hr
;
5588 dst_entry
= &This
->entries
[dst
];
5589 src_entry
= &This
->entries
[src
];
5591 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
5592 dst_entry
->stream_entry
= src_entry
->stream_entry
;
5593 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
5594 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
5595 dst_entry
->data
.size
= src_entry
->data
.size
;
5600 static HRESULT
TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl
*base
,
5601 ULONG
* result
, BOOL refresh
)
5606 static HRESULT
TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl
*base
,
5612 static HRESULT
TransactedSnapshotImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5617 static HRESULT
TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5622 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
5624 StorageBaseImpl_QueryInterface
,
5625 StorageBaseImpl_AddRef
,
5626 StorageBaseImpl_Release
,
5627 StorageBaseImpl_CreateStream
,
5628 StorageBaseImpl_OpenStream
,
5629 StorageBaseImpl_CreateStorage
,
5630 StorageBaseImpl_OpenStorage
,
5631 StorageBaseImpl_CopyTo
,
5632 StorageBaseImpl_MoveElementTo
,
5633 TransactedSnapshotImpl_Commit
,
5634 TransactedSnapshotImpl_Revert
,
5635 StorageBaseImpl_EnumElements
,
5636 StorageBaseImpl_DestroyElement
,
5637 StorageBaseImpl_RenameElement
,
5638 StorageBaseImpl_SetElementTimes
,
5639 StorageBaseImpl_SetClass
,
5640 StorageBaseImpl_SetStateBits
,
5641 StorageBaseImpl_Stat
5644 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
5646 TransactedSnapshotImpl_Destroy
,
5647 TransactedSnapshotImpl_Invalidate
,
5648 TransactedSnapshotImpl_Flush
,
5649 TransactedSnapshotImpl_GetFilename
,
5650 TransactedSnapshotImpl_CreateDirEntry
,
5651 TransactedSnapshotImpl_WriteDirEntry
,
5652 TransactedSnapshotImpl_ReadDirEntry
,
5653 TransactedSnapshotImpl_DestroyDirEntry
,
5654 TransactedSnapshotImpl_StreamReadAt
,
5655 TransactedSnapshotImpl_StreamWriteAt
,
5656 TransactedSnapshotImpl_StreamSetSize
,
5657 TransactedSnapshotImpl_StreamLink
,
5658 TransactedSnapshotImpl_GetTransactionSig
,
5659 TransactedSnapshotImpl_SetTransactionSig
,
5660 TransactedSnapshotImpl_LockTransaction
,
5661 TransactedSnapshotImpl_UnlockTransaction
5664 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
5665 TransactedSnapshotImpl
** result
)
5669 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
5674 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
5676 /* This is OK because the property set storage functions use the IStorage functions. */
5677 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
5678 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
5680 list_init(&(*result
)->base
.strmHead
);
5682 list_init(&(*result
)->base
.storageHead
);
5684 (*result
)->base
.ref
= 1;
5686 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5688 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5689 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
5691 /* Create a new temporary storage to act as the scratch file. */
5692 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
5694 (*result
)->scratch
= impl_from_IStorage(scratch
);
5698 ULONG num_entries
= 20;
5700 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
5701 (*result
)->entries_size
= num_entries
;
5702 (*result
)->firstFreeEntry
= 0;
5704 if ((*result
)->entries
)
5706 /* parentStorage already has 1 reference, which we take over here. */
5707 (*result
)->transactedParent
= parentStorage
;
5709 parentStorage
->transactedChild
= &(*result
)->base
;
5711 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
5715 IStorage_Release(scratch
);
5721 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
5726 return E_OUTOFMEMORY
;
5729 static void TransactedSharedImpl_Invalidate(StorageBaseImpl
* This
)
5731 if (!This
->reverted
)
5733 TRACE("Storage invalidated (stg=%p)\n", This
);
5735 This
->reverted
= TRUE
;
5737 StorageBaseImpl_DeleteAll(This
);
5741 static void TransactedSharedImpl_Destroy( StorageBaseImpl
*iface
)
5743 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
5745 TransactedSharedImpl_Invalidate(&This
->base
);
5746 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
5747 IStorage_Release(&This
->scratch
->base
.IStorage_iface
);
5748 HeapFree(GetProcessHeap(), 0, This
);
5751 static HRESULT
TransactedSharedImpl_Flush(StorageBaseImpl
* iface
)
5753 /* We only need to flush when committing. */
5757 static HRESULT
TransactedSharedImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5759 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
5761 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
5764 static HRESULT
TransactedSharedImpl_CreateDirEntry(StorageBaseImpl
*base
,
5765 const DirEntry
*newData
, DirRef
*index
)
5767 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5769 return StorageBaseImpl_CreateDirEntry(&This
->scratch
->base
,
5773 static HRESULT
TransactedSharedImpl_WriteDirEntry(StorageBaseImpl
*base
,
5774 DirRef index
, const DirEntry
*data
)
5776 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5778 return StorageBaseImpl_WriteDirEntry(&This
->scratch
->base
,
5782 static HRESULT
TransactedSharedImpl_ReadDirEntry(StorageBaseImpl
*base
,
5783 DirRef index
, DirEntry
*data
)
5785 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5787 return StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
,
5791 static HRESULT
TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5794 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5796 return StorageBaseImpl_DestroyDirEntry(&This
->scratch
->base
,
5800 static HRESULT
TransactedSharedImpl_StreamReadAt(StorageBaseImpl
*base
,
5801 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5803 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5805 return StorageBaseImpl_StreamReadAt(&This
->scratch
->base
,
5806 index
, offset
, size
, buffer
, bytesRead
);
5809 static HRESULT
TransactedSharedImpl_StreamWriteAt(StorageBaseImpl
*base
,
5810 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5812 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5814 return StorageBaseImpl_StreamWriteAt(&This
->scratch
->base
,
5815 index
, offset
, size
, buffer
, bytesWritten
);
5818 static HRESULT
TransactedSharedImpl_StreamSetSize(StorageBaseImpl
*base
,
5819 DirRef index
, ULARGE_INTEGER newsize
)
5821 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5823 return StorageBaseImpl_StreamSetSize(&This
->scratch
->base
,
5827 static HRESULT
TransactedSharedImpl_StreamLink(StorageBaseImpl
*base
,
5828 DirRef dst
, DirRef src
)
5830 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5832 return StorageBaseImpl_StreamLink(&This
->scratch
->base
,
5836 static HRESULT
TransactedSharedImpl_GetTransactionSig(StorageBaseImpl
*base
,
5837 ULONG
* result
, BOOL refresh
)
5842 static HRESULT
TransactedSharedImpl_SetTransactionSig(StorageBaseImpl
*base
,
5848 static HRESULT
TransactedSharedImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5853 static HRESULT
TransactedSharedImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5858 static HRESULT WINAPI
TransactedSharedImpl_Commit(
5860 DWORD grfCommitFlags
) /* [in] */
5862 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
5863 DirRef new_storage_ref
, prev_storage_ref
;
5864 DirEntry src_data
, dst_data
;
5866 ULONG transactionSig
;
5868 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5870 /* Cannot commit a read-only transacted storage */
5871 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5872 return STG_E_ACCESSDENIED
;
5874 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5875 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5878 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5881 if ((grfCommitFlags
& STGC_ONLYIFCURRENT
) && transactionSig
!= This
->lastTransactionSig
)
5882 hr
= STG_E_NOTCURRENT
;
5885 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, transactionSig
+1);
5887 else if (hr
== E_NOTIMPL
)
5891 hr
= StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
, This
->scratch
->base
.storageDirEntry
, &src_data
);
5893 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
5895 hr
= StorageBaseImpl_DupStorageTree(This
->transactedParent
, &new_storage_ref
, &This
->scratch
->base
, src_data
.dirRootEntry
);
5898 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5901 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
5905 prev_storage_ref
= dst_data
.dirRootEntry
;
5906 dst_data
.dirRootEntry
= new_storage_ref
;
5907 dst_data
.clsid
= src_data
.clsid
;
5908 dst_data
.ctime
= src_data
.ctime
;
5909 dst_data
.mtime
= src_data
.mtime
;
5910 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
5915 /* Try to flush after updating the root storage, but if the flush fails, keep
5916 * going, on the theory that it'll either succeed later or the subsequent
5917 * writes will fail. */
5918 StorageBaseImpl_Flush(This
->transactedParent
);
5920 hr
= StorageBaseImpl_DeleteStorageTree(This
->transactedParent
, prev_storage_ref
, TRUE
);
5924 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5926 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
5929 hr
= IStorage_Commit(&This
->scratch
->base
.IStorage_iface
, STGC_DEFAULT
);
5933 This
->lastTransactionSig
= transactionSig
+1;
5940 static HRESULT WINAPI
TransactedSharedImpl_Revert(
5943 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
5945 TRACE("(%p)\n", iface
);
5947 /* Destroy the open objects. */
5948 StorageBaseImpl_DeleteAll(&This
->base
);
5950 return IStorage_Revert(&This
->scratch
->base
.IStorage_iface
);
5953 static const IStorageVtbl TransactedSharedImpl_Vtbl
=
5955 StorageBaseImpl_QueryInterface
,
5956 StorageBaseImpl_AddRef
,
5957 StorageBaseImpl_Release
,
5958 StorageBaseImpl_CreateStream
,
5959 StorageBaseImpl_OpenStream
,
5960 StorageBaseImpl_CreateStorage
,
5961 StorageBaseImpl_OpenStorage
,
5962 StorageBaseImpl_CopyTo
,
5963 StorageBaseImpl_MoveElementTo
,
5964 TransactedSharedImpl_Commit
,
5965 TransactedSharedImpl_Revert
,
5966 StorageBaseImpl_EnumElements
,
5967 StorageBaseImpl_DestroyElement
,
5968 StorageBaseImpl_RenameElement
,
5969 StorageBaseImpl_SetElementTimes
,
5970 StorageBaseImpl_SetClass
,
5971 StorageBaseImpl_SetStateBits
,
5972 StorageBaseImpl_Stat
5975 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl
=
5977 TransactedSharedImpl_Destroy
,
5978 TransactedSharedImpl_Invalidate
,
5979 TransactedSharedImpl_Flush
,
5980 TransactedSharedImpl_GetFilename
,
5981 TransactedSharedImpl_CreateDirEntry
,
5982 TransactedSharedImpl_WriteDirEntry
,
5983 TransactedSharedImpl_ReadDirEntry
,
5984 TransactedSharedImpl_DestroyDirEntry
,
5985 TransactedSharedImpl_StreamReadAt
,
5986 TransactedSharedImpl_StreamWriteAt
,
5987 TransactedSharedImpl_StreamSetSize
,
5988 TransactedSharedImpl_StreamLink
,
5989 TransactedSharedImpl_GetTransactionSig
,
5990 TransactedSharedImpl_SetTransactionSig
,
5991 TransactedSharedImpl_LockTransaction
,
5992 TransactedSharedImpl_UnlockTransaction
5995 static HRESULT
TransactedSharedImpl_Construct(StorageBaseImpl
*parentStorage
,
5996 TransactedSharedImpl
** result
)
6000 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSharedImpl
));
6005 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSharedImpl_Vtbl
;
6007 /* This is OK because the property set storage functions use the IStorage functions. */
6008 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
6009 (*result
)->base
.baseVtbl
= &TransactedSharedImpl_BaseVtbl
;
6011 list_init(&(*result
)->base
.strmHead
);
6013 list_init(&(*result
)->base
.storageHead
);
6015 (*result
)->base
.ref
= 1;
6017 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
6019 hr
= StorageBaseImpl_LockTransaction(parentStorage
, FALSE
);
6025 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
6026 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
6030 stgo
.ulSectorSize
= 4096;
6031 stgo
.pwcsTemplateFile
= NULL
;
6033 /* Create a new temporary storage to act as the scratch file. */
6034 hr
= StgCreateStorageEx(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
|STGM_TRANSACTED
,
6035 STGFMT_DOCFILE
, 0, &stgo
, NULL
, &IID_IStorage
, (void**)&scratch
);
6036 (*result
)->scratch
= (TransactedSnapshotImpl
*)impl_from_IStorage(scratch
);
6040 hr
= StorageBaseImpl_CopyStorageTree(&(*result
)->scratch
->base
, (*result
)->scratch
->base
.storageDirEntry
,
6041 parentStorage
, parentStorage
->storageDirEntry
);
6045 hr
= IStorage_Commit(scratch
, STGC_DEFAULT
);
6047 (*result
)->base
.storageDirEntry
= (*result
)->scratch
->base
.storageDirEntry
;
6048 (*result
)->transactedParent
= parentStorage
;
6052 IStorage_Release(scratch
);
6055 StorageBaseImpl_UnlockTransaction(parentStorage
, FALSE
);
6058 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6063 return E_OUTOFMEMORY
;
6066 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
6067 BOOL toplevel
, StorageBaseImpl
** result
)
6069 static int fixme_flags
=STGM_NOSCRATCH
|STGM_NOSNAPSHOT
;
6071 if (parentStorage
->openFlags
& fixme_flags
)
6073 fixme_flags
&= ~parentStorage
->openFlags
;
6074 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
6077 if (toplevel
&& !(parentStorage
->openFlags
& STGM_NOSNAPSHOT
) &&
6078 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_DENY_WRITE
&&
6079 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_EXCLUSIVE
)
6081 /* Need to create a temp file for the snapshot */
6082 return TransactedSharedImpl_Construct(parentStorage
, (TransactedSharedImpl
**)result
);
6085 return TransactedSnapshotImpl_Construct(parentStorage
,
6086 (TransactedSnapshotImpl
**)result
);
6089 static HRESULT
Storage_Construct(
6097 StorageBaseImpl
** result
)
6099 StorageImpl
*newStorage
;
6100 StorageBaseImpl
*newTransactedStorage
;
6103 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
6104 if (FAILED(hr
)) goto end
;
6106 if (openFlags
& STGM_TRANSACTED
)
6108 hr
= Storage_ConstructTransacted(&newStorage
->base
, TRUE
, &newTransactedStorage
);
6110 IStorage_Release(&newStorage
->base
.IStorage_iface
);
6112 *result
= newTransactedStorage
;
6115 *result
= &newStorage
->base
;
6121 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
6123 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6125 if (!This
->base
.reverted
)
6127 TRACE("Storage invalidated (stg=%p)\n", This
);
6129 This
->base
.reverted
= TRUE
;
6131 This
->parentStorage
= NULL
;
6133 StorageBaseImpl_DeleteAll(&This
->base
);
6135 list_remove(&This
->ParentListEntry
);
6139 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
6141 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
6143 StorageInternalImpl_Invalidate(&This
->base
);
6145 HeapFree(GetProcessHeap(), 0, This
);
6148 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
6150 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
6152 return StorageBaseImpl_Flush(This
->parentStorage
);
6155 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6157 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
6159 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
6162 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
6163 const DirEntry
*newData
, DirRef
*index
)
6165 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6167 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
6171 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
6172 DirRef index
, const DirEntry
*data
)
6174 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6176 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
6180 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
6181 DirRef index
, DirEntry
*data
)
6183 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6185 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
6189 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6192 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6194 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
6198 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
6199 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6201 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6203 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
6204 index
, offset
, size
, buffer
, bytesRead
);
6207 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
6208 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6210 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6212 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
6213 index
, offset
, size
, buffer
, bytesWritten
);
6216 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
6217 DirRef index
, ULARGE_INTEGER newsize
)
6219 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6221 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
6225 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
6226 DirRef dst
, DirRef src
)
6228 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6230 return StorageBaseImpl_StreamLink(This
->parentStorage
,
6234 static HRESULT
StorageInternalImpl_GetTransactionSig(StorageBaseImpl
*base
,
6235 ULONG
* result
, BOOL refresh
)
6240 static HRESULT
StorageInternalImpl_SetTransactionSig(StorageBaseImpl
*base
,
6246 static HRESULT
StorageInternalImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6251 static HRESULT
StorageInternalImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6256 /******************************************************************************
6258 ** Storage32InternalImpl_Commit
6261 static HRESULT WINAPI
StorageInternalImpl_Commit(
6263 DWORD grfCommitFlags
) /* [in] */
6265 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
6266 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
6267 return StorageBaseImpl_Flush(This
);
6270 /******************************************************************************
6272 ** Storage32InternalImpl_Revert
6275 static HRESULT WINAPI
StorageInternalImpl_Revert(
6278 FIXME("(%p): stub\n", iface
);
6282 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
6284 IStorage_Release(&This
->parentStorage
->IStorage_iface
);
6285 HeapFree(GetProcessHeap(), 0, This
);
6288 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
6289 IEnumSTATSTG
* iface
,
6293 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6296 return E_INVALIDARG
;
6300 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
6301 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
6304 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
6308 return E_NOINTERFACE
;
6311 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
6312 IEnumSTATSTG
* iface
)
6314 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6315 return InterlockedIncrement(&This
->ref
);
6318 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
6319 IEnumSTATSTG
* iface
)
6321 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6325 newRef
= InterlockedDecrement(&This
->ref
);
6329 IEnumSTATSTGImpl_Destroy(This
);
6335 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
6336 IEnumSTATSTGImpl
* This
,
6339 DirRef result
= DIRENTRY_NULL
;
6343 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
6345 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
6346 This
->parentStorage
->storageDirEntry
, &entry
);
6347 searchNode
= entry
.dirRootEntry
;
6349 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
6351 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
6355 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
6359 searchNode
= entry
.rightChild
;
6363 result
= searchNode
;
6364 memcpy(result_name
, entry
.name
, sizeof(result_name
));
6365 searchNode
= entry
.leftChild
;
6373 if (result
!= DIRENTRY_NULL
)
6374 memcpy(This
->name
, result_name
, sizeof(result_name
));
6380 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
6381 IEnumSTATSTG
* iface
,
6384 ULONG
* pceltFetched
)
6386 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6388 DirEntry currentEntry
;
6389 STATSTG
* currentReturnStruct
= rgelt
;
6390 ULONG objectFetched
= 0;
6391 DirRef currentSearchNode
;
6394 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
6395 return E_INVALIDARG
;
6397 if (This
->parentStorage
->reverted
)
6398 return STG_E_REVERTED
;
6401 * To avoid the special case, get another pointer to a ULONG value if
6402 * the caller didn't supply one.
6404 if (pceltFetched
==0)
6405 pceltFetched
= &objectFetched
;
6408 * Start the iteration, we will iterate until we hit the end of the
6409 * linked list or until we hit the number of items to iterate through
6413 while ( *pceltFetched
< celt
)
6415 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
6417 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
6421 * Read the entry from the storage.
6423 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
6428 * Copy the information to the return buffer.
6430 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
6431 currentReturnStruct
,
6436 * Step to the next item in the iteration
6439 currentReturnStruct
++;
6442 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
6449 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
6450 IEnumSTATSTG
* iface
,
6453 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6455 ULONG objectFetched
= 0;
6456 DirRef currentSearchNode
;
6459 if (This
->parentStorage
->reverted
)
6460 return STG_E_REVERTED
;
6462 while ( (objectFetched
< celt
) )
6464 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
6466 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
6472 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
6478 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
6479 IEnumSTATSTG
* iface
)
6481 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6483 if (This
->parentStorage
->reverted
)
6484 return STG_E_REVERTED
;
6491 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
6492 IEnumSTATSTG
* iface
,
6493 IEnumSTATSTG
** ppenum
)
6495 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6496 IEnumSTATSTGImpl
* newClone
;
6498 if (This
->parentStorage
->reverted
)
6499 return STG_E_REVERTED
;
6502 return E_INVALIDARG
;
6504 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
6505 This
->storageDirEntry
);
6509 return E_OUTOFMEMORY
;
6513 * The new clone enumeration must point to the same current node as
6516 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
6518 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
6524 * Virtual function table for the IEnumSTATSTGImpl class.
6526 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
6528 IEnumSTATSTGImpl_QueryInterface
,
6529 IEnumSTATSTGImpl_AddRef
,
6530 IEnumSTATSTGImpl_Release
,
6531 IEnumSTATSTGImpl_Next
,
6532 IEnumSTATSTGImpl_Skip
,
6533 IEnumSTATSTGImpl_Reset
,
6534 IEnumSTATSTGImpl_Clone
6537 /******************************************************************************
6538 ** IEnumSTATSTGImpl implementation
6541 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
6542 StorageBaseImpl
* parentStorage
,
6543 DirRef storageDirEntry
)
6545 IEnumSTATSTGImpl
* newEnumeration
;
6547 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
6551 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
6552 newEnumeration
->ref
= 1;
6553 newEnumeration
->name
[0] = 0;
6556 * We want to nail-down the reference to the storage in case the
6557 * enumeration out-lives the storage in the client application.
6559 newEnumeration
->parentStorage
= parentStorage
;
6560 IStorage_AddRef(&newEnumeration
->parentStorage
->IStorage_iface
);
6562 newEnumeration
->storageDirEntry
= storageDirEntry
;
6565 return newEnumeration
;
6569 * Virtual function table for the Storage32InternalImpl class.
6571 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
6573 StorageBaseImpl_QueryInterface
,
6574 StorageBaseImpl_AddRef
,
6575 StorageBaseImpl_Release
,
6576 StorageBaseImpl_CreateStream
,
6577 StorageBaseImpl_OpenStream
,
6578 StorageBaseImpl_CreateStorage
,
6579 StorageBaseImpl_OpenStorage
,
6580 StorageBaseImpl_CopyTo
,
6581 StorageBaseImpl_MoveElementTo
,
6582 StorageInternalImpl_Commit
,
6583 StorageInternalImpl_Revert
,
6584 StorageBaseImpl_EnumElements
,
6585 StorageBaseImpl_DestroyElement
,
6586 StorageBaseImpl_RenameElement
,
6587 StorageBaseImpl_SetElementTimes
,
6588 StorageBaseImpl_SetClass
,
6589 StorageBaseImpl_SetStateBits
,
6590 StorageBaseImpl_Stat
6593 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
6595 StorageInternalImpl_Destroy
,
6596 StorageInternalImpl_Invalidate
,
6597 StorageInternalImpl_Flush
,
6598 StorageInternalImpl_GetFilename
,
6599 StorageInternalImpl_CreateDirEntry
,
6600 StorageInternalImpl_WriteDirEntry
,
6601 StorageInternalImpl_ReadDirEntry
,
6602 StorageInternalImpl_DestroyDirEntry
,
6603 StorageInternalImpl_StreamReadAt
,
6604 StorageInternalImpl_StreamWriteAt
,
6605 StorageInternalImpl_StreamSetSize
,
6606 StorageInternalImpl_StreamLink
,
6607 StorageInternalImpl_GetTransactionSig
,
6608 StorageInternalImpl_SetTransactionSig
,
6609 StorageInternalImpl_LockTransaction
,
6610 StorageInternalImpl_UnlockTransaction
6613 /******************************************************************************
6614 ** Storage32InternalImpl implementation
6617 static StorageInternalImpl
* StorageInternalImpl_Construct(
6618 StorageBaseImpl
* parentStorage
,
6620 DirRef storageDirEntry
)
6622 StorageInternalImpl
* newStorage
;
6624 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
6628 list_init(&newStorage
->base
.strmHead
);
6630 list_init(&newStorage
->base
.storageHead
);
6633 * Initialize the virtual function table.
6635 newStorage
->base
.IStorage_iface
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
6636 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
6637 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
6638 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
6640 newStorage
->base
.reverted
= FALSE
;
6642 newStorage
->base
.ref
= 1;
6644 newStorage
->parentStorage
= parentStorage
;
6647 * Keep a reference to the directory entry of this storage
6649 newStorage
->base
.storageDirEntry
= storageDirEntry
;
6651 newStorage
->base
.create
= FALSE
;
6659 /******************************************************************************
6660 ** StorageUtl implementation
6663 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
6667 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
6668 *value
= lendian16toh(tmp
);
6671 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
6673 value
= htole16(value
);
6674 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
6677 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
6681 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
6682 *value
= lendian32toh(tmp
);
6685 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
6687 value
= htole32(value
);
6688 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
6691 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
6692 ULARGE_INTEGER
* value
)
6694 #ifdef WORDS_BIGENDIAN
6697 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6698 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
6699 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
6701 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6705 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
6706 const ULARGE_INTEGER
*value
)
6708 #ifdef WORDS_BIGENDIAN
6711 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
6712 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
6713 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
6715 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
6719 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
6721 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
6722 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
6723 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
6725 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
6728 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
6730 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
6731 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
6732 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
6734 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
6737 void StorageUtl_CopyDirEntryToSTATSTG(
6738 StorageBaseImpl
* storage
,
6739 STATSTG
* destination
,
6740 const DirEntry
* source
,
6744 * The copy of the string occurs only when the flag is not set
6746 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
6748 /* Use the filename for the root storage. */
6749 destination
->pwcsName
= 0;
6750 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
6752 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
6753 (source
->name
[0] == 0) )
6755 destination
->pwcsName
= 0;
6759 destination
->pwcsName
=
6760 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
6762 strcpyW(destination
->pwcsName
, source
->name
);
6765 switch (source
->stgType
)
6769 destination
->type
= STGTY_STORAGE
;
6772 destination
->type
= STGTY_STREAM
;
6775 destination
->type
= STGTY_STREAM
;
6779 destination
->cbSize
= source
->size
;
6781 currentReturnStruct->mtime = {0}; TODO
6782 currentReturnStruct->ctime = {0};
6783 currentReturnStruct->atime = {0};
6785 destination
->grfMode
= 0;
6786 destination
->grfLocksSupported
= 0;
6787 destination
->clsid
= source
->clsid
;
6788 destination
->grfStateBits
= 0;
6789 destination
->reserved
= 0;
6792 /******************************************************************************
6793 ** BlockChainStream implementation
6796 /* Read and save the index of all blocks in this stream. */
6797 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
6799 ULONG next_sector
, next_offset
;
6801 struct BlockChainRun
*last_run
;
6803 if (This
->indexCacheLen
== 0)
6807 next_sector
= BlockChainStream_GetHeadOfChain(This
);
6811 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6812 next_offset
= last_run
->lastOffset
+1;
6813 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
6814 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
6816 if (FAILED(hr
)) return hr
;
6819 while (next_sector
!= BLOCK_END_OF_CHAIN
)
6821 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
6823 /* Add the current block to the cache. */
6824 if (This
->indexCacheSize
== 0)
6826 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
6827 if (!This
->indexCache
) return E_OUTOFMEMORY
;
6828 This
->indexCacheSize
= 16;
6830 else if (This
->indexCacheSize
== This
->indexCacheLen
)
6832 struct BlockChainRun
*new_cache
;
6835 new_size
= This
->indexCacheSize
* 2;
6836 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
6837 if (!new_cache
) return E_OUTOFMEMORY
;
6838 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
6840 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
6841 This
->indexCache
= new_cache
;
6842 This
->indexCacheSize
= new_size
;
6845 This
->indexCacheLen
++;
6846 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6847 last_run
->firstSector
= next_sector
;
6848 last_run
->firstOffset
= next_offset
;
6851 last_run
->lastOffset
= next_offset
;
6853 /* Find the next block. */
6855 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
6856 if (FAILED(hr
)) return hr
;
6859 if (This
->indexCacheLen
)
6861 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
6862 This
->numBlocks
= last_run
->lastOffset
+1;
6866 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
6867 This
->numBlocks
= 0;
6873 /* Locate the nth block in this stream. */
6874 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
6876 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
6877 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
6879 if (offset
>= This
->numBlocks
)
6880 return BLOCK_END_OF_CHAIN
;
6882 while (min_run
< max_run
)
6884 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
6885 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
6887 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
6888 max_run
= run_to_check
-1;
6890 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
6892 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
6893 min_run
= run_to_check
+1;
6896 /* Block is in this run. */
6897 min_run
= max_run
= run_to_check
;
6900 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
6903 HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
6904 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
6906 BlockChainBlock
*result
=NULL
;
6910 if (This
->cachedBlocks
[i
].index
== index
)
6912 *sector
= This
->cachedBlocks
[i
].sector
;
6913 *block
= &This
->cachedBlocks
[i
];
6917 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
6918 if (*sector
== BLOCK_END_OF_CHAIN
)
6919 return STG_E_DOCFILECORRUPT
;
6923 if (This
->cachedBlocks
[0].index
== 0xffffffff)
6924 result
= &This
->cachedBlocks
[0];
6925 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
6926 result
= &This
->cachedBlocks
[1];
6929 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
6930 if (This
->blockToEvict
== 2)
6931 This
->blockToEvict
= 0;
6936 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
6937 return STG_E_WRITEFAULT
;
6938 result
->dirty
= FALSE
;
6941 result
->read
= FALSE
;
6942 result
->index
= index
;
6943 result
->sector
= *sector
;
6950 BlockChainStream
* BlockChainStream_Construct(
6951 StorageImpl
* parentStorage
,
6952 ULONG
* headOfStreamPlaceHolder
,
6955 BlockChainStream
* newStream
;
6957 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
6959 newStream
->parentStorage
= parentStorage
;
6960 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6961 newStream
->ownerDirEntry
= dirEntry
;
6962 newStream
->indexCache
= NULL
;
6963 newStream
->indexCacheLen
= 0;
6964 newStream
->indexCacheSize
= 0;
6965 newStream
->cachedBlocks
[0].index
= 0xffffffff;
6966 newStream
->cachedBlocks
[0].dirty
= FALSE
;
6967 newStream
->cachedBlocks
[1].index
= 0xffffffff;
6968 newStream
->cachedBlocks
[1].dirty
= FALSE
;
6969 newStream
->blockToEvict
= 0;
6971 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
6973 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
6974 HeapFree(GetProcessHeap(), 0, newStream
);
6981 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
6984 if (!This
) return S_OK
;
6987 if (This
->cachedBlocks
[i
].dirty
)
6989 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
6990 This
->cachedBlocks
[i
].dirty
= FALSE
;
6992 return STG_E_WRITEFAULT
;
6998 void BlockChainStream_Destroy(BlockChainStream
* This
)
7002 BlockChainStream_Flush(This
);
7003 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
7005 HeapFree(GetProcessHeap(), 0, This
);
7008 /******************************************************************************
7009 * BlockChainStream_GetHeadOfChain
7011 * Returns the head of this stream chain.
7012 * Some special chains don't have directory entries, their heads are kept in
7013 * This->headOfStreamPlaceHolder.
7016 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
7018 DirEntry chainEntry
;
7021 if (This
->headOfStreamPlaceHolder
!= 0)
7022 return *(This
->headOfStreamPlaceHolder
);
7024 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
7026 hr
= StorageImpl_ReadDirEntry(
7027 This
->parentStorage
,
7028 This
->ownerDirEntry
,
7031 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
7032 return chainEntry
.startingBlock
;
7035 return BLOCK_END_OF_CHAIN
;
7038 /******************************************************************************
7039 * BlockChainStream_GetCount
7041 * Returns the number of blocks that comprises this chain.
7042 * This is not the size of the stream as the last block may not be full!
7044 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
7046 return This
->numBlocks
;
7049 /******************************************************************************
7050 * BlockChainStream_ReadAt
7052 * Reads a specified number of bytes from this chain at the specified offset.
7053 * bytesRead may be NULL.
7054 * Failure will be returned if the specified number of bytes has not been read.
7056 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
7057 ULARGE_INTEGER offset
,
7062 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7063 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7064 ULONG bytesToReadInBuffer
;
7067 ULARGE_INTEGER stream_size
;
7069 BlockChainBlock
*cachedBlock
;
7071 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
7074 * Find the first block in the stream that contains part of the buffer.
7076 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
7080 stream_size
= BlockChainStream_GetSize(This
);
7081 if (stream_size
.QuadPart
> offset
.QuadPart
)
7082 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7087 * Start reading the buffer.
7089 bufferWalker
= buffer
;
7093 ULARGE_INTEGER ulOffset
;
7097 * Calculate how many bytes we can copy from this big block.
7099 bytesToReadInBuffer
=
7100 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7102 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
7109 /* Not in cache, and we're going to read past the end of the block. */
7110 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7113 StorageImpl_ReadAt(This
->parentStorage
,
7116 bytesToReadInBuffer
,
7121 if (!cachedBlock
->read
)
7124 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7125 return STG_E_READFAULT
;
7127 cachedBlock
->read
= TRUE
;
7130 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
7131 bytesReadAt
= bytesToReadInBuffer
;
7134 blockNoInSequence
++;
7135 bufferWalker
+= bytesReadAt
;
7136 size
-= bytesReadAt
;
7137 *bytesRead
+= bytesReadAt
;
7138 offsetInBlock
= 0; /* There is no offset on the next block */
7140 if (bytesToReadInBuffer
!= bytesReadAt
)
7147 /******************************************************************************
7148 * BlockChainStream_WriteAt
7150 * Writes the specified number of bytes to this chain at the specified offset.
7151 * Will fail if not all specified number of bytes have been written.
7153 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
7154 ULARGE_INTEGER offset
,
7157 ULONG
* bytesWritten
)
7159 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7160 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7163 const BYTE
* bufferWalker
;
7165 BlockChainBlock
*cachedBlock
;
7168 bufferWalker
= buffer
;
7172 ULARGE_INTEGER ulOffset
;
7173 DWORD bytesWrittenAt
;
7176 * Calculate how many bytes we can copy to this big block.
7179 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7181 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
7183 /* BlockChainStream_SetSize should have already been called to ensure we have
7184 * enough blocks in the chain to write into */
7187 ERR("not enough blocks in chain to write data\n");
7193 /* Not in cache, and we're going to write past the end of the block. */
7194 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7197 StorageImpl_WriteAt(This
->parentStorage
,
7205 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
7208 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7209 return STG_E_READFAULT
;
7212 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
7213 bytesWrittenAt
= bytesToWrite
;
7214 cachedBlock
->read
= TRUE
;
7215 cachedBlock
->dirty
= TRUE
;
7218 blockNoInSequence
++;
7219 bufferWalker
+= bytesWrittenAt
;
7220 size
-= bytesWrittenAt
;
7221 *bytesWritten
+= bytesWrittenAt
;
7222 offsetInBlock
= 0; /* There is no offset on the next block */
7224 if (bytesWrittenAt
!= bytesToWrite
)
7228 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7231 /******************************************************************************
7232 * BlockChainStream_Shrink
7234 * Shrinks this chain in the big block depot.
7236 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
7237 ULARGE_INTEGER newSize
)
7244 * Figure out how many blocks are needed to contain the new size
7246 numBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7248 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7254 * Go to the new end of chain
7256 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
7258 /* Mark the new end of chain */
7259 StorageImpl_SetNextBlockInChain(
7260 This
->parentStorage
,
7262 BLOCK_END_OF_CHAIN
);
7264 This
->tailIndex
= blockIndex
;
7268 if (This
->headOfStreamPlaceHolder
!= 0)
7270 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
7274 DirEntry chainEntry
;
7275 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7277 StorageImpl_ReadDirEntry(
7278 This
->parentStorage
,
7279 This
->ownerDirEntry
,
7282 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7284 StorageImpl_WriteDirEntry(
7285 This
->parentStorage
,
7286 This
->ownerDirEntry
,
7290 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7293 This
->numBlocks
= numBlocks
;
7296 * Mark the extra blocks as free
7298 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
7300 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7301 StorageImpl_FreeBigBlock(This
->parentStorage
,
7302 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
7303 if (last_run
->lastOffset
== last_run
->firstOffset
)
7304 This
->indexCacheLen
--;
7306 last_run
->lastOffset
--;
7310 * Reset the last accessed block cache.
7314 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
7316 This
->cachedBlocks
[i
].index
= 0xffffffff;
7317 This
->cachedBlocks
[i
].dirty
= FALSE
;
7324 /******************************************************************************
7325 * BlockChainStream_Enlarge
7327 * Grows this chain in the big block depot.
7329 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
7330 ULARGE_INTEGER newSize
)
7332 ULONG blockIndex
, currentBlock
;
7334 ULONG oldNumBlocks
= 0;
7336 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
7339 * Empty chain. Create the head.
7341 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7343 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7344 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
7346 BLOCK_END_OF_CHAIN
);
7348 if (This
->headOfStreamPlaceHolder
!= 0)
7350 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7354 DirEntry chainEntry
;
7355 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7357 StorageImpl_ReadDirEntry(
7358 This
->parentStorage
,
7359 This
->ownerDirEntry
,
7362 chainEntry
.startingBlock
= blockIndex
;
7364 StorageImpl_WriteDirEntry(
7365 This
->parentStorage
,
7366 This
->ownerDirEntry
,
7370 This
->tailIndex
= blockIndex
;
7371 This
->numBlocks
= 1;
7375 * Figure out how many blocks are needed to contain this stream
7377 newNumBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7379 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7383 * Go to the current end of chain
7385 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
7387 currentBlock
= blockIndex
;
7389 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7392 currentBlock
= blockIndex
;
7394 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
7399 This
->tailIndex
= currentBlock
;
7402 currentBlock
= This
->tailIndex
;
7403 oldNumBlocks
= This
->numBlocks
;
7406 * Add new blocks to the chain
7408 if (oldNumBlocks
< newNumBlocks
)
7410 while (oldNumBlocks
< newNumBlocks
)
7412 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7414 StorageImpl_SetNextBlockInChain(
7415 This
->parentStorage
,
7419 StorageImpl_SetNextBlockInChain(
7420 This
->parentStorage
,
7422 BLOCK_END_OF_CHAIN
);
7424 currentBlock
= blockIndex
;
7428 This
->tailIndex
= blockIndex
;
7429 This
->numBlocks
= newNumBlocks
;
7432 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
7438 /******************************************************************************
7439 * BlockChainStream_SetSize
7441 * Sets the size of this stream. The big block depot will be updated.
7442 * The file will grow if we grow the chain.
7444 * TODO: Free the actual blocks in the file when we shrink the chain.
7445 * Currently, the blocks are still in the file. So the file size
7446 * doesn't shrink even if we shrink streams.
7448 BOOL
BlockChainStream_SetSize(
7449 BlockChainStream
* This
,
7450 ULARGE_INTEGER newSize
)
7452 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
7454 if (newSize
.QuadPart
== size
.QuadPart
)
7457 if (newSize
.QuadPart
< size
.QuadPart
)
7459 BlockChainStream_Shrink(This
, newSize
);
7463 BlockChainStream_Enlarge(This
, newSize
);
7469 /******************************************************************************
7470 * BlockChainStream_GetSize
7472 * Returns the size of this chain.
7473 * Will return the block count if this chain doesn't have a directory entry.
7475 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
7477 DirEntry chainEntry
;
7479 if(This
->headOfStreamPlaceHolder
== NULL
)
7482 * This chain has a directory entry so use the size value from there.
7484 StorageImpl_ReadDirEntry(
7485 This
->parentStorage
,
7486 This
->ownerDirEntry
,
7489 return chainEntry
.size
;
7494 * this chain is a chain that does not have a directory entry, figure out the
7495 * size by making the product number of used blocks times the
7498 ULARGE_INTEGER result
;
7500 (ULONGLONG
)BlockChainStream_GetCount(This
) *
7501 This
->parentStorage
->bigBlockSize
;
7507 /******************************************************************************
7508 ** SmallBlockChainStream implementation
7511 SmallBlockChainStream
* SmallBlockChainStream_Construct(
7512 StorageImpl
* parentStorage
,
7513 ULONG
* headOfStreamPlaceHolder
,
7516 SmallBlockChainStream
* newStream
;
7518 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
7520 newStream
->parentStorage
= parentStorage
;
7521 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7522 newStream
->ownerDirEntry
= dirEntry
;
7527 void SmallBlockChainStream_Destroy(
7528 SmallBlockChainStream
* This
)
7530 HeapFree(GetProcessHeap(), 0, This
);
7533 /******************************************************************************
7534 * SmallBlockChainStream_GetHeadOfChain
7536 * Returns the head of this chain of small blocks.
7538 static ULONG
SmallBlockChainStream_GetHeadOfChain(
7539 SmallBlockChainStream
* This
)
7541 DirEntry chainEntry
;
7544 if (This
->headOfStreamPlaceHolder
!= NULL
)
7545 return *(This
->headOfStreamPlaceHolder
);
7547 if (This
->ownerDirEntry
)
7549 hr
= StorageImpl_ReadDirEntry(
7550 This
->parentStorage
,
7551 This
->ownerDirEntry
,
7554 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
7555 return chainEntry
.startingBlock
;
7558 return BLOCK_END_OF_CHAIN
;
7561 /******************************************************************************
7562 * SmallBlockChainStream_GetNextBlockInChain
7564 * Returns the index of the next small block in this chain.
7567 * - BLOCK_END_OF_CHAIN: end of this chain
7568 * - BLOCK_UNUSED: small block 'blockIndex' is free
7570 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
7571 SmallBlockChainStream
* This
,
7573 ULONG
* nextBlockInChain
)
7575 ULARGE_INTEGER offsetOfBlockInDepot
;
7580 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
7582 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7585 * Read those bytes in the buffer from the small block file.
7587 res
= BlockChainStream_ReadAt(
7588 This
->parentStorage
->smallBlockDepotChain
,
7589 offsetOfBlockInDepot
,
7594 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
7595 res
= STG_E_READFAULT
;
7599 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
7606 /******************************************************************************
7607 * SmallBlockChainStream_SetNextBlockInChain
7609 * Writes the index of the next block of the specified block in the small
7611 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7612 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7614 static void SmallBlockChainStream_SetNextBlockInChain(
7615 SmallBlockChainStream
* This
,
7619 ULARGE_INTEGER offsetOfBlockInDepot
;
7623 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7625 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
7628 * Read those bytes in the buffer from the small block file.
7630 BlockChainStream_WriteAt(
7631 This
->parentStorage
->smallBlockDepotChain
,
7632 offsetOfBlockInDepot
,
7638 /******************************************************************************
7639 * SmallBlockChainStream_FreeBlock
7641 * Flag small block 'blockIndex' as free in the small block depot.
7643 static void SmallBlockChainStream_FreeBlock(
7644 SmallBlockChainStream
* This
,
7647 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
7650 /******************************************************************************
7651 * SmallBlockChainStream_GetNextFreeBlock
7653 * Returns the index of a free small block. The small block depot will be
7654 * enlarged if necessary. The small block chain will also be enlarged if
7657 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
7658 SmallBlockChainStream
* This
)
7660 ULARGE_INTEGER offsetOfBlockInDepot
;
7663 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
7664 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
7666 ULONG smallBlocksPerBigBlock
;
7668 ULONG blocksRequired
;
7669 ULARGE_INTEGER old_size
, size_required
;
7671 offsetOfBlockInDepot
.u
.HighPart
= 0;
7674 * Scan the small block depot for a free block
7676 while (nextBlockIndex
!= BLOCK_UNUSED
)
7678 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7680 res
= BlockChainStream_ReadAt(
7681 This
->parentStorage
->smallBlockDepotChain
,
7682 offsetOfBlockInDepot
,
7688 * If we run out of space for the small block depot, enlarge it
7690 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
7692 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
7694 if (nextBlockIndex
!= BLOCK_UNUSED
)
7700 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
7702 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
7703 ULARGE_INTEGER newSize
, offset
;
7706 newSize
.QuadPart
= (ULONGLONG
)(count
+ 1) * This
->parentStorage
->bigBlockSize
;
7707 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
7710 * Initialize all the small blocks to free
7712 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
7713 offset
.QuadPart
= (ULONGLONG
)count
* This
->parentStorage
->bigBlockSize
;
7714 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
7715 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
7717 StorageImpl_SaveFileHeader(This
->parentStorage
);
7721 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
7723 smallBlocksPerBigBlock
=
7724 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
7727 * Verify if we have to allocate big blocks to contain small blocks
7729 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
7731 size_required
.QuadPart
= (ULONGLONG
)blocksRequired
* This
->parentStorage
->bigBlockSize
;
7733 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
7735 if (size_required
.QuadPart
> old_size
.QuadPart
)
7737 BlockChainStream_SetSize(
7738 This
->parentStorage
->smallBlockRootChain
,
7741 StorageImpl_ReadDirEntry(
7742 This
->parentStorage
,
7743 This
->parentStorage
->base
.storageDirEntry
,
7746 rootEntry
.size
= size_required
;
7748 StorageImpl_WriteDirEntry(
7749 This
->parentStorage
,
7750 This
->parentStorage
->base
.storageDirEntry
,
7757 /******************************************************************************
7758 * SmallBlockChainStream_ReadAt
7760 * Reads a specified number of bytes from this chain at the specified offset.
7761 * bytesRead may be NULL.
7762 * Failure will be returned if the specified number of bytes has not been read.
7764 HRESULT
SmallBlockChainStream_ReadAt(
7765 SmallBlockChainStream
* This
,
7766 ULARGE_INTEGER offset
,
7772 ULARGE_INTEGER offsetInBigBlockFile
;
7773 ULONG blockNoInSequence
=
7774 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7776 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
7777 ULONG bytesToReadInBuffer
;
7779 ULONG bytesReadFromBigBlockFile
;
7781 ULARGE_INTEGER stream_size
;
7784 * This should never happen on a small block file.
7786 assert(offset
.u
.HighPart
==0);
7790 stream_size
= SmallBlockChainStream_GetSize(This
);
7791 if (stream_size
.QuadPart
> offset
.QuadPart
)
7792 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7797 * Find the first block in the stream that contains part of the buffer.
7799 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7801 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
7803 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7806 blockNoInSequence
--;
7810 * Start reading the buffer.
7812 bufferWalker
= buffer
;
7814 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
7817 * Calculate how many bytes we can copy from this small block.
7819 bytesToReadInBuffer
=
7820 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
7823 * Calculate the offset of the small block in the small block file.
7825 offsetInBigBlockFile
.QuadPart
=
7826 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
7828 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
7831 * Read those bytes in the buffer from the small block file.
7832 * The small block has already been identified so it shouldn't fail
7833 * unless the file is corrupt.
7835 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
7836 offsetInBigBlockFile
,
7837 bytesToReadInBuffer
,
7839 &bytesReadFromBigBlockFile
);
7844 if (!bytesReadFromBigBlockFile
)
7845 return STG_E_DOCFILECORRUPT
;
7848 * Step to the next big block.
7850 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7852 return STG_E_DOCFILECORRUPT
;
7854 bufferWalker
+= bytesReadFromBigBlockFile
;
7855 size
-= bytesReadFromBigBlockFile
;
7856 *bytesRead
+= bytesReadFromBigBlockFile
;
7857 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7863 /******************************************************************************
7864 * SmallBlockChainStream_WriteAt
7866 * Writes the specified number of bytes to this chain at the specified offset.
7867 * Will fail if not all specified number of bytes have been written.
7869 HRESULT
SmallBlockChainStream_WriteAt(
7870 SmallBlockChainStream
* This
,
7871 ULARGE_INTEGER offset
,
7874 ULONG
* bytesWritten
)
7876 ULARGE_INTEGER offsetInBigBlockFile
;
7877 ULONG blockNoInSequence
=
7878 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7880 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
7881 ULONG bytesToWriteInBuffer
;
7883 ULONG bytesWrittenToBigBlockFile
;
7884 const BYTE
* bufferWalker
;
7888 * This should never happen on a small block file.
7890 assert(offset
.u
.HighPart
==0);
7893 * Find the first block in the stream that contains part of the buffer.
7895 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7897 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
7899 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
7900 return STG_E_DOCFILECORRUPT
;
7901 blockNoInSequence
--;
7905 * Start writing the buffer.
7908 bufferWalker
= buffer
;
7909 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
7912 * Calculate how many bytes we can copy to this small block.
7914 bytesToWriteInBuffer
=
7915 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
7918 * Calculate the offset of the small block in the small block file.
7920 offsetInBigBlockFile
.QuadPart
=
7921 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
7923 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
7926 * Write those bytes in the buffer to the small block file.
7928 res
= BlockChainStream_WriteAt(
7929 This
->parentStorage
->smallBlockRootChain
,
7930 offsetInBigBlockFile
,
7931 bytesToWriteInBuffer
,
7933 &bytesWrittenToBigBlockFile
);
7938 * Step to the next big block.
7940 res
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7943 bufferWalker
+= bytesWrittenToBigBlockFile
;
7944 size
-= bytesWrittenToBigBlockFile
;
7945 *bytesWritten
+= bytesWrittenToBigBlockFile
;
7946 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7949 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7952 /******************************************************************************
7953 * SmallBlockChainStream_Shrink
7955 * Shrinks this chain in the small block depot.
7957 static BOOL
SmallBlockChainStream_Shrink(
7958 SmallBlockChainStream
* This
,
7959 ULARGE_INTEGER newSize
)
7961 ULONG blockIndex
, extraBlock
;
7965 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7967 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7970 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7973 * Go to the new end of chain
7975 while (count
< numBlocks
)
7977 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7984 * If the count is 0, we have a special case, the head of the chain was
7989 DirEntry chainEntry
;
7991 StorageImpl_ReadDirEntry(This
->parentStorage
,
7992 This
->ownerDirEntry
,
7995 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7997 StorageImpl_WriteDirEntry(This
->parentStorage
,
7998 This
->ownerDirEntry
,
8002 * We start freeing the chain at the head block.
8004 extraBlock
= blockIndex
;
8008 /* Get the next block before marking the new end */
8009 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
8013 /* Mark the new end of chain */
8014 SmallBlockChainStream_SetNextBlockInChain(
8017 BLOCK_END_OF_CHAIN
);
8021 * Mark the extra blocks as free
8023 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
8025 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
8028 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
8029 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
8030 extraBlock
= blockIndex
;
8036 /******************************************************************************
8037 * SmallBlockChainStream_Enlarge
8039 * Grows this chain in the small block depot.
8041 static BOOL
SmallBlockChainStream_Enlarge(
8042 SmallBlockChainStream
* This
,
8043 ULARGE_INTEGER newSize
)
8045 ULONG blockIndex
, currentBlock
;
8047 ULONG oldNumBlocks
= 0;
8049 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8052 * Empty chain. Create the head.
8054 if (blockIndex
== BLOCK_END_OF_CHAIN
)
8056 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8057 SmallBlockChainStream_SetNextBlockInChain(
8060 BLOCK_END_OF_CHAIN
);
8062 if (This
->headOfStreamPlaceHolder
!= NULL
)
8064 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
8068 DirEntry chainEntry
;
8070 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8073 chainEntry
.startingBlock
= blockIndex
;
8075 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8080 currentBlock
= blockIndex
;
8083 * Figure out how many blocks are needed to contain this stream
8085 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8087 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8091 * Go to the current end of chain
8093 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
8096 currentBlock
= blockIndex
;
8097 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
8102 * Add new blocks to the chain
8104 while (oldNumBlocks
< newNumBlocks
)
8106 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8107 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
8109 SmallBlockChainStream_SetNextBlockInChain(
8112 BLOCK_END_OF_CHAIN
);
8114 currentBlock
= blockIndex
;
8121 /******************************************************************************
8122 * SmallBlockChainStream_SetSize
8124 * Sets the size of this stream.
8125 * The file will grow if we grow the chain.
8127 * TODO: Free the actual blocks in the file when we shrink the chain.
8128 * Currently, the blocks are still in the file. So the file size
8129 * doesn't shrink even if we shrink streams.
8131 BOOL
SmallBlockChainStream_SetSize(
8132 SmallBlockChainStream
* This
,
8133 ULARGE_INTEGER newSize
)
8135 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
8137 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
8140 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
8142 SmallBlockChainStream_Shrink(This
, newSize
);
8146 SmallBlockChainStream_Enlarge(This
, newSize
);
8152 /******************************************************************************
8153 * SmallBlockChainStream_GetCount
8155 * Returns the number of small blocks that comprises this chain.
8156 * This is not the size of the stream as the last block may not be full!
8159 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
8164 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8166 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
8170 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
8171 blockIndex
, &blockIndex
)))
8178 /******************************************************************************
8179 * SmallBlockChainStream_GetSize
8181 * Returns the size of this chain.
8183 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
8185 DirEntry chainEntry
;
8187 if(This
->headOfStreamPlaceHolder
!= NULL
)
8189 ULARGE_INTEGER result
;
8190 result
.u
.HighPart
= 0;
8192 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
8193 This
->parentStorage
->smallBlockSize
;
8198 StorageImpl_ReadDirEntry(
8199 This
->parentStorage
,
8200 This
->ownerDirEntry
,
8203 return chainEntry
.size
;
8206 static HRESULT
create_storagefile(
8210 STGOPTIONS
* pStgOptions
,
8214 StorageBaseImpl
* newStorage
= 0;
8215 HANDLE hFile
= INVALID_HANDLE_VALUE
;
8216 HRESULT hr
= STG_E_INVALIDFLAG
;
8220 DWORD fileAttributes
;
8221 WCHAR tempFileName
[MAX_PATH
];
8224 return STG_E_INVALIDPOINTER
;
8226 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
8227 return STG_E_INVALIDPARAMETER
;
8229 /* if no share mode given then DENY_NONE is the default */
8230 if (STGM_SHARE_MODE(grfMode
) == 0)
8231 grfMode
|= STGM_SHARE_DENY_NONE
;
8233 if ( FAILED( validateSTGM(grfMode
) ))
8236 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8237 switch(STGM_ACCESS_MODE(grfMode
))
8240 case STGM_READWRITE
:
8246 /* in direct mode, can only use SHARE_EXCLUSIVE */
8247 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
8250 /* but in transacted mode, any share mode is valid */
8253 * Generate a unique name.
8257 WCHAR tempPath
[MAX_PATH
];
8258 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
8260 memset(tempPath
, 0, sizeof(tempPath
));
8261 memset(tempFileName
, 0, sizeof(tempFileName
));
8263 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
8266 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
8267 pwcsName
= tempFileName
;
8270 hr
= STG_E_INSUFFICIENTMEMORY
;
8274 creationMode
= TRUNCATE_EXISTING
;
8278 creationMode
= GetCreationModeFromSTGM(grfMode
);
8282 * Interpret the STGM value grfMode
8284 shareMode
= GetShareModeFromSTGM(grfMode
);
8285 accessMode
= GetAccessModeFromSTGM(grfMode
);
8287 if (grfMode
& STGM_DELETEONRELEASE
)
8288 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
8290 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
8294 hFile
= CreateFileW(pwcsName
,
8302 if (hFile
== INVALID_HANDLE_VALUE
)
8304 if(GetLastError() == ERROR_FILE_EXISTS
)
8305 hr
= STG_E_FILEALREADYEXISTS
;
8312 * Allocate and initialize the new IStorage32object.
8314 hr
= Storage_Construct(
8321 pStgOptions
->ulSectorSize
,
8329 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
8330 IStorage_Release(&newStorage
->IStorage_iface
);
8333 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
8338 /******************************************************************************
8339 * StgCreateDocfile [OLE32.@]
8340 * Creates a new compound file storage object
8343 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8344 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8345 * reserved [ ?] unused?, usually 0
8346 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8349 * S_OK if the file was successfully created
8350 * some STG_E_ value if error
8352 * if pwcsName is NULL, create file with new unique name
8353 * the function can returns
8354 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8357 HRESULT WINAPI
StgCreateDocfile(
8361 IStorage
**ppstgOpen
)
8363 STGOPTIONS stgoptions
= {1, 0, 512};
8365 TRACE("(%s, %x, %d, %p)\n",
8366 debugstr_w(pwcsName
), grfMode
,
8367 reserved
, ppstgOpen
);
8370 return STG_E_INVALIDPOINTER
;
8372 return STG_E_INVALIDPARAMETER
;
8374 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
8377 /******************************************************************************
8378 * StgCreateStorageEx [OLE32.@]
8380 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8382 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8383 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8385 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
8387 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8388 return STG_E_INVALIDPARAMETER
;
8391 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8393 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8394 return STG_E_INVALIDPARAMETER
;
8397 if (stgfmt
== STGFMT_FILE
)
8399 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8400 return STG_E_INVALIDPARAMETER
;
8403 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
8405 STGOPTIONS defaultOptions
= {1, 0, 512};
8407 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
8408 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
8412 ERR("Invalid stgfmt argument\n");
8413 return STG_E_INVALIDPARAMETER
;
8416 /******************************************************************************
8417 * StgCreatePropSetStg [OLE32.@]
8419 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
8420 IPropertySetStorage
**propset
)
8422 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
8424 return STG_E_INVALIDPARAMETER
;
8426 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
8429 /******************************************************************************
8430 * StgOpenStorageEx [OLE32.@]
8432 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8434 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8435 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8437 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
8439 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8440 return STG_E_INVALIDPARAMETER
;
8446 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8447 return STG_E_INVALIDPARAMETER
;
8449 case STGFMT_STORAGE
:
8452 case STGFMT_DOCFILE
:
8453 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8455 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8456 return STG_E_INVALIDPARAMETER
;
8458 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8462 WARN("STGFMT_ANY assuming storage\n");
8466 return STG_E_INVALIDPARAMETER
;
8469 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
8473 /******************************************************************************
8474 * StgOpenStorage [OLE32.@]
8476 HRESULT WINAPI
StgOpenStorage(
8477 const OLECHAR
*pwcsName
,
8478 IStorage
*pstgPriority
,
8482 IStorage
**ppstgOpen
)
8484 StorageBaseImpl
* newStorage
= 0;
8489 LPWSTR temp_name
= NULL
;
8491 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8492 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
8493 snbExclude
, reserved
, ppstgOpen
);
8497 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8498 hr
= StorageBaseImpl_GetFilename((StorageBaseImpl
*)pstgPriority
, &temp_name
);
8499 if (FAILED(hr
)) goto end
;
8500 pwcsName
= temp_name
;
8501 TRACE("using filename %s\n", debugstr_w(temp_name
));
8506 hr
= STG_E_INVALIDNAME
;
8512 hr
= STG_E_INVALIDPOINTER
;
8518 hr
= STG_E_INVALIDPARAMETER
;
8522 if (grfMode
& STGM_PRIORITY
)
8524 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
8525 return STG_E_INVALIDFLAG
;
8526 if (grfMode
& STGM_DELETEONRELEASE
)
8527 return STG_E_INVALIDFUNCTION
;
8528 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
8529 return STG_E_INVALIDFLAG
;
8530 grfMode
&= ~0xf0; /* remove the existing sharing mode */
8531 grfMode
|= STGM_SHARE_DENY_NONE
;
8535 * Validate the sharing mode
8537 if (grfMode
& STGM_DIRECT_SWMR
)
8539 if ((STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_WRITE
) &&
8540 (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_NONE
))
8542 hr
= STG_E_INVALIDFLAG
;
8546 else if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
8547 switch(STGM_SHARE_MODE(grfMode
))
8549 case STGM_SHARE_EXCLUSIVE
:
8550 case STGM_SHARE_DENY_WRITE
:
8553 hr
= STG_E_INVALIDFLAG
;
8557 if ( FAILED( validateSTGM(grfMode
) ) ||
8558 (grfMode
&STGM_CREATE
))
8560 hr
= STG_E_INVALIDFLAG
;
8564 /* shared reading requires transacted or single writer mode */
8565 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
8566 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
8567 !(grfMode
& STGM_TRANSACTED
) && !(grfMode
& STGM_DIRECT_SWMR
))
8569 hr
= STG_E_INVALIDFLAG
;
8574 * Interpret the STGM value grfMode
8576 shareMode
= GetShareModeFromSTGM(grfMode
);
8577 accessMode
= GetAccessModeFromSTGM(grfMode
);
8581 hFile
= CreateFileW( pwcsName
,
8586 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
8589 if (hFile
==INVALID_HANDLE_VALUE
)
8591 DWORD last_error
= GetLastError();
8597 case ERROR_FILE_NOT_FOUND
:
8598 hr
= STG_E_FILENOTFOUND
;
8601 case ERROR_PATH_NOT_FOUND
:
8602 hr
= STG_E_PATHNOTFOUND
;
8605 case ERROR_ACCESS_DENIED
:
8606 case ERROR_WRITE_PROTECT
:
8607 hr
= STG_E_ACCESSDENIED
;
8610 case ERROR_SHARING_VIOLATION
:
8611 hr
= STG_E_SHAREVIOLATION
;
8622 * Refuse to open the file if it's too small to be a structured storage file
8623 * FIXME: verify the file when reading instead of here
8625 if (GetFileSize(hFile
, NULL
) < 0x100)
8628 hr
= STG_E_FILEALREADYEXISTS
;
8633 * Allocate and initialize the new IStorage32object.
8635 hr
= Storage_Construct(
8648 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8650 if(hr
== STG_E_INVALIDHEADER
)
8651 hr
= STG_E_FILEALREADYEXISTS
;
8655 *ppstgOpen
= &newStorage
->IStorage_iface
;
8658 CoTaskMemFree(temp_name
);
8659 if (pstgPriority
) IStorage_Release(pstgPriority
);
8660 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
8664 /******************************************************************************
8665 * StgCreateDocfileOnILockBytes [OLE32.@]
8667 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
8671 IStorage
** ppstgOpen
)
8673 StorageBaseImpl
* newStorage
= 0;
8676 if ((ppstgOpen
== 0) || (plkbyt
== 0))
8677 return STG_E_INVALIDPOINTER
;
8680 * Allocate and initialize the new IStorage object.
8682 hr
= Storage_Construct(
8697 *ppstgOpen
= &newStorage
->IStorage_iface
;
8702 /******************************************************************************
8703 * StgOpenStorageOnILockBytes [OLE32.@]
8705 HRESULT WINAPI
StgOpenStorageOnILockBytes(
8707 IStorage
*pstgPriority
,
8711 IStorage
**ppstgOpen
)
8713 StorageBaseImpl
* newStorage
= 0;
8716 if ((plkbyt
== 0) || (ppstgOpen
== 0))
8717 return STG_E_INVALIDPOINTER
;
8719 if ( FAILED( validateSTGM(grfMode
) ))
8720 return STG_E_INVALIDFLAG
;
8725 * Allocate and initialize the new IStorage object.
8727 hr
= Storage_Construct(
8742 *ppstgOpen
= &newStorage
->IStorage_iface
;
8747 /******************************************************************************
8748 * StgSetTimes [ole32.@]
8749 * StgSetTimes [OLE32.@]
8753 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
8754 FILETIME
const *patime
, FILETIME
const *pmtime
)
8756 IStorage
*stg
= NULL
;
8759 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
8761 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
8765 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
8766 IStorage_Release(stg
);
8772 /******************************************************************************
8773 * StgIsStorageILockBytes [OLE32.@]
8775 * Determines if the ILockBytes contains a storage object.
8777 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
8779 BYTE sig
[sizeof(STORAGE_magic
)];
8780 ULARGE_INTEGER offset
;
8783 offset
.u
.HighPart
= 0;
8784 offset
.u
.LowPart
= 0;
8786 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
8788 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
8794 /******************************************************************************
8795 * WriteClassStg [OLE32.@]
8797 * This method will store the specified CLSID in the specified storage object
8799 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
8802 return E_INVALIDARG
;
8805 return STG_E_INVALIDPOINTER
;
8807 return IStorage_SetClass(pStg
, rclsid
);
8810 /***********************************************************************
8811 * ReadClassStg (OLE32.@)
8813 * This method reads the CLSID previously written to a storage object with
8814 * the WriteClassStg.
8817 * pstg [I] IStorage pointer
8818 * pclsid [O] Pointer to where the CLSID is written
8822 * Failure: HRESULT code.
8824 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
8829 TRACE("(%p, %p)\n", pstg
, pclsid
);
8831 if(!pstg
|| !pclsid
)
8832 return E_INVALIDARG
;
8835 * read a STATSTG structure (contains the clsid) from the storage
8837 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
8840 *pclsid
=pstatstg
.clsid
;
8845 /***********************************************************************
8846 * OleLoadFromStream (OLE32.@)
8848 * This function loads an object from stream
8850 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
8854 LPPERSISTSTREAM xstm
;
8856 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
8858 res
=ReadClassStm(pStm
,&clsid
);
8861 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
8864 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
8866 IUnknown_Release((IUnknown
*)*ppvObj
);
8869 res
=IPersistStream_Load(xstm
,pStm
);
8870 IPersistStream_Release(xstm
);
8871 /* FIXME: all refcounts ok at this point? I think they should be:
8874 * xstm : 0 (released)
8879 /***********************************************************************
8880 * OleSaveToStream (OLE32.@)
8882 * This function saves an object with the IPersistStream interface on it
8883 * to the specified stream.
8885 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
8891 TRACE("(%p,%p)\n",pPStm
,pStm
);
8893 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
8895 if (SUCCEEDED(res
)){
8897 res
=WriteClassStm(pStm
,&clsid
);
8901 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
8904 TRACE("Finished Save\n");
8908 /****************************************************************************
8909 * This method validate a STGM parameter that can contain the values below
8911 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8912 * The stgm values contained in 0xffff0000 are bitmasks.
8914 * STGM_DIRECT 0x00000000
8915 * STGM_TRANSACTED 0x00010000
8916 * STGM_SIMPLE 0x08000000
8918 * STGM_READ 0x00000000
8919 * STGM_WRITE 0x00000001
8920 * STGM_READWRITE 0x00000002
8922 * STGM_SHARE_DENY_NONE 0x00000040
8923 * STGM_SHARE_DENY_READ 0x00000030
8924 * STGM_SHARE_DENY_WRITE 0x00000020
8925 * STGM_SHARE_EXCLUSIVE 0x00000010
8927 * STGM_PRIORITY 0x00040000
8928 * STGM_DELETEONRELEASE 0x04000000
8930 * STGM_CREATE 0x00001000
8931 * STGM_CONVERT 0x00020000
8932 * STGM_FAILIFTHERE 0x00000000
8934 * STGM_NOSCRATCH 0x00100000
8935 * STGM_NOSNAPSHOT 0x00200000
8937 static HRESULT
validateSTGM(DWORD stgm
)
8939 DWORD access
= STGM_ACCESS_MODE(stgm
);
8940 DWORD share
= STGM_SHARE_MODE(stgm
);
8941 DWORD create
= STGM_CREATE_MODE(stgm
);
8943 if (stgm
&~STGM_KNOWN_FLAGS
)
8945 ERR("unknown flags %08x\n", stgm
);
8953 case STGM_READWRITE
:
8961 case STGM_SHARE_DENY_NONE
:
8962 case STGM_SHARE_DENY_READ
:
8963 case STGM_SHARE_DENY_WRITE
:
8964 case STGM_SHARE_EXCLUSIVE
:
8967 if (!(stgm
& STGM_TRANSACTED
))
8977 case STGM_FAILIFTHERE
:
8984 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8986 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
8990 * STGM_CREATE | STGM_CONVERT
8991 * if both are false, STGM_FAILIFTHERE is set to TRUE
8993 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
8997 * STGM_NOSCRATCH requires STGM_TRANSACTED
8999 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
9003 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
9004 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
9006 if ( (stgm
& STGM_NOSNAPSHOT
) &&
9007 (!(stgm
& STGM_TRANSACTED
) ||
9008 share
== STGM_SHARE_EXCLUSIVE
||
9009 share
== STGM_SHARE_DENY_WRITE
) )
9015 /****************************************************************************
9016 * GetShareModeFromSTGM
9018 * This method will return a share mode flag from a STGM value.
9019 * The STGM value is assumed valid.
9021 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
9023 switch (STGM_SHARE_MODE(stgm
))
9026 assert(stgm
& STGM_TRANSACTED
);
9028 case STGM_SHARE_DENY_NONE
:
9029 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
9030 case STGM_SHARE_DENY_READ
:
9031 return FILE_SHARE_WRITE
;
9032 case STGM_SHARE_DENY_WRITE
:
9033 case STGM_SHARE_EXCLUSIVE
:
9034 return FILE_SHARE_READ
;
9036 ERR("Invalid share mode!\n");
9041 /****************************************************************************
9042 * GetAccessModeFromSTGM
9044 * This method will return an access mode flag from a STGM value.
9045 * The STGM value is assumed valid.
9047 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
9049 switch (STGM_ACCESS_MODE(stgm
))
9052 return GENERIC_READ
;
9054 case STGM_READWRITE
:
9055 return GENERIC_READ
| GENERIC_WRITE
;
9057 ERR("Invalid access mode!\n");
9062 /****************************************************************************
9063 * GetCreationModeFromSTGM
9065 * This method will return a creation mode flag from a STGM value.
9066 * The STGM value is assumed valid.
9068 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
9070 switch(STGM_CREATE_MODE(stgm
))
9073 return CREATE_ALWAYS
;
9075 FIXME("STGM_CONVERT not implemented!\n");
9077 case STGM_FAILIFTHERE
:
9080 ERR("Invalid create mode!\n");
9086 /*************************************************************************
9087 * OLECONVERT_LoadOLE10 [Internal]
9089 * Loads the OLE10 STREAM to memory
9092 * pOleStream [I] The OLESTREAM
9093 * pData [I] Data Structure for the OLESTREAM Data
9097 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9098 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9101 * This function is used by OleConvertOLESTREAMToIStorage only.
9103 * Memory allocated for pData must be freed by the caller
9105 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
9108 HRESULT hRes
= S_OK
;
9112 pData
->pData
= NULL
;
9113 pData
->pstrOleObjFileName
= NULL
;
9115 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
9118 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9119 if(dwSize
!= sizeof(pData
->dwOleID
))
9121 hRes
= CONVERT10_E_OLESTREAM_GET
;
9123 else if(pData
->dwOleID
!= OLESTREAM_ID
)
9125 hRes
= CONVERT10_E_OLESTREAM_FMT
;
9136 /* Get the TypeID... more info needed for this field */
9137 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9138 if(dwSize
!= sizeof(pData
->dwTypeID
))
9140 hRes
= CONVERT10_E_OLESTREAM_GET
;
9145 if(pData
->dwTypeID
!= 0)
9147 /* Get the length of the OleTypeName */
9148 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9149 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9151 hRes
= CONVERT10_E_OLESTREAM_GET
;
9156 if(pData
->dwOleTypeNameLength
> 0)
9158 /* Get the OleTypeName */
9159 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9160 if(dwSize
!= pData
->dwOleTypeNameLength
)
9162 hRes
= CONVERT10_E_OLESTREAM_GET
;
9168 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
9169 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
9171 hRes
= CONVERT10_E_OLESTREAM_GET
;
9175 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
9176 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
9177 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
9178 if(pData
->pstrOleObjFileName
)
9180 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
9181 if(dwSize
!= pData
->dwOleObjFileNameLength
)
9183 hRes
= CONVERT10_E_OLESTREAM_GET
;
9187 hRes
= CONVERT10_E_OLESTREAM_GET
;
9192 /* Get the Width of the Metafile */
9193 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9194 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9196 hRes
= CONVERT10_E_OLESTREAM_GET
;
9200 /* Get the Height of the Metafile */
9201 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9202 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9204 hRes
= CONVERT10_E_OLESTREAM_GET
;
9210 /* Get the Length of the Data */
9211 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9212 if(dwSize
!= sizeof(pData
->dwDataLength
))
9214 hRes
= CONVERT10_E_OLESTREAM_GET
;
9218 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
9220 if(!bStrem1
) /* if it is a second OLE stream data */
9222 pData
->dwDataLength
-= 8;
9223 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
9224 if(dwSize
!= sizeof(pData
->strUnknown
))
9226 hRes
= CONVERT10_E_OLESTREAM_GET
;
9232 if(pData
->dwDataLength
> 0)
9234 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
9236 /* Get Data (ex. IStorage, Metafile, or BMP) */
9239 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
9240 if(dwSize
!= pData
->dwDataLength
)
9242 hRes
= CONVERT10_E_OLESTREAM_GET
;
9247 hRes
= CONVERT10_E_OLESTREAM_GET
;
9256 /*************************************************************************
9257 * OLECONVERT_SaveOLE10 [Internal]
9259 * Saves the OLE10 STREAM From memory
9262 * pData [I] Data Structure for the OLESTREAM Data
9263 * pOleStream [I] The OLESTREAM to save
9267 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9270 * This function is used by OleConvertIStorageToOLESTREAM only.
9273 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
9276 HRESULT hRes
= S_OK
;
9280 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9281 if(dwSize
!= sizeof(pData
->dwOleID
))
9283 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9288 /* Set the TypeID */
9289 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9290 if(dwSize
!= sizeof(pData
->dwTypeID
))
9292 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9296 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
9298 /* Set the Length of the OleTypeName */
9299 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9300 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9302 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9307 if(pData
->dwOleTypeNameLength
> 0)
9309 /* Set the OleTypeName */
9310 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9311 if(dwSize
!= pData
->dwOleTypeNameLength
)
9313 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9320 /* Set the width of the Metafile */
9321 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9322 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9324 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9330 /* Set the height of the Metafile */
9331 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9332 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9334 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9340 /* Set the length of the Data */
9341 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9342 if(dwSize
!= sizeof(pData
->dwDataLength
))
9344 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9350 if(pData
->dwDataLength
> 0)
9352 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9353 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
9354 if(dwSize
!= pData
->dwDataLength
)
9356 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9364 /*************************************************************************
9365 * OLECONVERT_GetOLE20FromOLE10[Internal]
9367 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9368 * opens it, and copies the content to the dest IStorage for
9369 * OleConvertOLESTREAMToIStorage
9373 * pDestStorage [I] The IStorage to copy the data to
9374 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9375 * nBufferLength [I] The size of the buffer
9384 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
9388 IStorage
*pTempStorage
;
9389 DWORD dwNumOfBytesWritten
;
9390 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9391 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9393 /* Create a temp File */
9394 GetTempPathW(MAX_PATH
, wstrTempDir
);
9395 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9396 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
9398 if(hFile
!= INVALID_HANDLE_VALUE
)
9400 /* Write IStorage Data to File */
9401 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
9404 /* Open and copy temp storage to the Dest Storage */
9405 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
9408 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
9409 IStorage_Release(pTempStorage
);
9411 DeleteFileW(wstrTempFile
);
9416 /*************************************************************************
9417 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9419 * Saves the OLE10 STREAM From memory
9422 * pStorage [I] The Src IStorage to copy
9423 * pData [I] The Dest Memory to write to.
9426 * The size in bytes allocated for pData
9429 * Memory allocated for pData must be freed by the caller
9431 * Used by OleConvertIStorageToOLESTREAM only.
9434 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
9438 DWORD nDataLength
= 0;
9439 IStorage
*pTempStorage
;
9440 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9441 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9445 /* Create temp Storage */
9446 GetTempPathW(MAX_PATH
, wstrTempDir
);
9447 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9448 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
9452 /* Copy Src Storage to the Temp Storage */
9453 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
9454 IStorage_Release(pTempStorage
);
9456 /* Open Temp Storage as a file and copy to memory */
9457 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9458 if(hFile
!= INVALID_HANDLE_VALUE
)
9460 nDataLength
= GetFileSize(hFile
, NULL
);
9461 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
9462 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
9465 DeleteFileW(wstrTempFile
);
9470 /*************************************************************************
9471 * STORAGE_CreateOleStream [Internal]
9473 * Creates the "\001OLE" stream in the IStorage if necessary.
9476 * storage [I] Dest storage to create the stream in
9477 * flags [I] flags to be set for newly created stream
9480 * HRESULT return value
9484 * This stream is still unknown, MS Word seems to have extra data
9485 * but since the data is stored in the OLESTREAM there should be
9486 * no need to recreate the stream. If the stream is manually
9487 * deleted it will create it with this default data.
9490 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
9492 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9493 static const DWORD version_magic
= 0x02000001;
9497 hr
= IStorage_CreateStream(storage
, stream_1oleW
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
9500 struct empty_1ole_stream
{
9501 DWORD version_magic
;
9503 DWORD update_options
;
9505 DWORD mon_stream_size
;
9507 struct empty_1ole_stream stream_data
;
9509 stream_data
.version_magic
= version_magic
;
9510 stream_data
.flags
= flags
;
9511 stream_data
.update_options
= 0;
9512 stream_data
.reserved
= 0;
9513 stream_data
.mon_stream_size
= 0;
9515 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
9516 IStream_Release(stream
);
9522 /* write a string to a stream, preceded by its length */
9523 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
9530 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
9531 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
9536 str
= CoTaskMemAlloc( len
);
9537 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
9538 r
= IStream_Write( stm
, str
, len
, NULL
);
9539 CoTaskMemFree( str
);
9543 /* read a string preceded by its length from a stream */
9544 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
9547 DWORD len
, count
= 0;
9551 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
9554 if( count
!= sizeof(len
) )
9555 return E_OUTOFMEMORY
;
9557 TRACE("%d bytes\n",len
);
9559 str
= CoTaskMemAlloc( len
);
9561 return E_OUTOFMEMORY
;
9563 r
= IStream_Read( stm
, str
, len
, &count
);
9568 CoTaskMemFree( str
);
9569 return E_OUTOFMEMORY
;
9572 TRACE("Read string %s\n",debugstr_an(str
,len
));
9574 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
9575 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
9578 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
9581 CoTaskMemFree( str
);
9589 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
9590 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
9594 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9596 static const BYTE unknown1
[12] =
9597 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9598 0xFF, 0xFF, 0xFF, 0xFF};
9599 static const BYTE unknown2
[16] =
9600 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9601 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9603 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
9604 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
9605 debugstr_w(szProgIDName
));
9607 /* Create a CompObj stream */
9608 r
= IStorage_CreateStream(pstg
, szwStreamName
,
9609 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
9613 /* Write CompObj Structure to stream */
9614 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
9616 if( SUCCEEDED( r
) )
9617 r
= WriteClassStm( pstm
, clsid
);
9619 if( SUCCEEDED( r
) )
9620 r
= STREAM_WriteString( pstm
, lpszUserType
);
9621 if( SUCCEEDED( r
) )
9622 r
= STREAM_WriteString( pstm
, szClipName
);
9623 if( SUCCEEDED( r
) )
9624 r
= STREAM_WriteString( pstm
, szProgIDName
);
9625 if( SUCCEEDED( r
) )
9626 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
9628 IStream_Release( pstm
);
9633 /***********************************************************************
9634 * WriteFmtUserTypeStg (OLE32.@)
9636 HRESULT WINAPI
WriteFmtUserTypeStg(
9637 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
9641 WCHAR szwClipName
[0x40];
9643 LPWSTR wstrProgID
= NULL
;
9646 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
9648 /* get the clipboard format name */
9651 n
= GetClipboardFormatNameW( cf
, szwClipName
,
9652 sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
9656 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
9658 r
= IStorage_Stat(pstg
, &stat
, STATFLAG_NONAME
);
9664 ProgIDFromCLSID(&clsid
, &wstrProgID
);
9666 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
9668 r
= STORAGE_WriteCompObj( pstg
, &clsid
, lpszUserType
,
9669 cf
? szwClipName
: NULL
, wstrProgID
);
9671 CoTaskMemFree(wstrProgID
);
9677 /******************************************************************************
9678 * ReadFmtUserTypeStg [OLE32.@]
9680 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
9684 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
9685 unsigned char unknown1
[12];
9686 unsigned char unknown2
[16];
9688 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
9691 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
9693 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
9694 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
9697 WARN("Failed to open stream r = %08x\n", r
);
9701 /* read the various parts of the structure */
9702 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
9703 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
9705 r
= ReadClassStm( stm
, &clsid
);
9709 r
= STREAM_ReadString( stm
, &szCLSIDName
);
9713 r
= STREAM_ReadString( stm
, &szOleTypeName
);
9717 r
= STREAM_ReadString( stm
, &szProgIDName
);
9721 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
9722 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
9725 /* ok, success... now we just need to store what we found */
9727 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
9729 if( lplpszUserType
)
9731 *lplpszUserType
= szCLSIDName
;
9736 CoTaskMemFree( szCLSIDName
);
9737 CoTaskMemFree( szOleTypeName
);
9738 CoTaskMemFree( szProgIDName
);
9739 IStream_Release( stm
);
9745 /*************************************************************************
9746 * OLECONVERT_CreateCompObjStream [Internal]
9748 * Creates a "\001CompObj" is the destination IStorage if necessary.
9751 * pStorage [I] The dest IStorage to create the CompObj Stream
9753 * strOleTypeName [I] The ProgID
9757 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9760 * This function is used by OleConvertOLESTREAMToIStorage only.
9762 * The stream data is stored in the OLESTREAM and there should be
9763 * no need to recreate the stream. If the stream is manually
9764 * deleted it will attempt to create it by querying the registry.
9768 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
9771 HRESULT hStorageRes
, hRes
= S_OK
;
9772 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
9773 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9774 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
9776 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9777 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
9779 /* Initialize the CompObj structure */
9780 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
9781 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
9782 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
9785 /* Create a CompObj stream if it doesn't exist */
9786 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9787 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9788 if(hStorageRes
== S_OK
)
9790 /* copy the OleTypeName to the compobj struct */
9791 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
9792 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
9794 /* copy the OleTypeName to the compobj struct */
9795 /* Note: in the test made, these were Identical */
9796 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
9797 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
9800 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
9801 bufferW
, OLESTREAM_MAX_STR_LEN
);
9802 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
9808 /* Get the CLSID Default Name from the Registry */
9809 hErr
= open_classes_key(HKEY_CLASSES_ROOT
, bufferW
, MAXIMUM_ALLOWED
, &hKey
);
9810 if(hErr
== ERROR_SUCCESS
)
9812 char strTemp
[OLESTREAM_MAX_STR_LEN
];
9813 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
9814 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
9815 if(hErr
== ERROR_SUCCESS
)
9817 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
9823 /* Write CompObj Structure to stream */
9824 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
9826 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
9828 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
9829 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
9831 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
9833 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
9834 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
9836 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
9838 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
9839 if(IStorageCompObj
.dwProgIDNameLength
> 0)
9841 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
9843 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
9844 IStream_Release(pStream
);
9850 /*************************************************************************
9851 * OLECONVERT_CreateOlePresStream[Internal]
9853 * Creates the "\002OlePres000" Stream with the Metafile data
9856 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
9857 * dwExtentX [I] Width of the Metafile
9858 * dwExtentY [I] Height of the Metafile
9859 * pData [I] Metafile data
9860 * dwDataLength [I] Size of the Metafile data
9864 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9867 * This function is used by OleConvertOLESTREAMToIStorage only.
9870 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
9874 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9875 BYTE pOlePresStreamHeader
[] =
9877 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
9878 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9879 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9880 0x00, 0x00, 0x00, 0x00
9883 BYTE pOlePresStreamHeaderEmpty
[] =
9885 0x00, 0x00, 0x00, 0x00,
9886 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9887 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9888 0x00, 0x00, 0x00, 0x00
9891 /* Create the OlePres000 Stream */
9892 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9893 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9898 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
9900 memset(&OlePres
, 0, sizeof(OlePres
));
9901 /* Do we have any metafile data to save */
9902 if(dwDataLength
> 0)
9904 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
9905 nHeaderSize
= sizeof(pOlePresStreamHeader
);
9909 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
9910 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
9912 /* Set width and height of the metafile */
9913 OlePres
.dwExtentX
= dwExtentX
;
9914 OlePres
.dwExtentY
= -dwExtentY
;
9916 /* Set Data and Length */
9917 if(dwDataLength
> sizeof(METAFILEPICT16
))
9919 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
9920 OlePres
.pData
= &(pData
[8]);
9922 /* Save OlePres000 Data to Stream */
9923 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
9924 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
9925 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
9926 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
9927 if(OlePres
.dwSize
> 0)
9929 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
9931 IStream_Release(pStream
);
9935 /*************************************************************************
9936 * OLECONVERT_CreateOle10NativeStream [Internal]
9938 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9941 * pStorage [I] Dest storage to create the stream in
9942 * pData [I] Ole10 Native Data (ex. bmp)
9943 * dwDataLength [I] Size of the Ole10 Native Data
9949 * This function is used by OleConvertOLESTREAMToIStorage only.
9951 * Might need to verify the data and return appropriate error message
9954 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
9958 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9960 /* Create the Ole10Native Stream */
9961 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9962 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9966 /* Write info to stream */
9967 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
9968 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
9969 IStream_Release(pStream
);
9974 /*************************************************************************
9975 * OLECONVERT_GetOLE10ProgID [Internal]
9977 * Finds the ProgID (or OleTypeID) from the IStorage
9980 * pStorage [I] The Src IStorage to get the ProgID
9981 * strProgID [I] the ProgID string to get
9982 * dwSize [I] the size of the string
9986 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9989 * This function is used by OleConvertIStorageToOLESTREAM only.
9993 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
9997 LARGE_INTEGER iSeekPos
;
9998 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
9999 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
10001 /* Open the CompObj Stream */
10002 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10003 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10007 /*Get the OleType from the CompObj Stream */
10008 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
10009 iSeekPos
.u
.HighPart
= 0;
10011 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10012 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
10013 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
10014 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10015 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
10016 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
10017 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
10019 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
10022 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
10024 IStream_Release(pStream
);
10029 LPOLESTR wstrProgID
;
10031 /* Get the OleType from the registry */
10032 REFCLSID clsid
= &(stat
.clsid
);
10033 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
10034 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
10037 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
10038 CoTaskMemFree(wstrProgID
);
10045 /*************************************************************************
10046 * OLECONVERT_GetOle10PresData [Internal]
10048 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10051 * pStorage [I] Src IStroage
10052 * pOleStream [I] Dest OleStream Mem Struct
10058 * This function is used by OleConvertIStorageToOLESTREAM only.
10060 * Memory allocated for pData must be freed by the caller
10064 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10069 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10071 /* Initialize Default data for OLESTREAM */
10072 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10073 pOleStreamData
[0].dwTypeID
= 2;
10074 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10075 pOleStreamData
[1].dwTypeID
= 0;
10076 pOleStreamData
[0].dwMetaFileWidth
= 0;
10077 pOleStreamData
[0].dwMetaFileHeight
= 0;
10078 pOleStreamData
[0].pData
= NULL
;
10079 pOleStreamData
[1].pData
= NULL
;
10081 /* Open Ole10Native Stream */
10082 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10083 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10087 /* Read Size and Data */
10088 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
10089 if(pOleStreamData
->dwDataLength
> 0)
10091 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
10092 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
10094 IStream_Release(pStream
);
10100 /*************************************************************************
10101 * OLECONVERT_GetOle20PresData[Internal]
10103 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10106 * pStorage [I] Src IStroage
10107 * pOleStreamData [I] Dest OleStream Mem Struct
10113 * This function is used by OleConvertIStorageToOLESTREAM only.
10115 * Memory allocated for pData must be freed by the caller
10117 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10121 OLECONVERT_ISTORAGE_OLEPRES olePress
;
10122 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10124 /* Initialize Default data for OLESTREAM */
10125 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10126 pOleStreamData
[0].dwTypeID
= 2;
10127 pOleStreamData
[0].dwMetaFileWidth
= 0;
10128 pOleStreamData
[0].dwMetaFileHeight
= 0;
10129 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
10130 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10131 pOleStreamData
[1].dwTypeID
= 0;
10132 pOleStreamData
[1].dwOleTypeNameLength
= 0;
10133 pOleStreamData
[1].strOleTypeName
[0] = 0;
10134 pOleStreamData
[1].dwMetaFileWidth
= 0;
10135 pOleStreamData
[1].dwMetaFileHeight
= 0;
10136 pOleStreamData
[1].pData
= NULL
;
10137 pOleStreamData
[1].dwDataLength
= 0;
10140 /* Open OlePress000 stream */
10141 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10142 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10145 LARGE_INTEGER iSeekPos
;
10146 METAFILEPICT16 MetaFilePict
;
10147 static const char strMetafilePictName
[] = "METAFILEPICT";
10149 /* Set the TypeID for a Metafile */
10150 pOleStreamData
[1].dwTypeID
= 5;
10152 /* Set the OleTypeName to Metafile */
10153 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
10154 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
10156 iSeekPos
.u
.HighPart
= 0;
10157 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
10159 /* Get Presentation Data */
10160 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10161 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
10162 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
10163 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
10165 /*Set width and Height */
10166 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
10167 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
10168 if(olePress
.dwSize
> 0)
10171 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
10173 /* Set MetaFilePict struct */
10174 MetaFilePict
.mm
= 8;
10175 MetaFilePict
.xExt
= olePress
.dwExtentX
;
10176 MetaFilePict
.yExt
= olePress
.dwExtentY
;
10177 MetaFilePict
.hMF
= 0;
10179 /* Get Metafile Data */
10180 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
10181 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
10182 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
10184 IStream_Release(pStream
);
10188 /*************************************************************************
10189 * OleConvertOLESTREAMToIStorage [OLE32.@]
10191 * Read info on MSDN
10194 * DVTARGETDEVICE parameter is not handled
10195 * Still unsure of some mem fields for OLE 10 Stream
10196 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10197 * and "\001OLE" streams
10200 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
10201 LPOLESTREAM pOleStream
,
10203 const DVTARGETDEVICE
* ptd
)
10207 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10209 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
10211 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10215 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10218 if(pstg
== NULL
|| pOleStream
== NULL
)
10220 hRes
= E_INVALIDARG
;
10225 /* Load the OLESTREAM to Memory */
10226 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
10231 /* Load the OLESTREAM to Memory (part 2)*/
10232 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
10238 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
10240 /* Do we have the IStorage Data in the OLESTREAM */
10241 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
10243 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10244 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
10248 /* It must be an original OLE 1.0 source */
10249 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10254 /* It must be an original OLE 1.0 source */
10255 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10258 /* Create CompObj Stream if necessary */
10259 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
10262 /*Create the Ole Stream if necessary */
10263 STORAGE_CreateOleStream(pstg
, 0);
10268 /* Free allocated memory */
10269 for(i
=0; i
< 2; i
++)
10271 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10272 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
10273 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
10278 /*************************************************************************
10279 * OleConvertIStorageToOLESTREAM [OLE32.@]
10281 * Read info on MSDN
10283 * Read info on MSDN
10286 * Still unsure of some mem fields for OLE 10 Stream
10287 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10288 * and "\001OLE" streams.
10291 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
10293 LPOLESTREAM pOleStream
)
10296 HRESULT hRes
= S_OK
;
10298 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10299 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10301 TRACE("%p %p\n", pstg
, pOleStream
);
10303 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10305 if(pstg
== NULL
|| pOleStream
== NULL
)
10307 hRes
= E_INVALIDARG
;
10311 /* Get the ProgID */
10312 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
10313 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
10317 /* Was it originally Ole10 */
10318 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10321 IStream_Release(pStream
);
10322 /* Get Presentation Data for Ole10Native */
10323 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
10327 /* Get Presentation Data (OLE20) */
10328 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
10331 /* Save OLESTREAM */
10332 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
10335 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
10340 /* Free allocated memory */
10341 for(i
=0; i
< 2; i
++)
10343 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10349 enum stream_1ole_flags
{
10350 OleStream_LinkedObject
= 0x00000001,
10351 OleStream_Convert
= 0x00000004
10354 /***********************************************************************
10355 * GetConvertStg (OLE32.@)
10357 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
10359 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10360 static const DWORD version_magic
= 0x02000001;
10365 TRACE("%p\n", stg
);
10367 if (!stg
) return E_INVALIDARG
;
10369 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10370 if (FAILED(hr
)) return hr
;
10372 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10373 IStream_Release(stream
);
10374 if (FAILED(hr
)) return hr
;
10376 if (header
[0] != version_magic
)
10378 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header
[0]);
10382 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
10385 /***********************************************************************
10386 * SetConvertStg (OLE32.@)
10388 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
10390 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10391 DWORD flags
= convert
? OleStream_Convert
: 0;
10396 TRACE("(%p, %d)\n", storage
, convert
);
10398 hr
= IStorage_OpenStream(storage
, stream_1oleW
, NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10401 if (hr
!= STG_E_FILENOTFOUND
)
10404 return STORAGE_CreateOleStream(storage
, flags
);
10407 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10410 IStream_Release(stream
);
10414 /* update flag if differs */
10415 if ((header
[1] ^ flags
) & OleStream_Convert
)
10417 LARGE_INTEGER pos
= {{0}};
10419 if (header
[1] & OleStream_Convert
)
10420 flags
= header
[1] & ~OleStream_Convert
;
10422 flags
= header
[1] | OleStream_Convert
;
10424 pos
.QuadPart
= sizeof(DWORD
);
10425 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
10428 IStream_Release(stream
);
10432 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
10435 IStream_Release(stream
);
10439 /******************************************************************************
10440 * StgIsStorageFile [OLE32.@]
10441 * Verify if the file contains a storage object
10447 * S_OK if file has magic bytes as a storage object
10448 * S_FALSE if file is not storage
10451 StgIsStorageFile(LPCOLESTR fn
)
10457 TRACE("%s\n", debugstr_w(fn
));
10458 hf
= CreateFileW(fn
, GENERIC_READ
,
10459 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
10460 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
10462 if (hf
== INVALID_HANDLE_VALUE
)
10463 return STG_E_FILENOTFOUND
;
10465 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
10467 WARN(" unable to read file\n");
10474 if (bytes_read
!= 8) {
10475 TRACE(" too short\n");
10479 if (!memcmp(magic
,STORAGE_magic
,8)) {
10480 TRACE(" -> YES\n");
10484 TRACE(" -> Invalid header.\n");
10488 /***********************************************************************
10489 * WriteClassStm (OLE32.@)
10491 * Writes a CLSID to a stream.
10494 * pStm [I] Stream to write to.
10495 * rclsid [I] CLSID to write.
10499 * Failure: HRESULT code.
10501 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
10503 TRACE("(%p,%p)\n",pStm
,rclsid
);
10505 if (!pStm
|| !rclsid
)
10506 return E_INVALIDARG
;
10508 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
10511 /***********************************************************************
10512 * ReadClassStm (OLE32.@)
10514 * Reads a CLSID from a stream.
10517 * pStm [I] Stream to read from.
10518 * rclsid [O] CLSID to read.
10522 * Failure: HRESULT code.
10524 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
10529 TRACE("(%p,%p)\n",pStm
,pclsid
);
10531 if (!pStm
|| !pclsid
)
10532 return E_INVALIDARG
;
10534 /* clear the output args */
10535 *pclsid
= CLSID_NULL
;
10537 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
10542 if (nbByte
!= sizeof(CLSID
))
10543 return STG_E_READFAULT
;