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
41 #define NONAMELESSSTRUCT
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
54 #include "wine/wingdi16.h"
55 #include "compobj_private.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
59 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
60 #define OLESTREAM_ID 0x501
61 #define OLESTREAM_MAX_STR_LEN 255
64 * These are signatures to detect the type of Document file.
66 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
67 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
69 static inline StorageBaseImpl
*impl_from_IStorage( IStorage
*iface
)
71 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IStorage_iface
);
74 static inline StorageBaseImpl
*impl_from_IDirectWriterLock( IDirectWriterLock
*iface
)
76 return CONTAINING_RECORD(iface
, StorageBaseImpl
, IDirectWriterLock_iface
);
79 /****************************************************************************
80 * Storage32InternalImpl definitions.
82 * Definition of the implementation structure for the IStorage32 interface.
83 * This one implements the IStorage32 interface for storage that are
84 * inside another storage.
86 struct StorageInternalImpl
88 struct StorageBaseImpl base
;
91 * Entry in the parent's stream tracking list
93 struct list ParentListEntry
;
95 StorageBaseImpl
*parentStorage
;
97 typedef struct StorageInternalImpl StorageInternalImpl
;
99 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
;
100 static const IStorageVtbl Storage32InternalImpl_Vtbl
;
102 /* Method definitions for the Storage32InternalImpl class. */
103 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageBaseImpl
* parentStorage
,
104 DWORD openFlags
, DirRef storageDirEntry
);
105 static HRESULT
StorageImpl_Refresh(StorageImpl
*This
, BOOL new_object
, BOOL create
);
106 static void StorageImpl_Destroy(StorageBaseImpl
* iface
);
107 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
);
108 static HRESULT
StorageImpl_Flush(StorageBaseImpl
* iface
);
109 static HRESULT
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
, ULONG
*read
);
110 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
111 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
112 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
113 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
114 static HRESULT
StorageImpl_LockRegionSync(StorageImpl
*This
, ULARGE_INTEGER offset
, ULARGE_INTEGER cb
, DWORD dwLockType
);
116 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
, ULONG depotIndex
);
117 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
118 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
119 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
120 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
122 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
123 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
124 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
126 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
127 static ULONG
SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream
* This
);
128 static BOOL
StorageImpl_WriteDWordToBigBlock( StorageImpl
* This
,
129 ULONG blockIndex
, ULONG offset
, DWORD value
);
130 static BOOL
StorageImpl_ReadDWordFromBigBlock( StorageImpl
* This
,
131 ULONG blockIndex
, ULONG offset
, DWORD
* value
);
133 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
);
134 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
);
136 typedef struct TransactedDirEntry
138 /* If applicable, a reference to the original DirEntry in the transacted
139 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
140 DirRef transactedParentEntry
;
142 /* True if this entry is being used. */
145 /* True if data is up to date. */
148 /* True if this entry has been modified. */
151 /* True if this entry's stream has been modified. */
154 /* True if this entry has been deleted in the transacted storage, but the
155 * delete has not yet been committed. */
158 /* If this entry's stream has been modified, a reference to where the stream
159 * is stored in the snapshot file. */
162 /* This directory entry's data, including any changes that have been made. */
165 /* A reference to the parent of this node. This is only valid while we are
166 * committing changes. */
169 /* A reference to a newly-created entry in the transacted parent. This is
170 * always equal to transactedParentEntry except when committing changes. */
171 DirRef newTransactedParentEntry
;
172 } TransactedDirEntry
;
174 /****************************************************************************
175 * Transacted storage object.
177 typedef struct TransactedSnapshotImpl
179 struct StorageBaseImpl base
;
182 * Modified streams are temporarily saved to the scratch file.
184 StorageBaseImpl
*scratch
;
186 /* The directory structure is kept here, so that we can track how these
187 * entries relate to those in the parent storage. */
188 TransactedDirEntry
*entries
;
190 ULONG firstFreeEntry
;
193 * Changes are committed to the transacted parent.
195 StorageBaseImpl
*transactedParent
;
197 /* The transaction signature from when we last committed */
198 ULONG lastTransactionSig
;
199 } TransactedSnapshotImpl
;
201 typedef struct TransactedSharedImpl
203 struct StorageBaseImpl base
;
206 * Snapshot and uncommitted changes go here.
208 TransactedSnapshotImpl
*scratch
;
211 * Changes are committed to the transacted parent.
213 StorageBaseImpl
*transactedParent
;
215 /* The transaction signature from when we last committed */
216 ULONG lastTransactionSig
;
217 } TransactedSharedImpl
;
219 /* Generic function to create a transacted wrapper for a direct storage object. */
220 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
* parent
, BOOL toplevel
, StorageBaseImpl
** result
);
222 /* OLESTREAM memory structure to use for Get and Put Routines */
223 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
228 DWORD dwOleTypeNameLength
;
229 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
230 CHAR
*pstrOleObjFileName
;
231 DWORD dwOleObjFileNameLength
;
232 DWORD dwMetaFileWidth
;
233 DWORD dwMetaFileHeight
;
234 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
237 }OLECONVERT_OLESTREAM_DATA
;
239 /* CompObj Stream structure */
240 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
245 DWORD dwCLSIDNameLength
;
246 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
247 DWORD dwOleTypeNameLength
;
248 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
249 DWORD dwProgIDNameLength
;
250 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
252 }OLECONVERT_ISTORAGE_COMPOBJ
;
255 /* Ole Presentation Stream structure */
256 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
264 }OLECONVERT_ISTORAGE_OLEPRES
;
268 /***********************************************************************
269 * Forward declaration of internal functions used by the method DestroyElement
271 static HRESULT
deleteStorageContents(
272 StorageBaseImpl
*parentStorage
,
273 DirRef indexToDelete
,
274 DirEntry entryDataToDelete
);
276 static HRESULT
deleteStreamContents(
277 StorageBaseImpl
*parentStorage
,
278 DirRef indexToDelete
,
279 DirEntry entryDataToDelete
);
281 static HRESULT
removeFromTree(
282 StorageBaseImpl
*This
,
283 DirRef parentStorageIndex
,
284 DirRef deletedIndex
);
286 /***********************************************************************
287 * Declaration of the functions used to manipulate DirEntry
290 static HRESULT
insertIntoTree(
291 StorageBaseImpl
*This
,
292 DirRef parentStorageIndex
,
293 DirRef newEntryIndex
);
295 static LONG
entryNameCmp(
296 const OLECHAR
*name1
,
297 const OLECHAR
*name2
);
299 static DirRef
findElement(
300 StorageBaseImpl
*storage
,
305 static HRESULT
findTreeParent(
306 StorageBaseImpl
*storage
,
308 const OLECHAR
*childName
,
309 DirEntry
*parentData
,
313 /***********************************************************************
314 * Declaration of miscellaneous functions...
316 static HRESULT
validateSTGM(DWORD stgmValue
);
318 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
319 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
320 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
322 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
325 /****************************************************************************
326 * IEnumSTATSTGImpl definitions.
328 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
329 * This class allows iterating through the content of a storage and to find
330 * specific items inside it.
332 struct IEnumSTATSTGImpl
334 IEnumSTATSTG IEnumSTATSTG_iface
;
336 LONG ref
; /* Reference count */
337 StorageBaseImpl
* parentStorage
; /* Reference to the parent storage */
338 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
340 WCHAR name
[DIRENTRY_NAME_MAX_LEN
]; /* The most recent name visited */
343 static inline IEnumSTATSTGImpl
*impl_from_IEnumSTATSTG(IEnumSTATSTG
*iface
)
345 return CONTAINING_RECORD(iface
, IEnumSTATSTGImpl
, IEnumSTATSTG_iface
);
349 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageBaseImpl
* This
, DirRef storageDirEntry
);
350 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
352 /************************************************************************
356 static ULONGLONG
StorageImpl_GetBigBlockOffset(StorageImpl
* This
, ULONG index
)
358 return (ULONGLONG
)(index
+1) * This
->bigBlockSize
;
361 /************************************************************************
362 ** Storage32BaseImpl implementation
364 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
365 ULARGE_INTEGER offset
,
370 return ILockBytes_ReadAt(This
->lockBytes
,offset
,buffer
,size
,bytesRead
);
373 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
374 ULARGE_INTEGER offset
,
379 return ILockBytes_WriteAt(This
->lockBytes
,offset
,buffer
,size
,bytesWritten
);
382 /************************************************************************
383 * Storage32BaseImpl_QueryInterface (IUnknown)
385 * This method implements the common QueryInterface for all IStorage32
386 * implementations contained in this file.
388 * See Windows documentation for more details on IUnknown methods.
390 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
395 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
402 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
403 IsEqualGUID(&IID_IStorage
, riid
))
405 *ppvObject
= &This
->IStorage_iface
;
407 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
409 *ppvObject
= &This
->IPropertySetStorage_iface
;
411 /* locking interface is reported for writer only */
412 else if (IsEqualGUID(&IID_IDirectWriterLock
, riid
) && This
->lockingrole
== SWMR_Writer
)
414 *ppvObject
= &This
->IDirectWriterLock_iface
;
417 return E_NOINTERFACE
;
419 IStorage_AddRef(iface
);
424 /************************************************************************
425 * Storage32BaseImpl_AddRef (IUnknown)
427 * This method implements the common AddRef for all IStorage32
428 * implementations contained in this file.
430 * See Windows documentation for more details on IUnknown methods.
432 static ULONG WINAPI
StorageBaseImpl_AddRef(
435 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
436 ULONG ref
= InterlockedIncrement(&This
->ref
);
438 TRACE("(%p) AddRef to %d\n", This
, ref
);
443 /************************************************************************
444 * Storage32BaseImpl_Release (IUnknown)
446 * This method implements the common Release for all IStorage32
447 * implementations contained in this file.
449 * See Windows documentation for more details on IUnknown methods.
451 static ULONG WINAPI
StorageBaseImpl_Release(
454 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
456 ULONG ref
= InterlockedDecrement(&This
->ref
);
458 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
463 * Since we are using a system of base-classes, we want to call the
464 * destructor of the appropriate derived class. To do this, we are
465 * using virtual functions to implement the destructor.
467 StorageBaseImpl_Destroy(This
);
473 /************************************************************************
474 * Storage32BaseImpl_OpenStream (IStorage)
476 * This method will open the specified stream object from the current storage.
478 * See Windows documentation for more details on IStorage methods.
480 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
482 const OLECHAR
* pwcsName
, /* [string][in] */
483 void* reserved1
, /* [unique][in] */
484 DWORD grfMode
, /* [in] */
485 DWORD reserved2
, /* [in] */
486 IStream
** ppstm
) /* [out] */
488 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
489 StgStreamImpl
* newStream
;
490 DirEntry currentEntry
;
491 DirRef streamEntryRef
;
492 HRESULT res
= STG_E_UNKNOWN
;
494 TRACE("(%p, %s, %p, %x, %d, %p)\n",
495 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
497 if ( (pwcsName
==NULL
) || (ppstm
==0) )
505 if ( FAILED( validateSTGM(grfMode
) ) ||
506 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
508 res
= STG_E_INVALIDFLAG
;
515 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
517 res
= STG_E_INVALIDFUNCTION
;
523 res
= STG_E_REVERTED
;
528 * Check that we're compatible with the parent's storage mode, but
529 * only if we are not in transacted mode
531 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
532 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
534 res
= STG_E_INVALIDFLAG
;
540 * Search for the element with the given name
542 streamEntryRef
= findElement(
544 This
->storageDirEntry
,
549 * If it was found, construct the stream object and return a pointer to it.
551 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
552 (currentEntry
.stgType
==STGTY_STREAM
) )
554 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
556 /* A single stream cannot be opened a second time. */
557 res
= STG_E_ACCESSDENIED
;
561 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
565 newStream
->grfMode
= grfMode
;
566 *ppstm
= &newStream
->IStream_iface
;
568 IStream_AddRef(*ppstm
);
578 res
= STG_E_FILENOTFOUND
;
582 TRACE("<-- IStream %p\n", *ppstm
);
583 TRACE("<-- %08x\n", res
);
587 /************************************************************************
588 * Storage32BaseImpl_OpenStorage (IStorage)
590 * This method will open a new storage object from the current storage.
592 * See Windows documentation for more details on IStorage methods.
594 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
596 const OLECHAR
* pwcsName
, /* [string][unique][in] */
597 IStorage
* pstgPriority
, /* [unique][in] */
598 DWORD grfMode
, /* [in] */
599 SNB snbExclude
, /* [unique][in] */
600 DWORD reserved
, /* [in] */
601 IStorage
** ppstg
) /* [out] */
603 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
604 StorageInternalImpl
* newStorage
;
605 StorageBaseImpl
* newTransactedStorage
;
606 DirEntry currentEntry
;
607 DirRef storageEntryRef
;
608 HRESULT res
= STG_E_UNKNOWN
;
610 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
611 iface
, debugstr_w(pwcsName
), pstgPriority
,
612 grfMode
, snbExclude
, reserved
, ppstg
);
614 if ((pwcsName
==NULL
) || (ppstg
==0) )
620 if (This
->openFlags
& STGM_SIMPLE
)
622 res
= STG_E_INVALIDFUNCTION
;
627 if (snbExclude
!= NULL
)
629 res
= STG_E_INVALIDPARAMETER
;
633 if ( FAILED( validateSTGM(grfMode
) ))
635 res
= STG_E_INVALIDFLAG
;
642 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
643 (grfMode
& STGM_DELETEONRELEASE
) ||
644 (grfMode
& STGM_PRIORITY
) )
646 res
= STG_E_INVALIDFUNCTION
;
651 return STG_E_REVERTED
;
654 * Check that we're compatible with the parent's storage mode,
655 * but only if we are not transacted
657 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
658 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
660 res
= STG_E_ACCESSDENIED
;
667 storageEntryRef
= findElement(
669 This
->storageDirEntry
,
673 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
674 (currentEntry
.stgType
==STGTY_STORAGE
) )
676 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
678 /* A single storage cannot be opened a second time. */
679 res
= STG_E_ACCESSDENIED
;
683 newStorage
= StorageInternalImpl_Construct(
690 if (grfMode
& STGM_TRANSACTED
)
692 res
= Storage_ConstructTransacted(&newStorage
->base
, FALSE
, &newTransactedStorage
);
696 HeapFree(GetProcessHeap(), 0, newStorage
);
700 *ppstg
= &newTransactedStorage
->IStorage_iface
;
704 *ppstg
= &newStorage
->base
.IStorage_iface
;
707 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
713 res
= STG_E_INSUFFICIENTMEMORY
;
717 res
= STG_E_FILENOTFOUND
;
720 TRACE("<-- %08x\n", res
);
724 /************************************************************************
725 * Storage32BaseImpl_EnumElements (IStorage)
727 * This method will create an enumerator object that can be used to
728 * retrieve information about all the elements in the storage object.
730 * See Windows documentation for more details on IStorage methods.
732 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
734 DWORD reserved1
, /* [in] */
735 void* reserved2
, /* [size_is][unique][in] */
736 DWORD reserved3
, /* [in] */
737 IEnumSTATSTG
** ppenum
) /* [out] */
739 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
740 IEnumSTATSTGImpl
* newEnum
;
742 TRACE("(%p, %d, %p, %d, %p)\n",
743 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
749 return STG_E_REVERTED
;
751 newEnum
= IEnumSTATSTGImpl_Construct(
753 This
->storageDirEntry
);
757 *ppenum
= &newEnum
->IEnumSTATSTG_iface
;
761 return E_OUTOFMEMORY
;
764 /************************************************************************
765 * Storage32BaseImpl_Stat (IStorage)
767 * This method will retrieve information about this storage object.
769 * See Windows documentation for more details on IStorage methods.
771 static HRESULT WINAPI
StorageBaseImpl_Stat(
773 STATSTG
* pstatstg
, /* [out] */
774 DWORD grfStatFlag
) /* [in] */
776 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
777 DirEntry currentEntry
;
778 HRESULT res
= STG_E_UNKNOWN
;
780 TRACE("(%p, %p, %x)\n",
781 iface
, pstatstg
, grfStatFlag
);
791 res
= STG_E_REVERTED
;
795 res
= StorageBaseImpl_ReadDirEntry(
797 This
->storageDirEntry
,
802 StorageUtl_CopyDirEntryToSTATSTG(
808 pstatstg
->grfMode
= This
->openFlags
;
809 pstatstg
->grfStateBits
= This
->stateBits
;
815 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg
->pwcsName
), pstatstg
->type
, pstatstg
->cbSize
.u
.LowPart
, pstatstg
->cbSize
.u
.HighPart
, pstatstg
->grfMode
, pstatstg
->grfLocksSupported
, pstatstg
->grfStateBits
);
817 TRACE("<-- %08x\n", res
);
821 /************************************************************************
822 * Storage32BaseImpl_RenameElement (IStorage)
824 * This method will rename the specified element.
826 * See Windows documentation for more details on IStorage methods.
828 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
830 const OLECHAR
* pwcsOldName
, /* [in] */
831 const OLECHAR
* pwcsNewName
) /* [in] */
833 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
834 DirEntry currentEntry
;
835 DirRef currentEntryRef
;
837 TRACE("(%p, %s, %s)\n",
838 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
841 return STG_E_REVERTED
;
843 currentEntryRef
= findElement(This
,
844 This
->storageDirEntry
,
848 if (currentEntryRef
!= DIRENTRY_NULL
)
851 * There is already an element with the new name
853 return STG_E_FILEALREADYEXISTS
;
857 * Search for the old element name
859 currentEntryRef
= findElement(This
,
860 This
->storageDirEntry
,
864 if (currentEntryRef
!= DIRENTRY_NULL
)
866 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
867 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
869 WARN("Element is already open; cannot rename.\n");
870 return STG_E_ACCESSDENIED
;
873 /* Remove the element from its current position in the tree */
874 removeFromTree(This
, This
->storageDirEntry
,
877 /* Change the name of the element */
878 strcpyW(currentEntry
.name
, pwcsNewName
);
880 /* Delete any sibling links */
881 currentEntry
.leftChild
= DIRENTRY_NULL
;
882 currentEntry
.rightChild
= DIRENTRY_NULL
;
884 StorageBaseImpl_WriteDirEntry(This
, currentEntryRef
,
887 /* Insert the element in a new position in the tree */
888 insertIntoTree(This
, This
->storageDirEntry
,
894 * There is no element with the old name
896 return STG_E_FILENOTFOUND
;
899 return StorageBaseImpl_Flush(This
);
902 /************************************************************************
903 * Storage32BaseImpl_CreateStream (IStorage)
905 * This method will create a stream object within this storage
907 * See Windows documentation for more details on IStorage methods.
909 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
911 const OLECHAR
* pwcsName
, /* [string][in] */
912 DWORD grfMode
, /* [in] */
913 DWORD reserved1
, /* [in] */
914 DWORD reserved2
, /* [in] */
915 IStream
** ppstm
) /* [out] */
917 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
918 StgStreamImpl
* newStream
;
919 DirEntry currentEntry
, newStreamEntry
;
920 DirRef currentEntryRef
, newStreamEntryRef
;
923 TRACE("(%p, %s, %x, %d, %d, %p)\n",
924 iface
, debugstr_w(pwcsName
), grfMode
,
925 reserved1
, reserved2
, ppstm
);
928 return STG_E_INVALIDPOINTER
;
931 return STG_E_INVALIDNAME
;
933 if (reserved1
|| reserved2
)
934 return STG_E_INVALIDPARAMETER
;
936 if ( FAILED( validateSTGM(grfMode
) ))
937 return STG_E_INVALIDFLAG
;
939 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
940 return STG_E_INVALIDFLAG
;
943 return STG_E_REVERTED
;
948 if ((grfMode
& STGM_DELETEONRELEASE
) ||
949 (grfMode
& STGM_TRANSACTED
))
950 return STG_E_INVALIDFUNCTION
;
953 * Don't worry about permissions in transacted mode, as we can always write
954 * changes; we just can't always commit them.
956 if(!(This
->openFlags
& STGM_TRANSACTED
)) {
957 /* Can't create a stream on read-only storage */
958 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
959 return STG_E_ACCESSDENIED
;
961 /* Can't create a stream with greater access than the parent. */
962 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
963 return STG_E_ACCESSDENIED
;
966 if(This
->openFlags
& STGM_SIMPLE
)
967 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
971 currentEntryRef
= findElement(This
,
972 This
->storageDirEntry
,
976 if (currentEntryRef
!= DIRENTRY_NULL
)
979 * An element with this name already exists
981 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
983 IStorage_DestroyElement(iface
, pwcsName
);
986 return STG_E_FILEALREADYEXISTS
;
990 * memset the empty entry
992 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
994 newStreamEntry
.sizeOfNameString
=
995 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
997 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
998 return STG_E_INVALIDNAME
;
1000 strcpyW(newStreamEntry
.name
, pwcsName
);
1002 newStreamEntry
.stgType
= STGTY_STREAM
;
1003 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1004 newStreamEntry
.size
.u
.LowPart
= 0;
1005 newStreamEntry
.size
.u
.HighPart
= 0;
1007 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
1008 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
1009 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
1011 /* call CoFileTime to get the current time
1012 newStreamEntry.ctime
1013 newStreamEntry.mtime
1016 /* newStreamEntry.clsid */
1019 * Create an entry with the new data
1021 hr
= StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
1026 * Insert the new entry in the parent storage's tree.
1028 hr
= insertIntoTree(
1030 This
->storageDirEntry
,
1034 StorageBaseImpl_DestroyDirEntry(This
, newStreamEntryRef
);
1039 * Open the stream to return it.
1041 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
1045 *ppstm
= &newStream
->IStream_iface
;
1046 IStream_AddRef(*ppstm
);
1050 return STG_E_INSUFFICIENTMEMORY
;
1053 return StorageBaseImpl_Flush(This
);
1056 /************************************************************************
1057 * Storage32BaseImpl_SetClass (IStorage)
1059 * This method will write the specified CLSID in the directory entry of this
1062 * See Windows documentation for more details on IStorage methods.
1064 static HRESULT WINAPI
StorageBaseImpl_SetClass(
1066 REFCLSID clsid
) /* [in] */
1068 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1070 DirEntry currentEntry
;
1072 TRACE("(%p, %p)\n", iface
, clsid
);
1075 return STG_E_REVERTED
;
1077 hRes
= StorageBaseImpl_ReadDirEntry(This
,
1078 This
->storageDirEntry
,
1080 if (SUCCEEDED(hRes
))
1082 currentEntry
.clsid
= *clsid
;
1084 hRes
= StorageBaseImpl_WriteDirEntry(This
,
1085 This
->storageDirEntry
,
1089 if (SUCCEEDED(hRes
))
1090 hRes
= StorageBaseImpl_Flush(This
);
1095 /************************************************************************
1096 ** Storage32Impl implementation
1099 /************************************************************************
1100 * Storage32BaseImpl_CreateStorage (IStorage)
1102 * This method will create the storage object within the provided storage.
1104 * See Windows documentation for more details on IStorage methods.
1106 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1108 const OLECHAR
*pwcsName
, /* [string][in] */
1109 DWORD grfMode
, /* [in] */
1110 DWORD reserved1
, /* [in] */
1111 DWORD reserved2
, /* [in] */
1112 IStorage
**ppstg
) /* [out] */
1114 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
1116 DirEntry currentEntry
;
1118 DirRef currentEntryRef
;
1122 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1123 iface
, debugstr_w(pwcsName
), grfMode
,
1124 reserved1
, reserved2
, ppstg
);
1127 return STG_E_INVALIDPOINTER
;
1129 if (This
->openFlags
& STGM_SIMPLE
)
1131 return STG_E_INVALIDFUNCTION
;
1135 return STG_E_INVALIDNAME
;
1139 if ( FAILED( validateSTGM(grfMode
) ) ||
1140 (grfMode
& STGM_DELETEONRELEASE
) )
1142 WARN("bad grfMode: 0x%x\n", grfMode
);
1143 return STG_E_INVALIDFLAG
;
1147 return STG_E_REVERTED
;
1150 * Check that we're compatible with the parent's storage mode
1152 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1153 STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1155 WARN("access denied\n");
1156 return STG_E_ACCESSDENIED
;
1159 currentEntryRef
= findElement(This
,
1160 This
->storageDirEntry
,
1164 if (currentEntryRef
!= DIRENTRY_NULL
)
1167 * An element with this name already exists
1169 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1170 ((This
->openFlags
& STGM_TRANSACTED
) ||
1171 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
))
1173 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1179 WARN("file already exists\n");
1180 return STG_E_FILEALREADYEXISTS
;
1183 else if (!(This
->openFlags
& STGM_TRANSACTED
) &&
1184 STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1186 WARN("read-only storage\n");
1187 return STG_E_ACCESSDENIED
;
1190 memset(&newEntry
, 0, sizeof(DirEntry
));
1192 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1194 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1196 FIXME("name too long\n");
1197 return STG_E_INVALIDNAME
;
1200 strcpyW(newEntry
.name
, pwcsName
);
1202 newEntry
.stgType
= STGTY_STORAGE
;
1203 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1204 newEntry
.size
.u
.LowPart
= 0;
1205 newEntry
.size
.u
.HighPart
= 0;
1207 newEntry
.leftChild
= DIRENTRY_NULL
;
1208 newEntry
.rightChild
= DIRENTRY_NULL
;
1209 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1211 /* call CoFileTime to get the current time
1216 /* newEntry.clsid */
1219 * Create a new directory entry for the storage
1221 hr
= StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1226 * Insert the new directory entry into the parent storage's tree
1228 hr
= insertIntoTree(
1230 This
->storageDirEntry
,
1234 StorageBaseImpl_DestroyDirEntry(This
, newEntryRef
);
1239 * Open it to get a pointer to return.
1241 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1243 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1249 hr
= StorageBaseImpl_Flush(This
);
1255 /***************************************************************************
1259 * Reserve a directory entry in the file and initialize it.
1261 static HRESULT
StorageImpl_CreateDirEntry(
1262 StorageBaseImpl
*base
,
1263 const DirEntry
*newData
,
1266 StorageImpl
*storage
= (StorageImpl
*)base
;
1267 ULONG currentEntryIndex
= 0;
1268 ULONG newEntryIndex
= DIRENTRY_NULL
;
1270 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1271 WORD sizeOfNameString
;
1275 hr
= StorageImpl_ReadRawDirEntry(storage
,
1281 StorageUtl_ReadWord(
1283 OFFSET_PS_NAMELENGTH
,
1286 if (sizeOfNameString
== 0)
1289 * The entry exists and is available, we found it.
1291 newEntryIndex
= currentEntryIndex
;
1297 * We exhausted the directory entries, we will create more space below
1299 newEntryIndex
= currentEntryIndex
;
1301 currentEntryIndex
++;
1303 } while (newEntryIndex
== DIRENTRY_NULL
);
1306 * grow the directory stream
1310 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1311 ULARGE_INTEGER newSize
;
1313 ULONG lastEntry
= 0;
1314 ULONG blockCount
= 0;
1317 * obtain the new count of blocks in the directory stream
1319 blockCount
= BlockChainStream_GetCount(
1320 storage
->rootBlockChain
)+1;
1323 * initialize the size used by the directory stream
1325 newSize
.QuadPart
= (ULONGLONG
)storage
->bigBlockSize
* blockCount
;
1328 * add a block to the directory stream
1330 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1333 * memset the empty entry in order to initialize the unused newly
1336 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1341 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1344 entryIndex
= newEntryIndex
+ 1;
1345 entryIndex
< lastEntry
;
1348 StorageImpl_WriteRawDirEntry(
1354 StorageImpl_SaveFileHeader(storage
);
1357 UpdateRawDirEntry(currentData
, newData
);
1359 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1362 *index
= newEntryIndex
;
1367 /***************************************************************************
1371 * Mark a directory entry in the file as free.
1373 static HRESULT
StorageImpl_DestroyDirEntry(
1374 StorageBaseImpl
*base
,
1377 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1378 StorageImpl
*storage
= (StorageImpl
*)base
;
1380 memset(emptyData
, 0, RAW_DIRENTRY_SIZE
);
1382 return StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1386 /****************************************************************************
1390 * Case insensitive comparison of DirEntry.name by first considering
1393 * Returns <0 when name1 < name2
1394 * >0 when name1 > name2
1395 * 0 when name1 == name2
1397 static LONG
entryNameCmp(
1398 const OLECHAR
*name1
,
1399 const OLECHAR
*name2
)
1401 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1403 while (diff
== 0 && *name1
!= 0)
1406 * We compare the string themselves only when they are of the same length
1408 diff
= toupperW(*name1
++) - toupperW(*name2
++);
1414 /****************************************************************************
1418 * Add a directory entry to a storage
1420 static HRESULT
insertIntoTree(
1421 StorageBaseImpl
*This
,
1422 DirRef parentStorageIndex
,
1423 DirRef newEntryIndex
)
1425 DirEntry currentEntry
;
1429 * Read the inserted entry
1431 StorageBaseImpl_ReadDirEntry(This
,
1436 * Read the storage entry
1438 StorageBaseImpl_ReadDirEntry(This
,
1442 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1445 * The root storage contains some element, therefore, start the research
1446 * for the appropriate location.
1449 DirRef current
, next
, previous
, currentEntryId
;
1452 * Keep a reference to the root of the storage's element tree
1454 currentEntryId
= currentEntry
.dirRootEntry
;
1459 StorageBaseImpl_ReadDirEntry(This
,
1460 currentEntry
.dirRootEntry
,
1463 previous
= currentEntry
.leftChild
;
1464 next
= currentEntry
.rightChild
;
1465 current
= currentEntryId
;
1469 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1473 if (previous
!= DIRENTRY_NULL
)
1475 StorageBaseImpl_ReadDirEntry(This
,
1482 currentEntry
.leftChild
= newEntryIndex
;
1483 StorageBaseImpl_WriteDirEntry(This
,
1491 if (next
!= DIRENTRY_NULL
)
1493 StorageBaseImpl_ReadDirEntry(This
,
1500 currentEntry
.rightChild
= newEntryIndex
;
1501 StorageBaseImpl_WriteDirEntry(This
,
1510 * Trying to insert an item with the same name in the
1511 * subtree structure.
1513 return STG_E_FILEALREADYEXISTS
;
1516 previous
= currentEntry
.leftChild
;
1517 next
= currentEntry
.rightChild
;
1523 * The storage is empty, make the new entry the root of its element tree
1525 currentEntry
.dirRootEntry
= newEntryIndex
;
1526 StorageBaseImpl_WriteDirEntry(This
,
1534 /****************************************************************************
1538 * Find and read the element of a storage with the given name.
1540 static DirRef
findElement(StorageBaseImpl
*storage
, DirRef storageEntry
,
1541 const OLECHAR
*name
, DirEntry
*data
)
1543 DirRef currentEntry
;
1545 /* Read the storage entry to find the root of the tree. */
1546 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, data
);
1548 currentEntry
= data
->dirRootEntry
;
1550 while (currentEntry
!= DIRENTRY_NULL
)
1554 StorageBaseImpl_ReadDirEntry(storage
, currentEntry
, data
);
1556 cmp
= entryNameCmp(name
, data
->name
);
1563 currentEntry
= data
->leftChild
;
1566 currentEntry
= data
->rightChild
;
1569 return currentEntry
;
1572 /****************************************************************************
1576 * Find and read the binary tree parent of the element with the given name.
1578 * If there is no such element, find a place where it could be inserted and
1579 * return STG_E_FILENOTFOUND.
1581 static HRESULT
findTreeParent(StorageBaseImpl
*storage
, DirRef storageEntry
,
1582 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1588 /* Read the storage entry to find the root of the tree. */
1589 StorageBaseImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1591 *parentEntry
= storageEntry
;
1592 *relation
= DIRENTRY_RELATION_DIR
;
1594 childEntry
= parentData
->dirRootEntry
;
1596 while (childEntry
!= DIRENTRY_NULL
)
1600 StorageBaseImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1602 cmp
= entryNameCmp(childName
, childData
.name
);
1610 *parentData
= childData
;
1611 *parentEntry
= childEntry
;
1612 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1614 childEntry
= parentData
->leftChild
;
1619 *parentData
= childData
;
1620 *parentEntry
= childEntry
;
1621 *relation
= DIRENTRY_RELATION_NEXT
;
1623 childEntry
= parentData
->rightChild
;
1627 if (childEntry
== DIRENTRY_NULL
)
1628 return STG_E_FILENOTFOUND
;
1634 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1635 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1636 SNB snbExclude
, IStorage
*pstgDest
);
1638 static HRESULT
StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl
*This
,
1639 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1640 SNB snbExclude
, IStorage
*pstgDest
)
1646 IStream
*pstrChild
, *pstrTmp
;
1649 if (srcEntry
== DIRENTRY_NULL
)
1652 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1659 WCHAR
**snb
= snbExclude
;
1661 while ( *snb
!= NULL
&& !skip
)
1663 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1671 if (data
.stgType
== STGTY_STORAGE
&& !skip_storage
)
1674 * create a new storage in destination storage
1676 hr
= IStorage_CreateStorage( pstgDest
, data
.name
,
1677 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1682 * if it already exist, don't create a new one use this one
1684 if (hr
== STG_E_FILEALREADYEXISTS
)
1686 hr
= IStorage_OpenStorage( pstgDest
, data
.name
, NULL
,
1687 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1688 NULL
, 0, &pstgTmp
);
1693 hr
= StorageBaseImpl_CopyStorageEntryTo( This
, srcEntry
, skip_storage
,
1694 skip_stream
, NULL
, pstgTmp
);
1696 IStorage_Release(pstgTmp
);
1699 else if (data
.stgType
== STGTY_STREAM
&& !skip_stream
)
1702 * create a new stream in destination storage. If the stream already
1703 * exist, it will be deleted and a new one will be created.
1705 hr
= IStorage_CreateStream( pstgDest
, data
.name
,
1706 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1710 * open child stream storage. This operation must succeed even if the
1711 * stream is already open, so we use internal functions to do it.
1715 StgStreamImpl
*streamimpl
= StgStreamImpl_Construct(This
, STGM_READ
|STGM_SHARE_EXCLUSIVE
, srcEntry
);
1719 pstrChild
= &streamimpl
->IStream_iface
;
1721 IStream_AddRef(pstrChild
);
1733 * Get the size of the source stream
1735 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1738 * Set the size of the destination stream.
1740 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1745 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1748 IStream_Release( pstrChild
);
1751 IStream_Release( pstrTmp
);
1757 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.leftChild
, skip_storage
,
1758 skip_stream
, snbExclude
, pstgDest
);
1761 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.rightChild
, skip_storage
,
1762 skip_stream
, snbExclude
, pstgDest
);
1767 static HRESULT
StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl
*This
,
1768 DirRef srcEntry
, BOOL skip_storage
, BOOL skip_stream
,
1769 SNB snbExclude
, IStorage
*pstgDest
)
1774 hr
= StorageBaseImpl_ReadDirEntry( This
, srcEntry
, &data
);
1777 hr
= IStorage_SetClass( pstgDest
, &data
.clsid
);
1780 hr
= StorageBaseImpl_CopyChildEntryTo( This
, data
.dirRootEntry
, skip_storage
,
1781 skip_stream
, snbExclude
, pstgDest
);
1786 /*************************************************************************
1789 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1791 DWORD ciidExclude
, /* [in] */
1792 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1793 SNB snbExclude
, /* [unique][in] */
1794 IStorage
* pstgDest
) /* [unique][in] */
1796 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1798 BOOL skip_storage
= FALSE
, skip_stream
= FALSE
;
1801 TRACE("(%p, %d, %p, %p, %p)\n",
1802 iface
, ciidExclude
, rgiidExclude
,
1803 snbExclude
, pstgDest
);
1805 if ( pstgDest
== 0 )
1806 return STG_E_INVALIDPOINTER
;
1808 for(i
= 0; i
< ciidExclude
; ++i
)
1810 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1811 skip_storage
= TRUE
;
1812 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1815 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1820 /* Give up early if it looks like this would be infinitely recursive.
1821 * Oddly enough, this includes some cases that aren't really recursive, like
1822 * copying to a transacted child. */
1823 IStorage
*pstgDestAncestor
= pstgDest
;
1824 IStorage
*pstgDestAncestorChild
= NULL
;
1826 /* Go up the chain from the destination until we find the source storage. */
1827 while (pstgDestAncestor
!= iface
) {
1828 pstgDestAncestorChild
= pstgDest
;
1830 if (pstgDestAncestor
->lpVtbl
== &TransactedSnapshotImpl_Vtbl
)
1832 TransactedSnapshotImpl
*snapshot
= (TransactedSnapshotImpl
*) pstgDestAncestor
;
1834 pstgDestAncestor
= &snapshot
->transactedParent
->IStorage_iface
;
1836 else if (pstgDestAncestor
->lpVtbl
== &Storage32InternalImpl_Vtbl
)
1838 StorageInternalImpl
*internal
= (StorageInternalImpl
*) pstgDestAncestor
;
1840 pstgDestAncestor
= &internal
->parentStorage
->IStorage_iface
;
1846 if (pstgDestAncestor
== iface
)
1850 if (pstgDestAncestorChild
&& snbExclude
)
1852 StorageBaseImpl
*ancestorChildBase
= (StorageBaseImpl
*)pstgDestAncestorChild
;
1854 WCHAR
**snb
= snbExclude
;
1856 StorageBaseImpl_ReadDirEntry(ancestorChildBase
, ancestorChildBase
->storageDirEntry
, &data
);
1858 while ( *snb
!= NULL
&& fail
)
1860 if ( lstrcmpW(data
.name
, *snb
) == 0 )
1867 return STG_E_ACCESSDENIED
;
1871 return StorageBaseImpl_CopyStorageEntryTo( This
, This
->storageDirEntry
,
1872 skip_storage
, skip_stream
, snbExclude
, pstgDest
);
1875 /*************************************************************************
1876 * MoveElementTo (IStorage)
1878 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1880 const OLECHAR
*pwcsName
, /* [string][in] */
1881 IStorage
*pstgDest
, /* [unique][in] */
1882 const OLECHAR
*pwcsNewName
,/* [string][in] */
1883 DWORD grfFlags
) /* [in] */
1885 FIXME("(%p %s %p %s %u): stub\n", iface
,
1886 debugstr_w(pwcsName
), pstgDest
,
1887 debugstr_w(pwcsNewName
), grfFlags
);
1891 /*************************************************************************
1894 * Ensures that any changes made to a storage object open in transacted mode
1895 * are reflected in the parent storage
1897 * In a non-transacted mode, this ensures all cached writes are completed.
1899 static HRESULT WINAPI
StorageImpl_Commit(
1901 DWORD grfCommitFlags
)/* [in] */
1903 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
1904 TRACE("(%p %d)\n", iface
, grfCommitFlags
);
1905 return StorageBaseImpl_Flush(This
);
1908 /*************************************************************************
1911 * Discard all changes that have been made since the last commit operation
1913 static HRESULT WINAPI
StorageImpl_Revert(
1916 TRACE("(%p)\n", iface
);
1920 /*************************************************************************
1921 * DestroyElement (IStorage)
1923 * Strategy: This implementation is built this way for simplicity not for speed.
1924 * I always delete the topmost element of the enumeration and adjust
1925 * the deleted element pointer all the time. This takes longer to
1926 * do but allow to reinvoke DestroyElement whenever we encounter a
1927 * storage object. The optimisation resides in the usage of another
1928 * enumeration strategy that would give all the leaves of a storage
1929 * first. (postfix order)
1931 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1933 const OLECHAR
*pwcsName
)/* [string][in] */
1935 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
1938 DirEntry entryToDelete
;
1939 DirRef entryToDeleteRef
;
1942 iface
, debugstr_w(pwcsName
));
1945 return STG_E_INVALIDPOINTER
;
1948 return STG_E_REVERTED
;
1950 if ( !(This
->openFlags
& STGM_TRANSACTED
) &&
1951 STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1952 return STG_E_ACCESSDENIED
;
1954 entryToDeleteRef
= findElement(
1956 This
->storageDirEntry
,
1960 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1962 return STG_E_FILENOTFOUND
;
1965 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1967 hr
= deleteStorageContents(
1972 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1974 hr
= deleteStreamContents(
1984 * Remove the entry from its parent storage
1986 hr
= removeFromTree(
1988 This
->storageDirEntry
,
1992 * Invalidate the entry
1995 StorageBaseImpl_DestroyDirEntry(This
, entryToDeleteRef
);
1998 hr
= StorageBaseImpl_Flush(This
);
2004 /******************************************************************************
2005 * Internal stream list handlers
2008 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2010 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
2011 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
2014 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
2016 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
2017 list_remove(&(strm
->StrmListEntry
));
2020 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
2022 StgStreamImpl
*strm
;
2024 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
2026 if (strm
->dirEntry
== streamEntry
)
2035 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
2037 StorageInternalImpl
*childstg
;
2039 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2041 if (childstg
->base
.storageDirEntry
== storageEntry
)
2050 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
2052 struct list
*cur
, *cur2
;
2053 StgStreamImpl
*strm
=NULL
;
2054 StorageInternalImpl
*childstg
=NULL
;
2056 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
2057 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
2058 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
2059 strm
->parentStorage
= NULL
;
2063 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
2064 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
2065 StorageBaseImpl_Invalidate( &childstg
->base
);
2068 if (stg
->transactedChild
)
2070 StorageBaseImpl_Invalidate(stg
->transactedChild
);
2072 stg
->transactedChild
= NULL
;
2077 /*********************************************************************
2081 * Delete the contents of a storage entry.
2084 static HRESULT
deleteStorageContents(
2085 StorageBaseImpl
*parentStorage
,
2086 DirRef indexToDelete
,
2087 DirEntry entryDataToDelete
)
2089 IEnumSTATSTG
*elements
= 0;
2090 IStorage
*childStorage
= 0;
2091 STATSTG currentElement
;
2093 HRESULT destroyHr
= S_OK
;
2094 StorageInternalImpl
*stg
, *stg2
;
2096 /* Invalidate any open storage objects. */
2097 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
2099 if (stg
->base
.storageDirEntry
== indexToDelete
)
2101 StorageBaseImpl_Invalidate(&stg
->base
);
2106 * Open the storage and enumerate it
2108 hr
= IStorage_OpenStorage(
2109 &parentStorage
->IStorage_iface
,
2110 entryDataToDelete
.name
,
2112 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
2123 * Enumerate the elements
2125 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
2130 * Obtain the next element
2132 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
2135 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
2137 CoTaskMemFree(currentElement
.pwcsName
);
2141 * We need to Reset the enumeration every time because we delete elements
2142 * and the enumeration could be invalid
2144 IEnumSTATSTG_Reset(elements
);
2146 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
2148 IStorage_Release(childStorage
);
2149 IEnumSTATSTG_Release(elements
);
2154 /*********************************************************************
2158 * Perform the deletion of a stream's data
2161 static HRESULT
deleteStreamContents(
2162 StorageBaseImpl
*parentStorage
,
2163 DirRef indexToDelete
,
2164 DirEntry entryDataToDelete
)
2168 ULARGE_INTEGER size
;
2169 StgStreamImpl
*strm
, *strm2
;
2171 /* Invalidate any open stream objects. */
2172 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2174 if (strm
->dirEntry
== indexToDelete
)
2176 TRACE("Stream deleted %p\n", strm
);
2177 strm
->parentStorage
= NULL
;
2178 list_remove(&strm
->StrmListEntry
);
2182 size
.u
.HighPart
= 0;
2185 hr
= IStorage_OpenStream(&parentStorage
->IStorage_iface
,
2186 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2196 hr
= IStream_SetSize(pis
, size
);
2204 * Release the stream object.
2206 IStream_Release(pis
);
2211 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2215 case DIRENTRY_RELATION_PREVIOUS
:
2216 entry
->leftChild
= new_target
;
2218 case DIRENTRY_RELATION_NEXT
:
2219 entry
->rightChild
= new_target
;
2221 case DIRENTRY_RELATION_DIR
:
2222 entry
->dirRootEntry
= new_target
;
2229 /*************************************************************************
2233 * This method removes a directory entry from its parent storage tree without
2234 * freeing any resources attached to it.
2236 static HRESULT
removeFromTree(
2237 StorageBaseImpl
*This
,
2238 DirRef parentStorageIndex
,
2239 DirRef deletedIndex
)
2241 DirEntry entryToDelete
;
2242 DirEntry parentEntry
;
2243 DirRef parentEntryRef
;
2244 ULONG typeOfRelation
;
2247 hr
= StorageBaseImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2253 * Find the element that links to the one we want to delete.
2255 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2256 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2261 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2264 * Replace the deleted entry with its left child
2266 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2268 hr
= StorageBaseImpl_WriteDirEntry(
2277 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2280 * We need to reinsert the right child somewhere. We already know it and
2281 * its children are greater than everything in the left tree, so we
2282 * insert it at the rightmost point in the left tree.
2284 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2285 DirEntry newRightChildParentEntry
;
2289 hr
= StorageBaseImpl_ReadDirEntry(
2291 newRightChildParent
,
2292 &newRightChildParentEntry
);
2298 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2299 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2300 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2302 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2304 hr
= StorageBaseImpl_WriteDirEntry(
2306 newRightChildParent
,
2307 &newRightChildParentEntry
);
2317 * Replace the deleted entry with its right child
2319 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2321 hr
= StorageBaseImpl_WriteDirEntry(
2335 /******************************************************************************
2336 * SetElementTimes (IStorage)
2338 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2340 const OLECHAR
*pwcsName
,/* [string][in] */
2341 const FILETIME
*pctime
, /* [in] */
2342 const FILETIME
*patime
, /* [in] */
2343 const FILETIME
*pmtime
) /* [in] */
2345 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2349 /******************************************************************************
2350 * SetStateBits (IStorage)
2352 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2354 DWORD grfStateBits
,/* [in] */
2355 DWORD grfMask
) /* [in] */
2357 StorageBaseImpl
*This
= impl_from_IStorage(iface
);
2360 return STG_E_REVERTED
;
2362 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2366 static HRESULT
StorageImpl_BaseWriteDirEntry(StorageBaseImpl
*base
,
2367 DirRef index
, const DirEntry
*data
)
2369 StorageImpl
*This
= (StorageImpl
*)base
;
2370 return StorageImpl_WriteDirEntry(This
, index
, data
);
2373 static HRESULT
StorageImpl_BaseReadDirEntry(StorageBaseImpl
*base
,
2374 DirRef index
, DirEntry
*data
)
2376 StorageImpl
*This
= (StorageImpl
*)base
;
2377 return StorageImpl_ReadDirEntry(This
, index
, data
);
2380 static BlockChainStream
**StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl
* This
)
2384 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2386 if (!This
->blockChainCache
[i
])
2388 return &This
->blockChainCache
[i
];
2392 i
= This
->blockChainToEvict
;
2394 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2395 This
->blockChainCache
[i
] = NULL
;
2397 This
->blockChainToEvict
++;
2398 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2399 This
->blockChainToEvict
= 0;
2401 return &This
->blockChainCache
[i
];
2404 static BlockChainStream
**StorageImpl_GetCachedBlockChainStream(StorageImpl
*This
,
2407 int i
, free_index
=-1;
2409 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2411 if (!This
->blockChainCache
[i
])
2413 if (free_index
== -1) free_index
= i
;
2415 else if (This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2417 return &This
->blockChainCache
[i
];
2421 if (free_index
== -1)
2423 free_index
= This
->blockChainToEvict
;
2425 BlockChainStream_Destroy(This
->blockChainCache
[free_index
]);
2426 This
->blockChainCache
[free_index
] = NULL
;
2428 This
->blockChainToEvict
++;
2429 if (This
->blockChainToEvict
== BLOCKCHAIN_CACHE_SIZE
)
2430 This
->blockChainToEvict
= 0;
2433 This
->blockChainCache
[free_index
] = BlockChainStream_Construct(This
, NULL
, index
);
2434 return &This
->blockChainCache
[free_index
];
2437 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl
*This
, DirRef index
)
2441 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
2443 if (This
->blockChainCache
[i
] && This
->blockChainCache
[i
]->ownerDirEntry
== index
)
2445 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
2446 This
->blockChainCache
[i
] = NULL
;
2452 static HRESULT
StorageImpl_StreamReadAt(StorageBaseImpl
*base
, DirRef index
,
2453 ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
2455 StorageImpl
*This
= (StorageImpl
*)base
;
2460 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2461 if (FAILED(hr
)) return hr
;
2463 if (data
.size
.QuadPart
== 0)
2469 if (offset
.QuadPart
+ size
> data
.size
.QuadPart
)
2471 bytesToRead
= data
.size
.QuadPart
- offset
.QuadPart
;
2478 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2480 SmallBlockChainStream
*stream
;
2482 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2483 if (!stream
) return E_OUTOFMEMORY
;
2485 hr
= SmallBlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2487 SmallBlockChainStream_Destroy(stream
);
2493 BlockChainStream
*stream
= NULL
;
2495 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2496 if (!stream
) return E_OUTOFMEMORY
;
2498 hr
= BlockChainStream_ReadAt(stream
, offset
, bytesToRead
, buffer
, bytesRead
);
2504 static HRESULT
StorageImpl_StreamSetSize(StorageBaseImpl
*base
, DirRef index
,
2505 ULARGE_INTEGER newsize
)
2507 StorageImpl
*This
= (StorageImpl
*)base
;
2510 SmallBlockChainStream
*smallblock
=NULL
;
2511 BlockChainStream
**pbigblock
=NULL
, *bigblock
=NULL
;
2513 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2514 if (FAILED(hr
)) return hr
;
2516 /* In simple mode keep the stream size above the small block limit */
2517 if (This
->base
.openFlags
& STGM_SIMPLE
)
2518 newsize
.QuadPart
= max(newsize
.QuadPart
, LIMIT_TO_USE_SMALL_BLOCK
);
2520 if (data
.size
.QuadPart
== newsize
.QuadPart
)
2523 /* Create a block chain object of the appropriate type */
2524 if (data
.size
.QuadPart
== 0)
2526 if (newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2528 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2529 if (!smallblock
) return E_OUTOFMEMORY
;
2533 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2534 bigblock
= *pbigblock
;
2535 if (!bigblock
) return E_OUTOFMEMORY
;
2538 else if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2540 smallblock
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2541 if (!smallblock
) return E_OUTOFMEMORY
;
2545 pbigblock
= StorageImpl_GetCachedBlockChainStream(This
, index
);
2546 bigblock
= *pbigblock
;
2547 if (!bigblock
) return E_OUTOFMEMORY
;
2550 /* Change the block chain type if necessary. */
2551 if (smallblock
&& newsize
.QuadPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
2553 bigblock
= Storage32Impl_SmallBlocksToBigBlocks(This
, &smallblock
);
2556 SmallBlockChainStream_Destroy(smallblock
);
2560 pbigblock
= StorageImpl_GetFreeBlockChainCacheEntry(This
);
2561 *pbigblock
= bigblock
;
2563 else if (bigblock
&& newsize
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2565 smallblock
= Storage32Impl_BigBlocksToSmallBlocks(This
, pbigblock
, newsize
);
2570 /* Set the size of the block chain. */
2573 SmallBlockChainStream_SetSize(smallblock
, newsize
);
2574 SmallBlockChainStream_Destroy(smallblock
);
2578 BlockChainStream_SetSize(bigblock
, newsize
);
2581 /* Set the size in the directory entry. */
2582 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2585 data
.size
= newsize
;
2587 hr
= StorageImpl_WriteDirEntry(This
, index
, &data
);
2592 static HRESULT
StorageImpl_StreamWriteAt(StorageBaseImpl
*base
, DirRef index
,
2593 ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
2595 StorageImpl
*This
= (StorageImpl
*)base
;
2598 ULARGE_INTEGER newSize
;
2600 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2601 if (FAILED(hr
)) return hr
;
2603 /* Grow the stream if necessary */
2604 newSize
.QuadPart
= offset
.QuadPart
+ size
;
2606 if (newSize
.QuadPart
> data
.size
.QuadPart
)
2608 hr
= StorageImpl_StreamSetSize(base
, index
, newSize
);
2612 hr
= StorageImpl_ReadDirEntry(This
, index
, &data
);
2613 if (FAILED(hr
)) return hr
;
2616 if (data
.size
.QuadPart
< LIMIT_TO_USE_SMALL_BLOCK
)
2618 SmallBlockChainStream
*stream
;
2620 stream
= SmallBlockChainStream_Construct(This
, NULL
, index
);
2621 if (!stream
) return E_OUTOFMEMORY
;
2623 hr
= SmallBlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2625 SmallBlockChainStream_Destroy(stream
);
2631 BlockChainStream
*stream
;
2633 stream
= *StorageImpl_GetCachedBlockChainStream(This
, index
);
2634 if (!stream
) return E_OUTOFMEMORY
;
2636 return BlockChainStream_WriteAt(stream
, offset
, size
, buffer
, bytesWritten
);
2640 static HRESULT
StorageImpl_StreamLink(StorageBaseImpl
*base
, DirRef dst
,
2643 StorageImpl
*This
= (StorageImpl
*)base
;
2644 DirEntry dst_data
, src_data
;
2647 hr
= StorageImpl_ReadDirEntry(This
, dst
, &dst_data
);
2650 hr
= StorageImpl_ReadDirEntry(This
, src
, &src_data
);
2654 StorageImpl_DeleteCachedBlockChainStream(This
, src
);
2655 dst_data
.startingBlock
= src_data
.startingBlock
;
2656 dst_data
.size
= src_data
.size
;
2658 hr
= StorageImpl_WriteDirEntry(This
, dst
, &dst_data
);
2664 static HRESULT
StorageImpl_GetTransactionSig(StorageBaseImpl
*base
,
2665 ULONG
* result
, BOOL refresh
)
2667 StorageImpl
*This
= (StorageImpl
*)base
;
2669 DWORD oldTransactionSig
= This
->transactionSig
;
2673 ULARGE_INTEGER offset
;
2677 offset
.u
.HighPart
= 0;
2678 offset
.u
.LowPart
= OFFSET_TRANSACTIONSIG
;
2679 hr
= StorageImpl_ReadAt(This
, offset
, data
, 4, &bytes_read
);
2683 StorageUtl_ReadDWord(data
, 0, &This
->transactionSig
);
2685 if (oldTransactionSig
!= This
->transactionSig
)
2687 /* Someone else wrote to this, so toss all cached information. */
2688 TRACE("signature changed\n");
2690 hr
= StorageImpl_Refresh(This
, FALSE
, FALSE
);
2694 This
->transactionSig
= oldTransactionSig
;
2698 *result
= This
->transactionSig
;
2703 static HRESULT
StorageImpl_SetTransactionSig(StorageBaseImpl
*base
,
2706 StorageImpl
*This
= (StorageImpl
*)base
;
2708 This
->transactionSig
= value
;
2709 StorageImpl_SaveFileHeader(This
);
2714 static HRESULT
StorageImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
2716 StorageImpl
*This
= (StorageImpl
*)base
;
2718 ULARGE_INTEGER offset
, cb
;
2722 /* Synchronous grab of second priority range, the commit lock, and the
2723 * lock-checking lock. */
2724 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
2725 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
2729 offset
.QuadPart
= RANGELOCK_COMMIT
;
2733 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
);
2735 if (hr
== STG_E_INVALIDFUNCTION
)
2741 static HRESULT
StorageImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
2743 StorageImpl
*This
= (StorageImpl
*)base
;
2745 ULARGE_INTEGER offset
, cb
;
2749 offset
.QuadPart
= RANGELOCK_TRANSACTION_FIRST
;
2750 cb
.QuadPart
= RANGELOCK_TRANSACTION_LAST
- RANGELOCK_TRANSACTION_FIRST
+ 1;
2754 offset
.QuadPart
= RANGELOCK_COMMIT
;
2758 hr
= ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2760 if (hr
== STG_E_INVALIDFUNCTION
)
2766 static HRESULT
StorageImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
2768 StorageImpl
*This
= (StorageImpl
*) iface
;
2772 hr
= ILockBytes_Stat(This
->lockBytes
, &statstg
, 0);
2774 *result
= statstg
.pwcsName
;
2779 static HRESULT WINAPI
directwriterlock_QueryInterface(IDirectWriterLock
*iface
, REFIID riid
, void **obj
)
2781 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2782 return IStorage_QueryInterface(&This
->IStorage_iface
, riid
, obj
);
2785 static ULONG WINAPI
directwriterlock_AddRef(IDirectWriterLock
*iface
)
2787 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2788 return IStorage_AddRef(&This
->IStorage_iface
);
2791 static ULONG WINAPI
directwriterlock_Release(IDirectWriterLock
*iface
)
2793 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2794 return IStorage_Release(&This
->IStorage_iface
);
2797 static HRESULT WINAPI
directwriterlock_WaitForWriteAccess(IDirectWriterLock
*iface
, DWORD timeout
)
2799 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2800 FIXME("(%p)->(%d): stub\n", This
, timeout
);
2804 static HRESULT WINAPI
directwriterlock_ReleaseWriteAccess(IDirectWriterLock
*iface
)
2806 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2807 FIXME("(%p): stub\n", This
);
2811 static HRESULT WINAPI
directwriterlock_HaveWriteAccess(IDirectWriterLock
*iface
)
2813 StorageBaseImpl
*This
= impl_from_IDirectWriterLock(iface
);
2814 FIXME("(%p): stub\n", This
);
2818 static const IDirectWriterLockVtbl DirectWriterLockVtbl
=
2820 directwriterlock_QueryInterface
,
2821 directwriterlock_AddRef
,
2822 directwriterlock_Release
,
2823 directwriterlock_WaitForWriteAccess
,
2824 directwriterlock_ReleaseWriteAccess
,
2825 directwriterlock_HaveWriteAccess
2829 * Virtual function table for the IStorage32Impl class.
2831 static const IStorageVtbl Storage32Impl_Vtbl
=
2833 StorageBaseImpl_QueryInterface
,
2834 StorageBaseImpl_AddRef
,
2835 StorageBaseImpl_Release
,
2836 StorageBaseImpl_CreateStream
,
2837 StorageBaseImpl_OpenStream
,
2838 StorageBaseImpl_CreateStorage
,
2839 StorageBaseImpl_OpenStorage
,
2840 StorageBaseImpl_CopyTo
,
2841 StorageBaseImpl_MoveElementTo
,
2844 StorageBaseImpl_EnumElements
,
2845 StorageBaseImpl_DestroyElement
,
2846 StorageBaseImpl_RenameElement
,
2847 StorageBaseImpl_SetElementTimes
,
2848 StorageBaseImpl_SetClass
,
2849 StorageBaseImpl_SetStateBits
,
2850 StorageBaseImpl_Stat
2853 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2855 StorageImpl_Destroy
,
2856 StorageImpl_Invalidate
,
2858 StorageImpl_GetFilename
,
2859 StorageImpl_CreateDirEntry
,
2860 StorageImpl_BaseWriteDirEntry
,
2861 StorageImpl_BaseReadDirEntry
,
2862 StorageImpl_DestroyDirEntry
,
2863 StorageImpl_StreamReadAt
,
2864 StorageImpl_StreamWriteAt
,
2865 StorageImpl_StreamSetSize
,
2866 StorageImpl_StreamLink
,
2867 StorageImpl_GetTransactionSig
,
2868 StorageImpl_SetTransactionSig
,
2869 StorageImpl_LockTransaction
,
2870 StorageImpl_UnlockTransaction
2873 static HRESULT
StorageImpl_LockRegionSync(StorageImpl
*This
, ULARGE_INTEGER offset
,
2874 ULARGE_INTEGER cb
, DWORD dwLockType
)
2878 /* if it's a FileLockBytesImpl use LockFileEx in blocking mode */
2879 if (SUCCEEDED(FileLockBytesImpl_LockRegionSync(This
->lockBytes
, offset
, cb
)))
2882 /* otherwise we have to fake it based on an async lock */
2887 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
2889 if (hr
== STG_E_ACCESSDENIED
)
2892 if (delay
< 150) delay
++;
2894 } while (hr
== STG_E_ACCESSDENIED
);
2899 static HRESULT
StorageImpl_CheckLockRange(StorageImpl
*This
, ULONG start
,
2900 ULONG end
, HRESULT fail_hr
)
2903 ULARGE_INTEGER offset
, cb
;
2905 offset
.QuadPart
= start
;
2906 cb
.QuadPart
= 1 + end
- start
;
2908 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2909 if (SUCCEEDED(hr
)) ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2911 if (hr
== STG_E_ACCESSDENIED
)
2917 static HRESULT
StorageImpl_LockOne(StorageImpl
*This
, ULONG start
, ULONG end
)
2921 ULARGE_INTEGER offset
, cb
;
2925 for (i
=start
; i
<=end
; i
++)
2927 offset
.QuadPart
= i
;
2928 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2929 if (hr
!= STG_E_ACCESSDENIED
)
2935 for (j
=0; j
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); j
++)
2937 if (This
->locked_bytes
[j
] == 0)
2939 This
->locked_bytes
[j
] = i
;
2948 static HRESULT
StorageImpl_GrabLocks(StorageImpl
*This
, DWORD openFlags
)
2951 ULARGE_INTEGER offset
;
2953 DWORD share_mode
= STGM_SHARE_MODE(openFlags
);
2955 if (openFlags
& STGM_NOSNAPSHOT
)
2957 /* STGM_NOSNAPSHOT implies deny write */
2958 if (share_mode
== STGM_SHARE_DENY_READ
) share_mode
= STGM_SHARE_EXCLUSIVE
;
2959 else if (share_mode
!= STGM_SHARE_EXCLUSIVE
) share_mode
= STGM_SHARE_DENY_WRITE
;
2962 /* Wrap all other locking inside a single lock so we can check ranges safely */
2963 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
2965 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
);
2967 /* If the ILockBytes doesn't support locking that's ok. */
2968 if (FAILED(hr
)) return S_OK
;
2972 /* First check for any conflicting locks. */
2973 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
2974 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_COMMIT
, RANGELOCK_COMMIT
, STG_E_LOCKVIOLATION
);
2976 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
2977 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
, STG_E_SHAREVIOLATION
);
2979 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
2980 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
, STG_E_SHAREVIOLATION
);
2982 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
2983 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
, STG_E_LOCKVIOLATION
);
2985 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
2986 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
, STG_E_LOCKVIOLATION
);
2988 /* Then grab our locks. */
2989 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
2991 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY1_FIRST
, RANGELOCK_PRIORITY1_LAST
);
2993 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY2_FIRST
, RANGELOCK_PRIORITY2_LAST
);
2996 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
2997 hr
= StorageImpl_LockOne(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
);
2999 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
3000 hr
= StorageImpl_LockOne(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
);
3002 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
3003 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
);
3005 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
3006 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
);
3008 if (SUCCEEDED(hr
) && (openFlags
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
)
3009 hr
= StorageImpl_LockOne(This
, RANGELOCK_NOSNAPSHOT_FIRST
, RANGELOCK_NOSNAPSHOT_LAST
);
3011 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
3013 ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
3018 static HRESULT
StorageImpl_Refresh(StorageImpl
*This
, BOOL new_object
, BOOL create
)
3021 DirEntry currentEntry
;
3022 DirRef currentEntryRef
;
3023 BlockChainStream
*blockChainStream
;
3027 ULARGE_INTEGER size
;
3028 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
3030 /* Discard any existing data. */
3032 ILockBytes_SetSize(This
->lockBytes
, size
);
3035 * Initialize all header variables:
3036 * - The big block depot consists of one block and it is at block 0
3037 * - The directory table starts at block 1
3038 * - There is no small block depot
3040 memset( This
->bigBlockDepotStart
,
3042 sizeof(This
->bigBlockDepotStart
));
3044 This
->bigBlockDepotCount
= 1;
3045 This
->bigBlockDepotStart
[0] = 0;
3046 This
->rootStartBlock
= 1;
3047 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
3048 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
3049 if (This
->bigBlockSize
== 4096)
3050 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
3052 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
3053 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
3054 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
3055 This
->extBigBlockDepotCount
= 0;
3057 StorageImpl_SaveFileHeader(This
);
3060 * Add one block for the big block depot and one block for the directory table
3062 size
.u
.HighPart
= 0;
3063 size
.u
.LowPart
= This
->bigBlockSize
* 3;
3064 ILockBytes_SetSize(This
->lockBytes
, size
);
3067 * Initialize the big block depot
3069 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3070 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
3071 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
3072 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
3077 * Load the header for the file.
3079 hr
= StorageImpl_LoadFileHeader(This
);
3088 * There is no block depot cached yet.
3090 This
->indexBlockDepotCached
= 0xFFFFFFFF;
3091 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
3094 * Start searching for free blocks with block 0.
3096 This
->prevFreeBlock
= 0;
3098 This
->firstFreeSmallBlock
= 0;
3100 /* Read the extended big block depot locations. */
3101 if (This
->extBigBlockDepotCount
!= 0)
3103 ULONG current_block
= This
->extBigBlockDepotStart
;
3104 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
3107 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
3108 if (!This
->extBigBlockDepotLocations
)
3110 return E_OUTOFMEMORY
;
3113 This
->extBigBlockDepotLocationsSize
= cache_size
;
3115 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
3117 if (current_block
== BLOCK_END_OF_CHAIN
)
3119 WARN("File has too few extended big block depot blocks.\n");
3120 return STG_E_DOCFILECORRUPT
;
3122 This
->extBigBlockDepotLocations
[i
] = current_block
;
3123 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
3128 This
->extBigBlockDepotLocations
= NULL
;
3129 This
->extBigBlockDepotLocationsSize
= 0;
3133 * Create the block chain abstractions.
3135 if(!(blockChainStream
=
3136 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
3138 return STG_E_READFAULT
;
3141 BlockChainStream_Destroy(This
->rootBlockChain
);
3142 This
->rootBlockChain
= blockChainStream
;
3144 if(!(blockChainStream
=
3145 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
3148 return STG_E_READFAULT
;
3151 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
3152 This
->smallBlockDepotChain
= blockChainStream
;
3155 * Write the root storage entry (memory only)
3159 static const WCHAR rootentryW
[] = {'R','o','o','t',' ','E','n','t','r','y',0};
3162 * Initialize the directory table
3164 memset(&rootEntry
, 0, sizeof(rootEntry
));
3165 strcpyW(rootEntry
.name
, rootentryW
);
3166 rootEntry
.sizeOfNameString
= sizeof(rootentryW
);
3167 rootEntry
.stgType
= STGTY_ROOT
;
3168 rootEntry
.leftChild
= DIRENTRY_NULL
;
3169 rootEntry
.rightChild
= DIRENTRY_NULL
;
3170 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
3171 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
3172 rootEntry
.size
.u
.HighPart
= 0;
3173 rootEntry
.size
.u
.LowPart
= 0;
3175 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
3179 * Find the ID of the root storage.
3181 currentEntryRef
= 0;
3185 hr
= StorageImpl_ReadDirEntry(
3192 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
3193 (currentEntry
.stgType
== STGTY_ROOT
) )
3195 This
->base
.storageDirEntry
= currentEntryRef
;
3201 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
3205 return STG_E_READFAULT
;
3209 * Create the block chain abstraction for the small block root chain.
3211 if(!(blockChainStream
=
3212 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
3214 return STG_E_READFAULT
;
3217 BlockChainStream_Destroy(This
->smallBlockRootChain
);
3218 This
->smallBlockRootChain
= blockChainStream
;
3223 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3225 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
3226 This
->blockChainCache
[i
] = NULL
;
3233 static HRESULT
StorageImpl_Construct(
3241 StorageImpl
** result
)
3246 if ( FAILED( validateSTGM(openFlags
) ))
3247 return STG_E_INVALIDFLAG
;
3249 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
3251 return E_OUTOFMEMORY
;
3253 memset(This
, 0, sizeof(StorageImpl
));
3255 list_init(&This
->base
.strmHead
);
3257 list_init(&This
->base
.storageHead
);
3259 This
->base
.IStorage_iface
.lpVtbl
= &Storage32Impl_Vtbl
;
3260 This
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
3261 This
->base
.IDirectWriterLock_iface
.lpVtbl
= &DirectWriterLockVtbl
;
3262 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
3263 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
3265 This
->base
.create
= create
;
3267 if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
))
3268 This
->base
.lockingrole
= SWMR_Writer
;
3269 else if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READ
|STGM_SHARE_DENY_NONE
))
3270 This
->base
.lockingrole
= SWMR_Reader
;
3272 This
->base
.lockingrole
= SWMR_None
;
3274 This
->base
.reverted
= FALSE
;
3277 * Initialize the big block cache.
3279 This
->bigBlockSize
= sector_size
;
3280 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
3282 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
3285 This
->lockBytes
= pLkbyt
;
3286 ILockBytes_AddRef(pLkbyt
);
3290 hr
= StorageImpl_GrabLocks(This
, openFlags
);
3293 hr
= StorageImpl_Refresh(This
, TRUE
, create
);
3297 IStorage_Release(&This
->base
.IStorage_iface
);
3302 StorageImpl_Flush(&This
->base
);
3309 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
3311 StorageImpl
*This
= (StorageImpl
*) iface
;
3313 StorageBaseImpl_DeleteAll(&This
->base
);
3315 This
->base
.reverted
= TRUE
;
3318 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
3320 StorageImpl
*This
= (StorageImpl
*) iface
;
3322 TRACE("(%p)\n", This
);
3324 StorageImpl_Flush(iface
);
3326 StorageImpl_Invalidate(iface
);
3328 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3330 BlockChainStream_Destroy(This
->smallBlockRootChain
);
3331 BlockChainStream_Destroy(This
->rootBlockChain
);
3332 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
3334 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3335 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
3337 for (i
=0; i
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); i
++)
3339 ULARGE_INTEGER offset
, cb
;
3341 if (This
->locked_bytes
[i
] != 0)
3343 offset
.QuadPart
= This
->locked_bytes
[i
];
3344 ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
3348 if (This
->lockBytes
)
3349 ILockBytes_Release(This
->lockBytes
);
3350 HeapFree(GetProcessHeap(), 0, This
);
3353 static HRESULT
StorageImpl_Flush(StorageBaseImpl
*storage
)
3355 StorageImpl
*This
= (StorageImpl
*)storage
;
3358 TRACE("(%p)\n", This
);
3360 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
3363 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
3366 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
3368 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3369 if (This
->blockChainCache
[i
])
3370 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
3373 hr
= ILockBytes_Flush(This
->lockBytes
);
3378 /******************************************************************************
3379 * Storage32Impl_GetNextFreeBigBlock
3381 * Returns the index of the next free big block.
3382 * If the big block depot is filled, this method will enlarge it.
3385 static ULONG
StorageImpl_GetNextFreeBigBlock(
3388 ULONG depotBlockIndexPos
;
3389 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3390 ULONG depotBlockOffset
;
3391 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3392 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3394 ULONG freeBlock
= BLOCK_UNUSED
;
3396 ULARGE_INTEGER neededSize
;
3399 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
3400 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
3403 * Scan the entire big block depot until we find a block marked free
3405 while (nextBlockIndex
!= BLOCK_UNUSED
)
3407 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
3409 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
3412 * Grow the primary depot.
3414 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3416 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
3419 * Add a block depot.
3421 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
3422 This
->bigBlockDepotCount
++;
3423 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
3426 * Flag it as a block depot.
3428 StorageImpl_SetNextBlockInChain(This
,
3432 /* Save new header information.
3434 StorageImpl_SaveFileHeader(This
);
3439 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
3441 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3444 * Grow the extended depot.
3446 ULONG extIndex
= BLOCK_UNUSED
;
3447 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3448 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
3450 if (extBlockOffset
== 0)
3452 /* We need an extended block.
3454 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
3455 This
->extBigBlockDepotCount
++;
3456 depotBlockIndexPos
= extIndex
+ 1;
3459 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
3462 * Add a block depot and mark it in the extended block.
3464 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
3465 This
->bigBlockDepotCount
++;
3466 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
3468 /* Flag the block depot.
3470 StorageImpl_SetNextBlockInChain(This
,
3474 /* If necessary, flag the extended depot block.
3476 if (extIndex
!= BLOCK_UNUSED
)
3477 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
3479 /* Save header information.
3481 StorageImpl_SaveFileHeader(This
);
3485 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
3489 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
3490 ( nextBlockIndex
!= BLOCK_UNUSED
))
3492 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
3494 if (nextBlockIndex
== BLOCK_UNUSED
)
3496 freeBlock
= (depotIndex
* blocksPerDepot
) +
3497 (depotBlockOffset
/sizeof(ULONG
));
3500 depotBlockOffset
+= sizeof(ULONG
);
3505 depotBlockOffset
= 0;
3509 * make sure that the block physically exists before using it
3511 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
3513 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
3515 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
3516 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
3518 This
->prevFreeBlock
= freeBlock
;
3523 /******************************************************************************
3524 * Storage32Impl_AddBlockDepot
3526 * This will create a depot block, essentially it is a block initialized
3529 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
, ULONG depotIndex
)
3531 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3532 ULONG rangeLockIndex
= RANGELOCK_FIRST
/ This
->bigBlockSize
- 1;
3533 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3534 ULONG rangeLockDepot
= rangeLockIndex
/ blocksPerDepot
;
3537 * Initialize blocks as free
3539 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3541 /* Reserve the range lock sector */
3542 if (depotIndex
== rangeLockDepot
)
3544 ((ULONG
*)blockBuffer
)[rangeLockIndex
% blocksPerDepot
] = BLOCK_END_OF_CHAIN
;
3547 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3550 /******************************************************************************
3551 * Storage32Impl_GetExtDepotBlock
3553 * Returns the index of the block that corresponds to the specified depot
3554 * index. This method is only for depot indexes equal or greater than
3555 * COUNT_BBDEPOTINHEADER.
3557 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3559 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3560 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3561 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3562 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3563 ULONG blockIndex
= BLOCK_UNUSED
;
3564 ULONG extBlockIndex
;
3565 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3566 int index
, num_blocks
;
3568 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3570 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3571 return BLOCK_UNUSED
;
3573 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3575 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3577 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
, NULL
);
3579 num_blocks
= This
->bigBlockSize
/ 4;
3581 for (index
= 0; index
< num_blocks
; index
++)
3583 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3584 This
->extBlockDepotCached
[index
] = blockIndex
;
3587 This
->indexExtBlockDepotCached
= extBlockCount
;
3590 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3595 /******************************************************************************
3596 * Storage32Impl_SetExtDepotBlock
3598 * Associates the specified block index to the specified depot index.
3599 * This method is only for depot indexes equal or greater than
3600 * COUNT_BBDEPOTINHEADER.
3602 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3604 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3605 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3606 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3607 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3608 ULONG extBlockIndex
;
3610 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3612 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3614 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3616 if (extBlockIndex
!= BLOCK_UNUSED
)
3618 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3619 extBlockOffset
* sizeof(ULONG
),
3623 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3625 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3629 /******************************************************************************
3630 * Storage32Impl_AddExtBlockDepot
3632 * Creates an extended depot block.
3634 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3636 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3637 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3638 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3639 ULONG index
= BLOCK_UNUSED
;
3640 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3641 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3642 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3644 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3645 blocksPerDepotBlock
;
3647 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3650 * The first extended block.
3652 This
->extBigBlockDepotStart
= index
;
3657 * Find the last existing extended block.
3659 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3662 * Add the new extended block to the chain.
3664 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3669 * Initialize this block.
3671 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3672 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3674 /* Add the block to our cache. */
3675 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3677 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3678 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3680 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3681 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3683 This
->extBigBlockDepotLocations
= new_cache
;
3684 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3686 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3691 /******************************************************************************
3692 * Storage32Impl_FreeBigBlock
3694 * This method will flag the specified block as free in the big block depot.
3696 static void StorageImpl_FreeBigBlock(
3700 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3702 if (blockIndex
< This
->prevFreeBlock
)
3703 This
->prevFreeBlock
= blockIndex
;
3706 /************************************************************************
3707 * Storage32Impl_GetNextBlockInChain
3709 * This method will retrieve the block index of the next big block in
3712 * Params: This - Pointer to the Storage object.
3713 * blockIndex - Index of the block to retrieve the chain
3715 * nextBlockIndex - receives the return value.
3717 * Returns: This method returns the index of the next block in the chain.
3718 * It will return the constants:
3719 * BLOCK_SPECIAL - If the block given was not part of a
3721 * BLOCK_END_OF_CHAIN - If the block given was the last in
3723 * BLOCK_UNUSED - If the block given was not past of a chain
3725 * BLOCK_EXTBBDEPOT - This block is part of the extended
3728 * See Windows documentation for more details on IStorage methods.
3730 static HRESULT
StorageImpl_GetNextBlockInChain(
3733 ULONG
* nextBlockIndex
)
3735 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3736 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3737 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3738 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3740 ULONG depotBlockIndexPos
;
3741 int index
, num_blocks
;
3743 *nextBlockIndex
= BLOCK_SPECIAL
;
3745 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3747 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3748 This
->bigBlockDepotCount
);
3749 return STG_E_READFAULT
;
3753 * Cache the currently accessed depot block.
3755 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3757 This
->indexBlockDepotCached
= depotBlockCount
;
3759 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3761 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3766 * We have to look in the extended depot.
3768 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3771 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
3774 return STG_E_READFAULT
;
3776 num_blocks
= This
->bigBlockSize
/ 4;
3778 for (index
= 0; index
< num_blocks
; index
++)
3780 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3781 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3785 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3790 /******************************************************************************
3791 * Storage32Impl_GetNextExtendedBlock
3793 * Given an extended block this method will return the next extended block.
3796 * The last ULONG of an extended block is the block index of the next
3797 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3801 * - The index of the next extended block
3802 * - BLOCK_UNUSED: there is no next extended block.
3803 * - Any other return values denotes failure.
3805 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3807 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3808 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3810 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3813 return nextBlockIndex
;
3816 /******************************************************************************
3817 * Storage32Impl_SetNextBlockInChain
3819 * This method will write the index of the specified block's next block
3820 * in the big block depot.
3822 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3825 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3826 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3827 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3830 static void StorageImpl_SetNextBlockInChain(
3835 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3836 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3837 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3838 ULONG depotBlockIndexPos
;
3840 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3841 assert(blockIndex
!= nextBlock
);
3843 if (blockIndex
== (RANGELOCK_FIRST
/ This
->bigBlockSize
) - 1)
3844 /* This should never happen (storage file format spec forbids it), but
3845 * older versions of Wine may have generated broken files. We don't want to
3846 * assert and potentially lose data, but we do want to know if this ever
3847 * happens in a newly-created file. */
3848 ERR("Using range lock page\n");
3850 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3852 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3857 * We have to look in the extended depot.
3859 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3862 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3865 * Update the cached block depot, if necessary.
3867 if (depotBlockCount
== This
->indexBlockDepotCached
)
3869 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3873 /******************************************************************************
3874 * Storage32Impl_LoadFileHeader
3876 * This method will read in the file header
3878 static HRESULT
StorageImpl_LoadFileHeader(
3882 BYTE headerBigBlock
[HEADER_SIZE
];
3884 ULARGE_INTEGER offset
;
3889 * Get a pointer to the big block of data containing the header.
3891 offset
.u
.HighPart
= 0;
3892 offset
.u
.LowPart
= 0;
3893 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3894 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3895 hr
= STG_E_FILENOTFOUND
;
3898 * Extract the information from the header.
3903 * Check for the "magic number" signature and return an error if it is not
3906 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3908 return STG_E_OLDFORMAT
;
3911 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3913 return STG_E_INVALIDHEADER
;
3916 StorageUtl_ReadWord(
3918 OFFSET_BIGBLOCKSIZEBITS
,
3919 &This
->bigBlockSizeBits
);
3921 StorageUtl_ReadWord(
3923 OFFSET_SMALLBLOCKSIZEBITS
,
3924 &This
->smallBlockSizeBits
);
3926 StorageUtl_ReadDWord(
3928 OFFSET_BBDEPOTCOUNT
,
3929 &This
->bigBlockDepotCount
);
3931 StorageUtl_ReadDWord(
3933 OFFSET_ROOTSTARTBLOCK
,
3934 &This
->rootStartBlock
);
3936 StorageUtl_ReadDWord(
3938 OFFSET_TRANSACTIONSIG
,
3939 &This
->transactionSig
);
3941 StorageUtl_ReadDWord(
3943 OFFSET_SMALLBLOCKLIMIT
,
3944 &This
->smallBlockLimit
);
3946 StorageUtl_ReadDWord(
3948 OFFSET_SBDEPOTSTART
,
3949 &This
->smallBlockDepotStart
);
3951 StorageUtl_ReadDWord(
3953 OFFSET_EXTBBDEPOTSTART
,
3954 &This
->extBigBlockDepotStart
);
3956 StorageUtl_ReadDWord(
3958 OFFSET_EXTBBDEPOTCOUNT
,
3959 &This
->extBigBlockDepotCount
);
3961 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3963 StorageUtl_ReadDWord(
3965 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3966 &(This
->bigBlockDepotStart
[index
]));
3970 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3972 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3973 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3976 * Right now, the code is making some assumptions about the size of the
3977 * blocks, just make sure they are what we're expecting.
3979 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3980 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
3981 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
3983 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3984 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3985 hr
= STG_E_INVALIDHEADER
;
3994 /******************************************************************************
3995 * Storage32Impl_SaveFileHeader
3997 * This method will save to the file the header
3999 static void StorageImpl_SaveFileHeader(
4002 BYTE headerBigBlock
[HEADER_SIZE
];
4005 ULARGE_INTEGER offset
;
4006 DWORD bytes_read
, bytes_written
;
4007 DWORD major_version
, dirsectorcount
;
4010 * Get a pointer to the big block of data containing the header.
4012 offset
.u
.HighPart
= 0;
4013 offset
.u
.LowPart
= 0;
4014 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
4015 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
4016 hr
= STG_E_FILENOTFOUND
;
4018 if (This
->bigBlockSizeBits
== 0x9)
4020 else if (This
->bigBlockSizeBits
== 0xc)
4024 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
4029 * If the block read failed, the file is probably new.
4034 * Initialize for all unknown fields.
4036 memset(headerBigBlock
, 0, HEADER_SIZE
);
4039 * Initialize the magic number.
4041 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
4045 * Write the information to the header.
4047 StorageUtl_WriteWord(
4049 OFFSET_MINORVERSION
,
4052 StorageUtl_WriteWord(
4054 OFFSET_MAJORVERSION
,
4057 StorageUtl_WriteWord(
4059 OFFSET_BYTEORDERMARKER
,
4062 StorageUtl_WriteWord(
4064 OFFSET_BIGBLOCKSIZEBITS
,
4065 This
->bigBlockSizeBits
);
4067 StorageUtl_WriteWord(
4069 OFFSET_SMALLBLOCKSIZEBITS
,
4070 This
->smallBlockSizeBits
);
4072 if (major_version
>= 4)
4074 if (This
->rootBlockChain
)
4075 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
4077 /* This file is being created, and it will start out with one block. */
4081 /* This field must be 0 in versions older than 4 */
4084 StorageUtl_WriteDWord(
4086 OFFSET_DIRSECTORCOUNT
,
4089 StorageUtl_WriteDWord(
4091 OFFSET_BBDEPOTCOUNT
,
4092 This
->bigBlockDepotCount
);
4094 StorageUtl_WriteDWord(
4096 OFFSET_ROOTSTARTBLOCK
,
4097 This
->rootStartBlock
);
4099 StorageUtl_WriteDWord(
4101 OFFSET_TRANSACTIONSIG
,
4102 This
->transactionSig
);
4104 StorageUtl_WriteDWord(
4106 OFFSET_SMALLBLOCKLIMIT
,
4107 This
->smallBlockLimit
);
4109 StorageUtl_WriteDWord(
4111 OFFSET_SBDEPOTSTART
,
4112 This
->smallBlockDepotStart
);
4114 StorageUtl_WriteDWord(
4116 OFFSET_SBDEPOTCOUNT
,
4117 This
->smallBlockDepotChain
?
4118 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
4120 StorageUtl_WriteDWord(
4122 OFFSET_EXTBBDEPOTSTART
,
4123 This
->extBigBlockDepotStart
);
4125 StorageUtl_WriteDWord(
4127 OFFSET_EXTBBDEPOTCOUNT
,
4128 This
->extBigBlockDepotCount
);
4130 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
4132 StorageUtl_WriteDWord(
4134 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
4135 (This
->bigBlockDepotStart
[index
]));
4139 * Write the big block back to the file.
4141 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
4144 /******************************************************************************
4145 * StorageImpl_ReadRawDirEntry
4147 * This method will read the raw data from a directory entry in the file.
4149 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4151 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
4153 ULARGE_INTEGER offset
;
4157 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
4159 hr
= BlockChainStream_ReadAt(
4160 This
->rootBlockChain
,
4166 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
4167 return STG_E_READFAULT
;
4172 /******************************************************************************
4173 * StorageImpl_WriteRawDirEntry
4175 * This method will write the raw data from a directory entry in the file.
4177 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4179 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
4181 ULARGE_INTEGER offset
;
4184 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
4186 return BlockChainStream_WriteAt(
4187 This
->rootBlockChain
,
4194 /******************************************************************************
4197 * Update raw directory entry data from the fields in newData.
4199 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4201 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
4203 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
4206 buffer
+ OFFSET_PS_NAME
,
4208 DIRENTRY_NAME_BUFFER_LEN
);
4210 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
4212 StorageUtl_WriteWord(
4214 OFFSET_PS_NAMELENGTH
,
4215 newData
->sizeOfNameString
);
4217 StorageUtl_WriteDWord(
4219 OFFSET_PS_LEFTCHILD
,
4220 newData
->leftChild
);
4222 StorageUtl_WriteDWord(
4224 OFFSET_PS_RIGHTCHILD
,
4225 newData
->rightChild
);
4227 StorageUtl_WriteDWord(
4230 newData
->dirRootEntry
);
4232 StorageUtl_WriteGUID(
4237 StorageUtl_WriteDWord(
4240 newData
->ctime
.dwLowDateTime
);
4242 StorageUtl_WriteDWord(
4244 OFFSET_PS_CTIMEHIGH
,
4245 newData
->ctime
.dwHighDateTime
);
4247 StorageUtl_WriteDWord(
4250 newData
->mtime
.dwLowDateTime
);
4252 StorageUtl_WriteDWord(
4254 OFFSET_PS_MTIMEHIGH
,
4255 newData
->ctime
.dwHighDateTime
);
4257 StorageUtl_WriteDWord(
4259 OFFSET_PS_STARTBLOCK
,
4260 newData
->startingBlock
);
4262 StorageUtl_WriteDWord(
4265 newData
->size
.u
.LowPart
);
4267 StorageUtl_WriteDWord(
4269 OFFSET_PS_SIZE_HIGH
,
4270 newData
->size
.u
.HighPart
);
4273 /******************************************************************************
4274 * Storage32Impl_ReadDirEntry
4276 * This method will read the specified directory entry.
4278 HRESULT
StorageImpl_ReadDirEntry(
4283 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
4286 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
4288 if (SUCCEEDED(readRes
))
4290 memset(buffer
->name
, 0, sizeof(buffer
->name
));
4293 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
4294 DIRENTRY_NAME_BUFFER_LEN
);
4295 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
4297 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
4299 StorageUtl_ReadWord(
4301 OFFSET_PS_NAMELENGTH
,
4302 &buffer
->sizeOfNameString
);
4304 StorageUtl_ReadDWord(
4306 OFFSET_PS_LEFTCHILD
,
4307 &buffer
->leftChild
);
4309 StorageUtl_ReadDWord(
4311 OFFSET_PS_RIGHTCHILD
,
4312 &buffer
->rightChild
);
4314 StorageUtl_ReadDWord(
4317 &buffer
->dirRootEntry
);
4319 StorageUtl_ReadGUID(
4324 StorageUtl_ReadDWord(
4327 &buffer
->ctime
.dwLowDateTime
);
4329 StorageUtl_ReadDWord(
4331 OFFSET_PS_CTIMEHIGH
,
4332 &buffer
->ctime
.dwHighDateTime
);
4334 StorageUtl_ReadDWord(
4337 &buffer
->mtime
.dwLowDateTime
);
4339 StorageUtl_ReadDWord(
4341 OFFSET_PS_MTIMEHIGH
,
4342 &buffer
->mtime
.dwHighDateTime
);
4344 StorageUtl_ReadDWord(
4346 OFFSET_PS_STARTBLOCK
,
4347 &buffer
->startingBlock
);
4349 StorageUtl_ReadDWord(
4352 &buffer
->size
.u
.LowPart
);
4354 StorageUtl_ReadDWord(
4356 OFFSET_PS_SIZE_HIGH
,
4357 &buffer
->size
.u
.HighPart
);
4363 /*********************************************************************
4364 * Write the specified directory entry to the file
4366 HRESULT
StorageImpl_WriteDirEntry(
4369 const DirEntry
* buffer
)
4371 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
4373 UpdateRawDirEntry(currentEntry
, buffer
);
4375 return StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
4378 static HRESULT
StorageImpl_ReadBigBlock(
4384 ULARGE_INTEGER ulOffset
;
4388 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4390 hr
= StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
4392 if (SUCCEEDED(hr
) && read
< This
->bigBlockSize
)
4394 /* File ends during this block; fill the rest with 0's. */
4395 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
4398 if (out_read
) *out_read
= read
;
4403 static BOOL
StorageImpl_ReadDWordFromBigBlock(
4409 ULARGE_INTEGER ulOffset
;
4413 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4414 ulOffset
.QuadPart
+= offset
;
4416 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
4417 *value
= lendian32toh(tmp
);
4418 return (read
== sizeof(DWORD
));
4421 static BOOL
StorageImpl_WriteBigBlock(
4426 ULARGE_INTEGER ulOffset
;
4429 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4431 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
4432 return (wrote
== This
->bigBlockSize
);
4435 static BOOL
StorageImpl_WriteDWordToBigBlock(
4441 ULARGE_INTEGER ulOffset
;
4444 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4445 ulOffset
.QuadPart
+= offset
;
4447 value
= htole32(value
);
4448 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
4449 return (wrote
== sizeof(DWORD
));
4452 /******************************************************************************
4453 * Storage32Impl_SmallBlocksToBigBlocks
4455 * This method will convert a small block chain to a big block chain.
4456 * The small block chain will be destroyed.
4458 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
4460 SmallBlockChainStream
** ppsbChain
)
4462 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4463 ULARGE_INTEGER size
, offset
;
4464 ULONG cbRead
, cbWritten
;
4465 ULARGE_INTEGER cbTotalRead
;
4466 DirRef streamEntryRef
;
4467 HRESULT resWrite
= S_OK
;
4469 DirEntry streamEntry
;
4471 BlockChainStream
*bbTempChain
= NULL
;
4472 BlockChainStream
*bigBlockChain
= NULL
;
4475 * Create a temporary big block chain that doesn't have
4476 * an associated directory entry. This temporary chain will be
4477 * used to copy data from small blocks to big blocks.
4479 bbTempChain
= BlockChainStream_Construct(This
,
4482 if(!bbTempChain
) return NULL
;
4484 * Grow the big block chain.
4486 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
4487 BlockChainStream_SetSize(bbTempChain
, size
);
4490 * Copy the contents of the small block chain to the big block chain
4491 * by small block size increments.
4493 offset
.u
.LowPart
= 0;
4494 offset
.u
.HighPart
= 0;
4495 cbTotalRead
.QuadPart
= 0;
4497 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
4500 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
4502 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4505 if (FAILED(resRead
))
4510 cbTotalRead
.QuadPart
+= cbRead
;
4512 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
4518 if (FAILED(resWrite
))
4521 offset
.u
.LowPart
+= cbRead
;
4525 resRead
= STG_E_READFAULT
;
4528 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
4529 HeapFree(GetProcessHeap(),0,buffer
);
4531 size
.u
.HighPart
= 0;
4534 if (FAILED(resRead
) || FAILED(resWrite
))
4536 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4537 BlockChainStream_SetSize(bbTempChain
, size
);
4538 BlockChainStream_Destroy(bbTempChain
);
4543 * Destroy the small block chain.
4545 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
4546 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
4547 SmallBlockChainStream_Destroy(*ppsbChain
);
4551 * Change the directory entry. This chain is now a big block chain
4552 * and it doesn't reside in the small blocks chain anymore.
4554 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4556 streamEntry
.startingBlock
= bbHeadOfChain
;
4558 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4561 * Destroy the temporary entryless big block chain.
4562 * Create a new big block chain associated with this entry.
4564 BlockChainStream_Destroy(bbTempChain
);
4565 bigBlockChain
= BlockChainStream_Construct(This
,
4569 return bigBlockChain
;
4572 /******************************************************************************
4573 * Storage32Impl_BigBlocksToSmallBlocks
4575 * This method will convert a big block chain to a small block chain.
4576 * The big block chain will be destroyed on success.
4578 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
4580 BlockChainStream
** ppbbChain
,
4581 ULARGE_INTEGER newSize
)
4583 ULARGE_INTEGER size
, offset
, cbTotalRead
;
4584 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4585 DirRef streamEntryRef
;
4586 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
4587 DirEntry streamEntry
;
4589 SmallBlockChainStream
* sbTempChain
;
4591 TRACE("%p %p\n", This
, ppbbChain
);
4593 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
4599 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
4600 size
= BlockChainStream_GetSize(*ppbbChain
);
4601 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
4603 offset
.u
.HighPart
= 0;
4604 offset
.u
.LowPart
= 0;
4605 cbTotalRead
.QuadPart
= 0;
4606 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
4607 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
4609 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
4610 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4618 cbTotalRead
.QuadPart
+= cbRead
;
4620 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
4621 cbRead
, buffer
, &cbWritten
);
4623 if(FAILED(resWrite
))
4626 offset
.u
.LowPart
+= cbRead
;
4630 resRead
= STG_E_READFAULT
;
4634 HeapFree(GetProcessHeap(), 0, buffer
);
4636 size
.u
.HighPart
= 0;
4639 if(FAILED(resRead
) || FAILED(resWrite
))
4641 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4642 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4643 SmallBlockChainStream_Destroy(sbTempChain
);
4647 /* destroy the original big block chain */
4648 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4649 BlockChainStream_SetSize(*ppbbChain
, size
);
4650 BlockChainStream_Destroy(*ppbbChain
);
4653 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4654 streamEntry
.startingBlock
= sbHeadOfChain
;
4655 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4657 SmallBlockChainStream_Destroy(sbTempChain
);
4658 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4661 static HRESULT
StorageBaseImpl_CopyStream(
4662 StorageBaseImpl
*dst
, DirRef dst_entry
,
4663 StorageBaseImpl
*src
, DirRef src_entry
)
4668 ULARGE_INTEGER bytes_copied
;
4669 ULONG bytestocopy
, bytesread
, byteswritten
;
4671 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
4675 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
4677 bytes_copied
.QuadPart
= 0;
4678 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
4680 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
4682 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
4684 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
4687 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
4688 data
, &byteswritten
);
4691 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
4692 bytes_copied
.QuadPart
+= byteswritten
;
4700 static HRESULT
StorageBaseImpl_DupStorageTree(
4701 StorageBaseImpl
*dst
, DirRef
*dst_entry
,
4702 StorageBaseImpl
*src
, DirRef src_entry
)
4706 BOOL has_stream
=FALSE
;
4708 if (src_entry
== DIRENTRY_NULL
)
4710 *dst_entry
= DIRENTRY_NULL
;
4714 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &data
);
4717 has_stream
= (data
.stgType
== STGTY_STREAM
&& data
.size
.QuadPart
!= 0);
4718 data
.startingBlock
= BLOCK_END_OF_CHAIN
;
4719 data
.size
.QuadPart
= 0;
4721 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.leftChild
, src
, data
.leftChild
);
4725 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.rightChild
, src
, data
.rightChild
);
4728 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.dirRootEntry
, src
, data
.dirRootEntry
);
4731 hr
= StorageBaseImpl_CreateDirEntry(dst
, &data
, dst_entry
);
4733 if (SUCCEEDED(hr
) && has_stream
)
4734 hr
= StorageBaseImpl_CopyStream(dst
, *dst_entry
, src
, src_entry
);
4739 static HRESULT
StorageBaseImpl_CopyStorageTree(
4740 StorageBaseImpl
*dst
, DirRef dst_entry
,
4741 StorageBaseImpl
*src
, DirRef src_entry
)
4744 DirEntry src_data
, dst_data
;
4745 DirRef new_root_entry
;
4747 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &src_data
);
4751 hr
= StorageBaseImpl_DupStorageTree(dst
, &new_root_entry
, src
, src_data
.dirRootEntry
);
4756 hr
= StorageBaseImpl_ReadDirEntry(dst
, dst_entry
, &dst_data
);
4757 dst_data
.clsid
= src_data
.clsid
;
4758 dst_data
.ctime
= src_data
.ctime
;
4759 dst_data
.mtime
= src_data
.mtime
;
4760 dst_data
.dirRootEntry
= new_root_entry
;
4764 hr
= StorageBaseImpl_WriteDirEntry(dst
, dst_entry
, &dst_data
);
4769 static HRESULT
StorageBaseImpl_DeleteStorageTree(StorageBaseImpl
*This
, DirRef entry
, BOOL include_siblings
)
4773 ULARGE_INTEGER zero
;
4775 if (entry
== DIRENTRY_NULL
)
4780 hr
= StorageBaseImpl_ReadDirEntry(This
, entry
, &data
);
4782 if (SUCCEEDED(hr
) && include_siblings
)
4783 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.leftChild
, TRUE
);
4785 if (SUCCEEDED(hr
) && include_siblings
)
4786 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.rightChild
, TRUE
);
4789 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.dirRootEntry
, TRUE
);
4791 if (SUCCEEDED(hr
) && data
.stgType
== STGTY_STREAM
)
4792 hr
= StorageBaseImpl_StreamSetSize(This
, entry
, zero
);
4795 hr
= StorageBaseImpl_DestroyDirEntry(This
, entry
);
4800 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
4802 DirRef result
=This
->firstFreeEntry
;
4804 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
4807 if (result
== This
->entries_size
)
4809 ULONG new_size
= This
->entries_size
* 2;
4810 TransactedDirEntry
*new_entries
;
4812 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
4813 if (!new_entries
) return DIRENTRY_NULL
;
4815 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
4816 HeapFree(GetProcessHeap(), 0, This
->entries
);
4818 This
->entries
= new_entries
;
4819 This
->entries_size
= new_size
;
4822 This
->entries
[result
].inuse
= TRUE
;
4824 This
->firstFreeEntry
= result
+1;
4829 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
4830 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
4832 DirRef stubEntryRef
;
4833 TransactedDirEntry
*entry
;
4835 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
4837 if (stubEntryRef
!= DIRENTRY_NULL
)
4839 entry
= &This
->entries
[stubEntryRef
];
4841 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
4843 entry
->read
= FALSE
;
4846 return stubEntryRef
;
4849 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
4850 TransactedSnapshotImpl
*This
, DirRef entry
)
4855 if (!This
->entries
[entry
].read
)
4857 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4858 This
->entries
[entry
].transactedParentEntry
,
4861 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
4863 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
4865 if (data
.leftChild
== DIRENTRY_NULL
)
4869 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
4871 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
4873 if (data
.rightChild
== DIRENTRY_NULL
)
4877 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
4879 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
4881 if (data
.dirRootEntry
== DIRENTRY_NULL
)
4887 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
4888 This
->entries
[entry
].read
= TRUE
;
4895 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
4896 TransactedSnapshotImpl
*This
, DirRef entry
)
4900 if (!This
->entries
[entry
].stream_dirty
)
4902 DirEntry new_entrydata
;
4904 memset(&new_entrydata
, 0, sizeof(DirEntry
));
4905 new_entrydata
.name
[0] = 'S';
4906 new_entrydata
.sizeOfNameString
= 1;
4907 new_entrydata
.stgType
= STGTY_STREAM
;
4908 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
4909 new_entrydata
.leftChild
= DIRENTRY_NULL
;
4910 new_entrydata
.rightChild
= DIRENTRY_NULL
;
4911 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
4913 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
4914 &This
->entries
[entry
].stream_entry
);
4916 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4918 hr
= StorageBaseImpl_CopyStream(
4919 This
->scratch
, This
->entries
[entry
].stream_entry
,
4920 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
4923 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
4927 This
->entries
[entry
].stream_dirty
= TRUE
;
4929 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4931 /* Since this entry is modified, and we aren't using its stream data, we
4932 * no longer care about the original entry. */
4934 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
4936 if (delete_ref
!= DIRENTRY_NULL
)
4937 This
->entries
[delete_ref
].deleted
= TRUE
;
4939 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
4946 /* Find the first entry in a depth-first traversal. */
4947 static DirRef
TransactedSnapshotImpl_FindFirstChild(
4948 TransactedSnapshotImpl
* This
, DirRef parent
)
4950 DirRef cursor
, prev
;
4951 TransactedDirEntry
*entry
;
4954 entry
= &This
->entries
[cursor
];
4957 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
4960 cursor
= entry
->data
.leftChild
;
4961 entry
= &This
->entries
[cursor
];
4962 entry
->parent
= prev
;
4964 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
4967 cursor
= entry
->data
.rightChild
;
4968 entry
= &This
->entries
[cursor
];
4969 entry
->parent
= prev
;
4971 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4974 cursor
= entry
->data
.dirRootEntry
;
4975 entry
= &This
->entries
[cursor
];
4976 entry
->parent
= prev
;
4985 /* Find the next entry in a depth-first traversal. */
4986 static DirRef
TransactedSnapshotImpl_FindNextChild(
4987 TransactedSnapshotImpl
* This
, DirRef current
)
4990 TransactedDirEntry
*parent_entry
;
4992 parent
= This
->entries
[current
].parent
;
4993 parent_entry
= &This
->entries
[parent
];
4995 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
4997 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
4999 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
5000 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
5003 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5005 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
5006 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
5013 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5014 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
5015 TransactedSnapshotImpl
* This
, DirRef entry
)
5017 return entry
!= DIRENTRY_NULL
&&
5018 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
5021 /* Destroy the entries created by CopyTree. */
5022 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5023 TransactedSnapshotImpl
* This
, DirRef stop
)
5026 TransactedDirEntry
*entry
;
5027 ULARGE_INTEGER zero
;
5031 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
5034 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
5036 if (cursor
== DIRENTRY_NULL
)
5039 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
5041 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
5043 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
5045 entry
= &This
->entries
[cursor
];
5047 if (entry
->stream_dirty
)
5048 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5049 entry
->newTransactedParentEntry
, zero
);
5051 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5052 entry
->newTransactedParentEntry
);
5054 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5057 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5061 /* Make a copy of our edited tree that we can use in the parent. */
5062 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
5065 TransactedDirEntry
*entry
;
5068 cursor
= This
->base
.storageDirEntry
;
5069 entry
= &This
->entries
[cursor
];
5070 entry
->parent
= DIRENTRY_NULL
;
5071 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5073 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5076 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
5078 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
5079 entry
= &This
->entries
[cursor
];
5081 while (cursor
!= DIRENTRY_NULL
)
5083 /* Make a copy of this entry in the transacted parent. */
5085 (!entry
->dirty
&& !entry
->stream_dirty
&&
5086 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
5087 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
5088 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
5089 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5094 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
5096 newData
.size
.QuadPart
= 0;
5097 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
5099 if (newData
.leftChild
!= DIRENTRY_NULL
)
5100 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
5102 if (newData
.rightChild
!= DIRENTRY_NULL
)
5103 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
5105 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
5106 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
5108 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
5109 &entry
->newTransactedParentEntry
);
5112 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5116 if (entry
->stream_dirty
)
5118 hr
= StorageBaseImpl_CopyStream(
5119 This
->transactedParent
, entry
->newTransactedParentEntry
,
5120 This
->scratch
, entry
->stream_entry
);
5122 else if (entry
->data
.size
.QuadPart
)
5124 hr
= StorageBaseImpl_StreamLink(
5125 This
->transactedParent
, entry
->newTransactedParentEntry
,
5126 entry
->transactedParentEntry
);
5131 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5132 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5137 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5138 entry
= &This
->entries
[cursor
];
5144 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
5146 DWORD grfCommitFlags
) /* [in] */
5148 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5149 TransactedDirEntry
*root_entry
;
5150 DirRef i
, dir_root_ref
;
5152 ULARGE_INTEGER zero
;
5154 ULONG transactionSig
;
5158 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5160 /* Cannot commit a read-only transacted storage */
5161 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5162 return STG_E_ACCESSDENIED
;
5164 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5165 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5168 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5171 if (transactionSig
!= This
->lastTransactionSig
)
5173 ERR("file was externally modified\n");
5174 hr
= STG_E_NOTCURRENT
;
5179 This
->lastTransactionSig
= transactionSig
+1;
5180 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, This
->lastTransactionSig
);
5183 else if (hr
== E_NOTIMPL
)
5186 if (FAILED(hr
)) goto end
;
5188 /* To prevent data loss, we create the new structure in the file before we
5189 * delete the old one, so that in case of errors the old data is intact. We
5190 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5191 * needed in the rare situation where we have just enough free disk space to
5192 * overwrite the existing data. */
5194 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
5196 if (!root_entry
->read
)
5199 hr
= TransactedSnapshotImpl_CopyTree(This
);
5200 if (FAILED(hr
)) goto end
;
5202 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5203 dir_root_ref
= DIRENTRY_NULL
;
5205 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
5207 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5209 /* Update the storage to use the new data in one step. */
5211 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5212 root_entry
->transactedParentEntry
, &data
);
5216 data
.dirRootEntry
= dir_root_ref
;
5217 data
.clsid
= root_entry
->data
.clsid
;
5218 data
.ctime
= root_entry
->data
.ctime
;
5219 data
.mtime
= root_entry
->data
.mtime
;
5221 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
5222 root_entry
->transactedParentEntry
, &data
);
5225 /* Try to flush after updating the root storage, but if the flush fails, keep
5226 * going, on the theory that it'll either succeed later or the subsequent
5227 * writes will fail. */
5228 StorageBaseImpl_Flush(This
->transactedParent
);
5232 /* Destroy the old now-orphaned data. */
5233 for (i
=0; i
<This
->entries_size
; i
++)
5235 TransactedDirEntry
*entry
= &This
->entries
[i
];
5240 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5241 entry
->transactedParentEntry
, zero
);
5242 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5243 entry
->transactedParentEntry
);
5244 memset(entry
, 0, sizeof(TransactedDirEntry
));
5245 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
5247 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
5249 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
5250 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5251 entry
->transactedParentEntry
);
5252 if (entry
->stream_dirty
)
5254 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
5255 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
5256 entry
->stream_dirty
= FALSE
;
5258 entry
->dirty
= FALSE
;
5259 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
5266 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
5270 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5272 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
5278 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
5281 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5282 ULARGE_INTEGER zero
;
5285 TRACE("(%p)\n", iface
);
5287 /* Destroy the open objects. */
5288 StorageBaseImpl_DeleteAll(&This
->base
);
5290 /* Clear out the scratch file. */
5292 for (i
=0; i
<This
->entries_size
; i
++)
5294 if (This
->entries
[i
].stream_dirty
)
5296 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
5299 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
5303 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
5305 This
->firstFreeEntry
= 0;
5306 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
5311 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
5313 if (!This
->reverted
)
5315 TRACE("Storage invalidated (stg=%p)\n", This
);
5317 This
->reverted
= TRUE
;
5319 StorageBaseImpl_DeleteAll(This
);
5323 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
5325 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
5327 IStorage_Revert(&This
->base
.IStorage_iface
);
5328 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
5329 IStorage_Release(&This
->scratch
->IStorage_iface
);
5330 HeapFree(GetProcessHeap(), 0, This
->entries
);
5331 HeapFree(GetProcessHeap(), 0, This
);
5334 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
5336 /* We only need to flush when committing. */
5340 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5342 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
5344 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
5347 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
5348 const DirEntry
*newData
, DirRef
*index
)
5350 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5352 TransactedDirEntry
*new_entry
;
5354 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
5355 if (new_ref
== DIRENTRY_NULL
)
5356 return E_OUTOFMEMORY
;
5358 new_entry
= &This
->entries
[new_ref
];
5360 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
5361 new_entry
->read
= TRUE
;
5362 new_entry
->dirty
= TRUE
;
5363 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
5367 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
5372 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
5373 DirRef index
, const DirEntry
*data
)
5375 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5378 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
5380 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5381 if (FAILED(hr
)) return hr
;
5383 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
5385 if (index
!= This
->base
.storageDirEntry
)
5387 This
->entries
[index
].dirty
= TRUE
;
5389 if (data
->size
.QuadPart
== 0 &&
5390 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
5392 /* Since this entry is modified, and we aren't using its stream data, we
5393 * no longer care about the original entry. */
5395 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
5397 if (delete_ref
!= DIRENTRY_NULL
)
5398 This
->entries
[delete_ref
].deleted
= TRUE
;
5400 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5407 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
5408 DirRef index
, DirEntry
*data
)
5410 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5413 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5414 if (FAILED(hr
)) return hr
;
5416 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
5418 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
5423 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5426 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5428 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
5429 This
->entries
[index
].data
.size
.QuadPart
!= 0)
5431 /* If we deleted this entry while it has stream data. We must have left the
5432 * data because some other entry is using it, and we need to leave the
5433 * original entry alone. */
5434 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
5435 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
5439 This
->entries
[index
].deleted
= TRUE
;
5445 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
5446 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5448 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5450 if (This
->entries
[index
].stream_dirty
)
5452 return StorageBaseImpl_StreamReadAt(This
->scratch
,
5453 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
5455 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
5457 /* This stream doesn't live in the parent, and we haven't allocated storage
5464 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
5465 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
5469 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
5470 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5472 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5475 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5476 if (FAILED(hr
)) return hr
;
5478 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5479 if (FAILED(hr
)) return hr
;
5481 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
5482 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
5484 if (SUCCEEDED(hr
) && size
!= 0)
5485 This
->entries
[index
].data
.size
.QuadPart
= max(
5486 This
->entries
[index
].data
.size
.QuadPart
,
5487 offset
.QuadPart
+ size
);
5492 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
5493 DirRef index
, ULARGE_INTEGER newsize
)
5495 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5498 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5499 if (FAILED(hr
)) return hr
;
5501 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
5504 if (newsize
.QuadPart
== 0)
5506 /* Destroy any parent references or entries in the scratch file. */
5507 if (This
->entries
[index
].stream_dirty
)
5509 ULARGE_INTEGER zero
;
5511 StorageBaseImpl_StreamSetSize(This
->scratch
,
5512 This
->entries
[index
].stream_entry
, zero
);
5513 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
5514 This
->entries
[index
].stream_entry
);
5515 This
->entries
[index
].stream_dirty
= FALSE
;
5517 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
5520 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
5522 if (delete_ref
!= DIRENTRY_NULL
)
5523 This
->entries
[delete_ref
].deleted
= TRUE
;
5525 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5530 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5531 if (FAILED(hr
)) return hr
;
5533 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
5534 This
->entries
[index
].stream_entry
, newsize
);
5538 This
->entries
[index
].data
.size
= newsize
;
5543 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
5544 DirRef dst
, DirRef src
)
5546 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5548 TransactedDirEntry
*dst_entry
, *src_entry
;
5550 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
5551 if (FAILED(hr
)) return hr
;
5553 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
5554 if (FAILED(hr
)) return hr
;
5556 dst_entry
= &This
->entries
[dst
];
5557 src_entry
= &This
->entries
[src
];
5559 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
5560 dst_entry
->stream_entry
= src_entry
->stream_entry
;
5561 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
5562 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
5563 dst_entry
->data
.size
= src_entry
->data
.size
;
5568 static HRESULT
TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl
*base
,
5569 ULONG
* result
, BOOL refresh
)
5574 static HRESULT
TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl
*base
,
5580 static HRESULT
TransactedSnapshotImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5585 static HRESULT
TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5590 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
5592 StorageBaseImpl_QueryInterface
,
5593 StorageBaseImpl_AddRef
,
5594 StorageBaseImpl_Release
,
5595 StorageBaseImpl_CreateStream
,
5596 StorageBaseImpl_OpenStream
,
5597 StorageBaseImpl_CreateStorage
,
5598 StorageBaseImpl_OpenStorage
,
5599 StorageBaseImpl_CopyTo
,
5600 StorageBaseImpl_MoveElementTo
,
5601 TransactedSnapshotImpl_Commit
,
5602 TransactedSnapshotImpl_Revert
,
5603 StorageBaseImpl_EnumElements
,
5604 StorageBaseImpl_DestroyElement
,
5605 StorageBaseImpl_RenameElement
,
5606 StorageBaseImpl_SetElementTimes
,
5607 StorageBaseImpl_SetClass
,
5608 StorageBaseImpl_SetStateBits
,
5609 StorageBaseImpl_Stat
5612 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
5614 TransactedSnapshotImpl_Destroy
,
5615 TransactedSnapshotImpl_Invalidate
,
5616 TransactedSnapshotImpl_Flush
,
5617 TransactedSnapshotImpl_GetFilename
,
5618 TransactedSnapshotImpl_CreateDirEntry
,
5619 TransactedSnapshotImpl_WriteDirEntry
,
5620 TransactedSnapshotImpl_ReadDirEntry
,
5621 TransactedSnapshotImpl_DestroyDirEntry
,
5622 TransactedSnapshotImpl_StreamReadAt
,
5623 TransactedSnapshotImpl_StreamWriteAt
,
5624 TransactedSnapshotImpl_StreamSetSize
,
5625 TransactedSnapshotImpl_StreamLink
,
5626 TransactedSnapshotImpl_GetTransactionSig
,
5627 TransactedSnapshotImpl_SetTransactionSig
,
5628 TransactedSnapshotImpl_LockTransaction
,
5629 TransactedSnapshotImpl_UnlockTransaction
5632 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
5633 TransactedSnapshotImpl
** result
)
5637 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
5642 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
5644 /* This is OK because the property set storage functions use the IStorage functions. */
5645 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
5646 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
5648 list_init(&(*result
)->base
.strmHead
);
5650 list_init(&(*result
)->base
.storageHead
);
5652 (*result
)->base
.ref
= 1;
5654 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5656 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5657 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
5659 /* Create a new temporary storage to act as the scratch file. */
5660 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
5662 (*result
)->scratch
= impl_from_IStorage(scratch
);
5666 ULONG num_entries
= 20;
5668 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
5669 (*result
)->entries_size
= num_entries
;
5670 (*result
)->firstFreeEntry
= 0;
5672 if ((*result
)->entries
)
5674 /* parentStorage already has 1 reference, which we take over here. */
5675 (*result
)->transactedParent
= parentStorage
;
5677 parentStorage
->transactedChild
= &(*result
)->base
;
5679 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
5683 IStorage_Release(scratch
);
5689 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
5694 return E_OUTOFMEMORY
;
5697 static void TransactedSharedImpl_Invalidate(StorageBaseImpl
* This
)
5699 if (!This
->reverted
)
5701 TRACE("Storage invalidated (stg=%p)\n", This
);
5703 This
->reverted
= TRUE
;
5705 StorageBaseImpl_DeleteAll(This
);
5709 static void TransactedSharedImpl_Destroy( StorageBaseImpl
*iface
)
5711 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
5713 TransactedSharedImpl_Invalidate(&This
->base
);
5714 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
5715 IStorage_Release(&This
->scratch
->base
.IStorage_iface
);
5716 HeapFree(GetProcessHeap(), 0, This
);
5719 static HRESULT
TransactedSharedImpl_Flush(StorageBaseImpl
* iface
)
5721 /* We only need to flush when committing. */
5725 static HRESULT
TransactedSharedImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5727 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
5729 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
5732 static HRESULT
TransactedSharedImpl_CreateDirEntry(StorageBaseImpl
*base
,
5733 const DirEntry
*newData
, DirRef
*index
)
5735 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5737 return StorageBaseImpl_CreateDirEntry(&This
->scratch
->base
,
5741 static HRESULT
TransactedSharedImpl_WriteDirEntry(StorageBaseImpl
*base
,
5742 DirRef index
, const DirEntry
*data
)
5744 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5746 return StorageBaseImpl_WriteDirEntry(&This
->scratch
->base
,
5750 static HRESULT
TransactedSharedImpl_ReadDirEntry(StorageBaseImpl
*base
,
5751 DirRef index
, DirEntry
*data
)
5753 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5755 return StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
,
5759 static HRESULT
TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5762 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5764 return StorageBaseImpl_DestroyDirEntry(&This
->scratch
->base
,
5768 static HRESULT
TransactedSharedImpl_StreamReadAt(StorageBaseImpl
*base
,
5769 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5771 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5773 return StorageBaseImpl_StreamReadAt(&This
->scratch
->base
,
5774 index
, offset
, size
, buffer
, bytesRead
);
5777 static HRESULT
TransactedSharedImpl_StreamWriteAt(StorageBaseImpl
*base
,
5778 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5780 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5782 return StorageBaseImpl_StreamWriteAt(&This
->scratch
->base
,
5783 index
, offset
, size
, buffer
, bytesWritten
);
5786 static HRESULT
TransactedSharedImpl_StreamSetSize(StorageBaseImpl
*base
,
5787 DirRef index
, ULARGE_INTEGER newsize
)
5789 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5791 return StorageBaseImpl_StreamSetSize(&This
->scratch
->base
,
5795 static HRESULT
TransactedSharedImpl_StreamLink(StorageBaseImpl
*base
,
5796 DirRef dst
, DirRef src
)
5798 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5800 return StorageBaseImpl_StreamLink(&This
->scratch
->base
,
5804 static HRESULT
TransactedSharedImpl_GetTransactionSig(StorageBaseImpl
*base
,
5805 ULONG
* result
, BOOL refresh
)
5810 static HRESULT
TransactedSharedImpl_SetTransactionSig(StorageBaseImpl
*base
,
5816 static HRESULT
TransactedSharedImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5821 static HRESULT
TransactedSharedImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5826 static HRESULT WINAPI
TransactedSharedImpl_Commit(
5828 DWORD grfCommitFlags
) /* [in] */
5830 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
5831 DirRef new_storage_ref
, prev_storage_ref
;
5832 DirEntry src_data
, dst_data
;
5834 ULONG transactionSig
;
5836 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5838 /* Cannot commit a read-only transacted storage */
5839 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5840 return STG_E_ACCESSDENIED
;
5842 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5843 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5846 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5849 if ((grfCommitFlags
& STGC_ONLYIFCURRENT
) && transactionSig
!= This
->lastTransactionSig
)
5850 hr
= STG_E_NOTCURRENT
;
5853 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, transactionSig
+1);
5855 else if (hr
== E_NOTIMPL
)
5859 hr
= StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
, This
->scratch
->base
.storageDirEntry
, &src_data
);
5861 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
5863 hr
= StorageBaseImpl_DupStorageTree(This
->transactedParent
, &new_storage_ref
, &This
->scratch
->base
, src_data
.dirRootEntry
);
5866 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5869 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
5873 prev_storage_ref
= dst_data
.dirRootEntry
;
5874 dst_data
.dirRootEntry
= new_storage_ref
;
5875 dst_data
.clsid
= src_data
.clsid
;
5876 dst_data
.ctime
= src_data
.ctime
;
5877 dst_data
.mtime
= src_data
.mtime
;
5878 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
5883 /* Try to flush after updating the root storage, but if the flush fails, keep
5884 * going, on the theory that it'll either succeed later or the subsequent
5885 * writes will fail. */
5886 StorageBaseImpl_Flush(This
->transactedParent
);
5888 hr
= StorageBaseImpl_DeleteStorageTree(This
->transactedParent
, prev_storage_ref
, TRUE
);
5892 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5894 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
5897 hr
= IStorage_Commit(&This
->scratch
->base
.IStorage_iface
, STGC_DEFAULT
);
5901 This
->lastTransactionSig
= transactionSig
+1;
5908 static HRESULT WINAPI
TransactedSharedImpl_Revert(
5911 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
5913 TRACE("(%p)\n", iface
);
5915 /* Destroy the open objects. */
5916 StorageBaseImpl_DeleteAll(&This
->base
);
5918 return IStorage_Revert(&This
->scratch
->base
.IStorage_iface
);
5921 static const IStorageVtbl TransactedSharedImpl_Vtbl
=
5923 StorageBaseImpl_QueryInterface
,
5924 StorageBaseImpl_AddRef
,
5925 StorageBaseImpl_Release
,
5926 StorageBaseImpl_CreateStream
,
5927 StorageBaseImpl_OpenStream
,
5928 StorageBaseImpl_CreateStorage
,
5929 StorageBaseImpl_OpenStorage
,
5930 StorageBaseImpl_CopyTo
,
5931 StorageBaseImpl_MoveElementTo
,
5932 TransactedSharedImpl_Commit
,
5933 TransactedSharedImpl_Revert
,
5934 StorageBaseImpl_EnumElements
,
5935 StorageBaseImpl_DestroyElement
,
5936 StorageBaseImpl_RenameElement
,
5937 StorageBaseImpl_SetElementTimes
,
5938 StorageBaseImpl_SetClass
,
5939 StorageBaseImpl_SetStateBits
,
5940 StorageBaseImpl_Stat
5943 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl
=
5945 TransactedSharedImpl_Destroy
,
5946 TransactedSharedImpl_Invalidate
,
5947 TransactedSharedImpl_Flush
,
5948 TransactedSharedImpl_GetFilename
,
5949 TransactedSharedImpl_CreateDirEntry
,
5950 TransactedSharedImpl_WriteDirEntry
,
5951 TransactedSharedImpl_ReadDirEntry
,
5952 TransactedSharedImpl_DestroyDirEntry
,
5953 TransactedSharedImpl_StreamReadAt
,
5954 TransactedSharedImpl_StreamWriteAt
,
5955 TransactedSharedImpl_StreamSetSize
,
5956 TransactedSharedImpl_StreamLink
,
5957 TransactedSharedImpl_GetTransactionSig
,
5958 TransactedSharedImpl_SetTransactionSig
,
5959 TransactedSharedImpl_LockTransaction
,
5960 TransactedSharedImpl_UnlockTransaction
5963 static HRESULT
TransactedSharedImpl_Construct(StorageBaseImpl
*parentStorage
,
5964 TransactedSharedImpl
** result
)
5968 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSharedImpl
));
5973 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSharedImpl_Vtbl
;
5975 /* This is OK because the property set storage functions use the IStorage functions. */
5976 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
5977 (*result
)->base
.baseVtbl
= &TransactedSharedImpl_BaseVtbl
;
5979 list_init(&(*result
)->base
.strmHead
);
5981 list_init(&(*result
)->base
.storageHead
);
5983 (*result
)->base
.ref
= 1;
5985 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5987 hr
= StorageBaseImpl_LockTransaction(parentStorage
, FALSE
);
5993 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5994 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
5998 stgo
.ulSectorSize
= 4096;
5999 stgo
.pwcsTemplateFile
= NULL
;
6001 /* Create a new temporary storage to act as the scratch file. */
6002 hr
= StgCreateStorageEx(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
|STGM_TRANSACTED
,
6003 STGFMT_DOCFILE
, 0, &stgo
, NULL
, &IID_IStorage
, (void**)&scratch
);
6004 (*result
)->scratch
= (TransactedSnapshotImpl
*)impl_from_IStorage(scratch
);
6008 hr
= StorageBaseImpl_CopyStorageTree(&(*result
)->scratch
->base
, (*result
)->scratch
->base
.storageDirEntry
,
6009 parentStorage
, parentStorage
->storageDirEntry
);
6013 hr
= IStorage_Commit(scratch
, STGC_DEFAULT
);
6015 (*result
)->base
.storageDirEntry
= (*result
)->scratch
->base
.storageDirEntry
;
6016 (*result
)->transactedParent
= parentStorage
;
6020 IStorage_Release(scratch
);
6023 StorageBaseImpl_UnlockTransaction(parentStorage
, FALSE
);
6026 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6031 return E_OUTOFMEMORY
;
6034 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
6035 BOOL toplevel
, StorageBaseImpl
** result
)
6037 static int fixme_flags
=STGM_NOSCRATCH
|STGM_NOSNAPSHOT
;
6039 if (parentStorage
->openFlags
& fixme_flags
)
6041 fixme_flags
&= ~parentStorage
->openFlags
;
6042 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
6045 if (toplevel
&& !(parentStorage
->openFlags
& STGM_NOSNAPSHOT
) &&
6046 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_DENY_WRITE
&&
6047 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_EXCLUSIVE
)
6049 /* Need to create a temp file for the snapshot */
6050 return TransactedSharedImpl_Construct(parentStorage
, (TransactedSharedImpl
**)result
);
6053 return TransactedSnapshotImpl_Construct(parentStorage
,
6054 (TransactedSnapshotImpl
**)result
);
6057 static HRESULT
Storage_Construct(
6065 StorageBaseImpl
** result
)
6067 StorageImpl
*newStorage
;
6068 StorageBaseImpl
*newTransactedStorage
;
6071 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
6072 if (FAILED(hr
)) goto end
;
6074 if (openFlags
& STGM_TRANSACTED
)
6076 hr
= Storage_ConstructTransacted(&newStorage
->base
, TRUE
, &newTransactedStorage
);
6078 IStorage_Release(&newStorage
->base
.IStorage_iface
);
6080 *result
= newTransactedStorage
;
6083 *result
= &newStorage
->base
;
6089 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
6091 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6093 if (!This
->base
.reverted
)
6095 TRACE("Storage invalidated (stg=%p)\n", This
);
6097 This
->base
.reverted
= TRUE
;
6099 This
->parentStorage
= NULL
;
6101 StorageBaseImpl_DeleteAll(&This
->base
);
6103 list_remove(&This
->ParentListEntry
);
6107 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
6109 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
6111 StorageInternalImpl_Invalidate(&This
->base
);
6113 HeapFree(GetProcessHeap(), 0, This
);
6116 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
6118 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
6120 return StorageBaseImpl_Flush(This
->parentStorage
);
6123 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6125 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
6127 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
6130 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
6131 const DirEntry
*newData
, DirRef
*index
)
6133 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6135 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
6139 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
6140 DirRef index
, const DirEntry
*data
)
6142 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6144 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
6148 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
6149 DirRef index
, DirEntry
*data
)
6151 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6153 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
6157 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6160 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6162 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
6166 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
6167 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6169 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6171 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
6172 index
, offset
, size
, buffer
, bytesRead
);
6175 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
6176 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6178 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6180 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
6181 index
, offset
, size
, buffer
, bytesWritten
);
6184 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
6185 DirRef index
, ULARGE_INTEGER newsize
)
6187 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6189 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
6193 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
6194 DirRef dst
, DirRef src
)
6196 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6198 return StorageBaseImpl_StreamLink(This
->parentStorage
,
6202 static HRESULT
StorageInternalImpl_GetTransactionSig(StorageBaseImpl
*base
,
6203 ULONG
* result
, BOOL refresh
)
6208 static HRESULT
StorageInternalImpl_SetTransactionSig(StorageBaseImpl
*base
,
6214 static HRESULT
StorageInternalImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6219 static HRESULT
StorageInternalImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6224 /******************************************************************************
6226 ** Storage32InternalImpl_Commit
6229 static HRESULT WINAPI
StorageInternalImpl_Commit(
6231 DWORD grfCommitFlags
) /* [in] */
6233 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
6234 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
6235 return StorageBaseImpl_Flush(This
);
6238 /******************************************************************************
6240 ** Storage32InternalImpl_Revert
6243 static HRESULT WINAPI
StorageInternalImpl_Revert(
6246 FIXME("(%p): stub\n", iface
);
6250 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
6252 IStorage_Release(&This
->parentStorage
->IStorage_iface
);
6253 HeapFree(GetProcessHeap(), 0, This
);
6256 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
6257 IEnumSTATSTG
* iface
,
6261 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6264 return E_INVALIDARG
;
6268 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
6269 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
6272 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
6276 return E_NOINTERFACE
;
6279 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
6280 IEnumSTATSTG
* iface
)
6282 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6283 return InterlockedIncrement(&This
->ref
);
6286 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
6287 IEnumSTATSTG
* iface
)
6289 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6293 newRef
= InterlockedDecrement(&This
->ref
);
6297 IEnumSTATSTGImpl_Destroy(This
);
6303 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
6304 IEnumSTATSTGImpl
* This
,
6307 DirRef result
= DIRENTRY_NULL
;
6311 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
6313 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
6314 This
->parentStorage
->storageDirEntry
, &entry
);
6315 searchNode
= entry
.dirRootEntry
;
6317 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
6319 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
6323 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
6327 searchNode
= entry
.rightChild
;
6331 result
= searchNode
;
6332 memcpy(result_name
, entry
.name
, sizeof(result_name
));
6333 searchNode
= entry
.leftChild
;
6341 if (result
!= DIRENTRY_NULL
)
6342 memcpy(This
->name
, result_name
, sizeof(result_name
));
6348 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
6349 IEnumSTATSTG
* iface
,
6352 ULONG
* pceltFetched
)
6354 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6356 DirEntry currentEntry
;
6357 STATSTG
* currentReturnStruct
= rgelt
;
6358 ULONG objectFetched
= 0;
6359 DirRef currentSearchNode
;
6362 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
6363 return E_INVALIDARG
;
6365 if (This
->parentStorage
->reverted
)
6366 return STG_E_REVERTED
;
6369 * To avoid the special case, get another pointer to a ULONG value if
6370 * the caller didn't supply one.
6372 if (pceltFetched
==0)
6373 pceltFetched
= &objectFetched
;
6376 * Start the iteration, we will iterate until we hit the end of the
6377 * linked list or until we hit the number of items to iterate through
6381 while ( *pceltFetched
< celt
)
6383 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
6385 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
6389 * Read the entry from the storage.
6391 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
6396 * Copy the information to the return buffer.
6398 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
6399 currentReturnStruct
,
6404 * Step to the next item in the iteration
6407 currentReturnStruct
++;
6410 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
6417 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
6418 IEnumSTATSTG
* iface
,
6421 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6423 ULONG objectFetched
= 0;
6424 DirRef currentSearchNode
;
6427 if (This
->parentStorage
->reverted
)
6428 return STG_E_REVERTED
;
6430 while ( (objectFetched
< celt
) )
6432 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
6434 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
6440 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
6446 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
6447 IEnumSTATSTG
* iface
)
6449 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6451 if (This
->parentStorage
->reverted
)
6452 return STG_E_REVERTED
;
6459 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
6460 IEnumSTATSTG
* iface
,
6461 IEnumSTATSTG
** ppenum
)
6463 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6464 IEnumSTATSTGImpl
* newClone
;
6466 if (This
->parentStorage
->reverted
)
6467 return STG_E_REVERTED
;
6470 return E_INVALIDARG
;
6472 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
6473 This
->storageDirEntry
);
6477 return E_OUTOFMEMORY
;
6481 * The new clone enumeration must point to the same current node as
6484 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
6486 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
6492 * Virtual function table for the IEnumSTATSTGImpl class.
6494 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
6496 IEnumSTATSTGImpl_QueryInterface
,
6497 IEnumSTATSTGImpl_AddRef
,
6498 IEnumSTATSTGImpl_Release
,
6499 IEnumSTATSTGImpl_Next
,
6500 IEnumSTATSTGImpl_Skip
,
6501 IEnumSTATSTGImpl_Reset
,
6502 IEnumSTATSTGImpl_Clone
6505 /******************************************************************************
6506 ** IEnumSTATSTGImpl implementation
6509 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
6510 StorageBaseImpl
* parentStorage
,
6511 DirRef storageDirEntry
)
6513 IEnumSTATSTGImpl
* newEnumeration
;
6515 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
6519 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
6520 newEnumeration
->ref
= 1;
6521 newEnumeration
->name
[0] = 0;
6524 * We want to nail-down the reference to the storage in case the
6525 * enumeration out-lives the storage in the client application.
6527 newEnumeration
->parentStorage
= parentStorage
;
6528 IStorage_AddRef(&newEnumeration
->parentStorage
->IStorage_iface
);
6530 newEnumeration
->storageDirEntry
= storageDirEntry
;
6533 return newEnumeration
;
6537 * Virtual function table for the Storage32InternalImpl class.
6539 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
6541 StorageBaseImpl_QueryInterface
,
6542 StorageBaseImpl_AddRef
,
6543 StorageBaseImpl_Release
,
6544 StorageBaseImpl_CreateStream
,
6545 StorageBaseImpl_OpenStream
,
6546 StorageBaseImpl_CreateStorage
,
6547 StorageBaseImpl_OpenStorage
,
6548 StorageBaseImpl_CopyTo
,
6549 StorageBaseImpl_MoveElementTo
,
6550 StorageInternalImpl_Commit
,
6551 StorageInternalImpl_Revert
,
6552 StorageBaseImpl_EnumElements
,
6553 StorageBaseImpl_DestroyElement
,
6554 StorageBaseImpl_RenameElement
,
6555 StorageBaseImpl_SetElementTimes
,
6556 StorageBaseImpl_SetClass
,
6557 StorageBaseImpl_SetStateBits
,
6558 StorageBaseImpl_Stat
6561 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
6563 StorageInternalImpl_Destroy
,
6564 StorageInternalImpl_Invalidate
,
6565 StorageInternalImpl_Flush
,
6566 StorageInternalImpl_GetFilename
,
6567 StorageInternalImpl_CreateDirEntry
,
6568 StorageInternalImpl_WriteDirEntry
,
6569 StorageInternalImpl_ReadDirEntry
,
6570 StorageInternalImpl_DestroyDirEntry
,
6571 StorageInternalImpl_StreamReadAt
,
6572 StorageInternalImpl_StreamWriteAt
,
6573 StorageInternalImpl_StreamSetSize
,
6574 StorageInternalImpl_StreamLink
,
6575 StorageInternalImpl_GetTransactionSig
,
6576 StorageInternalImpl_SetTransactionSig
,
6577 StorageInternalImpl_LockTransaction
,
6578 StorageInternalImpl_UnlockTransaction
6581 /******************************************************************************
6582 ** Storage32InternalImpl implementation
6585 static StorageInternalImpl
* StorageInternalImpl_Construct(
6586 StorageBaseImpl
* parentStorage
,
6588 DirRef storageDirEntry
)
6590 StorageInternalImpl
* newStorage
;
6592 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
6596 list_init(&newStorage
->base
.strmHead
);
6598 list_init(&newStorage
->base
.storageHead
);
6601 * Initialize the virtual function table.
6603 newStorage
->base
.IStorage_iface
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
6604 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
6605 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
6606 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
6608 newStorage
->base
.reverted
= FALSE
;
6610 newStorage
->base
.ref
= 1;
6612 newStorage
->parentStorage
= parentStorage
;
6615 * Keep a reference to the directory entry of this storage
6617 newStorage
->base
.storageDirEntry
= storageDirEntry
;
6619 newStorage
->base
.create
= FALSE
;
6627 /******************************************************************************
6628 ** StorageUtl implementation
6631 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
6635 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
6636 *value
= lendian16toh(tmp
);
6639 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
6641 value
= htole16(value
);
6642 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
6645 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
6649 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
6650 *value
= lendian32toh(tmp
);
6653 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
6655 value
= htole32(value
);
6656 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
6659 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
6660 ULARGE_INTEGER
* value
)
6662 #ifdef WORDS_BIGENDIAN
6665 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6666 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
6667 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
6669 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6673 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
6674 const ULARGE_INTEGER
*value
)
6676 #ifdef WORDS_BIGENDIAN
6679 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
6680 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
6681 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
6683 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
6687 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
6689 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
6690 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
6691 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
6693 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
6696 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
6698 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
6699 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
6700 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
6702 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
6705 void StorageUtl_CopyDirEntryToSTATSTG(
6706 StorageBaseImpl
* storage
,
6707 STATSTG
* destination
,
6708 const DirEntry
* source
,
6712 * The copy of the string occurs only when the flag is not set
6714 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
6716 /* Use the filename for the root storage. */
6717 destination
->pwcsName
= 0;
6718 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
6720 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
6721 (source
->name
[0] == 0) )
6723 destination
->pwcsName
= 0;
6727 destination
->pwcsName
=
6728 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
6730 strcpyW(destination
->pwcsName
, source
->name
);
6733 switch (source
->stgType
)
6737 destination
->type
= STGTY_STORAGE
;
6740 destination
->type
= STGTY_STREAM
;
6743 destination
->type
= STGTY_STREAM
;
6747 destination
->cbSize
= source
->size
;
6749 currentReturnStruct->mtime = {0}; TODO
6750 currentReturnStruct->ctime = {0};
6751 currentReturnStruct->atime = {0};
6753 destination
->grfMode
= 0;
6754 destination
->grfLocksSupported
= 0;
6755 destination
->clsid
= source
->clsid
;
6756 destination
->grfStateBits
= 0;
6757 destination
->reserved
= 0;
6760 /******************************************************************************
6761 ** BlockChainStream implementation
6764 /* Read and save the index of all blocks in this stream. */
6765 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
6767 ULONG next_sector
, next_offset
;
6769 struct BlockChainRun
*last_run
;
6771 if (This
->indexCacheLen
== 0)
6775 next_sector
= BlockChainStream_GetHeadOfChain(This
);
6779 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6780 next_offset
= last_run
->lastOffset
+1;
6781 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
6782 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
6784 if (FAILED(hr
)) return hr
;
6787 while (next_sector
!= BLOCK_END_OF_CHAIN
)
6789 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
6791 /* Add the current block to the cache. */
6792 if (This
->indexCacheSize
== 0)
6794 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
6795 if (!This
->indexCache
) return E_OUTOFMEMORY
;
6796 This
->indexCacheSize
= 16;
6798 else if (This
->indexCacheSize
== This
->indexCacheLen
)
6800 struct BlockChainRun
*new_cache
;
6803 new_size
= This
->indexCacheSize
* 2;
6804 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
6805 if (!new_cache
) return E_OUTOFMEMORY
;
6806 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
6808 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
6809 This
->indexCache
= new_cache
;
6810 This
->indexCacheSize
= new_size
;
6813 This
->indexCacheLen
++;
6814 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6815 last_run
->firstSector
= next_sector
;
6816 last_run
->firstOffset
= next_offset
;
6819 last_run
->lastOffset
= next_offset
;
6821 /* Find the next block. */
6823 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
6824 if (FAILED(hr
)) return hr
;
6827 if (This
->indexCacheLen
)
6829 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
6830 This
->numBlocks
= last_run
->lastOffset
+1;
6834 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
6835 This
->numBlocks
= 0;
6841 /* Locate the nth block in this stream. */
6842 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
6844 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
6845 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
6847 if (offset
>= This
->numBlocks
)
6848 return BLOCK_END_OF_CHAIN
;
6850 while (min_run
< max_run
)
6852 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
6853 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
6855 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
6856 max_run
= run_to_check
-1;
6858 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
6860 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
6861 min_run
= run_to_check
+1;
6864 /* Block is in this run. */
6865 min_run
= max_run
= run_to_check
;
6868 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
6871 HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
6872 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
6874 BlockChainBlock
*result
=NULL
;
6878 if (This
->cachedBlocks
[i
].index
== index
)
6880 *sector
= This
->cachedBlocks
[i
].sector
;
6881 *block
= &This
->cachedBlocks
[i
];
6885 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
6886 if (*sector
== BLOCK_END_OF_CHAIN
)
6887 return STG_E_DOCFILECORRUPT
;
6891 if (This
->cachedBlocks
[0].index
== 0xffffffff)
6892 result
= &This
->cachedBlocks
[0];
6893 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
6894 result
= &This
->cachedBlocks
[1];
6897 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
6898 if (This
->blockToEvict
== 2)
6899 This
->blockToEvict
= 0;
6904 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
6905 return STG_E_WRITEFAULT
;
6906 result
->dirty
= FALSE
;
6909 result
->read
= FALSE
;
6910 result
->index
= index
;
6911 result
->sector
= *sector
;
6918 BlockChainStream
* BlockChainStream_Construct(
6919 StorageImpl
* parentStorage
,
6920 ULONG
* headOfStreamPlaceHolder
,
6923 BlockChainStream
* newStream
;
6925 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
6927 newStream
->parentStorage
= parentStorage
;
6928 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6929 newStream
->ownerDirEntry
= dirEntry
;
6930 newStream
->indexCache
= NULL
;
6931 newStream
->indexCacheLen
= 0;
6932 newStream
->indexCacheSize
= 0;
6933 newStream
->cachedBlocks
[0].index
= 0xffffffff;
6934 newStream
->cachedBlocks
[0].dirty
= FALSE
;
6935 newStream
->cachedBlocks
[1].index
= 0xffffffff;
6936 newStream
->cachedBlocks
[1].dirty
= FALSE
;
6937 newStream
->blockToEvict
= 0;
6939 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
6941 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
6942 HeapFree(GetProcessHeap(), 0, newStream
);
6949 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
6952 if (!This
) return S_OK
;
6955 if (This
->cachedBlocks
[i
].dirty
)
6957 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
6958 This
->cachedBlocks
[i
].dirty
= FALSE
;
6960 return STG_E_WRITEFAULT
;
6966 void BlockChainStream_Destroy(BlockChainStream
* This
)
6970 BlockChainStream_Flush(This
);
6971 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
6973 HeapFree(GetProcessHeap(), 0, This
);
6976 /******************************************************************************
6977 * BlockChainStream_GetHeadOfChain
6979 * Returns the head of this stream chain.
6980 * Some special chains don't have directory entries, their heads are kept in
6981 * This->headOfStreamPlaceHolder.
6984 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
6986 DirEntry chainEntry
;
6989 if (This
->headOfStreamPlaceHolder
!= 0)
6990 return *(This
->headOfStreamPlaceHolder
);
6992 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
6994 hr
= StorageImpl_ReadDirEntry(
6995 This
->parentStorage
,
6996 This
->ownerDirEntry
,
7001 return chainEntry
.startingBlock
;
7005 return BLOCK_END_OF_CHAIN
;
7008 /******************************************************************************
7009 * BlockChainStream_GetCount
7011 * Returns the number of blocks that comprises this chain.
7012 * This is not the size of the stream as the last block may not be full!
7014 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
7016 return This
->numBlocks
;
7019 /******************************************************************************
7020 * BlockChainStream_ReadAt
7022 * Reads a specified number of bytes from this chain at the specified offset.
7023 * bytesRead may be NULL.
7024 * Failure will be returned if the specified number of bytes has not been read.
7026 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
7027 ULARGE_INTEGER offset
,
7032 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7033 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7034 ULONG bytesToReadInBuffer
;
7037 ULARGE_INTEGER stream_size
;
7039 BlockChainBlock
*cachedBlock
;
7041 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
7044 * Find the first block in the stream that contains part of the buffer.
7046 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
7050 stream_size
= BlockChainStream_GetSize(This
);
7051 if (stream_size
.QuadPart
> offset
.QuadPart
)
7052 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7057 * Start reading the buffer.
7059 bufferWalker
= buffer
;
7063 ULARGE_INTEGER ulOffset
;
7067 * Calculate how many bytes we can copy from this big block.
7069 bytesToReadInBuffer
=
7070 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7072 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
7079 /* Not in cache, and we're going to read past the end of the block. */
7080 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7083 StorageImpl_ReadAt(This
->parentStorage
,
7086 bytesToReadInBuffer
,
7091 if (!cachedBlock
->read
)
7094 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7095 return STG_E_READFAULT
;
7097 cachedBlock
->read
= TRUE
;
7100 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
7101 bytesReadAt
= bytesToReadInBuffer
;
7104 blockNoInSequence
++;
7105 bufferWalker
+= bytesReadAt
;
7106 size
-= bytesReadAt
;
7107 *bytesRead
+= bytesReadAt
;
7108 offsetInBlock
= 0; /* There is no offset on the next block */
7110 if (bytesToReadInBuffer
!= bytesReadAt
)
7117 /******************************************************************************
7118 * BlockChainStream_WriteAt
7120 * Writes the specified number of bytes to this chain at the specified offset.
7121 * Will fail if not all specified number of bytes have been written.
7123 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
7124 ULARGE_INTEGER offset
,
7127 ULONG
* bytesWritten
)
7129 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7130 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7133 const BYTE
* bufferWalker
;
7135 BlockChainBlock
*cachedBlock
;
7138 bufferWalker
= buffer
;
7142 ULARGE_INTEGER ulOffset
;
7143 DWORD bytesWrittenAt
;
7146 * Calculate how many bytes we can copy to this big block.
7149 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7151 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
7153 /* BlockChainStream_SetSize should have already been called to ensure we have
7154 * enough blocks in the chain to write into */
7157 ERR("not enough blocks in chain to write data\n");
7163 /* Not in cache, and we're going to write past the end of the block. */
7164 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7167 StorageImpl_WriteAt(This
->parentStorage
,
7175 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
7178 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7179 return STG_E_READFAULT
;
7182 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
7183 bytesWrittenAt
= bytesToWrite
;
7184 cachedBlock
->read
= TRUE
;
7185 cachedBlock
->dirty
= TRUE
;
7188 blockNoInSequence
++;
7189 bufferWalker
+= bytesWrittenAt
;
7190 size
-= bytesWrittenAt
;
7191 *bytesWritten
+= bytesWrittenAt
;
7192 offsetInBlock
= 0; /* There is no offset on the next block */
7194 if (bytesWrittenAt
!= bytesToWrite
)
7198 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7201 /******************************************************************************
7202 * BlockChainStream_Shrink
7204 * Shrinks this chain in the big block depot.
7206 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
7207 ULARGE_INTEGER newSize
)
7214 * Figure out how many blocks are needed to contain the new size
7216 numBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7218 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7224 * Go to the new end of chain
7226 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
7228 /* Mark the new end of chain */
7229 StorageImpl_SetNextBlockInChain(
7230 This
->parentStorage
,
7232 BLOCK_END_OF_CHAIN
);
7234 This
->tailIndex
= blockIndex
;
7238 if (This
->headOfStreamPlaceHolder
!= 0)
7240 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
7244 DirEntry chainEntry
;
7245 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7247 StorageImpl_ReadDirEntry(
7248 This
->parentStorage
,
7249 This
->ownerDirEntry
,
7252 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7254 StorageImpl_WriteDirEntry(
7255 This
->parentStorage
,
7256 This
->ownerDirEntry
,
7260 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7263 This
->numBlocks
= numBlocks
;
7266 * Mark the extra blocks as free
7268 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
7270 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7271 StorageImpl_FreeBigBlock(This
->parentStorage
,
7272 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
7273 if (last_run
->lastOffset
== last_run
->firstOffset
)
7274 This
->indexCacheLen
--;
7276 last_run
->lastOffset
--;
7280 * Reset the last accessed block cache.
7284 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
7286 This
->cachedBlocks
[i
].index
= 0xffffffff;
7287 This
->cachedBlocks
[i
].dirty
= FALSE
;
7294 /******************************************************************************
7295 * BlockChainStream_Enlarge
7297 * Grows this chain in the big block depot.
7299 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
7300 ULARGE_INTEGER newSize
)
7302 ULONG blockIndex
, currentBlock
;
7304 ULONG oldNumBlocks
= 0;
7306 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
7309 * Empty chain. Create the head.
7311 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7313 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7314 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
7316 BLOCK_END_OF_CHAIN
);
7318 if (This
->headOfStreamPlaceHolder
!= 0)
7320 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7324 DirEntry chainEntry
;
7325 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7327 StorageImpl_ReadDirEntry(
7328 This
->parentStorage
,
7329 This
->ownerDirEntry
,
7332 chainEntry
.startingBlock
= blockIndex
;
7334 StorageImpl_WriteDirEntry(
7335 This
->parentStorage
,
7336 This
->ownerDirEntry
,
7340 This
->tailIndex
= blockIndex
;
7341 This
->numBlocks
= 1;
7345 * Figure out how many blocks are needed to contain this stream
7347 newNumBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7349 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7353 * Go to the current end of chain
7355 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
7357 currentBlock
= blockIndex
;
7359 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7362 currentBlock
= blockIndex
;
7364 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
7369 This
->tailIndex
= currentBlock
;
7372 currentBlock
= This
->tailIndex
;
7373 oldNumBlocks
= This
->numBlocks
;
7376 * Add new blocks to the chain
7378 if (oldNumBlocks
< newNumBlocks
)
7380 while (oldNumBlocks
< newNumBlocks
)
7382 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7384 StorageImpl_SetNextBlockInChain(
7385 This
->parentStorage
,
7389 StorageImpl_SetNextBlockInChain(
7390 This
->parentStorage
,
7392 BLOCK_END_OF_CHAIN
);
7394 currentBlock
= blockIndex
;
7398 This
->tailIndex
= blockIndex
;
7399 This
->numBlocks
= newNumBlocks
;
7402 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
7408 /******************************************************************************
7409 * BlockChainStream_SetSize
7411 * Sets the size of this stream. The big block depot will be updated.
7412 * The file will grow if we grow the chain.
7414 * TODO: Free the actual blocks in the file when we shrink the chain.
7415 * Currently, the blocks are still in the file. So the file size
7416 * doesn't shrink even if we shrink streams.
7418 BOOL
BlockChainStream_SetSize(
7419 BlockChainStream
* This
,
7420 ULARGE_INTEGER newSize
)
7422 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
7424 if (newSize
.QuadPart
== size
.QuadPart
)
7427 if (newSize
.QuadPart
< size
.QuadPart
)
7429 BlockChainStream_Shrink(This
, newSize
);
7433 BlockChainStream_Enlarge(This
, newSize
);
7439 /******************************************************************************
7440 * BlockChainStream_GetSize
7442 * Returns the size of this chain.
7443 * Will return the block count if this chain doesn't have a directory entry.
7445 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
7447 DirEntry chainEntry
;
7449 if(This
->headOfStreamPlaceHolder
== NULL
)
7452 * This chain has a directory entry so use the size value from there.
7454 StorageImpl_ReadDirEntry(
7455 This
->parentStorage
,
7456 This
->ownerDirEntry
,
7459 return chainEntry
.size
;
7464 * this chain is a chain that does not have a directory entry, figure out the
7465 * size by making the product number of used blocks times the
7468 ULARGE_INTEGER result
;
7470 (ULONGLONG
)BlockChainStream_GetCount(This
) *
7471 This
->parentStorage
->bigBlockSize
;
7477 /******************************************************************************
7478 ** SmallBlockChainStream implementation
7481 SmallBlockChainStream
* SmallBlockChainStream_Construct(
7482 StorageImpl
* parentStorage
,
7483 ULONG
* headOfStreamPlaceHolder
,
7486 SmallBlockChainStream
* newStream
;
7488 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
7490 newStream
->parentStorage
= parentStorage
;
7491 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7492 newStream
->ownerDirEntry
= dirEntry
;
7497 void SmallBlockChainStream_Destroy(
7498 SmallBlockChainStream
* This
)
7500 HeapFree(GetProcessHeap(), 0, This
);
7503 /******************************************************************************
7504 * SmallBlockChainStream_GetHeadOfChain
7506 * Returns the head of this chain of small blocks.
7508 static ULONG
SmallBlockChainStream_GetHeadOfChain(
7509 SmallBlockChainStream
* This
)
7511 DirEntry chainEntry
;
7514 if (This
->headOfStreamPlaceHolder
!= NULL
)
7515 return *(This
->headOfStreamPlaceHolder
);
7517 if (This
->ownerDirEntry
)
7519 hr
= StorageImpl_ReadDirEntry(
7520 This
->parentStorage
,
7521 This
->ownerDirEntry
,
7526 return chainEntry
.startingBlock
;
7531 return BLOCK_END_OF_CHAIN
;
7534 /******************************************************************************
7535 * SmallBlockChainStream_GetNextBlockInChain
7537 * Returns the index of the next small block in this chain.
7540 * - BLOCK_END_OF_CHAIN: end of this chain
7541 * - BLOCK_UNUSED: small block 'blockIndex' is free
7543 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
7544 SmallBlockChainStream
* This
,
7546 ULONG
* nextBlockInChain
)
7548 ULARGE_INTEGER offsetOfBlockInDepot
;
7553 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
7555 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7558 * Read those bytes in the buffer from the small block file.
7560 res
= BlockChainStream_ReadAt(
7561 This
->parentStorage
->smallBlockDepotChain
,
7562 offsetOfBlockInDepot
,
7567 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
7568 res
= STG_E_READFAULT
;
7572 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
7579 /******************************************************************************
7580 * SmallBlockChainStream_SetNextBlockInChain
7582 * Writes the index of the next block of the specified block in the small
7584 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7585 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7587 static void SmallBlockChainStream_SetNextBlockInChain(
7588 SmallBlockChainStream
* This
,
7592 ULARGE_INTEGER offsetOfBlockInDepot
;
7596 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7598 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
7601 * Read those bytes in the buffer from the small block file.
7603 BlockChainStream_WriteAt(
7604 This
->parentStorage
->smallBlockDepotChain
,
7605 offsetOfBlockInDepot
,
7611 /******************************************************************************
7612 * SmallBlockChainStream_FreeBlock
7614 * Flag small block 'blockIndex' as free in the small block depot.
7616 static void SmallBlockChainStream_FreeBlock(
7617 SmallBlockChainStream
* This
,
7620 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
7623 /******************************************************************************
7624 * SmallBlockChainStream_GetNextFreeBlock
7626 * Returns the index of a free small block. The small block depot will be
7627 * enlarged if necessary. The small block chain will also be enlarged if
7630 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
7631 SmallBlockChainStream
* This
)
7633 ULARGE_INTEGER offsetOfBlockInDepot
;
7636 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
7637 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
7639 ULONG smallBlocksPerBigBlock
;
7641 ULONG blocksRequired
;
7642 ULARGE_INTEGER old_size
, size_required
;
7644 offsetOfBlockInDepot
.u
.HighPart
= 0;
7647 * Scan the small block depot for a free block
7649 while (nextBlockIndex
!= BLOCK_UNUSED
)
7651 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7653 res
= BlockChainStream_ReadAt(
7654 This
->parentStorage
->smallBlockDepotChain
,
7655 offsetOfBlockInDepot
,
7661 * If we run out of space for the small block depot, enlarge it
7663 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
7665 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
7667 if (nextBlockIndex
!= BLOCK_UNUSED
)
7673 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
7675 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
7676 ULARGE_INTEGER newSize
, offset
;
7679 newSize
.QuadPart
= (ULONGLONG
)(count
+ 1) * This
->parentStorage
->bigBlockSize
;
7680 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
7683 * Initialize all the small blocks to free
7685 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
7686 offset
.QuadPart
= (ULONGLONG
)count
* This
->parentStorage
->bigBlockSize
;
7687 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
7688 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
7690 StorageImpl_SaveFileHeader(This
->parentStorage
);
7694 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
7696 smallBlocksPerBigBlock
=
7697 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
7700 * Verify if we have to allocate big blocks to contain small blocks
7702 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
7704 size_required
.QuadPart
= (ULONGLONG
)blocksRequired
* This
->parentStorage
->bigBlockSize
;
7706 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
7708 if (size_required
.QuadPart
> old_size
.QuadPart
)
7710 BlockChainStream_SetSize(
7711 This
->parentStorage
->smallBlockRootChain
,
7714 StorageImpl_ReadDirEntry(
7715 This
->parentStorage
,
7716 This
->parentStorage
->base
.storageDirEntry
,
7719 rootEntry
.size
= size_required
;
7721 StorageImpl_WriteDirEntry(
7722 This
->parentStorage
,
7723 This
->parentStorage
->base
.storageDirEntry
,
7730 /******************************************************************************
7731 * SmallBlockChainStream_ReadAt
7733 * Reads a specified number of bytes from this chain at the specified offset.
7734 * bytesRead may be NULL.
7735 * Failure will be returned if the specified number of bytes has not been read.
7737 HRESULT
SmallBlockChainStream_ReadAt(
7738 SmallBlockChainStream
* This
,
7739 ULARGE_INTEGER offset
,
7745 ULARGE_INTEGER offsetInBigBlockFile
;
7746 ULONG blockNoInSequence
=
7747 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7749 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
7750 ULONG bytesToReadInBuffer
;
7752 ULONG bytesReadFromBigBlockFile
;
7754 ULARGE_INTEGER stream_size
;
7757 * This should never happen on a small block file.
7759 assert(offset
.u
.HighPart
==0);
7763 stream_size
= SmallBlockChainStream_GetSize(This
);
7764 if (stream_size
.QuadPart
> offset
.QuadPart
)
7765 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7770 * Find the first block in the stream that contains part of the buffer.
7772 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7774 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
7776 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7779 blockNoInSequence
--;
7783 * Start reading the buffer.
7785 bufferWalker
= buffer
;
7787 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
7790 * Calculate how many bytes we can copy from this small block.
7792 bytesToReadInBuffer
=
7793 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
7796 * Calculate the offset of the small block in the small block file.
7798 offsetInBigBlockFile
.QuadPart
=
7799 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
7801 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
7804 * Read those bytes in the buffer from the small block file.
7805 * The small block has already been identified so it shouldn't fail
7806 * unless the file is corrupt.
7808 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
7809 offsetInBigBlockFile
,
7810 bytesToReadInBuffer
,
7812 &bytesReadFromBigBlockFile
);
7817 if (!bytesReadFromBigBlockFile
)
7818 return STG_E_DOCFILECORRUPT
;
7821 * Step to the next big block.
7823 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7825 return STG_E_DOCFILECORRUPT
;
7827 bufferWalker
+= bytesReadFromBigBlockFile
;
7828 size
-= bytesReadFromBigBlockFile
;
7829 *bytesRead
+= bytesReadFromBigBlockFile
;
7830 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7836 /******************************************************************************
7837 * SmallBlockChainStream_WriteAt
7839 * Writes the specified number of bytes to this chain at the specified offset.
7840 * Will fail if not all specified number of bytes have been written.
7842 HRESULT
SmallBlockChainStream_WriteAt(
7843 SmallBlockChainStream
* This
,
7844 ULARGE_INTEGER offset
,
7847 ULONG
* bytesWritten
)
7849 ULARGE_INTEGER offsetInBigBlockFile
;
7850 ULONG blockNoInSequence
=
7851 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7853 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
7854 ULONG bytesToWriteInBuffer
;
7856 ULONG bytesWrittenToBigBlockFile
;
7857 const BYTE
* bufferWalker
;
7861 * This should never happen on a small block file.
7863 assert(offset
.u
.HighPart
==0);
7866 * Find the first block in the stream that contains part of the buffer.
7868 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7870 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
7872 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
7873 return STG_E_DOCFILECORRUPT
;
7874 blockNoInSequence
--;
7878 * Start writing the buffer.
7881 bufferWalker
= buffer
;
7882 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
7885 * Calculate how many bytes we can copy to this small block.
7887 bytesToWriteInBuffer
=
7888 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
7891 * Calculate the offset of the small block in the small block file.
7893 offsetInBigBlockFile
.QuadPart
=
7894 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
7896 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
7899 * Write those bytes in the buffer to the small block file.
7901 res
= BlockChainStream_WriteAt(
7902 This
->parentStorage
->smallBlockRootChain
,
7903 offsetInBigBlockFile
,
7904 bytesToWriteInBuffer
,
7906 &bytesWrittenToBigBlockFile
);
7911 * Step to the next big block.
7913 res
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7916 bufferWalker
+= bytesWrittenToBigBlockFile
;
7917 size
-= bytesWrittenToBigBlockFile
;
7918 *bytesWritten
+= bytesWrittenToBigBlockFile
;
7919 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7922 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7925 /******************************************************************************
7926 * SmallBlockChainStream_Shrink
7928 * Shrinks this chain in the small block depot.
7930 static BOOL
SmallBlockChainStream_Shrink(
7931 SmallBlockChainStream
* This
,
7932 ULARGE_INTEGER newSize
)
7934 ULONG blockIndex
, extraBlock
;
7938 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7940 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7943 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7946 * Go to the new end of chain
7948 while (count
< numBlocks
)
7950 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7957 * If the count is 0, we have a special case, the head of the chain was
7962 DirEntry chainEntry
;
7964 StorageImpl_ReadDirEntry(This
->parentStorage
,
7965 This
->ownerDirEntry
,
7968 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7970 StorageImpl_WriteDirEntry(This
->parentStorage
,
7971 This
->ownerDirEntry
,
7975 * We start freeing the chain at the head block.
7977 extraBlock
= blockIndex
;
7981 /* Get the next block before marking the new end */
7982 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7986 /* Mark the new end of chain */
7987 SmallBlockChainStream_SetNextBlockInChain(
7990 BLOCK_END_OF_CHAIN
);
7994 * Mark the extra blocks as free
7996 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
7998 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
8001 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
8002 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
8003 extraBlock
= blockIndex
;
8009 /******************************************************************************
8010 * SmallBlockChainStream_Enlarge
8012 * Grows this chain in the small block depot.
8014 static BOOL
SmallBlockChainStream_Enlarge(
8015 SmallBlockChainStream
* This
,
8016 ULARGE_INTEGER newSize
)
8018 ULONG blockIndex
, currentBlock
;
8020 ULONG oldNumBlocks
= 0;
8022 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8025 * Empty chain. Create the head.
8027 if (blockIndex
== BLOCK_END_OF_CHAIN
)
8029 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8030 SmallBlockChainStream_SetNextBlockInChain(
8033 BLOCK_END_OF_CHAIN
);
8035 if (This
->headOfStreamPlaceHolder
!= NULL
)
8037 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
8041 DirEntry chainEntry
;
8043 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8046 chainEntry
.startingBlock
= blockIndex
;
8048 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8053 currentBlock
= blockIndex
;
8056 * Figure out how many blocks are needed to contain this stream
8058 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8060 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8064 * Go to the current end of chain
8066 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
8069 currentBlock
= blockIndex
;
8070 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
8075 * Add new blocks to the chain
8077 while (oldNumBlocks
< newNumBlocks
)
8079 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8080 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
8082 SmallBlockChainStream_SetNextBlockInChain(
8085 BLOCK_END_OF_CHAIN
);
8087 currentBlock
= blockIndex
;
8094 /******************************************************************************
8095 * SmallBlockChainStream_SetSize
8097 * Sets the size of this stream.
8098 * The file will grow if we grow the chain.
8100 * TODO: Free the actual blocks in the file when we shrink the chain.
8101 * Currently, the blocks are still in the file. So the file size
8102 * doesn't shrink even if we shrink streams.
8104 BOOL
SmallBlockChainStream_SetSize(
8105 SmallBlockChainStream
* This
,
8106 ULARGE_INTEGER newSize
)
8108 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
8110 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
8113 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
8115 SmallBlockChainStream_Shrink(This
, newSize
);
8119 SmallBlockChainStream_Enlarge(This
, newSize
);
8125 /******************************************************************************
8126 * SmallBlockChainStream_GetCount
8128 * Returns the number of small blocks that comprises this chain.
8129 * This is not the size of the stream as the last block may not be full!
8132 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
8137 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8139 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
8143 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
8144 blockIndex
, &blockIndex
)))
8151 /******************************************************************************
8152 * SmallBlockChainStream_GetSize
8154 * Returns the size of this chain.
8156 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
8158 DirEntry chainEntry
;
8160 if(This
->headOfStreamPlaceHolder
!= NULL
)
8162 ULARGE_INTEGER result
;
8163 result
.u
.HighPart
= 0;
8165 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
8166 This
->parentStorage
->smallBlockSize
;
8171 StorageImpl_ReadDirEntry(
8172 This
->parentStorage
,
8173 This
->ownerDirEntry
,
8176 return chainEntry
.size
;
8179 static HRESULT
create_storagefile(
8183 STGOPTIONS
* pStgOptions
,
8187 StorageBaseImpl
* newStorage
= 0;
8188 HANDLE hFile
= INVALID_HANDLE_VALUE
;
8189 HRESULT hr
= STG_E_INVALIDFLAG
;
8193 DWORD fileAttributes
;
8194 WCHAR tempFileName
[MAX_PATH
];
8197 return STG_E_INVALIDPOINTER
;
8199 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
8200 return STG_E_INVALIDPARAMETER
;
8202 /* if no share mode given then DENY_NONE is the default */
8203 if (STGM_SHARE_MODE(grfMode
) == 0)
8204 grfMode
|= STGM_SHARE_DENY_NONE
;
8206 if ( FAILED( validateSTGM(grfMode
) ))
8209 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8210 switch(STGM_ACCESS_MODE(grfMode
))
8213 case STGM_READWRITE
:
8219 /* in direct mode, can only use SHARE_EXCLUSIVE */
8220 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
8223 /* but in transacted mode, any share mode is valid */
8226 * Generate a unique name.
8230 WCHAR tempPath
[MAX_PATH
];
8231 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
8233 memset(tempPath
, 0, sizeof(tempPath
));
8234 memset(tempFileName
, 0, sizeof(tempFileName
));
8236 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
8239 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
8240 pwcsName
= tempFileName
;
8243 hr
= STG_E_INSUFFICIENTMEMORY
;
8247 creationMode
= TRUNCATE_EXISTING
;
8251 creationMode
= GetCreationModeFromSTGM(grfMode
);
8255 * Interpret the STGM value grfMode
8257 shareMode
= GetShareModeFromSTGM(grfMode
);
8258 accessMode
= GetAccessModeFromSTGM(grfMode
);
8260 if (grfMode
& STGM_DELETEONRELEASE
)
8261 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
8263 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
8267 hFile
= CreateFileW(pwcsName
,
8275 if (hFile
== INVALID_HANDLE_VALUE
)
8277 if(GetLastError() == ERROR_FILE_EXISTS
)
8278 hr
= STG_E_FILEALREADYEXISTS
;
8285 * Allocate and initialize the new IStorage32object.
8287 hr
= Storage_Construct(
8294 pStgOptions
->ulSectorSize
,
8302 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
8303 IStorage_Release(&newStorage
->IStorage_iface
);
8306 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
8311 /******************************************************************************
8312 * StgCreateDocfile [OLE32.@]
8313 * Creates a new compound file storage object
8316 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8317 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8318 * reserved [ ?] unused?, usually 0
8319 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8322 * S_OK if the file was successfully created
8323 * some STG_E_ value if error
8325 * if pwcsName is NULL, create file with new unique name
8326 * the function can returns
8327 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8330 HRESULT WINAPI
StgCreateDocfile(
8334 IStorage
**ppstgOpen
)
8336 STGOPTIONS stgoptions
= {1, 0, 512};
8338 TRACE("(%s, %x, %d, %p)\n",
8339 debugstr_w(pwcsName
), grfMode
,
8340 reserved
, ppstgOpen
);
8343 return STG_E_INVALIDPOINTER
;
8345 return STG_E_INVALIDPARAMETER
;
8347 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
8350 /******************************************************************************
8351 * StgCreateStorageEx [OLE32.@]
8353 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8355 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8356 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8358 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
8360 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8361 return STG_E_INVALIDPARAMETER
;
8364 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8366 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8367 return STG_E_INVALIDPARAMETER
;
8370 if (stgfmt
== STGFMT_FILE
)
8372 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8373 return STG_E_INVALIDPARAMETER
;
8376 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
8378 STGOPTIONS defaultOptions
= {1, 0, 512};
8380 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
8381 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
8385 ERR("Invalid stgfmt argument\n");
8386 return STG_E_INVALIDPARAMETER
;
8389 /******************************************************************************
8390 * StgCreatePropSetStg [OLE32.@]
8392 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
8393 IPropertySetStorage
**propset
)
8395 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
8397 return STG_E_INVALIDPARAMETER
;
8399 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
8402 /******************************************************************************
8403 * StgOpenStorageEx [OLE32.@]
8405 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8407 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8408 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8410 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
8412 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8413 return STG_E_INVALIDPARAMETER
;
8419 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8420 return STG_E_INVALIDPARAMETER
;
8422 case STGFMT_STORAGE
:
8425 case STGFMT_DOCFILE
:
8426 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8428 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8429 return STG_E_INVALIDPARAMETER
;
8431 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8435 WARN("STGFMT_ANY assuming storage\n");
8439 return STG_E_INVALIDPARAMETER
;
8442 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
8446 /******************************************************************************
8447 * StgOpenStorage [OLE32.@]
8449 HRESULT WINAPI
StgOpenStorage(
8450 const OLECHAR
*pwcsName
,
8451 IStorage
*pstgPriority
,
8455 IStorage
**ppstgOpen
)
8457 StorageBaseImpl
* newStorage
= 0;
8462 LPWSTR temp_name
= NULL
;
8464 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8465 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
8466 snbExclude
, reserved
, ppstgOpen
);
8470 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8471 hr
= StorageBaseImpl_GetFilename((StorageBaseImpl
*)pstgPriority
, &temp_name
);
8472 if (FAILED(hr
)) goto end
;
8473 pwcsName
= temp_name
;
8474 TRACE("using filename %s\n", debugstr_w(temp_name
));
8479 hr
= STG_E_INVALIDNAME
;
8485 hr
= STG_E_INVALIDPOINTER
;
8491 hr
= STG_E_INVALIDPARAMETER
;
8495 if (grfMode
& STGM_PRIORITY
)
8497 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
8498 return STG_E_INVALIDFLAG
;
8499 if (grfMode
& STGM_DELETEONRELEASE
)
8500 return STG_E_INVALIDFUNCTION
;
8501 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
8502 return STG_E_INVALIDFLAG
;
8503 grfMode
&= ~0xf0; /* remove the existing sharing mode */
8504 grfMode
|= STGM_SHARE_DENY_NONE
;
8508 * Validate the sharing mode
8510 if (grfMode
& STGM_DIRECT_SWMR
)
8512 if ((STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_WRITE
) &&
8513 (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_NONE
))
8515 hr
= STG_E_INVALIDFLAG
;
8519 else if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
8520 switch(STGM_SHARE_MODE(grfMode
))
8522 case STGM_SHARE_EXCLUSIVE
:
8523 case STGM_SHARE_DENY_WRITE
:
8526 hr
= STG_E_INVALIDFLAG
;
8530 if ( FAILED( validateSTGM(grfMode
) ) ||
8531 (grfMode
&STGM_CREATE
))
8533 hr
= STG_E_INVALIDFLAG
;
8537 /* shared reading requires transacted or single writer mode */
8538 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
8539 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
8540 !(grfMode
& STGM_TRANSACTED
) && !(grfMode
& STGM_DIRECT_SWMR
))
8542 hr
= STG_E_INVALIDFLAG
;
8547 * Interpret the STGM value grfMode
8549 shareMode
= GetShareModeFromSTGM(grfMode
);
8550 accessMode
= GetAccessModeFromSTGM(grfMode
);
8554 hFile
= CreateFileW( pwcsName
,
8559 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
8562 if (hFile
==INVALID_HANDLE_VALUE
)
8564 DWORD last_error
= GetLastError();
8570 case ERROR_FILE_NOT_FOUND
:
8571 hr
= STG_E_FILENOTFOUND
;
8574 case ERROR_PATH_NOT_FOUND
:
8575 hr
= STG_E_PATHNOTFOUND
;
8578 case ERROR_ACCESS_DENIED
:
8579 case ERROR_WRITE_PROTECT
:
8580 hr
= STG_E_ACCESSDENIED
;
8583 case ERROR_SHARING_VIOLATION
:
8584 hr
= STG_E_SHAREVIOLATION
;
8595 * Refuse to open the file if it's too small to be a structured storage file
8596 * FIXME: verify the file when reading instead of here
8598 if (GetFileSize(hFile
, NULL
) < 0x100)
8601 hr
= STG_E_FILEALREADYEXISTS
;
8606 * Allocate and initialize the new IStorage32object.
8608 hr
= Storage_Construct(
8621 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8623 if(hr
== STG_E_INVALIDHEADER
)
8624 hr
= STG_E_FILEALREADYEXISTS
;
8628 *ppstgOpen
= &newStorage
->IStorage_iface
;
8631 CoTaskMemFree(temp_name
);
8632 if (pstgPriority
) IStorage_Release(pstgPriority
);
8633 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
8637 /******************************************************************************
8638 * StgCreateDocfileOnILockBytes [OLE32.@]
8640 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
8644 IStorage
** ppstgOpen
)
8646 StorageBaseImpl
* newStorage
= 0;
8649 if ((ppstgOpen
== 0) || (plkbyt
== 0))
8650 return STG_E_INVALIDPOINTER
;
8653 * Allocate and initialize the new IStorage object.
8655 hr
= Storage_Construct(
8670 *ppstgOpen
= &newStorage
->IStorage_iface
;
8675 /******************************************************************************
8676 * StgOpenStorageOnILockBytes [OLE32.@]
8678 HRESULT WINAPI
StgOpenStorageOnILockBytes(
8680 IStorage
*pstgPriority
,
8684 IStorage
**ppstgOpen
)
8686 StorageBaseImpl
* newStorage
= 0;
8689 if ((plkbyt
== 0) || (ppstgOpen
== 0))
8690 return STG_E_INVALIDPOINTER
;
8692 if ( FAILED( validateSTGM(grfMode
) ))
8693 return STG_E_INVALIDFLAG
;
8698 * Allocate and initialize the new IStorage object.
8700 hr
= Storage_Construct(
8715 *ppstgOpen
= &newStorage
->IStorage_iface
;
8720 /******************************************************************************
8721 * StgSetTimes [ole32.@]
8722 * StgSetTimes [OLE32.@]
8726 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
8727 FILETIME
const *patime
, FILETIME
const *pmtime
)
8729 IStorage
*stg
= NULL
;
8732 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
8734 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
8738 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
8739 IStorage_Release(stg
);
8745 /******************************************************************************
8746 * StgIsStorageILockBytes [OLE32.@]
8748 * Determines if the ILockBytes contains a storage object.
8750 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
8752 BYTE sig
[sizeof(STORAGE_magic
)];
8753 ULARGE_INTEGER offset
;
8756 offset
.u
.HighPart
= 0;
8757 offset
.u
.LowPart
= 0;
8759 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
8761 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
8767 /******************************************************************************
8768 * WriteClassStg [OLE32.@]
8770 * This method will store the specified CLSID in the specified storage object
8772 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
8775 return E_INVALIDARG
;
8778 return STG_E_INVALIDPOINTER
;
8780 return IStorage_SetClass(pStg
, rclsid
);
8783 /***********************************************************************
8784 * ReadClassStg (OLE32.@)
8786 * This method reads the CLSID previously written to a storage object with
8787 * the WriteClassStg.
8790 * pstg [I] IStorage pointer
8791 * pclsid [O] Pointer to where the CLSID is written
8795 * Failure: HRESULT code.
8797 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
8802 TRACE("(%p, %p)\n", pstg
, pclsid
);
8804 if(!pstg
|| !pclsid
)
8805 return E_INVALIDARG
;
8808 * read a STATSTG structure (contains the clsid) from the storage
8810 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
8813 *pclsid
=pstatstg
.clsid
;
8818 /***********************************************************************
8819 * OleLoadFromStream (OLE32.@)
8821 * This function loads an object from stream
8823 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
8827 LPPERSISTSTREAM xstm
;
8829 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
8831 res
=ReadClassStm(pStm
,&clsid
);
8834 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
8837 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
8839 IUnknown_Release((IUnknown
*)*ppvObj
);
8842 res
=IPersistStream_Load(xstm
,pStm
);
8843 IPersistStream_Release(xstm
);
8844 /* FIXME: all refcounts ok at this point? I think they should be:
8847 * xstm : 0 (released)
8852 /***********************************************************************
8853 * OleSaveToStream (OLE32.@)
8855 * This function saves an object with the IPersistStream interface on it
8856 * to the specified stream.
8858 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
8864 TRACE("(%p,%p)\n",pPStm
,pStm
);
8866 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
8868 if (SUCCEEDED(res
)){
8870 res
=WriteClassStm(pStm
,&clsid
);
8874 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
8877 TRACE("Finished Save\n");
8881 /****************************************************************************
8882 * This method validate a STGM parameter that can contain the values below
8884 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8885 * The stgm values contained in 0xffff0000 are bitmasks.
8887 * STGM_DIRECT 0x00000000
8888 * STGM_TRANSACTED 0x00010000
8889 * STGM_SIMPLE 0x08000000
8891 * STGM_READ 0x00000000
8892 * STGM_WRITE 0x00000001
8893 * STGM_READWRITE 0x00000002
8895 * STGM_SHARE_DENY_NONE 0x00000040
8896 * STGM_SHARE_DENY_READ 0x00000030
8897 * STGM_SHARE_DENY_WRITE 0x00000020
8898 * STGM_SHARE_EXCLUSIVE 0x00000010
8900 * STGM_PRIORITY 0x00040000
8901 * STGM_DELETEONRELEASE 0x04000000
8903 * STGM_CREATE 0x00001000
8904 * STGM_CONVERT 0x00020000
8905 * STGM_FAILIFTHERE 0x00000000
8907 * STGM_NOSCRATCH 0x00100000
8908 * STGM_NOSNAPSHOT 0x00200000
8910 static HRESULT
validateSTGM(DWORD stgm
)
8912 DWORD access
= STGM_ACCESS_MODE(stgm
);
8913 DWORD share
= STGM_SHARE_MODE(stgm
);
8914 DWORD create
= STGM_CREATE_MODE(stgm
);
8916 if (stgm
&~STGM_KNOWN_FLAGS
)
8918 ERR("unknown flags %08x\n", stgm
);
8926 case STGM_READWRITE
:
8934 case STGM_SHARE_DENY_NONE
:
8935 case STGM_SHARE_DENY_READ
:
8936 case STGM_SHARE_DENY_WRITE
:
8937 case STGM_SHARE_EXCLUSIVE
:
8940 if (!(stgm
& STGM_TRANSACTED
))
8950 case STGM_FAILIFTHERE
:
8957 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8959 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
8963 * STGM_CREATE | STGM_CONVERT
8964 * if both are false, STGM_FAILIFTHERE is set to TRUE
8966 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
8970 * STGM_NOSCRATCH requires STGM_TRANSACTED
8972 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
8976 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8977 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8979 if ( (stgm
& STGM_NOSNAPSHOT
) &&
8980 (!(stgm
& STGM_TRANSACTED
) ||
8981 share
== STGM_SHARE_EXCLUSIVE
||
8982 share
== STGM_SHARE_DENY_WRITE
) )
8988 /****************************************************************************
8989 * GetShareModeFromSTGM
8991 * This method will return a share mode flag from a STGM value.
8992 * The STGM value is assumed valid.
8994 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
8996 switch (STGM_SHARE_MODE(stgm
))
8999 assert(stgm
& STGM_TRANSACTED
);
9001 case STGM_SHARE_DENY_NONE
:
9002 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
9003 case STGM_SHARE_DENY_READ
:
9004 return FILE_SHARE_WRITE
;
9005 case STGM_SHARE_DENY_WRITE
:
9006 case STGM_SHARE_EXCLUSIVE
:
9007 return FILE_SHARE_READ
;
9009 ERR("Invalid share mode!\n");
9014 /****************************************************************************
9015 * GetAccessModeFromSTGM
9017 * This method will return an access mode flag from a STGM value.
9018 * The STGM value is assumed valid.
9020 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
9022 switch (STGM_ACCESS_MODE(stgm
))
9025 return GENERIC_READ
;
9027 case STGM_READWRITE
:
9028 return GENERIC_READ
| GENERIC_WRITE
;
9030 ERR("Invalid access mode!\n");
9035 /****************************************************************************
9036 * GetCreationModeFromSTGM
9038 * This method will return a creation mode flag from a STGM value.
9039 * The STGM value is assumed valid.
9041 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
9043 switch(STGM_CREATE_MODE(stgm
))
9046 return CREATE_ALWAYS
;
9048 FIXME("STGM_CONVERT not implemented!\n");
9050 case STGM_FAILIFTHERE
:
9053 ERR("Invalid create mode!\n");
9059 /*************************************************************************
9060 * OLECONVERT_LoadOLE10 [Internal]
9062 * Loads the OLE10 STREAM to memory
9065 * pOleStream [I] The OLESTREAM
9066 * pData [I] Data Structure for the OLESTREAM Data
9070 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9071 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9074 * This function is used by OleConvertOLESTREAMToIStorage only.
9076 * Memory allocated for pData must be freed by the caller
9078 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
9081 HRESULT hRes
= S_OK
;
9085 pData
->pData
= NULL
;
9086 pData
->pstrOleObjFileName
= NULL
;
9088 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
9091 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9092 if(dwSize
!= sizeof(pData
->dwOleID
))
9094 hRes
= CONVERT10_E_OLESTREAM_GET
;
9096 else if(pData
->dwOleID
!= OLESTREAM_ID
)
9098 hRes
= CONVERT10_E_OLESTREAM_FMT
;
9109 /* Get the TypeID... more info needed for this field */
9110 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9111 if(dwSize
!= sizeof(pData
->dwTypeID
))
9113 hRes
= CONVERT10_E_OLESTREAM_GET
;
9118 if(pData
->dwTypeID
!= 0)
9120 /* Get the length of the OleTypeName */
9121 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9122 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9124 hRes
= CONVERT10_E_OLESTREAM_GET
;
9129 if(pData
->dwOleTypeNameLength
> 0)
9131 /* Get the OleTypeName */
9132 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9133 if(dwSize
!= pData
->dwOleTypeNameLength
)
9135 hRes
= CONVERT10_E_OLESTREAM_GET
;
9141 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
9142 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
9144 hRes
= CONVERT10_E_OLESTREAM_GET
;
9148 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
9149 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
9150 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
9151 if(pData
->pstrOleObjFileName
)
9153 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
9154 if(dwSize
!= pData
->dwOleObjFileNameLength
)
9156 hRes
= CONVERT10_E_OLESTREAM_GET
;
9160 hRes
= CONVERT10_E_OLESTREAM_GET
;
9165 /* Get the Width of the Metafile */
9166 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9167 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9169 hRes
= CONVERT10_E_OLESTREAM_GET
;
9173 /* Get the Height of the Metafile */
9174 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9175 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9177 hRes
= CONVERT10_E_OLESTREAM_GET
;
9183 /* Get the Length of the Data */
9184 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9185 if(dwSize
!= sizeof(pData
->dwDataLength
))
9187 hRes
= CONVERT10_E_OLESTREAM_GET
;
9191 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
9193 if(!bStrem1
) /* if it is a second OLE stream data */
9195 pData
->dwDataLength
-= 8;
9196 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
9197 if(dwSize
!= sizeof(pData
->strUnknown
))
9199 hRes
= CONVERT10_E_OLESTREAM_GET
;
9205 if(pData
->dwDataLength
> 0)
9207 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
9209 /* Get Data (ex. IStorage, Metafile, or BMP) */
9212 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
9213 if(dwSize
!= pData
->dwDataLength
)
9215 hRes
= CONVERT10_E_OLESTREAM_GET
;
9220 hRes
= CONVERT10_E_OLESTREAM_GET
;
9229 /*************************************************************************
9230 * OLECONVERT_SaveOLE10 [Internal]
9232 * Saves the OLE10 STREAM From memory
9235 * pData [I] Data Structure for the OLESTREAM Data
9236 * pOleStream [I] The OLESTREAM to save
9240 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9243 * This function is used by OleConvertIStorageToOLESTREAM only.
9246 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
9249 HRESULT hRes
= S_OK
;
9253 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9254 if(dwSize
!= sizeof(pData
->dwOleID
))
9256 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9261 /* Set the TypeID */
9262 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9263 if(dwSize
!= sizeof(pData
->dwTypeID
))
9265 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9269 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
9271 /* Set the Length of the OleTypeName */
9272 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9273 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9275 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9280 if(pData
->dwOleTypeNameLength
> 0)
9282 /* Set the OleTypeName */
9283 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9284 if(dwSize
!= pData
->dwOleTypeNameLength
)
9286 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9293 /* Set the width of the Metafile */
9294 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9295 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9297 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9303 /* Set the height of the Metafile */
9304 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9305 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9307 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9313 /* Set the length of the Data */
9314 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9315 if(dwSize
!= sizeof(pData
->dwDataLength
))
9317 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9323 if(pData
->dwDataLength
> 0)
9325 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9326 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
9327 if(dwSize
!= pData
->dwDataLength
)
9329 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9337 /*************************************************************************
9338 * OLECONVERT_GetOLE20FromOLE10[Internal]
9340 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9341 * opens it, and copies the content to the dest IStorage for
9342 * OleConvertOLESTREAMToIStorage
9346 * pDestStorage [I] The IStorage to copy the data to
9347 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9348 * nBufferLength [I] The size of the buffer
9357 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
9361 IStorage
*pTempStorage
;
9362 DWORD dwNumOfBytesWritten
;
9363 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9364 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9366 /* Create a temp File */
9367 GetTempPathW(MAX_PATH
, wstrTempDir
);
9368 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9369 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
9371 if(hFile
!= INVALID_HANDLE_VALUE
)
9373 /* Write IStorage Data to File */
9374 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
9377 /* Open and copy temp storage to the Dest Storage */
9378 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
9381 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
9382 IStorage_Release(pTempStorage
);
9384 DeleteFileW(wstrTempFile
);
9389 /*************************************************************************
9390 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9392 * Saves the OLE10 STREAM From memory
9395 * pStorage [I] The Src IStorage to copy
9396 * pData [I] The Dest Memory to write to.
9399 * The size in bytes allocated for pData
9402 * Memory allocated for pData must be freed by the caller
9404 * Used by OleConvertIStorageToOLESTREAM only.
9407 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
9411 DWORD nDataLength
= 0;
9412 IStorage
*pTempStorage
;
9413 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9414 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9418 /* Create temp Storage */
9419 GetTempPathW(MAX_PATH
, wstrTempDir
);
9420 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9421 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
9425 /* Copy Src Storage to the Temp Storage */
9426 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
9427 IStorage_Release(pTempStorage
);
9429 /* Open Temp Storage as a file and copy to memory */
9430 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9431 if(hFile
!= INVALID_HANDLE_VALUE
)
9433 nDataLength
= GetFileSize(hFile
, NULL
);
9434 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
9435 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
9438 DeleteFileW(wstrTempFile
);
9443 /*************************************************************************
9444 * STORAGE_CreateOleStream [Internal]
9446 * Creates the "\001OLE" stream in the IStorage if necessary.
9449 * storage [I] Dest storage to create the stream in
9450 * flags [I] flags to be set for newly created stream
9453 * HRESULT return value
9457 * This stream is still unknown, MS Word seems to have extra data
9458 * but since the data is stored in the OLESTREAM there should be
9459 * no need to recreate the stream. If the stream is manually
9460 * deleted it will create it with this default data.
9463 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
9465 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9466 static const DWORD version_magic
= 0x02000001;
9470 hr
= IStorage_CreateStream(storage
, stream_1oleW
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
9473 struct empty_1ole_stream
{
9474 DWORD version_magic
;
9476 DWORD update_options
;
9478 DWORD mon_stream_size
;
9480 struct empty_1ole_stream stream_data
;
9482 stream_data
.version_magic
= version_magic
;
9483 stream_data
.flags
= flags
;
9484 stream_data
.update_options
= 0;
9485 stream_data
.reserved
= 0;
9486 stream_data
.mon_stream_size
= 0;
9488 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
9489 IStream_Release(stream
);
9495 /* write a string to a stream, preceded by its length */
9496 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
9503 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
9504 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
9509 str
= CoTaskMemAlloc( len
);
9510 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
9511 r
= IStream_Write( stm
, str
, len
, NULL
);
9512 CoTaskMemFree( str
);
9516 /* read a string preceded by its length from a stream */
9517 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
9520 DWORD len
, count
= 0;
9524 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
9527 if( count
!= sizeof(len
) )
9528 return E_OUTOFMEMORY
;
9530 TRACE("%d bytes\n",len
);
9532 str
= CoTaskMemAlloc( len
);
9534 return E_OUTOFMEMORY
;
9536 r
= IStream_Read( stm
, str
, len
, &count
);
9541 CoTaskMemFree( str
);
9542 return E_OUTOFMEMORY
;
9545 TRACE("Read string %s\n",debugstr_an(str
,len
));
9547 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
9548 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
9551 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
9554 CoTaskMemFree( str
);
9562 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
9563 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
9567 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9569 static const BYTE unknown1
[12] =
9570 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9571 0xFF, 0xFF, 0xFF, 0xFF};
9572 static const BYTE unknown2
[16] =
9573 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9574 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9576 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
9577 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
9578 debugstr_w(szProgIDName
));
9580 /* Create a CompObj stream */
9581 r
= IStorage_CreateStream(pstg
, szwStreamName
,
9582 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
9586 /* Write CompObj Structure to stream */
9587 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
9589 if( SUCCEEDED( r
) )
9590 r
= WriteClassStm( pstm
, clsid
);
9592 if( SUCCEEDED( r
) )
9593 r
= STREAM_WriteString( pstm
, lpszUserType
);
9594 if( SUCCEEDED( r
) )
9595 r
= STREAM_WriteString( pstm
, szClipName
);
9596 if( SUCCEEDED( r
) )
9597 r
= STREAM_WriteString( pstm
, szProgIDName
);
9598 if( SUCCEEDED( r
) )
9599 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
9601 IStream_Release( pstm
);
9606 /***********************************************************************
9607 * WriteFmtUserTypeStg (OLE32.@)
9609 HRESULT WINAPI
WriteFmtUserTypeStg(
9610 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
9614 WCHAR szwClipName
[0x40];
9616 LPWSTR wstrProgID
= NULL
;
9619 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
9621 /* get the clipboard format name */
9624 n
= GetClipboardFormatNameW( cf
, szwClipName
,
9625 sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
9629 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
9631 r
= IStorage_Stat(pstg
, &stat
, STATFLAG_NONAME
);
9637 ProgIDFromCLSID(&clsid
, &wstrProgID
);
9639 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
9641 r
= STORAGE_WriteCompObj( pstg
, &clsid
, lpszUserType
,
9642 cf
? szwClipName
: NULL
, wstrProgID
);
9644 CoTaskMemFree(wstrProgID
);
9650 /******************************************************************************
9651 * ReadFmtUserTypeStg [OLE32.@]
9653 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
9657 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
9658 unsigned char unknown1
[12];
9659 unsigned char unknown2
[16];
9661 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
9664 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
9666 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
9667 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
9670 WARN("Failed to open stream r = %08x\n", r
);
9674 /* read the various parts of the structure */
9675 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
9676 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
9678 r
= ReadClassStm( stm
, &clsid
);
9682 r
= STREAM_ReadString( stm
, &szCLSIDName
);
9686 r
= STREAM_ReadString( stm
, &szOleTypeName
);
9690 r
= STREAM_ReadString( stm
, &szProgIDName
);
9694 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
9695 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
9698 /* ok, success... now we just need to store what we found */
9700 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
9702 if( lplpszUserType
)
9704 *lplpszUserType
= szCLSIDName
;
9709 CoTaskMemFree( szCLSIDName
);
9710 CoTaskMemFree( szOleTypeName
);
9711 CoTaskMemFree( szProgIDName
);
9712 IStream_Release( stm
);
9718 /*************************************************************************
9719 * OLECONVERT_CreateCompObjStream [Internal]
9721 * Creates a "\001CompObj" is the destination IStorage if necessary.
9724 * pStorage [I] The dest IStorage to create the CompObj Stream
9726 * strOleTypeName [I] The ProgID
9730 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9733 * This function is used by OleConvertOLESTREAMToIStorage only.
9735 * The stream data is stored in the OLESTREAM and there should be
9736 * no need to recreate the stream. If the stream is manually
9737 * deleted it will attempt to create it by querying the registry.
9741 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
9744 HRESULT hStorageRes
, hRes
= S_OK
;
9745 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
9746 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9747 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
9749 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9750 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
9752 /* Initialize the CompObj structure */
9753 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
9754 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
9755 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
9758 /* Create a CompObj stream if it doesn't exist */
9759 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9760 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9761 if(hStorageRes
== S_OK
)
9763 /* copy the OleTypeName to the compobj struct */
9764 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
9765 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
9767 /* copy the OleTypeName to the compobj struct */
9768 /* Note: in the test made, these were Identical */
9769 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
9770 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
9773 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
9774 bufferW
, OLESTREAM_MAX_STR_LEN
);
9775 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
9781 /* Get the CLSID Default Name from the Registry */
9782 hErr
= open_classes_key(HKEY_CLASSES_ROOT
, bufferW
, MAXIMUM_ALLOWED
, &hKey
);
9783 if(hErr
== ERROR_SUCCESS
)
9785 char strTemp
[OLESTREAM_MAX_STR_LEN
];
9786 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
9787 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
9788 if(hErr
== ERROR_SUCCESS
)
9790 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
9796 /* Write CompObj Structure to stream */
9797 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
9799 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
9801 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
9802 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
9804 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
9806 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
9807 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
9809 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
9811 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
9812 if(IStorageCompObj
.dwProgIDNameLength
> 0)
9814 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
9816 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
9817 IStream_Release(pStream
);
9823 /*************************************************************************
9824 * OLECONVERT_CreateOlePresStream[Internal]
9826 * Creates the "\002OlePres000" Stream with the Metafile data
9829 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
9830 * dwExtentX [I] Width of the Metafile
9831 * dwExtentY [I] Height of the Metafile
9832 * pData [I] Metafile data
9833 * dwDataLength [I] Size of the Metafile data
9837 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9840 * This function is used by OleConvertOLESTREAMToIStorage only.
9843 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
9847 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9848 BYTE pOlePresStreamHeader
[] =
9850 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
9851 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9852 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9853 0x00, 0x00, 0x00, 0x00
9856 BYTE pOlePresStreamHeaderEmpty
[] =
9858 0x00, 0x00, 0x00, 0x00,
9859 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9860 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9861 0x00, 0x00, 0x00, 0x00
9864 /* Create the OlePres000 Stream */
9865 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9866 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9871 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
9873 memset(&OlePres
, 0, sizeof(OlePres
));
9874 /* Do we have any metafile data to save */
9875 if(dwDataLength
> 0)
9877 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
9878 nHeaderSize
= sizeof(pOlePresStreamHeader
);
9882 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
9883 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
9885 /* Set width and height of the metafile */
9886 OlePres
.dwExtentX
= dwExtentX
;
9887 OlePres
.dwExtentY
= -dwExtentY
;
9889 /* Set Data and Length */
9890 if(dwDataLength
> sizeof(METAFILEPICT16
))
9892 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
9893 OlePres
.pData
= &(pData
[8]);
9895 /* Save OlePres000 Data to Stream */
9896 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
9897 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
9898 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
9899 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
9900 if(OlePres
.dwSize
> 0)
9902 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
9904 IStream_Release(pStream
);
9908 /*************************************************************************
9909 * OLECONVERT_CreateOle10NativeStream [Internal]
9911 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9914 * pStorage [I] Dest storage to create the stream in
9915 * pData [I] Ole10 Native Data (ex. bmp)
9916 * dwDataLength [I] Size of the Ole10 Native Data
9922 * This function is used by OleConvertOLESTREAMToIStorage only.
9924 * Might need to verify the data and return appropriate error message
9927 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
9931 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9933 /* Create the Ole10Native Stream */
9934 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9935 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9939 /* Write info to stream */
9940 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
9941 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
9942 IStream_Release(pStream
);
9947 /*************************************************************************
9948 * OLECONVERT_GetOLE10ProgID [Internal]
9950 * Finds the ProgID (or OleTypeID) from the IStorage
9953 * pStorage [I] The Src IStorage to get the ProgID
9954 * strProgID [I] the ProgID string to get
9955 * dwSize [I] the size of the string
9959 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9962 * This function is used by OleConvertIStorageToOLESTREAM only.
9966 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
9970 LARGE_INTEGER iSeekPos
;
9971 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
9972 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9974 /* Open the CompObj Stream */
9975 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9976 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9980 /*Get the OleType from the CompObj Stream */
9981 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
9982 iSeekPos
.u
.HighPart
= 0;
9984 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9985 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
9986 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
9987 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9988 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
9989 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
9990 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9992 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
9995 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
9997 IStream_Release(pStream
);
10002 LPOLESTR wstrProgID
;
10004 /* Get the OleType from the registry */
10005 REFCLSID clsid
= &(stat
.clsid
);
10006 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
10007 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
10010 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
10011 CoTaskMemFree(wstrProgID
);
10018 /*************************************************************************
10019 * OLECONVERT_GetOle10PresData [Internal]
10021 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10024 * pStorage [I] Src IStroage
10025 * pOleStream [I] Dest OleStream Mem Struct
10031 * This function is used by OleConvertIStorageToOLESTREAM only.
10033 * Memory allocated for pData must be freed by the caller
10037 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10042 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10044 /* Initialize Default data for OLESTREAM */
10045 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10046 pOleStreamData
[0].dwTypeID
= 2;
10047 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10048 pOleStreamData
[1].dwTypeID
= 0;
10049 pOleStreamData
[0].dwMetaFileWidth
= 0;
10050 pOleStreamData
[0].dwMetaFileHeight
= 0;
10051 pOleStreamData
[0].pData
= NULL
;
10052 pOleStreamData
[1].pData
= NULL
;
10054 /* Open Ole10Native Stream */
10055 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10056 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10060 /* Read Size and Data */
10061 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
10062 if(pOleStreamData
->dwDataLength
> 0)
10064 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
10065 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
10067 IStream_Release(pStream
);
10073 /*************************************************************************
10074 * OLECONVERT_GetOle20PresData[Internal]
10076 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10079 * pStorage [I] Src IStroage
10080 * pOleStreamData [I] Dest OleStream Mem Struct
10086 * This function is used by OleConvertIStorageToOLESTREAM only.
10088 * Memory allocated for pData must be freed by the caller
10090 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10094 OLECONVERT_ISTORAGE_OLEPRES olePress
;
10095 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10097 /* Initialize Default data for OLESTREAM */
10098 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10099 pOleStreamData
[0].dwTypeID
= 2;
10100 pOleStreamData
[0].dwMetaFileWidth
= 0;
10101 pOleStreamData
[0].dwMetaFileHeight
= 0;
10102 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
10103 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10104 pOleStreamData
[1].dwTypeID
= 0;
10105 pOleStreamData
[1].dwOleTypeNameLength
= 0;
10106 pOleStreamData
[1].strOleTypeName
[0] = 0;
10107 pOleStreamData
[1].dwMetaFileWidth
= 0;
10108 pOleStreamData
[1].dwMetaFileHeight
= 0;
10109 pOleStreamData
[1].pData
= NULL
;
10110 pOleStreamData
[1].dwDataLength
= 0;
10113 /* Open OlePress000 stream */
10114 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10115 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10118 LARGE_INTEGER iSeekPos
;
10119 METAFILEPICT16 MetaFilePict
;
10120 static const char strMetafilePictName
[] = "METAFILEPICT";
10122 /* Set the TypeID for a Metafile */
10123 pOleStreamData
[1].dwTypeID
= 5;
10125 /* Set the OleTypeName to Metafile */
10126 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
10127 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
10129 iSeekPos
.u
.HighPart
= 0;
10130 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
10132 /* Get Presentation Data */
10133 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10134 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
10135 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
10136 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
10138 /*Set width and Height */
10139 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
10140 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
10141 if(olePress
.dwSize
> 0)
10144 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
10146 /* Set MetaFilePict struct */
10147 MetaFilePict
.mm
= 8;
10148 MetaFilePict
.xExt
= olePress
.dwExtentX
;
10149 MetaFilePict
.yExt
= olePress
.dwExtentY
;
10150 MetaFilePict
.hMF
= 0;
10152 /* Get Metafile Data */
10153 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
10154 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
10155 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
10157 IStream_Release(pStream
);
10161 /*************************************************************************
10162 * OleConvertOLESTREAMToIStorage [OLE32.@]
10164 * Read info on MSDN
10167 * DVTARGETDEVICE parameter is not handled
10168 * Still unsure of some mem fields for OLE 10 Stream
10169 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10170 * and "\001OLE" streams
10173 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
10174 LPOLESTREAM pOleStream
,
10176 const DVTARGETDEVICE
* ptd
)
10180 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10182 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
10184 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10188 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10191 if(pstg
== NULL
|| pOleStream
== NULL
)
10193 hRes
= E_INVALIDARG
;
10198 /* Load the OLESTREAM to Memory */
10199 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
10204 /* Load the OLESTREAM to Memory (part 2)*/
10205 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
10211 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
10213 /* Do we have the IStorage Data in the OLESTREAM */
10214 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
10216 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10217 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
10221 /* It must be an original OLE 1.0 source */
10222 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10227 /* It must be an original OLE 1.0 source */
10228 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10231 /* Create CompObj Stream if necessary */
10232 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
10235 /*Create the Ole Stream if necessary */
10236 STORAGE_CreateOleStream(pstg
, 0);
10241 /* Free allocated memory */
10242 for(i
=0; i
< 2; i
++)
10244 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10245 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
10246 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
10251 /*************************************************************************
10252 * OleConvertIStorageToOLESTREAM [OLE32.@]
10254 * Read info on MSDN
10256 * Read info on MSDN
10259 * Still unsure of some mem fields for OLE 10 Stream
10260 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10261 * and "\001OLE" streams.
10264 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
10266 LPOLESTREAM pOleStream
)
10269 HRESULT hRes
= S_OK
;
10271 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10272 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10274 TRACE("%p %p\n", pstg
, pOleStream
);
10276 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10278 if(pstg
== NULL
|| pOleStream
== NULL
)
10280 hRes
= E_INVALIDARG
;
10284 /* Get the ProgID */
10285 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
10286 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
10290 /* Was it originally Ole10 */
10291 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10294 IStream_Release(pStream
);
10295 /* Get Presentation Data for Ole10Native */
10296 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
10300 /* Get Presentation Data (OLE20) */
10301 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
10304 /* Save OLESTREAM */
10305 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
10308 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
10313 /* Free allocated memory */
10314 for(i
=0; i
< 2; i
++)
10316 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10322 enum stream_1ole_flags
{
10323 OleStream_LinkedObject
= 0x00000001,
10324 OleStream_Convert
= 0x00000004
10327 /***********************************************************************
10328 * GetConvertStg (OLE32.@)
10330 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
10332 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10333 static const DWORD version_magic
= 0x02000001;
10338 TRACE("%p\n", stg
);
10340 if (!stg
) return E_INVALIDARG
;
10342 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10343 if (FAILED(hr
)) return hr
;
10345 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10346 IStream_Release(stream
);
10347 if (FAILED(hr
)) return hr
;
10349 if (header
[0] != version_magic
)
10351 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header
[0]);
10355 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
10358 /***********************************************************************
10359 * SetConvertStg (OLE32.@)
10361 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
10363 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10364 DWORD flags
= convert
? OleStream_Convert
: 0;
10369 TRACE("(%p, %d)\n", storage
, convert
);
10371 hr
= IStorage_OpenStream(storage
, stream_1oleW
, NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10374 if (hr
!= STG_E_FILENOTFOUND
)
10377 return STORAGE_CreateOleStream(storage
, flags
);
10380 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10383 IStream_Release(stream
);
10387 /* update flag if differs */
10388 if ((header
[1] ^ flags
) & OleStream_Convert
)
10390 LARGE_INTEGER pos
= {{0}};
10392 if (header
[1] & OleStream_Convert
)
10393 flags
= header
[1] & ~OleStream_Convert
;
10395 flags
= header
[1] | OleStream_Convert
;
10397 pos
.QuadPart
= sizeof(DWORD
);
10398 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
10401 IStream_Release(stream
);
10405 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
10408 IStream_Release(stream
);
10412 /******************************************************************************
10413 * StgIsStorageFile [OLE32.@]
10414 * Verify if the file contains a storage object
10420 * S_OK if file has magic bytes as a storage object
10421 * S_FALSE if file is not storage
10424 StgIsStorageFile(LPCOLESTR fn
)
10430 TRACE("%s\n", debugstr_w(fn
));
10431 hf
= CreateFileW(fn
, GENERIC_READ
,
10432 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
10433 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
10435 if (hf
== INVALID_HANDLE_VALUE
)
10436 return STG_E_FILENOTFOUND
;
10438 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
10440 WARN(" unable to read file\n");
10447 if (bytes_read
!= 8) {
10448 TRACE(" too short\n");
10452 if (!memcmp(magic
,STORAGE_magic
,8)) {
10453 TRACE(" -> YES\n");
10457 TRACE(" -> Invalid header.\n");
10461 /***********************************************************************
10462 * WriteClassStm (OLE32.@)
10464 * Writes a CLSID to a stream.
10467 * pStm [I] Stream to write to.
10468 * rclsid [I] CLSID to write.
10472 * Failure: HRESULT code.
10474 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
10476 TRACE("(%p,%p)\n",pStm
,rclsid
);
10478 if (!pStm
|| !rclsid
)
10479 return E_INVALIDARG
;
10481 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
10484 /***********************************************************************
10485 * ReadClassStm (OLE32.@)
10487 * Reads a CLSID from a stream.
10490 * pStm [I] Stream to read from.
10491 * rclsid [O] CLSID to read.
10495 * Failure: HRESULT code.
10497 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
10502 TRACE("(%p,%p)\n",pStm
,pclsid
);
10504 if (!pStm
|| !pclsid
)
10505 return E_INVALIDARG
;
10507 /* clear the output args */
10508 *pclsid
= CLSID_NULL
;
10510 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
10515 if (nbByte
!= sizeof(CLSID
))
10516 return STG_E_READFAULT
;