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
)
2879 /* if it's a FileLockBytesImpl use LockFileEx in blocking mode */
2880 if (SUCCEEDED(FileLockBytesImpl_LockRegionSync(This
->lockBytes
, offset
, cb
)))
2883 /* otherwise we have to fake it based on an async lock */
2886 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, dwLockType
);
2888 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
2891 if (delay
< 150) delay
++;
2893 } while (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
);
2898 static HRESULT
StorageImpl_CheckLockRange(StorageImpl
*This
, ULONG start
,
2899 ULONG end
, HRESULT fail_hr
)
2902 ULARGE_INTEGER offset
, cb
;
2904 offset
.QuadPart
= start
;
2905 cb
.QuadPart
= 1 + end
- start
;
2907 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2908 if (SUCCEEDED(hr
)) ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2910 if (hr
== STG_E_ACCESSDENIED
|| hr
== STG_E_LOCKVIOLATION
)
2916 static HRESULT
StorageImpl_LockOne(StorageImpl
*This
, ULONG start
, ULONG end
)
2920 ULARGE_INTEGER offset
, cb
;
2924 for (i
=start
; i
<=end
; i
++)
2926 offset
.QuadPart
= i
;
2927 hr
= ILockBytes_LockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
2928 if (hr
!= STG_E_ACCESSDENIED
&& hr
!= STG_E_LOCKVIOLATION
)
2934 for (j
=0; j
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); j
++)
2936 if (This
->locked_bytes
[j
] == 0)
2938 This
->locked_bytes
[j
] = i
;
2947 static HRESULT
StorageImpl_GrabLocks(StorageImpl
*This
, DWORD openFlags
)
2950 ULARGE_INTEGER offset
;
2952 DWORD share_mode
= STGM_SHARE_MODE(openFlags
);
2954 if (openFlags
& STGM_NOSNAPSHOT
)
2956 /* STGM_NOSNAPSHOT implies deny write */
2957 if (share_mode
== STGM_SHARE_DENY_READ
) share_mode
= STGM_SHARE_EXCLUSIVE
;
2958 else if (share_mode
!= STGM_SHARE_EXCLUSIVE
) share_mode
= STGM_SHARE_DENY_WRITE
;
2961 /* Wrap all other locking inside a single lock so we can check ranges safely */
2962 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
2964 hr
= StorageImpl_LockRegionSync(This
, offset
, cb
, LOCK_ONLYONCE
);
2966 /* If the ILockBytes doesn't support locking that's ok. */
2967 if (FAILED(hr
)) return S_OK
;
2971 /* First check for any conflicting locks. */
2972 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
2973 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_COMMIT
, RANGELOCK_COMMIT
, STG_E_LOCKVIOLATION
);
2975 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
2976 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
, STG_E_SHAREVIOLATION
);
2978 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
2979 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
, STG_E_SHAREVIOLATION
);
2981 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
2982 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
, STG_E_LOCKVIOLATION
);
2984 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
2985 hr
= StorageImpl_CheckLockRange(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
, STG_E_LOCKVIOLATION
);
2987 /* Then grab our locks. */
2988 if (SUCCEEDED(hr
) && (openFlags
& STGM_PRIORITY
) == STGM_PRIORITY
)
2990 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY1_FIRST
, RANGELOCK_PRIORITY1_LAST
);
2992 hr
= StorageImpl_LockOne(This
, RANGELOCK_PRIORITY2_FIRST
, RANGELOCK_PRIORITY2_LAST
);
2995 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_WRITE
))
2996 hr
= StorageImpl_LockOne(This
, RANGELOCK_READ_FIRST
, RANGELOCK_READ_LAST
);
2998 if (SUCCEEDED(hr
) && (STGM_ACCESS_MODE(openFlags
) != STGM_READ
))
2999 hr
= StorageImpl_LockOne(This
, RANGELOCK_WRITE_FIRST
, RANGELOCK_WRITE_LAST
);
3001 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_READ
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
3002 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_READ_FIRST
, RANGELOCK_DENY_READ_LAST
);
3004 if (SUCCEEDED(hr
) && (share_mode
== STGM_SHARE_DENY_WRITE
|| share_mode
== STGM_SHARE_EXCLUSIVE
))
3005 hr
= StorageImpl_LockOne(This
, RANGELOCK_DENY_WRITE_FIRST
, RANGELOCK_DENY_WRITE_LAST
);
3007 if (SUCCEEDED(hr
) && (openFlags
& STGM_NOSNAPSHOT
) == STGM_NOSNAPSHOT
)
3008 hr
= StorageImpl_LockOne(This
, RANGELOCK_NOSNAPSHOT_FIRST
, RANGELOCK_NOSNAPSHOT_LAST
);
3010 offset
.QuadPart
= RANGELOCK_CHECKLOCKS
;
3012 ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
3017 static HRESULT
StorageImpl_Refresh(StorageImpl
*This
, BOOL new_object
, BOOL create
)
3020 DirEntry currentEntry
;
3021 DirRef currentEntryRef
;
3022 BlockChainStream
*blockChainStream
;
3026 ULARGE_INTEGER size
;
3027 BYTE bigBlockBuffer
[MAX_BIG_BLOCK_SIZE
];
3029 /* Discard any existing data. */
3031 ILockBytes_SetSize(This
->lockBytes
, size
);
3034 * Initialize all header variables:
3035 * - The big block depot consists of one block and it is at block 0
3036 * - The directory table starts at block 1
3037 * - There is no small block depot
3039 memset( This
->bigBlockDepotStart
,
3041 sizeof(This
->bigBlockDepotStart
));
3043 This
->bigBlockDepotCount
= 1;
3044 This
->bigBlockDepotStart
[0] = 0;
3045 This
->rootStartBlock
= 1;
3046 This
->smallBlockLimit
= LIMIT_TO_USE_SMALL_BLOCK
;
3047 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
3048 if (This
->bigBlockSize
== 4096)
3049 This
->bigBlockSizeBits
= MAX_BIG_BLOCK_SIZE_BITS
;
3051 This
->bigBlockSizeBits
= MIN_BIG_BLOCK_SIZE_BITS
;
3052 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
3053 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
3054 This
->extBigBlockDepotCount
= 0;
3056 StorageImpl_SaveFileHeader(This
);
3059 * Add one block for the big block depot and one block for the directory table
3061 size
.u
.HighPart
= 0;
3062 size
.u
.LowPart
= This
->bigBlockSize
* 3;
3063 ILockBytes_SetSize(This
->lockBytes
, size
);
3066 * Initialize the big block depot
3068 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3069 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
3070 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
3071 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
3076 * Load the header for the file.
3078 hr
= StorageImpl_LoadFileHeader(This
);
3087 * There is no block depot cached yet.
3089 This
->indexBlockDepotCached
= 0xFFFFFFFF;
3090 This
->indexExtBlockDepotCached
= 0xFFFFFFFF;
3093 * Start searching for free blocks with block 0.
3095 This
->prevFreeBlock
= 0;
3097 This
->firstFreeSmallBlock
= 0;
3099 /* Read the extended big block depot locations. */
3100 if (This
->extBigBlockDepotCount
!= 0)
3102 ULONG current_block
= This
->extBigBlockDepotStart
;
3103 ULONG cache_size
= This
->extBigBlockDepotCount
* 2;
3106 This
->extBigBlockDepotLocations
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * cache_size
);
3107 if (!This
->extBigBlockDepotLocations
)
3109 return E_OUTOFMEMORY
;
3112 This
->extBigBlockDepotLocationsSize
= cache_size
;
3114 for (i
=0; i
<This
->extBigBlockDepotCount
; i
++)
3116 if (current_block
== BLOCK_END_OF_CHAIN
)
3118 WARN("File has too few extended big block depot blocks.\n");
3119 return STG_E_DOCFILECORRUPT
;
3121 This
->extBigBlockDepotLocations
[i
] = current_block
;
3122 current_block
= Storage32Impl_GetNextExtendedBlock(This
, current_block
);
3127 This
->extBigBlockDepotLocations
= NULL
;
3128 This
->extBigBlockDepotLocationsSize
= 0;
3132 * Create the block chain abstractions.
3134 if(!(blockChainStream
=
3135 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
3137 return STG_E_READFAULT
;
3140 BlockChainStream_Destroy(This
->rootBlockChain
);
3141 This
->rootBlockChain
= blockChainStream
;
3143 if(!(blockChainStream
=
3144 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
3147 return STG_E_READFAULT
;
3150 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
3151 This
->smallBlockDepotChain
= blockChainStream
;
3154 * Write the root storage entry (memory only)
3158 static const WCHAR rootentryW
[] = {'R','o','o','t',' ','E','n','t','r','y',0};
3161 * Initialize the directory table
3163 memset(&rootEntry
, 0, sizeof(rootEntry
));
3164 strcpyW(rootEntry
.name
, rootentryW
);
3165 rootEntry
.sizeOfNameString
= sizeof(rootentryW
);
3166 rootEntry
.stgType
= STGTY_ROOT
;
3167 rootEntry
.leftChild
= DIRENTRY_NULL
;
3168 rootEntry
.rightChild
= DIRENTRY_NULL
;
3169 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
3170 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
3171 rootEntry
.size
.u
.HighPart
= 0;
3172 rootEntry
.size
.u
.LowPart
= 0;
3174 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
3178 * Find the ID of the root storage.
3180 currentEntryRef
= 0;
3184 hr
= StorageImpl_ReadDirEntry(
3191 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
3192 (currentEntry
.stgType
== STGTY_ROOT
) )
3194 This
->base
.storageDirEntry
= currentEntryRef
;
3200 } while (SUCCEEDED(hr
) && (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
3204 return STG_E_READFAULT
;
3208 * Create the block chain abstraction for the small block root chain.
3210 if(!(blockChainStream
=
3211 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
3213 return STG_E_READFAULT
;
3216 BlockChainStream_Destroy(This
->smallBlockRootChain
);
3217 This
->smallBlockRootChain
= blockChainStream
;
3222 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3224 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
3225 This
->blockChainCache
[i
] = NULL
;
3232 static HRESULT
StorageImpl_Construct(
3240 StorageImpl
** result
)
3245 if ( FAILED( validateSTGM(openFlags
) ))
3246 return STG_E_INVALIDFLAG
;
3248 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
3250 return E_OUTOFMEMORY
;
3252 memset(This
, 0, sizeof(StorageImpl
));
3254 list_init(&This
->base
.strmHead
);
3256 list_init(&This
->base
.storageHead
);
3258 This
->base
.IStorage_iface
.lpVtbl
= &Storage32Impl_Vtbl
;
3259 This
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
3260 This
->base
.IDirectWriterLock_iface
.lpVtbl
= &DirectWriterLockVtbl
;
3261 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
3262 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
3264 This
->base
.create
= create
;
3266 if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READWRITE
|STGM_SHARE_DENY_WRITE
))
3267 This
->base
.lockingrole
= SWMR_Writer
;
3268 else if (openFlags
== (STGM_DIRECT_SWMR
|STGM_READ
|STGM_SHARE_DENY_NONE
))
3269 This
->base
.lockingrole
= SWMR_Reader
;
3271 This
->base
.lockingrole
= SWMR_None
;
3273 This
->base
.reverted
= FALSE
;
3276 * Initialize the big block cache.
3278 This
->bigBlockSize
= sector_size
;
3279 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
3281 hr
= FileLockBytesImpl_Construct(hFile
, openFlags
, pwcsName
, &This
->lockBytes
);
3284 This
->lockBytes
= pLkbyt
;
3285 ILockBytes_AddRef(pLkbyt
);
3289 hr
= StorageImpl_GrabLocks(This
, openFlags
);
3292 hr
= StorageImpl_Refresh(This
, TRUE
, create
);
3296 IStorage_Release(&This
->base
.IStorage_iface
);
3301 StorageImpl_Flush(&This
->base
);
3308 static void StorageImpl_Invalidate(StorageBaseImpl
* iface
)
3310 StorageImpl
*This
= (StorageImpl
*) iface
;
3312 StorageBaseImpl_DeleteAll(&This
->base
);
3314 This
->base
.reverted
= TRUE
;
3317 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
3319 StorageImpl
*This
= (StorageImpl
*) iface
;
3321 TRACE("(%p)\n", This
);
3323 StorageImpl_Flush(iface
);
3325 StorageImpl_Invalidate(iface
);
3327 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3329 BlockChainStream_Destroy(This
->smallBlockRootChain
);
3330 BlockChainStream_Destroy(This
->rootBlockChain
);
3331 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
3333 for (i
=0; i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3334 BlockChainStream_Destroy(This
->blockChainCache
[i
]);
3336 for (i
=0; i
<sizeof(This
->locked_bytes
)/sizeof(This
->locked_bytes
[0]); i
++)
3338 ULARGE_INTEGER offset
, cb
;
3340 if (This
->locked_bytes
[i
] != 0)
3342 offset
.QuadPart
= This
->locked_bytes
[i
];
3343 ILockBytes_UnlockRegion(This
->lockBytes
, offset
, cb
, LOCK_ONLYONCE
);
3347 if (This
->lockBytes
)
3348 ILockBytes_Release(This
->lockBytes
);
3349 HeapFree(GetProcessHeap(), 0, This
);
3352 static HRESULT
StorageImpl_Flush(StorageBaseImpl
*storage
)
3354 StorageImpl
*This
= (StorageImpl
*)storage
;
3357 TRACE("(%p)\n", This
);
3359 hr
= BlockChainStream_Flush(This
->smallBlockRootChain
);
3362 hr
= BlockChainStream_Flush(This
->rootBlockChain
);
3365 hr
= BlockChainStream_Flush(This
->smallBlockDepotChain
);
3367 for (i
=0; SUCCEEDED(hr
) && i
<BLOCKCHAIN_CACHE_SIZE
; i
++)
3368 if (This
->blockChainCache
[i
])
3369 hr
= BlockChainStream_Flush(This
->blockChainCache
[i
]);
3372 hr
= ILockBytes_Flush(This
->lockBytes
);
3377 /******************************************************************************
3378 * Storage32Impl_GetNextFreeBigBlock
3380 * Returns the index of the next free big block.
3381 * If the big block depot is filled, this method will enlarge it.
3384 static ULONG
StorageImpl_GetNextFreeBigBlock(
3387 ULONG depotBlockIndexPos
;
3388 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3389 ULONG depotBlockOffset
;
3390 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3391 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3393 ULONG freeBlock
= BLOCK_UNUSED
;
3395 ULARGE_INTEGER neededSize
;
3398 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
3399 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
3402 * Scan the entire big block depot until we find a block marked free
3404 while (nextBlockIndex
!= BLOCK_UNUSED
)
3406 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
3408 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
3411 * Grow the primary depot.
3413 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3415 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
3418 * Add a block depot.
3420 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
3421 This
->bigBlockDepotCount
++;
3422 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
3425 * Flag it as a block depot.
3427 StorageImpl_SetNextBlockInChain(This
,
3431 /* Save new header information.
3433 StorageImpl_SaveFileHeader(This
);
3438 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
3440 if (depotBlockIndexPos
== BLOCK_UNUSED
)
3443 * Grow the extended depot.
3445 ULONG extIndex
= BLOCK_UNUSED
;
3446 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3447 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
3449 if (extBlockOffset
== 0)
3451 /* We need an extended block.
3453 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
3454 This
->extBigBlockDepotCount
++;
3455 depotBlockIndexPos
= extIndex
+ 1;
3458 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
3461 * Add a block depot and mark it in the extended block.
3463 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
, depotIndex
);
3464 This
->bigBlockDepotCount
++;
3465 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
3467 /* Flag the block depot.
3469 StorageImpl_SetNextBlockInChain(This
,
3473 /* If necessary, flag the extended depot block.
3475 if (extIndex
!= BLOCK_UNUSED
)
3476 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
3478 /* Save header information.
3480 StorageImpl_SaveFileHeader(This
);
3484 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
3488 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
3489 ( nextBlockIndex
!= BLOCK_UNUSED
))
3491 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
3493 if (nextBlockIndex
== BLOCK_UNUSED
)
3495 freeBlock
= (depotIndex
* blocksPerDepot
) +
3496 (depotBlockOffset
/sizeof(ULONG
));
3499 depotBlockOffset
+= sizeof(ULONG
);
3504 depotBlockOffset
= 0;
3508 * make sure that the block physically exists before using it
3510 neededSize
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, freeBlock
)+This
->bigBlockSize
;
3512 ILockBytes_Stat(This
->lockBytes
, &statstg
, STATFLAG_NONAME
);
3514 if (neededSize
.QuadPart
> statstg
.cbSize
.QuadPart
)
3515 ILockBytes_SetSize(This
->lockBytes
, neededSize
);
3517 This
->prevFreeBlock
= freeBlock
;
3522 /******************************************************************************
3523 * Storage32Impl_AddBlockDepot
3525 * This will create a depot block, essentially it is a block initialized
3528 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
, ULONG depotIndex
)
3530 BYTE blockBuffer
[MAX_BIG_BLOCK_SIZE
];
3531 ULONG rangeLockIndex
= RANGELOCK_FIRST
/ This
->bigBlockSize
- 1;
3532 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
3533 ULONG rangeLockDepot
= rangeLockIndex
/ blocksPerDepot
;
3536 * Initialize blocks as free
3538 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3540 /* Reserve the range lock sector */
3541 if (depotIndex
== rangeLockDepot
)
3543 ((ULONG
*)blockBuffer
)[rangeLockIndex
% blocksPerDepot
] = BLOCK_END_OF_CHAIN
;
3546 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
3549 /******************************************************************************
3550 * Storage32Impl_GetExtDepotBlock
3552 * Returns the index of the block that corresponds to the specified depot
3553 * index. This method is only for depot indexes equal or greater than
3554 * COUNT_BBDEPOTINHEADER.
3556 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
3558 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3559 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3560 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3561 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3562 ULONG blockIndex
= BLOCK_UNUSED
;
3563 ULONG extBlockIndex
;
3564 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3565 int index
, num_blocks
;
3567 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3569 if (extBlockCount
>= This
->extBigBlockDepotCount
)
3570 return BLOCK_UNUSED
;
3572 if (This
->indexExtBlockDepotCached
!= extBlockCount
)
3574 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3576 StorageImpl_ReadBigBlock(This
, extBlockIndex
, depotBuffer
, NULL
);
3578 num_blocks
= This
->bigBlockSize
/ 4;
3580 for (index
= 0; index
< num_blocks
; index
++)
3582 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), &blockIndex
);
3583 This
->extBlockDepotCached
[index
] = blockIndex
;
3586 This
->indexExtBlockDepotCached
= extBlockCount
;
3589 blockIndex
= This
->extBlockDepotCached
[extBlockOffset
];
3594 /******************************************************************************
3595 * Storage32Impl_SetExtDepotBlock
3597 * Associates the specified block index to the specified depot index.
3598 * This method is only for depot indexes equal or greater than
3599 * COUNT_BBDEPOTINHEADER.
3601 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
3603 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
3604 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
3605 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
3606 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
3607 ULONG extBlockIndex
;
3609 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
3611 assert(extBlockCount
< This
->extBigBlockDepotCount
);
3613 extBlockIndex
= This
->extBigBlockDepotLocations
[extBlockCount
];
3615 if (extBlockIndex
!= BLOCK_UNUSED
)
3617 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
3618 extBlockOffset
* sizeof(ULONG
),
3622 if (This
->indexExtBlockDepotCached
== extBlockCount
)
3624 This
->extBlockDepotCached
[extBlockOffset
] = blockIndex
;
3628 /******************************************************************************
3629 * Storage32Impl_AddExtBlockDepot
3631 * Creates an extended depot block.
3633 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
3635 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
3636 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
3637 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3638 ULONG index
= BLOCK_UNUSED
;
3639 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3640 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
3641 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
3643 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
3644 blocksPerDepotBlock
;
3646 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
3649 * The first extended block.
3651 This
->extBigBlockDepotStart
= index
;
3656 * Find the last existing extended block.
3658 nextExtBlock
= This
->extBigBlockDepotLocations
[This
->extBigBlockDepotCount
-1];
3661 * Add the new extended block to the chain.
3663 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
3668 * Initialize this block.
3670 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
3671 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
3673 /* Add the block to our cache. */
3674 if (This
->extBigBlockDepotLocationsSize
== numExtBlocks
)
3676 ULONG new_cache_size
= (This
->extBigBlockDepotLocationsSize
+1)*2;
3677 ULONG
*new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * new_cache_size
);
3679 memcpy(new_cache
, This
->extBigBlockDepotLocations
, sizeof(ULONG
) * This
->extBigBlockDepotLocationsSize
);
3680 HeapFree(GetProcessHeap(), 0, This
->extBigBlockDepotLocations
);
3682 This
->extBigBlockDepotLocations
= new_cache
;
3683 This
->extBigBlockDepotLocationsSize
= new_cache_size
;
3685 This
->extBigBlockDepotLocations
[numExtBlocks
] = index
;
3690 /******************************************************************************
3691 * Storage32Impl_FreeBigBlock
3693 * This method will flag the specified block as free in the big block depot.
3695 static void StorageImpl_FreeBigBlock(
3699 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
3701 if (blockIndex
< This
->prevFreeBlock
)
3702 This
->prevFreeBlock
= blockIndex
;
3705 /************************************************************************
3706 * Storage32Impl_GetNextBlockInChain
3708 * This method will retrieve the block index of the next big block in
3711 * Params: This - Pointer to the Storage object.
3712 * blockIndex - Index of the block to retrieve the chain
3714 * nextBlockIndex - receives the return value.
3716 * Returns: This method returns the index of the next block in the chain.
3717 * It will return the constants:
3718 * BLOCK_SPECIAL - If the block given was not part of a
3720 * BLOCK_END_OF_CHAIN - If the block given was the last in
3722 * BLOCK_UNUSED - If the block given was not past of a chain
3724 * BLOCK_EXTBBDEPOT - This block is part of the extended
3727 * See Windows documentation for more details on IStorage methods.
3729 static HRESULT
StorageImpl_GetNextBlockInChain(
3732 ULONG
* nextBlockIndex
)
3734 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3735 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3736 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3737 BYTE depotBuffer
[MAX_BIG_BLOCK_SIZE
];
3739 ULONG depotBlockIndexPos
;
3740 int index
, num_blocks
;
3742 *nextBlockIndex
= BLOCK_SPECIAL
;
3744 if(depotBlockCount
>= This
->bigBlockDepotCount
)
3746 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
3747 This
->bigBlockDepotCount
);
3748 return STG_E_READFAULT
;
3752 * Cache the currently accessed depot block.
3754 if (depotBlockCount
!= This
->indexBlockDepotCached
)
3756 This
->indexBlockDepotCached
= depotBlockCount
;
3758 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3760 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3765 * We have to look in the extended depot.
3767 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3770 StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
, &read
);
3773 return STG_E_READFAULT
;
3775 num_blocks
= This
->bigBlockSize
/ 4;
3777 for (index
= 0; index
< num_blocks
; index
++)
3779 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
3780 This
->blockDepotCached
[index
] = *nextBlockIndex
;
3784 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
3789 /******************************************************************************
3790 * Storage32Impl_GetNextExtendedBlock
3792 * Given an extended block this method will return the next extended block.
3795 * The last ULONG of an extended block is the block index of the next
3796 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3800 * - The index of the next extended block
3801 * - BLOCK_UNUSED: there is no next extended block.
3802 * - Any other return values denotes failure.
3804 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
3806 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
3807 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
3809 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
3812 return nextBlockIndex
;
3815 /******************************************************************************
3816 * Storage32Impl_SetNextBlockInChain
3818 * This method will write the index of the specified block's next block
3819 * in the big block depot.
3821 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3824 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3825 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3826 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3829 static void StorageImpl_SetNextBlockInChain(
3834 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
3835 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
3836 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
3837 ULONG depotBlockIndexPos
;
3839 assert(depotBlockCount
< This
->bigBlockDepotCount
);
3840 assert(blockIndex
!= nextBlock
);
3842 if (blockIndex
== (RANGELOCK_FIRST
/ This
->bigBlockSize
) - 1)
3843 /* This should never happen (storage file format spec forbids it), but
3844 * older versions of Wine may have generated broken files. We don't want to
3845 * assert and potentially lose data, but we do want to know if this ever
3846 * happens in a newly-created file. */
3847 ERR("Using range lock page\n");
3849 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
3851 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
3856 * We have to look in the extended depot.
3858 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
3861 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
3864 * Update the cached block depot, if necessary.
3866 if (depotBlockCount
== This
->indexBlockDepotCached
)
3868 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
3872 /******************************************************************************
3873 * Storage32Impl_LoadFileHeader
3875 * This method will read in the file header
3877 static HRESULT
StorageImpl_LoadFileHeader(
3881 BYTE headerBigBlock
[HEADER_SIZE
];
3883 ULARGE_INTEGER offset
;
3888 * Get a pointer to the big block of data containing the header.
3890 offset
.u
.HighPart
= 0;
3891 offset
.u
.LowPart
= 0;
3892 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
3893 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
3894 hr
= STG_E_FILENOTFOUND
;
3897 * Extract the information from the header.
3902 * Check for the "magic number" signature and return an error if it is not
3905 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
3907 return STG_E_OLDFORMAT
;
3910 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
3912 return STG_E_INVALIDHEADER
;
3915 StorageUtl_ReadWord(
3917 OFFSET_BIGBLOCKSIZEBITS
,
3918 &This
->bigBlockSizeBits
);
3920 StorageUtl_ReadWord(
3922 OFFSET_SMALLBLOCKSIZEBITS
,
3923 &This
->smallBlockSizeBits
);
3925 StorageUtl_ReadDWord(
3927 OFFSET_BBDEPOTCOUNT
,
3928 &This
->bigBlockDepotCount
);
3930 StorageUtl_ReadDWord(
3932 OFFSET_ROOTSTARTBLOCK
,
3933 &This
->rootStartBlock
);
3935 StorageUtl_ReadDWord(
3937 OFFSET_TRANSACTIONSIG
,
3938 &This
->transactionSig
);
3940 StorageUtl_ReadDWord(
3942 OFFSET_SMALLBLOCKLIMIT
,
3943 &This
->smallBlockLimit
);
3945 StorageUtl_ReadDWord(
3947 OFFSET_SBDEPOTSTART
,
3948 &This
->smallBlockDepotStart
);
3950 StorageUtl_ReadDWord(
3952 OFFSET_EXTBBDEPOTSTART
,
3953 &This
->extBigBlockDepotStart
);
3955 StorageUtl_ReadDWord(
3957 OFFSET_EXTBBDEPOTCOUNT
,
3958 &This
->extBigBlockDepotCount
);
3960 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3962 StorageUtl_ReadDWord(
3964 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3965 &(This
->bigBlockDepotStart
[index
]));
3969 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3971 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3972 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3975 * Right now, the code is making some assumptions about the size of the
3976 * blocks, just make sure they are what we're expecting.
3978 if ((This
->bigBlockSize
!= MIN_BIG_BLOCK_SIZE
&& This
->bigBlockSize
!= MAX_BIG_BLOCK_SIZE
) ||
3979 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
||
3980 This
->smallBlockLimit
!= LIMIT_TO_USE_SMALL_BLOCK
)
3982 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3983 This
->bigBlockSize
, This
->smallBlockSize
, This
->smallBlockLimit
);
3984 hr
= STG_E_INVALIDHEADER
;
3993 /******************************************************************************
3994 * Storage32Impl_SaveFileHeader
3996 * This method will save to the file the header
3998 static void StorageImpl_SaveFileHeader(
4001 BYTE headerBigBlock
[HEADER_SIZE
];
4004 ULARGE_INTEGER offset
;
4005 DWORD bytes_read
, bytes_written
;
4006 DWORD major_version
, dirsectorcount
;
4009 * Get a pointer to the big block of data containing the header.
4011 offset
.u
.HighPart
= 0;
4012 offset
.u
.LowPart
= 0;
4013 hr
= StorageImpl_ReadAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_read
);
4014 if (SUCCEEDED(hr
) && bytes_read
!= HEADER_SIZE
)
4015 hr
= STG_E_FILENOTFOUND
;
4017 if (This
->bigBlockSizeBits
== 0x9)
4019 else if (This
->bigBlockSizeBits
== 0xc)
4023 ERR("invalid big block shift 0x%x\n", This
->bigBlockSizeBits
);
4028 * If the block read failed, the file is probably new.
4033 * Initialize for all unknown fields.
4035 memset(headerBigBlock
, 0, HEADER_SIZE
);
4038 * Initialize the magic number.
4040 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
4044 * Write the information to the header.
4046 StorageUtl_WriteWord(
4048 OFFSET_MINORVERSION
,
4051 StorageUtl_WriteWord(
4053 OFFSET_MAJORVERSION
,
4056 StorageUtl_WriteWord(
4058 OFFSET_BYTEORDERMARKER
,
4061 StorageUtl_WriteWord(
4063 OFFSET_BIGBLOCKSIZEBITS
,
4064 This
->bigBlockSizeBits
);
4066 StorageUtl_WriteWord(
4068 OFFSET_SMALLBLOCKSIZEBITS
,
4069 This
->smallBlockSizeBits
);
4071 if (major_version
>= 4)
4073 if (This
->rootBlockChain
)
4074 dirsectorcount
= BlockChainStream_GetCount(This
->rootBlockChain
);
4076 /* This file is being created, and it will start out with one block. */
4080 /* This field must be 0 in versions older than 4 */
4083 StorageUtl_WriteDWord(
4085 OFFSET_DIRSECTORCOUNT
,
4088 StorageUtl_WriteDWord(
4090 OFFSET_BBDEPOTCOUNT
,
4091 This
->bigBlockDepotCount
);
4093 StorageUtl_WriteDWord(
4095 OFFSET_ROOTSTARTBLOCK
,
4096 This
->rootStartBlock
);
4098 StorageUtl_WriteDWord(
4100 OFFSET_TRANSACTIONSIG
,
4101 This
->transactionSig
);
4103 StorageUtl_WriteDWord(
4105 OFFSET_SMALLBLOCKLIMIT
,
4106 This
->smallBlockLimit
);
4108 StorageUtl_WriteDWord(
4110 OFFSET_SBDEPOTSTART
,
4111 This
->smallBlockDepotStart
);
4113 StorageUtl_WriteDWord(
4115 OFFSET_SBDEPOTCOUNT
,
4116 This
->smallBlockDepotChain
?
4117 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
4119 StorageUtl_WriteDWord(
4121 OFFSET_EXTBBDEPOTSTART
,
4122 This
->extBigBlockDepotStart
);
4124 StorageUtl_WriteDWord(
4126 OFFSET_EXTBBDEPOTCOUNT
,
4127 This
->extBigBlockDepotCount
);
4129 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
4131 StorageUtl_WriteDWord(
4133 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
4134 (This
->bigBlockDepotStart
[index
]));
4138 * Write the big block back to the file.
4140 StorageImpl_WriteAt(This
, offset
, headerBigBlock
, HEADER_SIZE
, &bytes_written
);
4143 /******************************************************************************
4144 * StorageImpl_ReadRawDirEntry
4146 * This method will read the raw data from a directory entry in the file.
4148 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4150 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
4152 ULARGE_INTEGER offset
;
4156 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
4158 hr
= BlockChainStream_ReadAt(
4159 This
->rootBlockChain
,
4165 if (bytesRead
!= RAW_DIRENTRY_SIZE
)
4166 return STG_E_READFAULT
;
4171 /******************************************************************************
4172 * StorageImpl_WriteRawDirEntry
4174 * This method will write the raw data from a directory entry in the file.
4176 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4178 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
4180 ULARGE_INTEGER offset
;
4183 offset
.QuadPart
= (ULONGLONG
)index
* RAW_DIRENTRY_SIZE
;
4185 return BlockChainStream_WriteAt(
4186 This
->rootBlockChain
,
4193 /******************************************************************************
4196 * Update raw directory entry data from the fields in newData.
4198 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4200 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
4202 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
4205 buffer
+ OFFSET_PS_NAME
,
4207 DIRENTRY_NAME_BUFFER_LEN
);
4209 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
4211 StorageUtl_WriteWord(
4213 OFFSET_PS_NAMELENGTH
,
4214 newData
->sizeOfNameString
);
4216 StorageUtl_WriteDWord(
4218 OFFSET_PS_LEFTCHILD
,
4219 newData
->leftChild
);
4221 StorageUtl_WriteDWord(
4223 OFFSET_PS_RIGHTCHILD
,
4224 newData
->rightChild
);
4226 StorageUtl_WriteDWord(
4229 newData
->dirRootEntry
);
4231 StorageUtl_WriteGUID(
4236 StorageUtl_WriteDWord(
4239 newData
->ctime
.dwLowDateTime
);
4241 StorageUtl_WriteDWord(
4243 OFFSET_PS_CTIMEHIGH
,
4244 newData
->ctime
.dwHighDateTime
);
4246 StorageUtl_WriteDWord(
4249 newData
->mtime
.dwLowDateTime
);
4251 StorageUtl_WriteDWord(
4253 OFFSET_PS_MTIMEHIGH
,
4254 newData
->ctime
.dwHighDateTime
);
4256 StorageUtl_WriteDWord(
4258 OFFSET_PS_STARTBLOCK
,
4259 newData
->startingBlock
);
4261 StorageUtl_WriteDWord(
4264 newData
->size
.u
.LowPart
);
4266 StorageUtl_WriteDWord(
4268 OFFSET_PS_SIZE_HIGH
,
4269 newData
->size
.u
.HighPart
);
4272 /******************************************************************************
4273 * Storage32Impl_ReadDirEntry
4275 * This method will read the specified directory entry.
4277 HRESULT
StorageImpl_ReadDirEntry(
4282 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
4285 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
4287 if (SUCCEEDED(readRes
))
4289 memset(buffer
->name
, 0, sizeof(buffer
->name
));
4292 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
4293 DIRENTRY_NAME_BUFFER_LEN
);
4294 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
4296 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
4298 StorageUtl_ReadWord(
4300 OFFSET_PS_NAMELENGTH
,
4301 &buffer
->sizeOfNameString
);
4303 StorageUtl_ReadDWord(
4305 OFFSET_PS_LEFTCHILD
,
4306 &buffer
->leftChild
);
4308 StorageUtl_ReadDWord(
4310 OFFSET_PS_RIGHTCHILD
,
4311 &buffer
->rightChild
);
4313 StorageUtl_ReadDWord(
4316 &buffer
->dirRootEntry
);
4318 StorageUtl_ReadGUID(
4323 StorageUtl_ReadDWord(
4326 &buffer
->ctime
.dwLowDateTime
);
4328 StorageUtl_ReadDWord(
4330 OFFSET_PS_CTIMEHIGH
,
4331 &buffer
->ctime
.dwHighDateTime
);
4333 StorageUtl_ReadDWord(
4336 &buffer
->mtime
.dwLowDateTime
);
4338 StorageUtl_ReadDWord(
4340 OFFSET_PS_MTIMEHIGH
,
4341 &buffer
->mtime
.dwHighDateTime
);
4343 StorageUtl_ReadDWord(
4345 OFFSET_PS_STARTBLOCK
,
4346 &buffer
->startingBlock
);
4348 StorageUtl_ReadDWord(
4351 &buffer
->size
.u
.LowPart
);
4353 StorageUtl_ReadDWord(
4355 OFFSET_PS_SIZE_HIGH
,
4356 &buffer
->size
.u
.HighPart
);
4362 /*********************************************************************
4363 * Write the specified directory entry to the file
4365 HRESULT
StorageImpl_WriteDirEntry(
4368 const DirEntry
* buffer
)
4370 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
4372 UpdateRawDirEntry(currentEntry
, buffer
);
4374 return StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
4377 static HRESULT
StorageImpl_ReadBigBlock(
4383 ULARGE_INTEGER ulOffset
;
4387 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4389 hr
= StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
4391 if (SUCCEEDED(hr
) && read
< This
->bigBlockSize
)
4393 /* File ends during this block; fill the rest with 0's. */
4394 memset((LPBYTE
)buffer
+read
, 0, This
->bigBlockSize
-read
);
4397 if (out_read
) *out_read
= read
;
4402 static BOOL
StorageImpl_ReadDWordFromBigBlock(
4408 ULARGE_INTEGER ulOffset
;
4412 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4413 ulOffset
.QuadPart
+= offset
;
4415 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
4416 *value
= lendian32toh(tmp
);
4417 return (read
== sizeof(DWORD
));
4420 static BOOL
StorageImpl_WriteBigBlock(
4425 ULARGE_INTEGER ulOffset
;
4428 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4430 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
4431 return (wrote
== This
->bigBlockSize
);
4434 static BOOL
StorageImpl_WriteDWordToBigBlock(
4440 ULARGE_INTEGER ulOffset
;
4443 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
, blockIndex
);
4444 ulOffset
.QuadPart
+= offset
;
4446 value
= htole32(value
);
4447 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
4448 return (wrote
== sizeof(DWORD
));
4451 /******************************************************************************
4452 * Storage32Impl_SmallBlocksToBigBlocks
4454 * This method will convert a small block chain to a big block chain.
4455 * The small block chain will be destroyed.
4457 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
4459 SmallBlockChainStream
** ppsbChain
)
4461 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4462 ULARGE_INTEGER size
, offset
;
4463 ULONG cbRead
, cbWritten
;
4464 ULARGE_INTEGER cbTotalRead
;
4465 DirRef streamEntryRef
;
4466 HRESULT resWrite
= S_OK
;
4468 DirEntry streamEntry
;
4470 BlockChainStream
*bbTempChain
= NULL
;
4471 BlockChainStream
*bigBlockChain
= NULL
;
4474 * Create a temporary big block chain that doesn't have
4475 * an associated directory entry. This temporary chain will be
4476 * used to copy data from small blocks to big blocks.
4478 bbTempChain
= BlockChainStream_Construct(This
,
4481 if(!bbTempChain
) return NULL
;
4483 * Grow the big block chain.
4485 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
4486 BlockChainStream_SetSize(bbTempChain
, size
);
4489 * Copy the contents of the small block chain to the big block chain
4490 * by small block size increments.
4492 offset
.u
.LowPart
= 0;
4493 offset
.u
.HighPart
= 0;
4494 cbTotalRead
.QuadPart
= 0;
4496 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
4499 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
4501 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4504 if (FAILED(resRead
))
4509 cbTotalRead
.QuadPart
+= cbRead
;
4511 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
4517 if (FAILED(resWrite
))
4520 offset
.u
.LowPart
+= cbRead
;
4524 resRead
= STG_E_READFAULT
;
4527 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
4528 HeapFree(GetProcessHeap(),0,buffer
);
4530 size
.u
.HighPart
= 0;
4533 if (FAILED(resRead
) || FAILED(resWrite
))
4535 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4536 BlockChainStream_SetSize(bbTempChain
, size
);
4537 BlockChainStream_Destroy(bbTempChain
);
4542 * Destroy the small block chain.
4544 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
4545 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
4546 SmallBlockChainStream_Destroy(*ppsbChain
);
4550 * Change the directory entry. This chain is now a big block chain
4551 * and it doesn't reside in the small blocks chain anymore.
4553 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4555 streamEntry
.startingBlock
= bbHeadOfChain
;
4557 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4560 * Destroy the temporary entryless big block chain.
4561 * Create a new big block chain associated with this entry.
4563 BlockChainStream_Destroy(bbTempChain
);
4564 bigBlockChain
= BlockChainStream_Construct(This
,
4568 return bigBlockChain
;
4571 /******************************************************************************
4572 * Storage32Impl_BigBlocksToSmallBlocks
4574 * This method will convert a big block chain to a small block chain.
4575 * The big block chain will be destroyed on success.
4577 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
4579 BlockChainStream
** ppbbChain
,
4580 ULARGE_INTEGER newSize
)
4582 ULARGE_INTEGER size
, offset
, cbTotalRead
;
4583 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
4584 DirRef streamEntryRef
;
4585 HRESULT resWrite
= S_OK
, resRead
= S_OK
;
4586 DirEntry streamEntry
;
4588 SmallBlockChainStream
* sbTempChain
;
4590 TRACE("%p %p\n", This
, ppbbChain
);
4592 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
4598 SmallBlockChainStream_SetSize(sbTempChain
, newSize
);
4599 size
= BlockChainStream_GetSize(*ppbbChain
);
4600 size
.QuadPart
= min(size
.QuadPart
, newSize
.QuadPart
);
4602 offset
.u
.HighPart
= 0;
4603 offset
.u
.LowPart
= 0;
4604 cbTotalRead
.QuadPart
= 0;
4605 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
4606 while(cbTotalRead
.QuadPart
< size
.QuadPart
)
4608 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
4609 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
4617 cbTotalRead
.QuadPart
+= cbRead
;
4619 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
4620 cbRead
, buffer
, &cbWritten
);
4622 if(FAILED(resWrite
))
4625 offset
.u
.LowPart
+= cbRead
;
4629 resRead
= STG_E_READFAULT
;
4633 HeapFree(GetProcessHeap(), 0, buffer
);
4635 size
.u
.HighPart
= 0;
4638 if(FAILED(resRead
) || FAILED(resWrite
))
4640 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
4641 SmallBlockChainStream_SetSize(sbTempChain
, size
);
4642 SmallBlockChainStream_Destroy(sbTempChain
);
4646 /* destroy the original big block chain */
4647 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
4648 BlockChainStream_SetSize(*ppbbChain
, size
);
4649 BlockChainStream_Destroy(*ppbbChain
);
4652 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
4653 streamEntry
.startingBlock
= sbHeadOfChain
;
4654 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
4656 SmallBlockChainStream_Destroy(sbTempChain
);
4657 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
4660 static HRESULT
StorageBaseImpl_CopyStream(
4661 StorageBaseImpl
*dst
, DirRef dst_entry
,
4662 StorageBaseImpl
*src
, DirRef src_entry
)
4667 ULARGE_INTEGER bytes_copied
;
4668 ULONG bytestocopy
, bytesread
, byteswritten
;
4670 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &srcdata
);
4674 hr
= StorageBaseImpl_StreamSetSize(dst
, dst_entry
, srcdata
.size
);
4676 bytes_copied
.QuadPart
= 0;
4677 while (bytes_copied
.QuadPart
< srcdata
.size
.QuadPart
&& SUCCEEDED(hr
))
4679 bytestocopy
= min(4096, srcdata
.size
.QuadPart
- bytes_copied
.QuadPart
);
4681 hr
= StorageBaseImpl_StreamReadAt(src
, src_entry
, bytes_copied
, bytestocopy
,
4683 if (SUCCEEDED(hr
) && bytesread
!= bytestocopy
) hr
= STG_E_READFAULT
;
4686 hr
= StorageBaseImpl_StreamWriteAt(dst
, dst_entry
, bytes_copied
, bytestocopy
,
4687 data
, &byteswritten
);
4690 if (byteswritten
!= bytestocopy
) hr
= STG_E_WRITEFAULT
;
4691 bytes_copied
.QuadPart
+= byteswritten
;
4699 static HRESULT
StorageBaseImpl_DupStorageTree(
4700 StorageBaseImpl
*dst
, DirRef
*dst_entry
,
4701 StorageBaseImpl
*src
, DirRef src_entry
)
4705 BOOL has_stream
=FALSE
;
4707 if (src_entry
== DIRENTRY_NULL
)
4709 *dst_entry
= DIRENTRY_NULL
;
4713 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &data
);
4716 has_stream
= (data
.stgType
== STGTY_STREAM
&& data
.size
.QuadPart
!= 0);
4717 data
.startingBlock
= BLOCK_END_OF_CHAIN
;
4718 data
.size
.QuadPart
= 0;
4720 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.leftChild
, src
, data
.leftChild
);
4724 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.rightChild
, src
, data
.rightChild
);
4727 hr
= StorageBaseImpl_DupStorageTree(dst
, &data
.dirRootEntry
, src
, data
.dirRootEntry
);
4730 hr
= StorageBaseImpl_CreateDirEntry(dst
, &data
, dst_entry
);
4732 if (SUCCEEDED(hr
) && has_stream
)
4733 hr
= StorageBaseImpl_CopyStream(dst
, *dst_entry
, src
, src_entry
);
4738 static HRESULT
StorageBaseImpl_CopyStorageTree(
4739 StorageBaseImpl
*dst
, DirRef dst_entry
,
4740 StorageBaseImpl
*src
, DirRef src_entry
)
4743 DirEntry src_data
, dst_data
;
4744 DirRef new_root_entry
;
4746 hr
= StorageBaseImpl_ReadDirEntry(src
, src_entry
, &src_data
);
4750 hr
= StorageBaseImpl_DupStorageTree(dst
, &new_root_entry
, src
, src_data
.dirRootEntry
);
4755 hr
= StorageBaseImpl_ReadDirEntry(dst
, dst_entry
, &dst_data
);
4756 dst_data
.clsid
= src_data
.clsid
;
4757 dst_data
.ctime
= src_data
.ctime
;
4758 dst_data
.mtime
= src_data
.mtime
;
4759 dst_data
.dirRootEntry
= new_root_entry
;
4763 hr
= StorageBaseImpl_WriteDirEntry(dst
, dst_entry
, &dst_data
);
4768 static HRESULT
StorageBaseImpl_DeleteStorageTree(StorageBaseImpl
*This
, DirRef entry
, BOOL include_siblings
)
4772 ULARGE_INTEGER zero
;
4774 if (entry
== DIRENTRY_NULL
)
4779 hr
= StorageBaseImpl_ReadDirEntry(This
, entry
, &data
);
4781 if (SUCCEEDED(hr
) && include_siblings
)
4782 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.leftChild
, TRUE
);
4784 if (SUCCEEDED(hr
) && include_siblings
)
4785 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.rightChild
, TRUE
);
4788 hr
= StorageBaseImpl_DeleteStorageTree(This
, data
.dirRootEntry
, TRUE
);
4790 if (SUCCEEDED(hr
) && data
.stgType
== STGTY_STREAM
)
4791 hr
= StorageBaseImpl_StreamSetSize(This
, entry
, zero
);
4794 hr
= StorageBaseImpl_DestroyDirEntry(This
, entry
);
4799 static DirRef
TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl
*This
)
4801 DirRef result
=This
->firstFreeEntry
;
4803 while (result
< This
->entries_size
&& This
->entries
[result
].inuse
)
4806 if (result
== This
->entries_size
)
4808 ULONG new_size
= This
->entries_size
* 2;
4809 TransactedDirEntry
*new_entries
;
4811 new_entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * new_size
);
4812 if (!new_entries
) return DIRENTRY_NULL
;
4814 memcpy(new_entries
, This
->entries
, sizeof(TransactedDirEntry
) * This
->entries_size
);
4815 HeapFree(GetProcessHeap(), 0, This
->entries
);
4817 This
->entries
= new_entries
;
4818 This
->entries_size
= new_size
;
4821 This
->entries
[result
].inuse
= TRUE
;
4823 This
->firstFreeEntry
= result
+1;
4828 static DirRef
TransactedSnapshotImpl_CreateStubEntry(
4829 TransactedSnapshotImpl
*This
, DirRef parentEntryRef
)
4831 DirRef stubEntryRef
;
4832 TransactedDirEntry
*entry
;
4834 stubEntryRef
= TransactedSnapshotImpl_FindFreeEntry(This
);
4836 if (stubEntryRef
!= DIRENTRY_NULL
)
4838 entry
= &This
->entries
[stubEntryRef
];
4840 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
= parentEntryRef
;
4842 entry
->read
= FALSE
;
4845 return stubEntryRef
;
4848 static HRESULT
TransactedSnapshotImpl_EnsureReadEntry(
4849 TransactedSnapshotImpl
*This
, DirRef entry
)
4854 if (!This
->entries
[entry
].read
)
4856 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
4857 This
->entries
[entry
].transactedParentEntry
,
4860 if (SUCCEEDED(hr
) && data
.leftChild
!= DIRENTRY_NULL
)
4862 data
.leftChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.leftChild
);
4864 if (data
.leftChild
== DIRENTRY_NULL
)
4868 if (SUCCEEDED(hr
) && data
.rightChild
!= DIRENTRY_NULL
)
4870 data
.rightChild
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.rightChild
);
4872 if (data
.rightChild
== DIRENTRY_NULL
)
4876 if (SUCCEEDED(hr
) && data
.dirRootEntry
!= DIRENTRY_NULL
)
4878 data
.dirRootEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, data
.dirRootEntry
);
4880 if (data
.dirRootEntry
== DIRENTRY_NULL
)
4886 memcpy(&This
->entries
[entry
].data
, &data
, sizeof(DirEntry
));
4887 This
->entries
[entry
].read
= TRUE
;
4894 static HRESULT
TransactedSnapshotImpl_MakeStreamDirty(
4895 TransactedSnapshotImpl
*This
, DirRef entry
)
4899 if (!This
->entries
[entry
].stream_dirty
)
4901 DirEntry new_entrydata
;
4903 memset(&new_entrydata
, 0, sizeof(DirEntry
));
4904 new_entrydata
.name
[0] = 'S';
4905 new_entrydata
.sizeOfNameString
= 1;
4906 new_entrydata
.stgType
= STGTY_STREAM
;
4907 new_entrydata
.startingBlock
= BLOCK_END_OF_CHAIN
;
4908 new_entrydata
.leftChild
= DIRENTRY_NULL
;
4909 new_entrydata
.rightChild
= DIRENTRY_NULL
;
4910 new_entrydata
.dirRootEntry
= DIRENTRY_NULL
;
4912 hr
= StorageBaseImpl_CreateDirEntry(This
->scratch
, &new_entrydata
,
4913 &This
->entries
[entry
].stream_entry
);
4915 if (SUCCEEDED(hr
) && This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4917 hr
= StorageBaseImpl_CopyStream(
4918 This
->scratch
, This
->entries
[entry
].stream_entry
,
4919 This
->transactedParent
, This
->entries
[entry
].transactedParentEntry
);
4922 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[entry
].stream_entry
);
4926 This
->entries
[entry
].stream_dirty
= TRUE
;
4928 if (This
->entries
[entry
].transactedParentEntry
!= DIRENTRY_NULL
)
4930 /* Since this entry is modified, and we aren't using its stream data, we
4931 * no longer care about the original entry. */
4933 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[entry
].transactedParentEntry
);
4935 if (delete_ref
!= DIRENTRY_NULL
)
4936 This
->entries
[delete_ref
].deleted
= TRUE
;
4938 This
->entries
[entry
].transactedParentEntry
= This
->entries
[entry
].newTransactedParentEntry
= DIRENTRY_NULL
;
4945 /* Find the first entry in a depth-first traversal. */
4946 static DirRef
TransactedSnapshotImpl_FindFirstChild(
4947 TransactedSnapshotImpl
* This
, DirRef parent
)
4949 DirRef cursor
, prev
;
4950 TransactedDirEntry
*entry
;
4953 entry
= &This
->entries
[cursor
];
4956 if (entry
->data
.leftChild
!= DIRENTRY_NULL
)
4959 cursor
= entry
->data
.leftChild
;
4960 entry
= &This
->entries
[cursor
];
4961 entry
->parent
= prev
;
4963 else if (entry
->data
.rightChild
!= DIRENTRY_NULL
)
4966 cursor
= entry
->data
.rightChild
;
4967 entry
= &This
->entries
[cursor
];
4968 entry
->parent
= prev
;
4970 else if (entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
4973 cursor
= entry
->data
.dirRootEntry
;
4974 entry
= &This
->entries
[cursor
];
4975 entry
->parent
= prev
;
4984 /* Find the next entry in a depth-first traversal. */
4985 static DirRef
TransactedSnapshotImpl_FindNextChild(
4986 TransactedSnapshotImpl
* This
, DirRef current
)
4989 TransactedDirEntry
*parent_entry
;
4991 parent
= This
->entries
[current
].parent
;
4992 parent_entry
= &This
->entries
[parent
];
4994 if (parent
!= DIRENTRY_NULL
&& parent_entry
->data
.dirRootEntry
!= current
)
4996 if (parent_entry
->data
.rightChild
!= current
&& parent_entry
->data
.rightChild
!= DIRENTRY_NULL
)
4998 This
->entries
[parent_entry
->data
.rightChild
].parent
= parent
;
4999 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.rightChild
);
5002 if (parent_entry
->data
.dirRootEntry
!= DIRENTRY_NULL
)
5004 This
->entries
[parent_entry
->data
.dirRootEntry
].parent
= parent
;
5005 return TransactedSnapshotImpl_FindFirstChild(This
, parent_entry
->data
.dirRootEntry
);
5012 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
5013 static inline BOOL
TransactedSnapshotImpl_MadeCopy(
5014 TransactedSnapshotImpl
* This
, DirRef entry
)
5016 return entry
!= DIRENTRY_NULL
&&
5017 This
->entries
[entry
].newTransactedParentEntry
!= This
->entries
[entry
].transactedParentEntry
;
5020 /* Destroy the entries created by CopyTree. */
5021 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
5022 TransactedSnapshotImpl
* This
, DirRef stop
)
5025 TransactedDirEntry
*entry
;
5026 ULARGE_INTEGER zero
;
5030 if (!This
->entries
[This
->base
.storageDirEntry
].read
)
5033 cursor
= This
->entries
[This
->base
.storageDirEntry
].data
.dirRootEntry
;
5035 if (cursor
== DIRENTRY_NULL
)
5038 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, cursor
);
5040 while (cursor
!= DIRENTRY_NULL
&& cursor
!= stop
)
5042 if (TransactedSnapshotImpl_MadeCopy(This
, cursor
))
5044 entry
= &This
->entries
[cursor
];
5046 if (entry
->stream_dirty
)
5047 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5048 entry
->newTransactedParentEntry
, zero
);
5050 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5051 entry
->newTransactedParentEntry
);
5053 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5056 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5060 /* Make a copy of our edited tree that we can use in the parent. */
5061 static HRESULT
TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl
* This
)
5064 TransactedDirEntry
*entry
;
5067 cursor
= This
->base
.storageDirEntry
;
5068 entry
= &This
->entries
[cursor
];
5069 entry
->parent
= DIRENTRY_NULL
;
5070 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5072 if (entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5075 This
->entries
[entry
->data
.dirRootEntry
].parent
= DIRENTRY_NULL
;
5077 cursor
= TransactedSnapshotImpl_FindFirstChild(This
, entry
->data
.dirRootEntry
);
5078 entry
= &This
->entries
[cursor
];
5080 while (cursor
!= DIRENTRY_NULL
)
5082 /* Make a copy of this entry in the transacted parent. */
5084 (!entry
->dirty
&& !entry
->stream_dirty
&&
5085 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.leftChild
) &&
5086 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.rightChild
) &&
5087 !TransactedSnapshotImpl_MadeCopy(This
, entry
->data
.dirRootEntry
)))
5088 entry
->newTransactedParentEntry
= entry
->transactedParentEntry
;
5093 memcpy(&newData
, &entry
->data
, sizeof(DirEntry
));
5095 newData
.size
.QuadPart
= 0;
5096 newData
.startingBlock
= BLOCK_END_OF_CHAIN
;
5098 if (newData
.leftChild
!= DIRENTRY_NULL
)
5099 newData
.leftChild
= This
->entries
[newData
.leftChild
].newTransactedParentEntry
;
5101 if (newData
.rightChild
!= DIRENTRY_NULL
)
5102 newData
.rightChild
= This
->entries
[newData
.rightChild
].newTransactedParentEntry
;
5104 if (newData
.dirRootEntry
!= DIRENTRY_NULL
)
5105 newData
.dirRootEntry
= This
->entries
[newData
.dirRootEntry
].newTransactedParentEntry
;
5107 hr
= StorageBaseImpl_CreateDirEntry(This
->transactedParent
, &newData
,
5108 &entry
->newTransactedParentEntry
);
5111 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5115 if (entry
->stream_dirty
)
5117 hr
= StorageBaseImpl_CopyStream(
5118 This
->transactedParent
, entry
->newTransactedParentEntry
,
5119 This
->scratch
, entry
->stream_entry
);
5121 else if (entry
->data
.size
.QuadPart
)
5123 hr
= StorageBaseImpl_StreamLink(
5124 This
->transactedParent
, entry
->newTransactedParentEntry
,
5125 entry
->transactedParentEntry
);
5130 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5131 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, cursor
);
5136 cursor
= TransactedSnapshotImpl_FindNextChild(This
, cursor
);
5137 entry
= &This
->entries
[cursor
];
5143 static HRESULT WINAPI
TransactedSnapshotImpl_Commit(
5145 DWORD grfCommitFlags
) /* [in] */
5147 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5148 TransactedDirEntry
*root_entry
;
5149 DirRef i
, dir_root_ref
;
5151 ULARGE_INTEGER zero
;
5153 ULONG transactionSig
;
5157 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5159 /* Cannot commit a read-only transacted storage */
5160 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5161 return STG_E_ACCESSDENIED
;
5163 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5164 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5167 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5170 if (transactionSig
!= This
->lastTransactionSig
)
5172 ERR("file was externally modified\n");
5173 hr
= STG_E_NOTCURRENT
;
5178 This
->lastTransactionSig
= transactionSig
+1;
5179 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, This
->lastTransactionSig
);
5182 else if (hr
== E_NOTIMPL
)
5185 if (FAILED(hr
)) goto end
;
5187 /* To prevent data loss, we create the new structure in the file before we
5188 * delete the old one, so that in case of errors the old data is intact. We
5189 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5190 * needed in the rare situation where we have just enough free disk space to
5191 * overwrite the existing data. */
5193 root_entry
= &This
->entries
[This
->base
.storageDirEntry
];
5195 if (!root_entry
->read
)
5198 hr
= TransactedSnapshotImpl_CopyTree(This
);
5199 if (FAILED(hr
)) goto end
;
5201 if (root_entry
->data
.dirRootEntry
== DIRENTRY_NULL
)
5202 dir_root_ref
= DIRENTRY_NULL
;
5204 dir_root_ref
= This
->entries
[root_entry
->data
.dirRootEntry
].newTransactedParentEntry
;
5206 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5208 /* Update the storage to use the new data in one step. */
5210 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
,
5211 root_entry
->transactedParentEntry
, &data
);
5215 data
.dirRootEntry
= dir_root_ref
;
5216 data
.clsid
= root_entry
->data
.clsid
;
5217 data
.ctime
= root_entry
->data
.ctime
;
5218 data
.mtime
= root_entry
->data
.mtime
;
5220 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
,
5221 root_entry
->transactedParentEntry
, &data
);
5224 /* Try to flush after updating the root storage, but if the flush fails, keep
5225 * going, on the theory that it'll either succeed later or the subsequent
5226 * writes will fail. */
5227 StorageBaseImpl_Flush(This
->transactedParent
);
5231 /* Destroy the old now-orphaned data. */
5232 for (i
=0; i
<This
->entries_size
; i
++)
5234 TransactedDirEntry
*entry
= &This
->entries
[i
];
5239 StorageBaseImpl_StreamSetSize(This
->transactedParent
,
5240 entry
->transactedParentEntry
, zero
);
5241 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5242 entry
->transactedParentEntry
);
5243 memset(entry
, 0, sizeof(TransactedDirEntry
));
5244 This
->firstFreeEntry
= min(i
, This
->firstFreeEntry
);
5246 else if (entry
->read
&& entry
->transactedParentEntry
!= entry
->newTransactedParentEntry
)
5248 if (entry
->transactedParentEntry
!= DIRENTRY_NULL
)
5249 StorageBaseImpl_DestroyDirEntry(This
->transactedParent
,
5250 entry
->transactedParentEntry
);
5251 if (entry
->stream_dirty
)
5253 StorageBaseImpl_StreamSetSize(This
->scratch
, entry
->stream_entry
, zero
);
5254 StorageBaseImpl_DestroyDirEntry(This
->scratch
, entry
->stream_entry
);
5255 entry
->stream_dirty
= FALSE
;
5257 entry
->dirty
= FALSE
;
5258 entry
->transactedParentEntry
= entry
->newTransactedParentEntry
;
5265 TransactedSnapshotImpl_DestroyTemporaryCopy(This
, DIRENTRY_NULL
);
5269 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5271 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
5277 static HRESULT WINAPI
TransactedSnapshotImpl_Revert(
5280 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*)impl_from_IStorage(iface
);
5281 ULARGE_INTEGER zero
;
5284 TRACE("(%p)\n", iface
);
5286 /* Destroy the open objects. */
5287 StorageBaseImpl_DeleteAll(&This
->base
);
5289 /* Clear out the scratch file. */
5291 for (i
=0; i
<This
->entries_size
; i
++)
5293 if (This
->entries
[i
].stream_dirty
)
5295 StorageBaseImpl_StreamSetSize(This
->scratch
, This
->entries
[i
].stream_entry
,
5298 StorageBaseImpl_DestroyDirEntry(This
->scratch
, This
->entries
[i
].stream_entry
);
5302 memset(This
->entries
, 0, sizeof(TransactedDirEntry
) * This
->entries_size
);
5304 This
->firstFreeEntry
= 0;
5305 This
->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->transactedParent
->storageDirEntry
);
5310 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl
* This
)
5312 if (!This
->reverted
)
5314 TRACE("Storage invalidated (stg=%p)\n", This
);
5316 This
->reverted
= TRUE
;
5318 StorageBaseImpl_DeleteAll(This
);
5322 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl
*iface
)
5324 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
5326 IStorage_Revert(&This
->base
.IStorage_iface
);
5327 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
5328 IStorage_Release(&This
->scratch
->IStorage_iface
);
5329 HeapFree(GetProcessHeap(), 0, This
->entries
);
5330 HeapFree(GetProcessHeap(), 0, This
);
5333 static HRESULT
TransactedSnapshotImpl_Flush(StorageBaseImpl
* iface
)
5335 /* We only need to flush when committing. */
5339 static HRESULT
TransactedSnapshotImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5341 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) iface
;
5343 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
5346 static HRESULT
TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl
*base
,
5347 const DirEntry
*newData
, DirRef
*index
)
5349 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5351 TransactedDirEntry
*new_entry
;
5353 new_ref
= TransactedSnapshotImpl_FindFreeEntry(This
);
5354 if (new_ref
== DIRENTRY_NULL
)
5355 return E_OUTOFMEMORY
;
5357 new_entry
= &This
->entries
[new_ref
];
5359 new_entry
->newTransactedParentEntry
= new_entry
->transactedParentEntry
= DIRENTRY_NULL
;
5360 new_entry
->read
= TRUE
;
5361 new_entry
->dirty
= TRUE
;
5362 memcpy(&new_entry
->data
, newData
, sizeof(DirEntry
));
5366 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData
->name
), newData
->leftChild
, newData
->rightChild
, newData
->dirRootEntry
, *index
);
5371 static HRESULT
TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl
*base
,
5372 DirRef index
, const DirEntry
*data
)
5374 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5377 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
5379 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5380 if (FAILED(hr
)) return hr
;
5382 memcpy(&This
->entries
[index
].data
, data
, sizeof(DirEntry
));
5384 if (index
!= This
->base
.storageDirEntry
)
5386 This
->entries
[index
].dirty
= TRUE
;
5388 if (data
->size
.QuadPart
== 0 &&
5389 This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
5391 /* Since this entry is modified, and we aren't using its stream data, we
5392 * no longer care about the original entry. */
5394 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
5396 if (delete_ref
!= DIRENTRY_NULL
)
5397 This
->entries
[delete_ref
].deleted
= TRUE
;
5399 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5406 static HRESULT
TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl
*base
,
5407 DirRef index
, DirEntry
*data
)
5409 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5412 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5413 if (FAILED(hr
)) return hr
;
5415 memcpy(data
, &This
->entries
[index
].data
, sizeof(DirEntry
));
5417 TRACE("%x %s l=%x r=%x d=%x\n", index
, debugstr_w(data
->name
), data
->leftChild
, data
->rightChild
, data
->dirRootEntry
);
5422 static HRESULT
TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5425 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5427 if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
||
5428 This
->entries
[index
].data
.size
.QuadPart
!= 0)
5430 /* If we deleted this entry while it has stream data. We must have left the
5431 * data because some other entry is using it, and we need to leave the
5432 * original entry alone. */
5433 memset(&This
->entries
[index
], 0, sizeof(TransactedDirEntry
));
5434 This
->firstFreeEntry
= min(index
, This
->firstFreeEntry
);
5438 This
->entries
[index
].deleted
= TRUE
;
5444 static HRESULT
TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl
*base
,
5445 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5447 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5449 if (This
->entries
[index
].stream_dirty
)
5451 return StorageBaseImpl_StreamReadAt(This
->scratch
,
5452 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesRead
);
5454 else if (This
->entries
[index
].transactedParentEntry
== DIRENTRY_NULL
)
5456 /* This stream doesn't live in the parent, and we haven't allocated storage
5463 return StorageBaseImpl_StreamReadAt(This
->transactedParent
,
5464 This
->entries
[index
].transactedParentEntry
, offset
, size
, buffer
, bytesRead
);
5468 static HRESULT
TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl
*base
,
5469 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5471 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5474 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5475 if (FAILED(hr
)) return hr
;
5477 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5478 if (FAILED(hr
)) return hr
;
5480 hr
= StorageBaseImpl_StreamWriteAt(This
->scratch
,
5481 This
->entries
[index
].stream_entry
, offset
, size
, buffer
, bytesWritten
);
5483 if (SUCCEEDED(hr
) && size
!= 0)
5484 This
->entries
[index
].data
.size
.QuadPart
= max(
5485 This
->entries
[index
].data
.size
.QuadPart
,
5486 offset
.QuadPart
+ size
);
5491 static HRESULT
TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl
*base
,
5492 DirRef index
, ULARGE_INTEGER newsize
)
5494 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5497 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, index
);
5498 if (FAILED(hr
)) return hr
;
5500 if (This
->entries
[index
].data
.size
.QuadPart
== newsize
.QuadPart
)
5503 if (newsize
.QuadPart
== 0)
5505 /* Destroy any parent references or entries in the scratch file. */
5506 if (This
->entries
[index
].stream_dirty
)
5508 ULARGE_INTEGER zero
;
5510 StorageBaseImpl_StreamSetSize(This
->scratch
,
5511 This
->entries
[index
].stream_entry
, zero
);
5512 StorageBaseImpl_DestroyDirEntry(This
->scratch
,
5513 This
->entries
[index
].stream_entry
);
5514 This
->entries
[index
].stream_dirty
= FALSE
;
5516 else if (This
->entries
[index
].transactedParentEntry
!= DIRENTRY_NULL
)
5519 delete_ref
= TransactedSnapshotImpl_CreateStubEntry(This
, This
->entries
[index
].transactedParentEntry
);
5521 if (delete_ref
!= DIRENTRY_NULL
)
5522 This
->entries
[delete_ref
].deleted
= TRUE
;
5524 This
->entries
[index
].transactedParentEntry
= This
->entries
[index
].newTransactedParentEntry
= DIRENTRY_NULL
;
5529 hr
= TransactedSnapshotImpl_MakeStreamDirty(This
, index
);
5530 if (FAILED(hr
)) return hr
;
5532 hr
= StorageBaseImpl_StreamSetSize(This
->scratch
,
5533 This
->entries
[index
].stream_entry
, newsize
);
5537 This
->entries
[index
].data
.size
= newsize
;
5542 static HRESULT
TransactedSnapshotImpl_StreamLink(StorageBaseImpl
*base
,
5543 DirRef dst
, DirRef src
)
5545 TransactedSnapshotImpl
* This
= (TransactedSnapshotImpl
*) base
;
5547 TransactedDirEntry
*dst_entry
, *src_entry
;
5549 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, src
);
5550 if (FAILED(hr
)) return hr
;
5552 hr
= TransactedSnapshotImpl_EnsureReadEntry(This
, dst
);
5553 if (FAILED(hr
)) return hr
;
5555 dst_entry
= &This
->entries
[dst
];
5556 src_entry
= &This
->entries
[src
];
5558 dst_entry
->stream_dirty
= src_entry
->stream_dirty
;
5559 dst_entry
->stream_entry
= src_entry
->stream_entry
;
5560 dst_entry
->transactedParentEntry
= src_entry
->transactedParentEntry
;
5561 dst_entry
->newTransactedParentEntry
= src_entry
->newTransactedParentEntry
;
5562 dst_entry
->data
.size
= src_entry
->data
.size
;
5567 static HRESULT
TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl
*base
,
5568 ULONG
* result
, BOOL refresh
)
5573 static HRESULT
TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl
*base
,
5579 static HRESULT
TransactedSnapshotImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5584 static HRESULT
TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5589 static const IStorageVtbl TransactedSnapshotImpl_Vtbl
=
5591 StorageBaseImpl_QueryInterface
,
5592 StorageBaseImpl_AddRef
,
5593 StorageBaseImpl_Release
,
5594 StorageBaseImpl_CreateStream
,
5595 StorageBaseImpl_OpenStream
,
5596 StorageBaseImpl_CreateStorage
,
5597 StorageBaseImpl_OpenStorage
,
5598 StorageBaseImpl_CopyTo
,
5599 StorageBaseImpl_MoveElementTo
,
5600 TransactedSnapshotImpl_Commit
,
5601 TransactedSnapshotImpl_Revert
,
5602 StorageBaseImpl_EnumElements
,
5603 StorageBaseImpl_DestroyElement
,
5604 StorageBaseImpl_RenameElement
,
5605 StorageBaseImpl_SetElementTimes
,
5606 StorageBaseImpl_SetClass
,
5607 StorageBaseImpl_SetStateBits
,
5608 StorageBaseImpl_Stat
5611 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl
=
5613 TransactedSnapshotImpl_Destroy
,
5614 TransactedSnapshotImpl_Invalidate
,
5615 TransactedSnapshotImpl_Flush
,
5616 TransactedSnapshotImpl_GetFilename
,
5617 TransactedSnapshotImpl_CreateDirEntry
,
5618 TransactedSnapshotImpl_WriteDirEntry
,
5619 TransactedSnapshotImpl_ReadDirEntry
,
5620 TransactedSnapshotImpl_DestroyDirEntry
,
5621 TransactedSnapshotImpl_StreamReadAt
,
5622 TransactedSnapshotImpl_StreamWriteAt
,
5623 TransactedSnapshotImpl_StreamSetSize
,
5624 TransactedSnapshotImpl_StreamLink
,
5625 TransactedSnapshotImpl_GetTransactionSig
,
5626 TransactedSnapshotImpl_SetTransactionSig
,
5627 TransactedSnapshotImpl_LockTransaction
,
5628 TransactedSnapshotImpl_UnlockTransaction
5631 static HRESULT
TransactedSnapshotImpl_Construct(StorageBaseImpl
*parentStorage
,
5632 TransactedSnapshotImpl
** result
)
5636 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSnapshotImpl
));
5641 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSnapshotImpl_Vtbl
;
5643 /* This is OK because the property set storage functions use the IStorage functions. */
5644 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
5645 (*result
)->base
.baseVtbl
= &TransactedSnapshotImpl_BaseVtbl
;
5647 list_init(&(*result
)->base
.strmHead
);
5649 list_init(&(*result
)->base
.storageHead
);
5651 (*result
)->base
.ref
= 1;
5653 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5655 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5656 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
5658 /* Create a new temporary storage to act as the scratch file. */
5659 hr
= StgCreateDocfile(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
,
5661 (*result
)->scratch
= impl_from_IStorage(scratch
);
5665 ULONG num_entries
= 20;
5667 (*result
)->entries
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedDirEntry
) * num_entries
);
5668 (*result
)->entries_size
= num_entries
;
5669 (*result
)->firstFreeEntry
= 0;
5671 if ((*result
)->entries
)
5673 /* parentStorage already has 1 reference, which we take over here. */
5674 (*result
)->transactedParent
= parentStorage
;
5676 parentStorage
->transactedChild
= &(*result
)->base
;
5678 (*result
)->base
.storageDirEntry
= TransactedSnapshotImpl_CreateStubEntry(*result
, parentStorage
->storageDirEntry
);
5682 IStorage_Release(scratch
);
5688 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
5693 return E_OUTOFMEMORY
;
5696 static void TransactedSharedImpl_Invalidate(StorageBaseImpl
* This
)
5698 if (!This
->reverted
)
5700 TRACE("Storage invalidated (stg=%p)\n", This
);
5702 This
->reverted
= TRUE
;
5704 StorageBaseImpl_DeleteAll(This
);
5708 static void TransactedSharedImpl_Destroy( StorageBaseImpl
*iface
)
5710 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
5712 TransactedSharedImpl_Invalidate(&This
->base
);
5713 IStorage_Release(&This
->transactedParent
->IStorage_iface
);
5714 IStorage_Release(&This
->scratch
->base
.IStorage_iface
);
5715 HeapFree(GetProcessHeap(), 0, This
);
5718 static HRESULT
TransactedSharedImpl_Flush(StorageBaseImpl
* iface
)
5720 /* We only need to flush when committing. */
5724 static HRESULT
TransactedSharedImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
5726 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) iface
;
5728 return StorageBaseImpl_GetFilename(This
->transactedParent
, result
);
5731 static HRESULT
TransactedSharedImpl_CreateDirEntry(StorageBaseImpl
*base
,
5732 const DirEntry
*newData
, DirRef
*index
)
5734 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5736 return StorageBaseImpl_CreateDirEntry(&This
->scratch
->base
,
5740 static HRESULT
TransactedSharedImpl_WriteDirEntry(StorageBaseImpl
*base
,
5741 DirRef index
, const DirEntry
*data
)
5743 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5745 return StorageBaseImpl_WriteDirEntry(&This
->scratch
->base
,
5749 static HRESULT
TransactedSharedImpl_ReadDirEntry(StorageBaseImpl
*base
,
5750 DirRef index
, DirEntry
*data
)
5752 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5754 return StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
,
5758 static HRESULT
TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl
*base
,
5761 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5763 return StorageBaseImpl_DestroyDirEntry(&This
->scratch
->base
,
5767 static HRESULT
TransactedSharedImpl_StreamReadAt(StorageBaseImpl
*base
,
5768 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
5770 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5772 return StorageBaseImpl_StreamReadAt(&This
->scratch
->base
,
5773 index
, offset
, size
, buffer
, bytesRead
);
5776 static HRESULT
TransactedSharedImpl_StreamWriteAt(StorageBaseImpl
*base
,
5777 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
5779 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5781 return StorageBaseImpl_StreamWriteAt(&This
->scratch
->base
,
5782 index
, offset
, size
, buffer
, bytesWritten
);
5785 static HRESULT
TransactedSharedImpl_StreamSetSize(StorageBaseImpl
*base
,
5786 DirRef index
, ULARGE_INTEGER newsize
)
5788 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5790 return StorageBaseImpl_StreamSetSize(&This
->scratch
->base
,
5794 static HRESULT
TransactedSharedImpl_StreamLink(StorageBaseImpl
*base
,
5795 DirRef dst
, DirRef src
)
5797 TransactedSharedImpl
* This
= (TransactedSharedImpl
*) base
;
5799 return StorageBaseImpl_StreamLink(&This
->scratch
->base
,
5803 static HRESULT
TransactedSharedImpl_GetTransactionSig(StorageBaseImpl
*base
,
5804 ULONG
* result
, BOOL refresh
)
5809 static HRESULT
TransactedSharedImpl_SetTransactionSig(StorageBaseImpl
*base
,
5815 static HRESULT
TransactedSharedImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
5820 static HRESULT
TransactedSharedImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
5825 static HRESULT WINAPI
TransactedSharedImpl_Commit(
5827 DWORD grfCommitFlags
) /* [in] */
5829 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
5830 DirRef new_storage_ref
, prev_storage_ref
;
5831 DirEntry src_data
, dst_data
;
5833 ULONG transactionSig
;
5835 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
5837 /* Cannot commit a read-only transacted storage */
5838 if ( STGM_ACCESS_MODE( This
->base
.openFlags
) == STGM_READ
)
5839 return STG_E_ACCESSDENIED
;
5841 hr
= StorageBaseImpl_LockTransaction(This
->transactedParent
, TRUE
);
5842 if (hr
== E_NOTIMPL
) hr
= S_OK
;
5845 hr
= StorageBaseImpl_GetTransactionSig(This
->transactedParent
, &transactionSig
, TRUE
);
5848 if ((grfCommitFlags
& STGC_ONLYIFCURRENT
) && transactionSig
!= This
->lastTransactionSig
)
5849 hr
= STG_E_NOTCURRENT
;
5852 hr
= StorageBaseImpl_SetTransactionSig(This
->transactedParent
, transactionSig
+1);
5854 else if (hr
== E_NOTIMPL
)
5858 hr
= StorageBaseImpl_ReadDirEntry(&This
->scratch
->base
, This
->scratch
->base
.storageDirEntry
, &src_data
);
5860 /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
5862 hr
= StorageBaseImpl_DupStorageTree(This
->transactedParent
, &new_storage_ref
, &This
->scratch
->base
, src_data
.dirRootEntry
);
5865 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5868 hr
= StorageBaseImpl_ReadDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
5872 prev_storage_ref
= dst_data
.dirRootEntry
;
5873 dst_data
.dirRootEntry
= new_storage_ref
;
5874 dst_data
.clsid
= src_data
.clsid
;
5875 dst_data
.ctime
= src_data
.ctime
;
5876 dst_data
.mtime
= src_data
.mtime
;
5877 hr
= StorageBaseImpl_WriteDirEntry(This
->transactedParent
, This
->transactedParent
->storageDirEntry
, &dst_data
);
5882 /* Try to flush after updating the root storage, but if the flush fails, keep
5883 * going, on the theory that it'll either succeed later or the subsequent
5884 * writes will fail. */
5885 StorageBaseImpl_Flush(This
->transactedParent
);
5887 hr
= StorageBaseImpl_DeleteStorageTree(This
->transactedParent
, prev_storage_ref
, TRUE
);
5891 hr
= StorageBaseImpl_Flush(This
->transactedParent
);
5893 StorageBaseImpl_UnlockTransaction(This
->transactedParent
, TRUE
);
5896 hr
= IStorage_Commit(&This
->scratch
->base
.IStorage_iface
, STGC_DEFAULT
);
5900 This
->lastTransactionSig
= transactionSig
+1;
5907 static HRESULT WINAPI
TransactedSharedImpl_Revert(
5910 TransactedSharedImpl
* This
= (TransactedSharedImpl
*)impl_from_IStorage(iface
);
5912 TRACE("(%p)\n", iface
);
5914 /* Destroy the open objects. */
5915 StorageBaseImpl_DeleteAll(&This
->base
);
5917 return IStorage_Revert(&This
->scratch
->base
.IStorage_iface
);
5920 static const IStorageVtbl TransactedSharedImpl_Vtbl
=
5922 StorageBaseImpl_QueryInterface
,
5923 StorageBaseImpl_AddRef
,
5924 StorageBaseImpl_Release
,
5925 StorageBaseImpl_CreateStream
,
5926 StorageBaseImpl_OpenStream
,
5927 StorageBaseImpl_CreateStorage
,
5928 StorageBaseImpl_OpenStorage
,
5929 StorageBaseImpl_CopyTo
,
5930 StorageBaseImpl_MoveElementTo
,
5931 TransactedSharedImpl_Commit
,
5932 TransactedSharedImpl_Revert
,
5933 StorageBaseImpl_EnumElements
,
5934 StorageBaseImpl_DestroyElement
,
5935 StorageBaseImpl_RenameElement
,
5936 StorageBaseImpl_SetElementTimes
,
5937 StorageBaseImpl_SetClass
,
5938 StorageBaseImpl_SetStateBits
,
5939 StorageBaseImpl_Stat
5942 static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl
=
5944 TransactedSharedImpl_Destroy
,
5945 TransactedSharedImpl_Invalidate
,
5946 TransactedSharedImpl_Flush
,
5947 TransactedSharedImpl_GetFilename
,
5948 TransactedSharedImpl_CreateDirEntry
,
5949 TransactedSharedImpl_WriteDirEntry
,
5950 TransactedSharedImpl_ReadDirEntry
,
5951 TransactedSharedImpl_DestroyDirEntry
,
5952 TransactedSharedImpl_StreamReadAt
,
5953 TransactedSharedImpl_StreamWriteAt
,
5954 TransactedSharedImpl_StreamSetSize
,
5955 TransactedSharedImpl_StreamLink
,
5956 TransactedSharedImpl_GetTransactionSig
,
5957 TransactedSharedImpl_SetTransactionSig
,
5958 TransactedSharedImpl_LockTransaction
,
5959 TransactedSharedImpl_UnlockTransaction
5962 static HRESULT
TransactedSharedImpl_Construct(StorageBaseImpl
*parentStorage
,
5963 TransactedSharedImpl
** result
)
5967 *result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(TransactedSharedImpl
));
5972 (*result
)->base
.IStorage_iface
.lpVtbl
= &TransactedSharedImpl_Vtbl
;
5974 /* This is OK because the property set storage functions use the IStorage functions. */
5975 (*result
)->base
.IPropertySetStorage_iface
.lpVtbl
= parentStorage
->IPropertySetStorage_iface
.lpVtbl
;
5976 (*result
)->base
.baseVtbl
= &TransactedSharedImpl_BaseVtbl
;
5978 list_init(&(*result
)->base
.strmHead
);
5980 list_init(&(*result
)->base
.storageHead
);
5982 (*result
)->base
.ref
= 1;
5984 (*result
)->base
.openFlags
= parentStorage
->openFlags
;
5986 hr
= StorageBaseImpl_LockTransaction(parentStorage
, FALSE
);
5992 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5993 StorageBaseImpl_GetTransactionSig(parentStorage
, &(*result
)->lastTransactionSig
, FALSE
);
5997 stgo
.ulSectorSize
= 4096;
5998 stgo
.pwcsTemplateFile
= NULL
;
6000 /* Create a new temporary storage to act as the scratch file. */
6001 hr
= StgCreateStorageEx(NULL
, STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
|STGM_CREATE
|STGM_DELETEONRELEASE
|STGM_TRANSACTED
,
6002 STGFMT_DOCFILE
, 0, &stgo
, NULL
, &IID_IStorage
, (void**)&scratch
);
6003 (*result
)->scratch
= (TransactedSnapshotImpl
*)impl_from_IStorage(scratch
);
6007 hr
= StorageBaseImpl_CopyStorageTree(&(*result
)->scratch
->base
, (*result
)->scratch
->base
.storageDirEntry
,
6008 parentStorage
, parentStorage
->storageDirEntry
);
6012 hr
= IStorage_Commit(scratch
, STGC_DEFAULT
);
6014 (*result
)->base
.storageDirEntry
= (*result
)->scratch
->base
.storageDirEntry
;
6015 (*result
)->transactedParent
= parentStorage
;
6019 IStorage_Release(scratch
);
6022 StorageBaseImpl_UnlockTransaction(parentStorage
, FALSE
);
6025 if (FAILED(hr
)) HeapFree(GetProcessHeap(), 0, *result
);
6030 return E_OUTOFMEMORY
;
6033 static HRESULT
Storage_ConstructTransacted(StorageBaseImpl
*parentStorage
,
6034 BOOL toplevel
, StorageBaseImpl
** result
)
6036 static int fixme_flags
=STGM_NOSCRATCH
|STGM_NOSNAPSHOT
;
6038 if (parentStorage
->openFlags
& fixme_flags
)
6040 fixme_flags
&= ~parentStorage
->openFlags
;
6041 FIXME("Unimplemented flags %x\n", parentStorage
->openFlags
);
6044 if (toplevel
&& !(parentStorage
->openFlags
& STGM_NOSNAPSHOT
) &&
6045 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_DENY_WRITE
&&
6046 STGM_SHARE_MODE(parentStorage
->openFlags
) != STGM_SHARE_EXCLUSIVE
)
6048 /* Need to create a temp file for the snapshot */
6049 return TransactedSharedImpl_Construct(parentStorage
, (TransactedSharedImpl
**)result
);
6052 return TransactedSnapshotImpl_Construct(parentStorage
,
6053 (TransactedSnapshotImpl
**)result
);
6056 static HRESULT
Storage_Construct(
6064 StorageBaseImpl
** result
)
6066 StorageImpl
*newStorage
;
6067 StorageBaseImpl
*newTransactedStorage
;
6070 hr
= StorageImpl_Construct(hFile
, pwcsName
, pLkbyt
, openFlags
, fileBased
, create
, sector_size
, &newStorage
);
6071 if (FAILED(hr
)) goto end
;
6073 if (openFlags
& STGM_TRANSACTED
)
6075 hr
= Storage_ConstructTransacted(&newStorage
->base
, TRUE
, &newTransactedStorage
);
6077 IStorage_Release(&newStorage
->base
.IStorage_iface
);
6079 *result
= newTransactedStorage
;
6082 *result
= &newStorage
->base
;
6088 static void StorageInternalImpl_Invalidate( StorageBaseImpl
*base
)
6090 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6092 if (!This
->base
.reverted
)
6094 TRACE("Storage invalidated (stg=%p)\n", This
);
6096 This
->base
.reverted
= TRUE
;
6098 This
->parentStorage
= NULL
;
6100 StorageBaseImpl_DeleteAll(&This
->base
);
6102 list_remove(&This
->ParentListEntry
);
6106 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
6108 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
6110 StorageInternalImpl_Invalidate(&This
->base
);
6112 HeapFree(GetProcessHeap(), 0, This
);
6115 static HRESULT
StorageInternalImpl_Flush(StorageBaseImpl
* iface
)
6117 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
6119 return StorageBaseImpl_Flush(This
->parentStorage
);
6122 static HRESULT
StorageInternalImpl_GetFilename(StorageBaseImpl
* iface
, LPWSTR
*result
)
6124 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
6126 return StorageBaseImpl_GetFilename(This
->parentStorage
, result
);
6129 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
6130 const DirEntry
*newData
, DirRef
*index
)
6132 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6134 return StorageBaseImpl_CreateDirEntry(This
->parentStorage
,
6138 static HRESULT
StorageInternalImpl_WriteDirEntry(StorageBaseImpl
*base
,
6139 DirRef index
, const DirEntry
*data
)
6141 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6143 return StorageBaseImpl_WriteDirEntry(This
->parentStorage
,
6147 static HRESULT
StorageInternalImpl_ReadDirEntry(StorageBaseImpl
*base
,
6148 DirRef index
, DirEntry
*data
)
6150 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6152 return StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
6156 static HRESULT
StorageInternalImpl_DestroyDirEntry(StorageBaseImpl
*base
,
6159 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6161 return StorageBaseImpl_DestroyDirEntry(This
->parentStorage
,
6165 static HRESULT
StorageInternalImpl_StreamReadAt(StorageBaseImpl
*base
,
6166 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, void *buffer
, ULONG
*bytesRead
)
6168 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6170 return StorageBaseImpl_StreamReadAt(This
->parentStorage
,
6171 index
, offset
, size
, buffer
, bytesRead
);
6174 static HRESULT
StorageInternalImpl_StreamWriteAt(StorageBaseImpl
*base
,
6175 DirRef index
, ULARGE_INTEGER offset
, ULONG size
, const void *buffer
, ULONG
*bytesWritten
)
6177 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6179 return StorageBaseImpl_StreamWriteAt(This
->parentStorage
,
6180 index
, offset
, size
, buffer
, bytesWritten
);
6183 static HRESULT
StorageInternalImpl_StreamSetSize(StorageBaseImpl
*base
,
6184 DirRef index
, ULARGE_INTEGER newsize
)
6186 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6188 return StorageBaseImpl_StreamSetSize(This
->parentStorage
,
6192 static HRESULT
StorageInternalImpl_StreamLink(StorageBaseImpl
*base
,
6193 DirRef dst
, DirRef src
)
6195 StorageInternalImpl
* This
= (StorageInternalImpl
*) base
;
6197 return StorageBaseImpl_StreamLink(This
->parentStorage
,
6201 static HRESULT
StorageInternalImpl_GetTransactionSig(StorageBaseImpl
*base
,
6202 ULONG
* result
, BOOL refresh
)
6207 static HRESULT
StorageInternalImpl_SetTransactionSig(StorageBaseImpl
*base
,
6213 static HRESULT
StorageInternalImpl_LockTransaction(StorageBaseImpl
*base
, BOOL write
)
6218 static HRESULT
StorageInternalImpl_UnlockTransaction(StorageBaseImpl
*base
, BOOL write
)
6223 /******************************************************************************
6225 ** Storage32InternalImpl_Commit
6228 static HRESULT WINAPI
StorageInternalImpl_Commit(
6230 DWORD grfCommitFlags
) /* [in] */
6232 StorageBaseImpl
* This
= impl_from_IStorage(iface
);
6233 TRACE("(%p,%x)\n", iface
, grfCommitFlags
);
6234 return StorageBaseImpl_Flush(This
);
6237 /******************************************************************************
6239 ** Storage32InternalImpl_Revert
6242 static HRESULT WINAPI
StorageInternalImpl_Revert(
6245 FIXME("(%p): stub\n", iface
);
6249 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
6251 IStorage_Release(&This
->parentStorage
->IStorage_iface
);
6252 HeapFree(GetProcessHeap(), 0, This
);
6255 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
6256 IEnumSTATSTG
* iface
,
6260 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6263 return E_INVALIDARG
;
6267 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
6268 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
6271 IEnumSTATSTG_AddRef(&This
->IEnumSTATSTG_iface
);
6275 return E_NOINTERFACE
;
6278 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
6279 IEnumSTATSTG
* iface
)
6281 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6282 return InterlockedIncrement(&This
->ref
);
6285 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
6286 IEnumSTATSTG
* iface
)
6288 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6292 newRef
= InterlockedDecrement(&This
->ref
);
6296 IEnumSTATSTGImpl_Destroy(This
);
6302 static HRESULT
IEnumSTATSTGImpl_GetNextRef(
6303 IEnumSTATSTGImpl
* This
,
6306 DirRef result
= DIRENTRY_NULL
;
6310 WCHAR result_name
[DIRENTRY_NAME_MAX_LEN
];
6312 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
6313 This
->parentStorage
->storageDirEntry
, &entry
);
6314 searchNode
= entry
.dirRootEntry
;
6316 while (SUCCEEDED(hr
) && searchNode
!= DIRENTRY_NULL
)
6318 hr
= StorageBaseImpl_ReadDirEntry(This
->parentStorage
, searchNode
, &entry
);
6322 LONG diff
= entryNameCmp( entry
.name
, This
->name
);
6326 searchNode
= entry
.rightChild
;
6330 result
= searchNode
;
6331 memcpy(result_name
, entry
.name
, sizeof(result_name
));
6332 searchNode
= entry
.leftChild
;
6340 if (result
!= DIRENTRY_NULL
)
6341 memcpy(This
->name
, result_name
, sizeof(result_name
));
6347 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
6348 IEnumSTATSTG
* iface
,
6351 ULONG
* pceltFetched
)
6353 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6355 DirEntry currentEntry
;
6356 STATSTG
* currentReturnStruct
= rgelt
;
6357 ULONG objectFetched
= 0;
6358 DirRef currentSearchNode
;
6361 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
6362 return E_INVALIDARG
;
6364 if (This
->parentStorage
->reverted
)
6365 return STG_E_REVERTED
;
6368 * To avoid the special case, get another pointer to a ULONG value if
6369 * the caller didn't supply one.
6371 if (pceltFetched
==0)
6372 pceltFetched
= &objectFetched
;
6375 * Start the iteration, we will iterate until we hit the end of the
6376 * linked list or until we hit the number of items to iterate through
6380 while ( *pceltFetched
< celt
)
6382 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
6384 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
6388 * Read the entry from the storage.
6390 StorageBaseImpl_ReadDirEntry(This
->parentStorage
,
6395 * Copy the information to the return buffer.
6397 StorageUtl_CopyDirEntryToSTATSTG(This
->parentStorage
,
6398 currentReturnStruct
,
6403 * Step to the next item in the iteration
6406 currentReturnStruct
++;
6409 if (SUCCEEDED(hr
) && *pceltFetched
!= celt
)
6416 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
6417 IEnumSTATSTG
* iface
,
6420 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6422 ULONG objectFetched
= 0;
6423 DirRef currentSearchNode
;
6426 if (This
->parentStorage
->reverted
)
6427 return STG_E_REVERTED
;
6429 while ( (objectFetched
< celt
) )
6431 hr
= IEnumSTATSTGImpl_GetNextRef(This
, ¤tSearchNode
);
6433 if (FAILED(hr
) || currentSearchNode
== DIRENTRY_NULL
)
6439 if (SUCCEEDED(hr
) && objectFetched
!= celt
)
6445 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
6446 IEnumSTATSTG
* iface
)
6448 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6450 if (This
->parentStorage
->reverted
)
6451 return STG_E_REVERTED
;
6458 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
6459 IEnumSTATSTG
* iface
,
6460 IEnumSTATSTG
** ppenum
)
6462 IEnumSTATSTGImpl
* const This
= impl_from_IEnumSTATSTG(iface
);
6463 IEnumSTATSTGImpl
* newClone
;
6465 if (This
->parentStorage
->reverted
)
6466 return STG_E_REVERTED
;
6469 return E_INVALIDARG
;
6471 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
6472 This
->storageDirEntry
);
6476 return E_OUTOFMEMORY
;
6480 * The new clone enumeration must point to the same current node as
6483 memcpy(newClone
->name
, This
->name
, sizeof(newClone
->name
));
6485 *ppenum
= &newClone
->IEnumSTATSTG_iface
;
6491 * Virtual function table for the IEnumSTATSTGImpl class.
6493 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
6495 IEnumSTATSTGImpl_QueryInterface
,
6496 IEnumSTATSTGImpl_AddRef
,
6497 IEnumSTATSTGImpl_Release
,
6498 IEnumSTATSTGImpl_Next
,
6499 IEnumSTATSTGImpl_Skip
,
6500 IEnumSTATSTGImpl_Reset
,
6501 IEnumSTATSTGImpl_Clone
6504 /******************************************************************************
6505 ** IEnumSTATSTGImpl implementation
6508 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
6509 StorageBaseImpl
* parentStorage
,
6510 DirRef storageDirEntry
)
6512 IEnumSTATSTGImpl
* newEnumeration
;
6514 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
6518 newEnumeration
->IEnumSTATSTG_iface
.lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
6519 newEnumeration
->ref
= 1;
6520 newEnumeration
->name
[0] = 0;
6523 * We want to nail-down the reference to the storage in case the
6524 * enumeration out-lives the storage in the client application.
6526 newEnumeration
->parentStorage
= parentStorage
;
6527 IStorage_AddRef(&newEnumeration
->parentStorage
->IStorage_iface
);
6529 newEnumeration
->storageDirEntry
= storageDirEntry
;
6532 return newEnumeration
;
6536 * Virtual function table for the Storage32InternalImpl class.
6538 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
6540 StorageBaseImpl_QueryInterface
,
6541 StorageBaseImpl_AddRef
,
6542 StorageBaseImpl_Release
,
6543 StorageBaseImpl_CreateStream
,
6544 StorageBaseImpl_OpenStream
,
6545 StorageBaseImpl_CreateStorage
,
6546 StorageBaseImpl_OpenStorage
,
6547 StorageBaseImpl_CopyTo
,
6548 StorageBaseImpl_MoveElementTo
,
6549 StorageInternalImpl_Commit
,
6550 StorageInternalImpl_Revert
,
6551 StorageBaseImpl_EnumElements
,
6552 StorageBaseImpl_DestroyElement
,
6553 StorageBaseImpl_RenameElement
,
6554 StorageBaseImpl_SetElementTimes
,
6555 StorageBaseImpl_SetClass
,
6556 StorageBaseImpl_SetStateBits
,
6557 StorageBaseImpl_Stat
6560 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
6562 StorageInternalImpl_Destroy
,
6563 StorageInternalImpl_Invalidate
,
6564 StorageInternalImpl_Flush
,
6565 StorageInternalImpl_GetFilename
,
6566 StorageInternalImpl_CreateDirEntry
,
6567 StorageInternalImpl_WriteDirEntry
,
6568 StorageInternalImpl_ReadDirEntry
,
6569 StorageInternalImpl_DestroyDirEntry
,
6570 StorageInternalImpl_StreamReadAt
,
6571 StorageInternalImpl_StreamWriteAt
,
6572 StorageInternalImpl_StreamSetSize
,
6573 StorageInternalImpl_StreamLink
,
6574 StorageInternalImpl_GetTransactionSig
,
6575 StorageInternalImpl_SetTransactionSig
,
6576 StorageInternalImpl_LockTransaction
,
6577 StorageInternalImpl_UnlockTransaction
6580 /******************************************************************************
6581 ** Storage32InternalImpl implementation
6584 static StorageInternalImpl
* StorageInternalImpl_Construct(
6585 StorageBaseImpl
* parentStorage
,
6587 DirRef storageDirEntry
)
6589 StorageInternalImpl
* newStorage
;
6591 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
6595 list_init(&newStorage
->base
.strmHead
);
6597 list_init(&newStorage
->base
.storageHead
);
6600 * Initialize the virtual function table.
6602 newStorage
->base
.IStorage_iface
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
6603 newStorage
->base
.IPropertySetStorage_iface
.lpVtbl
= &IPropertySetStorage_Vtbl
;
6604 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
6605 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
6607 newStorage
->base
.reverted
= FALSE
;
6609 newStorage
->base
.ref
= 1;
6611 newStorage
->parentStorage
= parentStorage
;
6614 * Keep a reference to the directory entry of this storage
6616 newStorage
->base
.storageDirEntry
= storageDirEntry
;
6618 newStorage
->base
.create
= FALSE
;
6626 /******************************************************************************
6627 ** StorageUtl implementation
6630 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
6634 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
6635 *value
= lendian16toh(tmp
);
6638 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
6640 value
= htole16(value
);
6641 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
6644 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
6648 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
6649 *value
= lendian32toh(tmp
);
6652 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
6654 value
= htole32(value
);
6655 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
6658 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
6659 ULARGE_INTEGER
* value
)
6661 #ifdef WORDS_BIGENDIAN
6664 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6665 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
6666 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
6668 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
6672 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
6673 const ULARGE_INTEGER
*value
)
6675 #ifdef WORDS_BIGENDIAN
6678 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
6679 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
6680 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
6682 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
6686 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
6688 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
6689 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
6690 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
6692 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
6695 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
6697 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
6698 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
6699 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
6701 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
6704 void StorageUtl_CopyDirEntryToSTATSTG(
6705 StorageBaseImpl
* storage
,
6706 STATSTG
* destination
,
6707 const DirEntry
* source
,
6711 * The copy of the string occurs only when the flag is not set
6713 if (!(statFlags
& STATFLAG_NONAME
) && source
->stgType
== STGTY_ROOT
)
6715 /* Use the filename for the root storage. */
6716 destination
->pwcsName
= 0;
6717 StorageBaseImpl_GetFilename(storage
, &destination
->pwcsName
);
6719 else if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
6720 (source
->name
[0] == 0) )
6722 destination
->pwcsName
= 0;
6726 destination
->pwcsName
=
6727 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
6729 strcpyW(destination
->pwcsName
, source
->name
);
6732 switch (source
->stgType
)
6736 destination
->type
= STGTY_STORAGE
;
6739 destination
->type
= STGTY_STREAM
;
6742 destination
->type
= STGTY_STREAM
;
6746 destination
->cbSize
= source
->size
;
6748 currentReturnStruct->mtime = {0}; TODO
6749 currentReturnStruct->ctime = {0};
6750 currentReturnStruct->atime = {0};
6752 destination
->grfMode
= 0;
6753 destination
->grfLocksSupported
= 0;
6754 destination
->clsid
= source
->clsid
;
6755 destination
->grfStateBits
= 0;
6756 destination
->reserved
= 0;
6759 /******************************************************************************
6760 ** BlockChainStream implementation
6763 /* Read and save the index of all blocks in this stream. */
6764 HRESULT
BlockChainStream_UpdateIndexCache(BlockChainStream
* This
)
6766 ULONG next_sector
, next_offset
;
6768 struct BlockChainRun
*last_run
;
6770 if (This
->indexCacheLen
== 0)
6774 next_sector
= BlockChainStream_GetHeadOfChain(This
);
6778 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6779 next_offset
= last_run
->lastOffset
+1;
6780 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
,
6781 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
,
6783 if (FAILED(hr
)) return hr
;
6786 while (next_sector
!= BLOCK_END_OF_CHAIN
)
6788 if (!last_run
|| next_sector
!= last_run
->firstSector
+ next_offset
- last_run
->firstOffset
)
6790 /* Add the current block to the cache. */
6791 if (This
->indexCacheSize
== 0)
6793 This
->indexCache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*16);
6794 if (!This
->indexCache
) return E_OUTOFMEMORY
;
6795 This
->indexCacheSize
= 16;
6797 else if (This
->indexCacheSize
== This
->indexCacheLen
)
6799 struct BlockChainRun
*new_cache
;
6802 new_size
= This
->indexCacheSize
* 2;
6803 new_cache
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun
)*new_size
);
6804 if (!new_cache
) return E_OUTOFMEMORY
;
6805 memcpy(new_cache
, This
->indexCache
, sizeof(struct BlockChainRun
)*This
->indexCacheLen
);
6807 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
6808 This
->indexCache
= new_cache
;
6809 This
->indexCacheSize
= new_size
;
6812 This
->indexCacheLen
++;
6813 last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
6814 last_run
->firstSector
= next_sector
;
6815 last_run
->firstOffset
= next_offset
;
6818 last_run
->lastOffset
= next_offset
;
6820 /* Find the next block. */
6822 hr
= StorageImpl_GetNextBlockInChain(This
->parentStorage
, next_sector
, &next_sector
);
6823 if (FAILED(hr
)) return hr
;
6826 if (This
->indexCacheLen
)
6828 This
->tailIndex
= last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
;
6829 This
->numBlocks
= last_run
->lastOffset
+1;
6833 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
6834 This
->numBlocks
= 0;
6840 /* Locate the nth block in this stream. */
6841 ULONG
BlockChainStream_GetSectorOfOffset(BlockChainStream
*This
, ULONG offset
)
6843 ULONG min_offset
= 0, max_offset
= This
->numBlocks
-1;
6844 ULONG min_run
= 0, max_run
= This
->indexCacheLen
-1;
6846 if (offset
>= This
->numBlocks
)
6847 return BLOCK_END_OF_CHAIN
;
6849 while (min_run
< max_run
)
6851 ULONG run_to_check
= min_run
+ (offset
- min_offset
) * (max_run
- min_run
) / (max_offset
- min_offset
);
6852 if (offset
< This
->indexCache
[run_to_check
].firstOffset
)
6854 max_offset
= This
->indexCache
[run_to_check
].firstOffset
-1;
6855 max_run
= run_to_check
-1;
6857 else if (offset
> This
->indexCache
[run_to_check
].lastOffset
)
6859 min_offset
= This
->indexCache
[run_to_check
].lastOffset
+1;
6860 min_run
= run_to_check
+1;
6863 /* Block is in this run. */
6864 min_run
= max_run
= run_to_check
;
6867 return This
->indexCache
[min_run
].firstSector
+ offset
- This
->indexCache
[min_run
].firstOffset
;
6870 HRESULT
BlockChainStream_GetBlockAtOffset(BlockChainStream
*This
,
6871 ULONG index
, BlockChainBlock
**block
, ULONG
*sector
, BOOL create
)
6873 BlockChainBlock
*result
=NULL
;
6877 if (This
->cachedBlocks
[i
].index
== index
)
6879 *sector
= This
->cachedBlocks
[i
].sector
;
6880 *block
= &This
->cachedBlocks
[i
];
6884 *sector
= BlockChainStream_GetSectorOfOffset(This
, index
);
6885 if (*sector
== BLOCK_END_OF_CHAIN
)
6886 return STG_E_DOCFILECORRUPT
;
6890 if (This
->cachedBlocks
[0].index
== 0xffffffff)
6891 result
= &This
->cachedBlocks
[0];
6892 else if (This
->cachedBlocks
[1].index
== 0xffffffff)
6893 result
= &This
->cachedBlocks
[1];
6896 result
= &This
->cachedBlocks
[This
->blockToEvict
++];
6897 if (This
->blockToEvict
== 2)
6898 This
->blockToEvict
= 0;
6903 if (!StorageImpl_WriteBigBlock(This
->parentStorage
, result
->sector
, result
->data
))
6904 return STG_E_WRITEFAULT
;
6905 result
->dirty
= FALSE
;
6908 result
->read
= FALSE
;
6909 result
->index
= index
;
6910 result
->sector
= *sector
;
6917 BlockChainStream
* BlockChainStream_Construct(
6918 StorageImpl
* parentStorage
,
6919 ULONG
* headOfStreamPlaceHolder
,
6922 BlockChainStream
* newStream
;
6924 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
6926 newStream
->parentStorage
= parentStorage
;
6927 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
6928 newStream
->ownerDirEntry
= dirEntry
;
6929 newStream
->indexCache
= NULL
;
6930 newStream
->indexCacheLen
= 0;
6931 newStream
->indexCacheSize
= 0;
6932 newStream
->cachedBlocks
[0].index
= 0xffffffff;
6933 newStream
->cachedBlocks
[0].dirty
= FALSE
;
6934 newStream
->cachedBlocks
[1].index
= 0xffffffff;
6935 newStream
->cachedBlocks
[1].dirty
= FALSE
;
6936 newStream
->blockToEvict
= 0;
6938 if (FAILED(BlockChainStream_UpdateIndexCache(newStream
)))
6940 HeapFree(GetProcessHeap(), 0, newStream
->indexCache
);
6941 HeapFree(GetProcessHeap(), 0, newStream
);
6948 HRESULT
BlockChainStream_Flush(BlockChainStream
* This
)
6951 if (!This
) return S_OK
;
6954 if (This
->cachedBlocks
[i
].dirty
)
6956 if (StorageImpl_WriteBigBlock(This
->parentStorage
, This
->cachedBlocks
[i
].sector
, This
->cachedBlocks
[i
].data
))
6957 This
->cachedBlocks
[i
].dirty
= FALSE
;
6959 return STG_E_WRITEFAULT
;
6965 void BlockChainStream_Destroy(BlockChainStream
* This
)
6969 BlockChainStream_Flush(This
);
6970 HeapFree(GetProcessHeap(), 0, This
->indexCache
);
6972 HeapFree(GetProcessHeap(), 0, This
);
6975 /******************************************************************************
6976 * BlockChainStream_GetHeadOfChain
6978 * Returns the head of this stream chain.
6979 * Some special chains don't have directory entries, their heads are kept in
6980 * This->headOfStreamPlaceHolder.
6983 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
6985 DirEntry chainEntry
;
6988 if (This
->headOfStreamPlaceHolder
!= 0)
6989 return *(This
->headOfStreamPlaceHolder
);
6991 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
6993 hr
= StorageImpl_ReadDirEntry(
6994 This
->parentStorage
,
6995 This
->ownerDirEntry
,
7000 return chainEntry
.startingBlock
;
7004 return BLOCK_END_OF_CHAIN
;
7007 /******************************************************************************
7008 * BlockChainStream_GetCount
7010 * Returns the number of blocks that comprises this chain.
7011 * This is not the size of the stream as the last block may not be full!
7013 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
7015 return This
->numBlocks
;
7018 /******************************************************************************
7019 * BlockChainStream_ReadAt
7021 * Reads a specified number of bytes from this chain at the specified offset.
7022 * bytesRead may be NULL.
7023 * Failure will be returned if the specified number of bytes has not been read.
7025 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
7026 ULARGE_INTEGER offset
,
7031 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7032 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7033 ULONG bytesToReadInBuffer
;
7036 ULARGE_INTEGER stream_size
;
7038 BlockChainBlock
*cachedBlock
;
7040 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
7043 * Find the first block in the stream that contains part of the buffer.
7045 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
7049 stream_size
= BlockChainStream_GetSize(This
);
7050 if (stream_size
.QuadPart
> offset
.QuadPart
)
7051 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7056 * Start reading the buffer.
7058 bufferWalker
= buffer
;
7062 ULARGE_INTEGER ulOffset
;
7066 * Calculate how many bytes we can copy from this big block.
7068 bytesToReadInBuffer
=
7069 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7071 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
7078 /* Not in cache, and we're going to read past the end of the block. */
7079 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7082 StorageImpl_ReadAt(This
->parentStorage
,
7085 bytesToReadInBuffer
,
7090 if (!cachedBlock
->read
)
7093 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7094 return STG_E_READFAULT
;
7096 cachedBlock
->read
= TRUE
;
7099 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
7100 bytesReadAt
= bytesToReadInBuffer
;
7103 blockNoInSequence
++;
7104 bufferWalker
+= bytesReadAt
;
7105 size
-= bytesReadAt
;
7106 *bytesRead
+= bytesReadAt
;
7107 offsetInBlock
= 0; /* There is no offset on the next block */
7109 if (bytesToReadInBuffer
!= bytesReadAt
)
7116 /******************************************************************************
7117 * BlockChainStream_WriteAt
7119 * Writes the specified number of bytes to this chain at the specified offset.
7120 * Will fail if not all specified number of bytes have been written.
7122 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
7123 ULARGE_INTEGER offset
,
7126 ULONG
* bytesWritten
)
7128 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7129 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7132 const BYTE
* bufferWalker
;
7134 BlockChainBlock
*cachedBlock
;
7137 bufferWalker
= buffer
;
7141 ULARGE_INTEGER ulOffset
;
7142 DWORD bytesWrittenAt
;
7145 * Calculate how many bytes we can copy to this big block.
7148 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7150 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
7152 /* BlockChainStream_SetSize should have already been called to ensure we have
7153 * enough blocks in the chain to write into */
7156 ERR("not enough blocks in chain to write data\n");
7162 /* Not in cache, and we're going to write past the end of the block. */
7163 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7166 StorageImpl_WriteAt(This
->parentStorage
,
7174 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
7177 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7178 return STG_E_READFAULT
;
7181 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
7182 bytesWrittenAt
= bytesToWrite
;
7183 cachedBlock
->read
= TRUE
;
7184 cachedBlock
->dirty
= TRUE
;
7187 blockNoInSequence
++;
7188 bufferWalker
+= bytesWrittenAt
;
7189 size
-= bytesWrittenAt
;
7190 *bytesWritten
+= bytesWrittenAt
;
7191 offsetInBlock
= 0; /* There is no offset on the next block */
7193 if (bytesWrittenAt
!= bytesToWrite
)
7197 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7200 /******************************************************************************
7201 * BlockChainStream_Shrink
7203 * Shrinks this chain in the big block depot.
7205 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
7206 ULARGE_INTEGER newSize
)
7213 * Figure out how many blocks are needed to contain the new size
7215 numBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7217 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7223 * Go to the new end of chain
7225 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
7227 /* Mark the new end of chain */
7228 StorageImpl_SetNextBlockInChain(
7229 This
->parentStorage
,
7231 BLOCK_END_OF_CHAIN
);
7233 This
->tailIndex
= blockIndex
;
7237 if (This
->headOfStreamPlaceHolder
!= 0)
7239 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
7243 DirEntry chainEntry
;
7244 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7246 StorageImpl_ReadDirEntry(
7247 This
->parentStorage
,
7248 This
->ownerDirEntry
,
7251 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7253 StorageImpl_WriteDirEntry(
7254 This
->parentStorage
,
7255 This
->ownerDirEntry
,
7259 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7262 This
->numBlocks
= numBlocks
;
7265 * Mark the extra blocks as free
7267 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
7269 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7270 StorageImpl_FreeBigBlock(This
->parentStorage
,
7271 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
7272 if (last_run
->lastOffset
== last_run
->firstOffset
)
7273 This
->indexCacheLen
--;
7275 last_run
->lastOffset
--;
7279 * Reset the last accessed block cache.
7283 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
7285 This
->cachedBlocks
[i
].index
= 0xffffffff;
7286 This
->cachedBlocks
[i
].dirty
= FALSE
;
7293 /******************************************************************************
7294 * BlockChainStream_Enlarge
7296 * Grows this chain in the big block depot.
7298 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
7299 ULARGE_INTEGER newSize
)
7301 ULONG blockIndex
, currentBlock
;
7303 ULONG oldNumBlocks
= 0;
7305 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
7308 * Empty chain. Create the head.
7310 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7312 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7313 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
7315 BLOCK_END_OF_CHAIN
);
7317 if (This
->headOfStreamPlaceHolder
!= 0)
7319 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7323 DirEntry chainEntry
;
7324 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7326 StorageImpl_ReadDirEntry(
7327 This
->parentStorage
,
7328 This
->ownerDirEntry
,
7331 chainEntry
.startingBlock
= blockIndex
;
7333 StorageImpl_WriteDirEntry(
7334 This
->parentStorage
,
7335 This
->ownerDirEntry
,
7339 This
->tailIndex
= blockIndex
;
7340 This
->numBlocks
= 1;
7344 * Figure out how many blocks are needed to contain this stream
7346 newNumBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7348 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7352 * Go to the current end of chain
7354 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
7356 currentBlock
= blockIndex
;
7358 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7361 currentBlock
= blockIndex
;
7363 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
7368 This
->tailIndex
= currentBlock
;
7371 currentBlock
= This
->tailIndex
;
7372 oldNumBlocks
= This
->numBlocks
;
7375 * Add new blocks to the chain
7377 if (oldNumBlocks
< newNumBlocks
)
7379 while (oldNumBlocks
< newNumBlocks
)
7381 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7383 StorageImpl_SetNextBlockInChain(
7384 This
->parentStorage
,
7388 StorageImpl_SetNextBlockInChain(
7389 This
->parentStorage
,
7391 BLOCK_END_OF_CHAIN
);
7393 currentBlock
= blockIndex
;
7397 This
->tailIndex
= blockIndex
;
7398 This
->numBlocks
= newNumBlocks
;
7401 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
7407 /******************************************************************************
7408 * BlockChainStream_SetSize
7410 * Sets the size of this stream. The big block depot will be updated.
7411 * The file will grow if we grow the chain.
7413 * TODO: Free the actual blocks in the file when we shrink the chain.
7414 * Currently, the blocks are still in the file. So the file size
7415 * doesn't shrink even if we shrink streams.
7417 BOOL
BlockChainStream_SetSize(
7418 BlockChainStream
* This
,
7419 ULARGE_INTEGER newSize
)
7421 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
7423 if (newSize
.QuadPart
== size
.QuadPart
)
7426 if (newSize
.QuadPart
< size
.QuadPart
)
7428 BlockChainStream_Shrink(This
, newSize
);
7432 BlockChainStream_Enlarge(This
, newSize
);
7438 /******************************************************************************
7439 * BlockChainStream_GetSize
7441 * Returns the size of this chain.
7442 * Will return the block count if this chain doesn't have a directory entry.
7444 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
7446 DirEntry chainEntry
;
7448 if(This
->headOfStreamPlaceHolder
== NULL
)
7451 * This chain has a directory entry so use the size value from there.
7453 StorageImpl_ReadDirEntry(
7454 This
->parentStorage
,
7455 This
->ownerDirEntry
,
7458 return chainEntry
.size
;
7463 * this chain is a chain that does not have a directory entry, figure out the
7464 * size by making the product number of used blocks times the
7467 ULARGE_INTEGER result
;
7469 (ULONGLONG
)BlockChainStream_GetCount(This
) *
7470 This
->parentStorage
->bigBlockSize
;
7476 /******************************************************************************
7477 ** SmallBlockChainStream implementation
7480 SmallBlockChainStream
* SmallBlockChainStream_Construct(
7481 StorageImpl
* parentStorage
,
7482 ULONG
* headOfStreamPlaceHolder
,
7485 SmallBlockChainStream
* newStream
;
7487 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
7489 newStream
->parentStorage
= parentStorage
;
7490 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7491 newStream
->ownerDirEntry
= dirEntry
;
7496 void SmallBlockChainStream_Destroy(
7497 SmallBlockChainStream
* This
)
7499 HeapFree(GetProcessHeap(), 0, This
);
7502 /******************************************************************************
7503 * SmallBlockChainStream_GetHeadOfChain
7505 * Returns the head of this chain of small blocks.
7507 static ULONG
SmallBlockChainStream_GetHeadOfChain(
7508 SmallBlockChainStream
* This
)
7510 DirEntry chainEntry
;
7513 if (This
->headOfStreamPlaceHolder
!= NULL
)
7514 return *(This
->headOfStreamPlaceHolder
);
7516 if (This
->ownerDirEntry
)
7518 hr
= StorageImpl_ReadDirEntry(
7519 This
->parentStorage
,
7520 This
->ownerDirEntry
,
7525 return chainEntry
.startingBlock
;
7530 return BLOCK_END_OF_CHAIN
;
7533 /******************************************************************************
7534 * SmallBlockChainStream_GetNextBlockInChain
7536 * Returns the index of the next small block in this chain.
7539 * - BLOCK_END_OF_CHAIN: end of this chain
7540 * - BLOCK_UNUSED: small block 'blockIndex' is free
7542 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
7543 SmallBlockChainStream
* This
,
7545 ULONG
* nextBlockInChain
)
7547 ULARGE_INTEGER offsetOfBlockInDepot
;
7552 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
7554 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7557 * Read those bytes in the buffer from the small block file.
7559 res
= BlockChainStream_ReadAt(
7560 This
->parentStorage
->smallBlockDepotChain
,
7561 offsetOfBlockInDepot
,
7566 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
7567 res
= STG_E_READFAULT
;
7571 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
7578 /******************************************************************************
7579 * SmallBlockChainStream_SetNextBlockInChain
7581 * Writes the index of the next block of the specified block in the small
7583 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7584 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7586 static void SmallBlockChainStream_SetNextBlockInChain(
7587 SmallBlockChainStream
* This
,
7591 ULARGE_INTEGER offsetOfBlockInDepot
;
7595 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7597 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
7600 * Read those bytes in the buffer from the small block file.
7602 BlockChainStream_WriteAt(
7603 This
->parentStorage
->smallBlockDepotChain
,
7604 offsetOfBlockInDepot
,
7610 /******************************************************************************
7611 * SmallBlockChainStream_FreeBlock
7613 * Flag small block 'blockIndex' as free in the small block depot.
7615 static void SmallBlockChainStream_FreeBlock(
7616 SmallBlockChainStream
* This
,
7619 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
7622 /******************************************************************************
7623 * SmallBlockChainStream_GetNextFreeBlock
7625 * Returns the index of a free small block. The small block depot will be
7626 * enlarged if necessary. The small block chain will also be enlarged if
7629 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
7630 SmallBlockChainStream
* This
)
7632 ULARGE_INTEGER offsetOfBlockInDepot
;
7635 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
7636 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
7638 ULONG smallBlocksPerBigBlock
;
7640 ULONG blocksRequired
;
7641 ULARGE_INTEGER old_size
, size_required
;
7643 offsetOfBlockInDepot
.u
.HighPart
= 0;
7646 * Scan the small block depot for a free block
7648 while (nextBlockIndex
!= BLOCK_UNUSED
)
7650 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7652 res
= BlockChainStream_ReadAt(
7653 This
->parentStorage
->smallBlockDepotChain
,
7654 offsetOfBlockInDepot
,
7660 * If we run out of space for the small block depot, enlarge it
7662 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
7664 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
7666 if (nextBlockIndex
!= BLOCK_UNUSED
)
7672 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
7674 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
7675 ULARGE_INTEGER newSize
, offset
;
7678 newSize
.QuadPart
= (ULONGLONG
)(count
+ 1) * This
->parentStorage
->bigBlockSize
;
7679 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
7682 * Initialize all the small blocks to free
7684 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
7685 offset
.QuadPart
= (ULONGLONG
)count
* This
->parentStorage
->bigBlockSize
;
7686 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
7687 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
7689 StorageImpl_SaveFileHeader(This
->parentStorage
);
7693 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
7695 smallBlocksPerBigBlock
=
7696 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
7699 * Verify if we have to allocate big blocks to contain small blocks
7701 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
7703 size_required
.QuadPart
= (ULONGLONG
)blocksRequired
* This
->parentStorage
->bigBlockSize
;
7705 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
7707 if (size_required
.QuadPart
> old_size
.QuadPart
)
7709 BlockChainStream_SetSize(
7710 This
->parentStorage
->smallBlockRootChain
,
7713 StorageImpl_ReadDirEntry(
7714 This
->parentStorage
,
7715 This
->parentStorage
->base
.storageDirEntry
,
7718 rootEntry
.size
= size_required
;
7720 StorageImpl_WriteDirEntry(
7721 This
->parentStorage
,
7722 This
->parentStorage
->base
.storageDirEntry
,
7729 /******************************************************************************
7730 * SmallBlockChainStream_ReadAt
7732 * Reads a specified number of bytes from this chain at the specified offset.
7733 * bytesRead may be NULL.
7734 * Failure will be returned if the specified number of bytes has not been read.
7736 HRESULT
SmallBlockChainStream_ReadAt(
7737 SmallBlockChainStream
* This
,
7738 ULARGE_INTEGER offset
,
7744 ULARGE_INTEGER offsetInBigBlockFile
;
7745 ULONG blockNoInSequence
=
7746 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7748 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
7749 ULONG bytesToReadInBuffer
;
7751 ULONG bytesReadFromBigBlockFile
;
7753 ULARGE_INTEGER stream_size
;
7756 * This should never happen on a small block file.
7758 assert(offset
.u
.HighPart
==0);
7762 stream_size
= SmallBlockChainStream_GetSize(This
);
7763 if (stream_size
.QuadPart
> offset
.QuadPart
)
7764 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7769 * Find the first block in the stream that contains part of the buffer.
7771 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7773 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
7775 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7778 blockNoInSequence
--;
7782 * Start reading the buffer.
7784 bufferWalker
= buffer
;
7786 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
7789 * Calculate how many bytes we can copy from this small block.
7791 bytesToReadInBuffer
=
7792 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
7795 * Calculate the offset of the small block in the small block file.
7797 offsetInBigBlockFile
.QuadPart
=
7798 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
7800 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
7803 * Read those bytes in the buffer from the small block file.
7804 * The small block has already been identified so it shouldn't fail
7805 * unless the file is corrupt.
7807 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
7808 offsetInBigBlockFile
,
7809 bytesToReadInBuffer
,
7811 &bytesReadFromBigBlockFile
);
7816 if (!bytesReadFromBigBlockFile
)
7817 return STG_E_DOCFILECORRUPT
;
7820 * Step to the next big block.
7822 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7824 return STG_E_DOCFILECORRUPT
;
7826 bufferWalker
+= bytesReadFromBigBlockFile
;
7827 size
-= bytesReadFromBigBlockFile
;
7828 *bytesRead
+= bytesReadFromBigBlockFile
;
7829 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7835 /******************************************************************************
7836 * SmallBlockChainStream_WriteAt
7838 * Writes the specified number of bytes to this chain at the specified offset.
7839 * Will fail if not all specified number of bytes have been written.
7841 HRESULT
SmallBlockChainStream_WriteAt(
7842 SmallBlockChainStream
* This
,
7843 ULARGE_INTEGER offset
,
7846 ULONG
* bytesWritten
)
7848 ULARGE_INTEGER offsetInBigBlockFile
;
7849 ULONG blockNoInSequence
=
7850 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7852 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
7853 ULONG bytesToWriteInBuffer
;
7855 ULONG bytesWrittenToBigBlockFile
;
7856 const BYTE
* bufferWalker
;
7860 * This should never happen on a small block file.
7862 assert(offset
.u
.HighPart
==0);
7865 * Find the first block in the stream that contains part of the buffer.
7867 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7869 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
7871 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
7872 return STG_E_DOCFILECORRUPT
;
7873 blockNoInSequence
--;
7877 * Start writing the buffer.
7880 bufferWalker
= buffer
;
7881 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
7884 * Calculate how many bytes we can copy to this small block.
7886 bytesToWriteInBuffer
=
7887 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
7890 * Calculate the offset of the small block in the small block file.
7892 offsetInBigBlockFile
.QuadPart
=
7893 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
7895 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
7898 * Write those bytes in the buffer to the small block file.
7900 res
= BlockChainStream_WriteAt(
7901 This
->parentStorage
->smallBlockRootChain
,
7902 offsetInBigBlockFile
,
7903 bytesToWriteInBuffer
,
7905 &bytesWrittenToBigBlockFile
);
7910 * Step to the next big block.
7912 res
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7915 bufferWalker
+= bytesWrittenToBigBlockFile
;
7916 size
-= bytesWrittenToBigBlockFile
;
7917 *bytesWritten
+= bytesWrittenToBigBlockFile
;
7918 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7921 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7924 /******************************************************************************
7925 * SmallBlockChainStream_Shrink
7927 * Shrinks this chain in the small block depot.
7929 static BOOL
SmallBlockChainStream_Shrink(
7930 SmallBlockChainStream
* This
,
7931 ULARGE_INTEGER newSize
)
7933 ULONG blockIndex
, extraBlock
;
7937 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7939 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7942 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7945 * Go to the new end of chain
7947 while (count
< numBlocks
)
7949 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7956 * If the count is 0, we have a special case, the head of the chain was
7961 DirEntry chainEntry
;
7963 StorageImpl_ReadDirEntry(This
->parentStorage
,
7964 This
->ownerDirEntry
,
7967 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7969 StorageImpl_WriteDirEntry(This
->parentStorage
,
7970 This
->ownerDirEntry
,
7974 * We start freeing the chain at the head block.
7976 extraBlock
= blockIndex
;
7980 /* Get the next block before marking the new end */
7981 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7985 /* Mark the new end of chain */
7986 SmallBlockChainStream_SetNextBlockInChain(
7989 BLOCK_END_OF_CHAIN
);
7993 * Mark the extra blocks as free
7995 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
7997 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
8000 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
8001 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
8002 extraBlock
= blockIndex
;
8008 /******************************************************************************
8009 * SmallBlockChainStream_Enlarge
8011 * Grows this chain in the small block depot.
8013 static BOOL
SmallBlockChainStream_Enlarge(
8014 SmallBlockChainStream
* This
,
8015 ULARGE_INTEGER newSize
)
8017 ULONG blockIndex
, currentBlock
;
8019 ULONG oldNumBlocks
= 0;
8021 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8024 * Empty chain. Create the head.
8026 if (blockIndex
== BLOCK_END_OF_CHAIN
)
8028 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8029 SmallBlockChainStream_SetNextBlockInChain(
8032 BLOCK_END_OF_CHAIN
);
8034 if (This
->headOfStreamPlaceHolder
!= NULL
)
8036 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
8040 DirEntry chainEntry
;
8042 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8045 chainEntry
.startingBlock
= blockIndex
;
8047 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8052 currentBlock
= blockIndex
;
8055 * Figure out how many blocks are needed to contain this stream
8057 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8059 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8063 * Go to the current end of chain
8065 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
8068 currentBlock
= blockIndex
;
8069 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
8074 * Add new blocks to the chain
8076 while (oldNumBlocks
< newNumBlocks
)
8078 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8079 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
8081 SmallBlockChainStream_SetNextBlockInChain(
8084 BLOCK_END_OF_CHAIN
);
8086 currentBlock
= blockIndex
;
8093 /******************************************************************************
8094 * SmallBlockChainStream_SetSize
8096 * Sets the size of this stream.
8097 * The file will grow if we grow the chain.
8099 * TODO: Free the actual blocks in the file when we shrink the chain.
8100 * Currently, the blocks are still in the file. So the file size
8101 * doesn't shrink even if we shrink streams.
8103 BOOL
SmallBlockChainStream_SetSize(
8104 SmallBlockChainStream
* This
,
8105 ULARGE_INTEGER newSize
)
8107 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
8109 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
8112 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
8114 SmallBlockChainStream_Shrink(This
, newSize
);
8118 SmallBlockChainStream_Enlarge(This
, newSize
);
8124 /******************************************************************************
8125 * SmallBlockChainStream_GetCount
8127 * Returns the number of small blocks that comprises this chain.
8128 * This is not the size of the stream as the last block may not be full!
8131 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
8136 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8138 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
8142 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
8143 blockIndex
, &blockIndex
)))
8150 /******************************************************************************
8151 * SmallBlockChainStream_GetSize
8153 * Returns the size of this chain.
8155 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
8157 DirEntry chainEntry
;
8159 if(This
->headOfStreamPlaceHolder
!= NULL
)
8161 ULARGE_INTEGER result
;
8162 result
.u
.HighPart
= 0;
8164 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
8165 This
->parentStorage
->smallBlockSize
;
8170 StorageImpl_ReadDirEntry(
8171 This
->parentStorage
,
8172 This
->ownerDirEntry
,
8175 return chainEntry
.size
;
8178 static HRESULT
create_storagefile(
8182 STGOPTIONS
* pStgOptions
,
8186 StorageBaseImpl
* newStorage
= 0;
8187 HANDLE hFile
= INVALID_HANDLE_VALUE
;
8188 HRESULT hr
= STG_E_INVALIDFLAG
;
8192 DWORD fileAttributes
;
8193 WCHAR tempFileName
[MAX_PATH
];
8196 return STG_E_INVALIDPOINTER
;
8198 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
8199 return STG_E_INVALIDPARAMETER
;
8201 /* if no share mode given then DENY_NONE is the default */
8202 if (STGM_SHARE_MODE(grfMode
) == 0)
8203 grfMode
|= STGM_SHARE_DENY_NONE
;
8205 if ( FAILED( validateSTGM(grfMode
) ))
8208 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8209 switch(STGM_ACCESS_MODE(grfMode
))
8212 case STGM_READWRITE
:
8218 /* in direct mode, can only use SHARE_EXCLUSIVE */
8219 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
8222 /* but in transacted mode, any share mode is valid */
8225 * Generate a unique name.
8229 WCHAR tempPath
[MAX_PATH
];
8230 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
8232 memset(tempPath
, 0, sizeof(tempPath
));
8233 memset(tempFileName
, 0, sizeof(tempFileName
));
8235 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
8238 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
8239 pwcsName
= tempFileName
;
8242 hr
= STG_E_INSUFFICIENTMEMORY
;
8246 creationMode
= TRUNCATE_EXISTING
;
8250 creationMode
= GetCreationModeFromSTGM(grfMode
);
8254 * Interpret the STGM value grfMode
8256 shareMode
= GetShareModeFromSTGM(grfMode
);
8257 accessMode
= GetAccessModeFromSTGM(grfMode
);
8259 if (grfMode
& STGM_DELETEONRELEASE
)
8260 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
8262 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
8266 hFile
= CreateFileW(pwcsName
,
8274 if (hFile
== INVALID_HANDLE_VALUE
)
8276 if(GetLastError() == ERROR_FILE_EXISTS
)
8277 hr
= STG_E_FILEALREADYEXISTS
;
8284 * Allocate and initialize the new IStorage32object.
8286 hr
= Storage_Construct(
8293 pStgOptions
->ulSectorSize
,
8301 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
8302 IStorage_Release(&newStorage
->IStorage_iface
);
8305 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
8310 /******************************************************************************
8311 * StgCreateDocfile [OLE32.@]
8312 * Creates a new compound file storage object
8315 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8316 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8317 * reserved [ ?] unused?, usually 0
8318 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8321 * S_OK if the file was successfully created
8322 * some STG_E_ value if error
8324 * if pwcsName is NULL, create file with new unique name
8325 * the function can returns
8326 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8329 HRESULT WINAPI
StgCreateDocfile(
8333 IStorage
**ppstgOpen
)
8335 STGOPTIONS stgoptions
= {1, 0, 512};
8337 TRACE("(%s, %x, %d, %p)\n",
8338 debugstr_w(pwcsName
), grfMode
,
8339 reserved
, ppstgOpen
);
8342 return STG_E_INVALIDPOINTER
;
8344 return STG_E_INVALIDPARAMETER
;
8346 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
8349 /******************************************************************************
8350 * StgCreateStorageEx [OLE32.@]
8352 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8354 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8355 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8357 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
8359 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8360 return STG_E_INVALIDPARAMETER
;
8363 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8365 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8366 return STG_E_INVALIDPARAMETER
;
8369 if (stgfmt
== STGFMT_FILE
)
8371 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8372 return STG_E_INVALIDPARAMETER
;
8375 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
8377 STGOPTIONS defaultOptions
= {1, 0, 512};
8379 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
8380 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
8384 ERR("Invalid stgfmt argument\n");
8385 return STG_E_INVALIDPARAMETER
;
8388 /******************************************************************************
8389 * StgCreatePropSetStg [OLE32.@]
8391 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
8392 IPropertySetStorage
**propset
)
8394 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
8396 return STG_E_INVALIDPARAMETER
;
8398 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
8401 /******************************************************************************
8402 * StgOpenStorageEx [OLE32.@]
8404 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8406 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8407 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8409 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
8411 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8412 return STG_E_INVALIDPARAMETER
;
8418 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8419 return STG_E_INVALIDPARAMETER
;
8421 case STGFMT_STORAGE
:
8424 case STGFMT_DOCFILE
:
8425 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8427 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8428 return STG_E_INVALIDPARAMETER
;
8430 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8434 WARN("STGFMT_ANY assuming storage\n");
8438 return STG_E_INVALIDPARAMETER
;
8441 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
8445 /******************************************************************************
8446 * StgOpenStorage [OLE32.@]
8448 HRESULT WINAPI
StgOpenStorage(
8449 const OLECHAR
*pwcsName
,
8450 IStorage
*pstgPriority
,
8454 IStorage
**ppstgOpen
)
8456 StorageBaseImpl
* newStorage
= 0;
8461 LPWSTR temp_name
= NULL
;
8463 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8464 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
8465 snbExclude
, reserved
, ppstgOpen
);
8469 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8470 hr
= StorageBaseImpl_GetFilename((StorageBaseImpl
*)pstgPriority
, &temp_name
);
8471 if (FAILED(hr
)) goto end
;
8472 pwcsName
= temp_name
;
8473 TRACE("using filename %s\n", debugstr_w(temp_name
));
8478 hr
= STG_E_INVALIDNAME
;
8484 hr
= STG_E_INVALIDPOINTER
;
8490 hr
= STG_E_INVALIDPARAMETER
;
8494 if (grfMode
& STGM_PRIORITY
)
8496 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
8497 return STG_E_INVALIDFLAG
;
8498 if (grfMode
& STGM_DELETEONRELEASE
)
8499 return STG_E_INVALIDFUNCTION
;
8500 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
8501 return STG_E_INVALIDFLAG
;
8502 grfMode
&= ~0xf0; /* remove the existing sharing mode */
8503 grfMode
|= STGM_SHARE_DENY_NONE
;
8507 * Validate the sharing mode
8509 if (grfMode
& STGM_DIRECT_SWMR
)
8511 if ((STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_WRITE
) &&
8512 (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_NONE
))
8514 hr
= STG_E_INVALIDFLAG
;
8518 else if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
8519 switch(STGM_SHARE_MODE(grfMode
))
8521 case STGM_SHARE_EXCLUSIVE
:
8522 case STGM_SHARE_DENY_WRITE
:
8525 hr
= STG_E_INVALIDFLAG
;
8529 if ( FAILED( validateSTGM(grfMode
) ) ||
8530 (grfMode
&STGM_CREATE
))
8532 hr
= STG_E_INVALIDFLAG
;
8536 /* shared reading requires transacted or single writer mode */
8537 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
8538 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
8539 !(grfMode
& STGM_TRANSACTED
) && !(grfMode
& STGM_DIRECT_SWMR
))
8541 hr
= STG_E_INVALIDFLAG
;
8546 * Interpret the STGM value grfMode
8548 shareMode
= GetShareModeFromSTGM(grfMode
);
8549 accessMode
= GetAccessModeFromSTGM(grfMode
);
8553 hFile
= CreateFileW( pwcsName
,
8558 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
8561 if (hFile
==INVALID_HANDLE_VALUE
)
8563 DWORD last_error
= GetLastError();
8569 case ERROR_FILE_NOT_FOUND
:
8570 hr
= STG_E_FILENOTFOUND
;
8573 case ERROR_PATH_NOT_FOUND
:
8574 hr
= STG_E_PATHNOTFOUND
;
8577 case ERROR_ACCESS_DENIED
:
8578 case ERROR_WRITE_PROTECT
:
8579 hr
= STG_E_ACCESSDENIED
;
8582 case ERROR_SHARING_VIOLATION
:
8583 hr
= STG_E_SHAREVIOLATION
;
8594 * Refuse to open the file if it's too small to be a structured storage file
8595 * FIXME: verify the file when reading instead of here
8597 if (GetFileSize(hFile
, NULL
) < 0x100)
8600 hr
= STG_E_FILEALREADYEXISTS
;
8605 * Allocate and initialize the new IStorage32object.
8607 hr
= Storage_Construct(
8620 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8622 if(hr
== STG_E_INVALIDHEADER
)
8623 hr
= STG_E_FILEALREADYEXISTS
;
8627 *ppstgOpen
= &newStorage
->IStorage_iface
;
8630 CoTaskMemFree(temp_name
);
8631 if (pstgPriority
) IStorage_Release(pstgPriority
);
8632 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
8636 /******************************************************************************
8637 * StgCreateDocfileOnILockBytes [OLE32.@]
8639 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
8643 IStorage
** ppstgOpen
)
8645 StorageBaseImpl
* newStorage
= 0;
8648 if ((ppstgOpen
== 0) || (plkbyt
== 0))
8649 return STG_E_INVALIDPOINTER
;
8652 * Allocate and initialize the new IStorage object.
8654 hr
= Storage_Construct(
8669 *ppstgOpen
= &newStorage
->IStorage_iface
;
8674 /******************************************************************************
8675 * StgOpenStorageOnILockBytes [OLE32.@]
8677 HRESULT WINAPI
StgOpenStorageOnILockBytes(
8679 IStorage
*pstgPriority
,
8683 IStorage
**ppstgOpen
)
8685 StorageBaseImpl
* newStorage
= 0;
8688 if ((plkbyt
== 0) || (ppstgOpen
== 0))
8689 return STG_E_INVALIDPOINTER
;
8691 if ( FAILED( validateSTGM(grfMode
) ))
8692 return STG_E_INVALIDFLAG
;
8697 * Allocate and initialize the new IStorage object.
8699 hr
= Storage_Construct(
8714 *ppstgOpen
= &newStorage
->IStorage_iface
;
8719 /******************************************************************************
8720 * StgSetTimes [ole32.@]
8721 * StgSetTimes [OLE32.@]
8725 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
8726 FILETIME
const *patime
, FILETIME
const *pmtime
)
8728 IStorage
*stg
= NULL
;
8731 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
8733 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
8737 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
8738 IStorage_Release(stg
);
8744 /******************************************************************************
8745 * StgIsStorageILockBytes [OLE32.@]
8747 * Determines if the ILockBytes contains a storage object.
8749 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
8751 BYTE sig
[sizeof(STORAGE_magic
)];
8752 ULARGE_INTEGER offset
;
8755 offset
.u
.HighPart
= 0;
8756 offset
.u
.LowPart
= 0;
8758 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
8760 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
8766 /******************************************************************************
8767 * WriteClassStg [OLE32.@]
8769 * This method will store the specified CLSID in the specified storage object
8771 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
8774 return E_INVALIDARG
;
8777 return STG_E_INVALIDPOINTER
;
8779 return IStorage_SetClass(pStg
, rclsid
);
8782 /***********************************************************************
8783 * ReadClassStg (OLE32.@)
8785 * This method reads the CLSID previously written to a storage object with
8786 * the WriteClassStg.
8789 * pstg [I] IStorage pointer
8790 * pclsid [O] Pointer to where the CLSID is written
8794 * Failure: HRESULT code.
8796 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
8801 TRACE("(%p, %p)\n", pstg
, pclsid
);
8803 if(!pstg
|| !pclsid
)
8804 return E_INVALIDARG
;
8807 * read a STATSTG structure (contains the clsid) from the storage
8809 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
8812 *pclsid
=pstatstg
.clsid
;
8817 /***********************************************************************
8818 * OleLoadFromStream (OLE32.@)
8820 * This function loads an object from stream
8822 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
8826 LPPERSISTSTREAM xstm
;
8828 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
8830 res
=ReadClassStm(pStm
,&clsid
);
8833 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
8836 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
8838 IUnknown_Release((IUnknown
*)*ppvObj
);
8841 res
=IPersistStream_Load(xstm
,pStm
);
8842 IPersistStream_Release(xstm
);
8843 /* FIXME: all refcounts ok at this point? I think they should be:
8846 * xstm : 0 (released)
8851 /***********************************************************************
8852 * OleSaveToStream (OLE32.@)
8854 * This function saves an object with the IPersistStream interface on it
8855 * to the specified stream.
8857 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
8863 TRACE("(%p,%p)\n",pPStm
,pStm
);
8865 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
8867 if (SUCCEEDED(res
)){
8869 res
=WriteClassStm(pStm
,&clsid
);
8873 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
8876 TRACE("Finished Save\n");
8880 /****************************************************************************
8881 * This method validate a STGM parameter that can contain the values below
8883 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8884 * The stgm values contained in 0xffff0000 are bitmasks.
8886 * STGM_DIRECT 0x00000000
8887 * STGM_TRANSACTED 0x00010000
8888 * STGM_SIMPLE 0x08000000
8890 * STGM_READ 0x00000000
8891 * STGM_WRITE 0x00000001
8892 * STGM_READWRITE 0x00000002
8894 * STGM_SHARE_DENY_NONE 0x00000040
8895 * STGM_SHARE_DENY_READ 0x00000030
8896 * STGM_SHARE_DENY_WRITE 0x00000020
8897 * STGM_SHARE_EXCLUSIVE 0x00000010
8899 * STGM_PRIORITY 0x00040000
8900 * STGM_DELETEONRELEASE 0x04000000
8902 * STGM_CREATE 0x00001000
8903 * STGM_CONVERT 0x00020000
8904 * STGM_FAILIFTHERE 0x00000000
8906 * STGM_NOSCRATCH 0x00100000
8907 * STGM_NOSNAPSHOT 0x00200000
8909 static HRESULT
validateSTGM(DWORD stgm
)
8911 DWORD access
= STGM_ACCESS_MODE(stgm
);
8912 DWORD share
= STGM_SHARE_MODE(stgm
);
8913 DWORD create
= STGM_CREATE_MODE(stgm
);
8915 if (stgm
&~STGM_KNOWN_FLAGS
)
8917 ERR("unknown flags %08x\n", stgm
);
8925 case STGM_READWRITE
:
8933 case STGM_SHARE_DENY_NONE
:
8934 case STGM_SHARE_DENY_READ
:
8935 case STGM_SHARE_DENY_WRITE
:
8936 case STGM_SHARE_EXCLUSIVE
:
8939 if (!(stgm
& STGM_TRANSACTED
))
8949 case STGM_FAILIFTHERE
:
8956 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8958 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
8962 * STGM_CREATE | STGM_CONVERT
8963 * if both are false, STGM_FAILIFTHERE is set to TRUE
8965 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
8969 * STGM_NOSCRATCH requires STGM_TRANSACTED
8971 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
8975 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8976 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8978 if ( (stgm
& STGM_NOSNAPSHOT
) &&
8979 (!(stgm
& STGM_TRANSACTED
) ||
8980 share
== STGM_SHARE_EXCLUSIVE
||
8981 share
== STGM_SHARE_DENY_WRITE
) )
8987 /****************************************************************************
8988 * GetShareModeFromSTGM
8990 * This method will return a share mode flag from a STGM value.
8991 * The STGM value is assumed valid.
8993 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
8995 switch (STGM_SHARE_MODE(stgm
))
8998 assert(stgm
& STGM_TRANSACTED
);
9000 case STGM_SHARE_DENY_NONE
:
9001 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
9002 case STGM_SHARE_DENY_READ
:
9003 return FILE_SHARE_WRITE
;
9004 case STGM_SHARE_DENY_WRITE
:
9005 case STGM_SHARE_EXCLUSIVE
:
9006 return FILE_SHARE_READ
;
9008 ERR("Invalid share mode!\n");
9013 /****************************************************************************
9014 * GetAccessModeFromSTGM
9016 * This method will return an access mode flag from a STGM value.
9017 * The STGM value is assumed valid.
9019 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
9021 switch (STGM_ACCESS_MODE(stgm
))
9024 return GENERIC_READ
;
9026 case STGM_READWRITE
:
9027 return GENERIC_READ
| GENERIC_WRITE
;
9029 ERR("Invalid access mode!\n");
9034 /****************************************************************************
9035 * GetCreationModeFromSTGM
9037 * This method will return a creation mode flag from a STGM value.
9038 * The STGM value is assumed valid.
9040 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
9042 switch(STGM_CREATE_MODE(stgm
))
9045 return CREATE_ALWAYS
;
9047 FIXME("STGM_CONVERT not implemented!\n");
9049 case STGM_FAILIFTHERE
:
9052 ERR("Invalid create mode!\n");
9058 /*************************************************************************
9059 * OLECONVERT_LoadOLE10 [Internal]
9061 * Loads the OLE10 STREAM to memory
9064 * pOleStream [I] The OLESTREAM
9065 * pData [I] Data Structure for the OLESTREAM Data
9069 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9070 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9073 * This function is used by OleConvertOLESTREAMToIStorage only.
9075 * Memory allocated for pData must be freed by the caller
9077 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
9080 HRESULT hRes
= S_OK
;
9084 pData
->pData
= NULL
;
9085 pData
->pstrOleObjFileName
= NULL
;
9087 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
9090 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9091 if(dwSize
!= sizeof(pData
->dwOleID
))
9093 hRes
= CONVERT10_E_OLESTREAM_GET
;
9095 else if(pData
->dwOleID
!= OLESTREAM_ID
)
9097 hRes
= CONVERT10_E_OLESTREAM_FMT
;
9108 /* Get the TypeID... more info needed for this field */
9109 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9110 if(dwSize
!= sizeof(pData
->dwTypeID
))
9112 hRes
= CONVERT10_E_OLESTREAM_GET
;
9117 if(pData
->dwTypeID
!= 0)
9119 /* Get the length of the OleTypeName */
9120 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9121 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9123 hRes
= CONVERT10_E_OLESTREAM_GET
;
9128 if(pData
->dwOleTypeNameLength
> 0)
9130 /* Get the OleTypeName */
9131 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9132 if(dwSize
!= pData
->dwOleTypeNameLength
)
9134 hRes
= CONVERT10_E_OLESTREAM_GET
;
9140 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
9141 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
9143 hRes
= CONVERT10_E_OLESTREAM_GET
;
9147 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
9148 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
9149 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
9150 if(pData
->pstrOleObjFileName
)
9152 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
9153 if(dwSize
!= pData
->dwOleObjFileNameLength
)
9155 hRes
= CONVERT10_E_OLESTREAM_GET
;
9159 hRes
= CONVERT10_E_OLESTREAM_GET
;
9164 /* Get the Width of the Metafile */
9165 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9166 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9168 hRes
= CONVERT10_E_OLESTREAM_GET
;
9172 /* Get the Height of the Metafile */
9173 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9174 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9176 hRes
= CONVERT10_E_OLESTREAM_GET
;
9182 /* Get the Length of the Data */
9183 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9184 if(dwSize
!= sizeof(pData
->dwDataLength
))
9186 hRes
= CONVERT10_E_OLESTREAM_GET
;
9190 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
9192 if(!bStrem1
) /* if it is a second OLE stream data */
9194 pData
->dwDataLength
-= 8;
9195 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
9196 if(dwSize
!= sizeof(pData
->strUnknown
))
9198 hRes
= CONVERT10_E_OLESTREAM_GET
;
9204 if(pData
->dwDataLength
> 0)
9206 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
9208 /* Get Data (ex. IStorage, Metafile, or BMP) */
9211 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
9212 if(dwSize
!= pData
->dwDataLength
)
9214 hRes
= CONVERT10_E_OLESTREAM_GET
;
9219 hRes
= CONVERT10_E_OLESTREAM_GET
;
9228 /*************************************************************************
9229 * OLECONVERT_SaveOLE10 [Internal]
9231 * Saves the OLE10 STREAM From memory
9234 * pData [I] Data Structure for the OLESTREAM Data
9235 * pOleStream [I] The OLESTREAM to save
9239 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9242 * This function is used by OleConvertIStorageToOLESTREAM only.
9245 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
9248 HRESULT hRes
= S_OK
;
9252 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9253 if(dwSize
!= sizeof(pData
->dwOleID
))
9255 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9260 /* Set the TypeID */
9261 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9262 if(dwSize
!= sizeof(pData
->dwTypeID
))
9264 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9268 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
9270 /* Set the Length of the OleTypeName */
9271 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9272 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9274 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9279 if(pData
->dwOleTypeNameLength
> 0)
9281 /* Set the OleTypeName */
9282 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9283 if(dwSize
!= pData
->dwOleTypeNameLength
)
9285 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9292 /* Set the width of the Metafile */
9293 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9294 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9296 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9302 /* Set the height of the Metafile */
9303 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9304 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9306 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9312 /* Set the length of the Data */
9313 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9314 if(dwSize
!= sizeof(pData
->dwDataLength
))
9316 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9322 if(pData
->dwDataLength
> 0)
9324 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9325 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
9326 if(dwSize
!= pData
->dwDataLength
)
9328 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9336 /*************************************************************************
9337 * OLECONVERT_GetOLE20FromOLE10[Internal]
9339 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9340 * opens it, and copies the content to the dest IStorage for
9341 * OleConvertOLESTREAMToIStorage
9345 * pDestStorage [I] The IStorage to copy the data to
9346 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9347 * nBufferLength [I] The size of the buffer
9356 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
9360 IStorage
*pTempStorage
;
9361 DWORD dwNumOfBytesWritten
;
9362 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9363 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9365 /* Create a temp File */
9366 GetTempPathW(MAX_PATH
, wstrTempDir
);
9367 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9368 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
9370 if(hFile
!= INVALID_HANDLE_VALUE
)
9372 /* Write IStorage Data to File */
9373 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
9376 /* Open and copy temp storage to the Dest Storage */
9377 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
9380 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
9381 IStorage_Release(pTempStorage
);
9383 DeleteFileW(wstrTempFile
);
9388 /*************************************************************************
9389 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9391 * Saves the OLE10 STREAM From memory
9394 * pStorage [I] The Src IStorage to copy
9395 * pData [I] The Dest Memory to write to.
9398 * The size in bytes allocated for pData
9401 * Memory allocated for pData must be freed by the caller
9403 * Used by OleConvertIStorageToOLESTREAM only.
9406 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
9410 DWORD nDataLength
= 0;
9411 IStorage
*pTempStorage
;
9412 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9413 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9417 /* Create temp Storage */
9418 GetTempPathW(MAX_PATH
, wstrTempDir
);
9419 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9420 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
9424 /* Copy Src Storage to the Temp Storage */
9425 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
9426 IStorage_Release(pTempStorage
);
9428 /* Open Temp Storage as a file and copy to memory */
9429 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9430 if(hFile
!= INVALID_HANDLE_VALUE
)
9432 nDataLength
= GetFileSize(hFile
, NULL
);
9433 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
9434 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
9437 DeleteFileW(wstrTempFile
);
9442 /*************************************************************************
9443 * STORAGE_CreateOleStream [Internal]
9445 * Creates the "\001OLE" stream in the IStorage if necessary.
9448 * storage [I] Dest storage to create the stream in
9449 * flags [I] flags to be set for newly created stream
9452 * HRESULT return value
9456 * This stream is still unknown, MS Word seems to have extra data
9457 * but since the data is stored in the OLESTREAM there should be
9458 * no need to recreate the stream. If the stream is manually
9459 * deleted it will create it with this default data.
9462 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
9464 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9465 static const DWORD version_magic
= 0x02000001;
9469 hr
= IStorage_CreateStream(storage
, stream_1oleW
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
9472 struct empty_1ole_stream
{
9473 DWORD version_magic
;
9475 DWORD update_options
;
9477 DWORD mon_stream_size
;
9479 struct empty_1ole_stream stream_data
;
9481 stream_data
.version_magic
= version_magic
;
9482 stream_data
.flags
= flags
;
9483 stream_data
.update_options
= 0;
9484 stream_data
.reserved
= 0;
9485 stream_data
.mon_stream_size
= 0;
9487 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
9488 IStream_Release(stream
);
9494 /* write a string to a stream, preceded by its length */
9495 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
9502 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
9503 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
9508 str
= CoTaskMemAlloc( len
);
9509 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
9510 r
= IStream_Write( stm
, str
, len
, NULL
);
9511 CoTaskMemFree( str
);
9515 /* read a string preceded by its length from a stream */
9516 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
9519 DWORD len
, count
= 0;
9523 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
9526 if( count
!= sizeof(len
) )
9527 return E_OUTOFMEMORY
;
9529 TRACE("%d bytes\n",len
);
9531 str
= CoTaskMemAlloc( len
);
9533 return E_OUTOFMEMORY
;
9535 r
= IStream_Read( stm
, str
, len
, &count
);
9540 CoTaskMemFree( str
);
9541 return E_OUTOFMEMORY
;
9544 TRACE("Read string %s\n",debugstr_an(str
,len
));
9546 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
9547 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
9550 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
9553 CoTaskMemFree( str
);
9561 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
9562 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
9566 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9568 static const BYTE unknown1
[12] =
9569 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9570 0xFF, 0xFF, 0xFF, 0xFF};
9571 static const BYTE unknown2
[16] =
9572 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9573 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9575 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
9576 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
9577 debugstr_w(szProgIDName
));
9579 /* Create a CompObj stream */
9580 r
= IStorage_CreateStream(pstg
, szwStreamName
,
9581 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
9585 /* Write CompObj Structure to stream */
9586 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
9588 if( SUCCEEDED( r
) )
9589 r
= WriteClassStm( pstm
, clsid
);
9591 if( SUCCEEDED( r
) )
9592 r
= STREAM_WriteString( pstm
, lpszUserType
);
9593 if( SUCCEEDED( r
) )
9594 r
= STREAM_WriteString( pstm
, szClipName
);
9595 if( SUCCEEDED( r
) )
9596 r
= STREAM_WriteString( pstm
, szProgIDName
);
9597 if( SUCCEEDED( r
) )
9598 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
9600 IStream_Release( pstm
);
9605 /***********************************************************************
9606 * WriteFmtUserTypeStg (OLE32.@)
9608 HRESULT WINAPI
WriteFmtUserTypeStg(
9609 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
9613 WCHAR szwClipName
[0x40];
9615 LPWSTR wstrProgID
= NULL
;
9618 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
9620 /* get the clipboard format name */
9623 n
= GetClipboardFormatNameW( cf
, szwClipName
,
9624 sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
9628 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
9630 r
= IStorage_Stat(pstg
, &stat
, STATFLAG_NONAME
);
9636 ProgIDFromCLSID(&clsid
, &wstrProgID
);
9638 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
9640 r
= STORAGE_WriteCompObj( pstg
, &clsid
, lpszUserType
,
9641 cf
? szwClipName
: NULL
, wstrProgID
);
9643 CoTaskMemFree(wstrProgID
);
9649 /******************************************************************************
9650 * ReadFmtUserTypeStg [OLE32.@]
9652 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
9656 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
9657 unsigned char unknown1
[12];
9658 unsigned char unknown2
[16];
9660 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
9663 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
9665 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
9666 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
9669 WARN("Failed to open stream r = %08x\n", r
);
9673 /* read the various parts of the structure */
9674 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
9675 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
9677 r
= ReadClassStm( stm
, &clsid
);
9681 r
= STREAM_ReadString( stm
, &szCLSIDName
);
9685 r
= STREAM_ReadString( stm
, &szOleTypeName
);
9689 r
= STREAM_ReadString( stm
, &szProgIDName
);
9693 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
9694 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
9697 /* ok, success... now we just need to store what we found */
9699 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
9701 if( lplpszUserType
)
9703 *lplpszUserType
= szCLSIDName
;
9708 CoTaskMemFree( szCLSIDName
);
9709 CoTaskMemFree( szOleTypeName
);
9710 CoTaskMemFree( szProgIDName
);
9711 IStream_Release( stm
);
9717 /*************************************************************************
9718 * OLECONVERT_CreateCompObjStream [Internal]
9720 * Creates a "\001CompObj" is the destination IStorage if necessary.
9723 * pStorage [I] The dest IStorage to create the CompObj Stream
9725 * strOleTypeName [I] The ProgID
9729 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9732 * This function is used by OleConvertOLESTREAMToIStorage only.
9734 * The stream data is stored in the OLESTREAM and there should be
9735 * no need to recreate the stream. If the stream is manually
9736 * deleted it will attempt to create it by querying the registry.
9740 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
9743 HRESULT hStorageRes
, hRes
= S_OK
;
9744 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
9745 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9746 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
9748 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9749 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
9751 /* Initialize the CompObj structure */
9752 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
9753 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
9754 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
9757 /* Create a CompObj stream if it doesn't exist */
9758 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9759 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9760 if(hStorageRes
== S_OK
)
9762 /* copy the OleTypeName to the compobj struct */
9763 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
9764 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
9766 /* copy the OleTypeName to the compobj struct */
9767 /* Note: in the test made, these were Identical */
9768 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
9769 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
9772 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
9773 bufferW
, OLESTREAM_MAX_STR_LEN
);
9774 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
9780 /* Get the CLSID Default Name from the Registry */
9781 hErr
= open_classes_key(HKEY_CLASSES_ROOT
, bufferW
, MAXIMUM_ALLOWED
, &hKey
);
9782 if(hErr
== ERROR_SUCCESS
)
9784 char strTemp
[OLESTREAM_MAX_STR_LEN
];
9785 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
9786 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
9787 if(hErr
== ERROR_SUCCESS
)
9789 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
9795 /* Write CompObj Structure to stream */
9796 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
9798 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
9800 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
9801 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
9803 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
9805 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
9806 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
9808 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
9810 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
9811 if(IStorageCompObj
.dwProgIDNameLength
> 0)
9813 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
9815 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
9816 IStream_Release(pStream
);
9822 /*************************************************************************
9823 * OLECONVERT_CreateOlePresStream[Internal]
9825 * Creates the "\002OlePres000" Stream with the Metafile data
9828 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
9829 * dwExtentX [I] Width of the Metafile
9830 * dwExtentY [I] Height of the Metafile
9831 * pData [I] Metafile data
9832 * dwDataLength [I] Size of the Metafile data
9836 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9839 * This function is used by OleConvertOLESTREAMToIStorage only.
9842 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
9846 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9847 BYTE pOlePresStreamHeader
[] =
9849 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
9850 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9851 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9852 0x00, 0x00, 0x00, 0x00
9855 BYTE pOlePresStreamHeaderEmpty
[] =
9857 0x00, 0x00, 0x00, 0x00,
9858 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9859 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9860 0x00, 0x00, 0x00, 0x00
9863 /* Create the OlePres000 Stream */
9864 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9865 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9870 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
9872 memset(&OlePres
, 0, sizeof(OlePres
));
9873 /* Do we have any metafile data to save */
9874 if(dwDataLength
> 0)
9876 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
9877 nHeaderSize
= sizeof(pOlePresStreamHeader
);
9881 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
9882 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
9884 /* Set width and height of the metafile */
9885 OlePres
.dwExtentX
= dwExtentX
;
9886 OlePres
.dwExtentY
= -dwExtentY
;
9888 /* Set Data and Length */
9889 if(dwDataLength
> sizeof(METAFILEPICT16
))
9891 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
9892 OlePres
.pData
= &(pData
[8]);
9894 /* Save OlePres000 Data to Stream */
9895 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
9896 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
9897 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
9898 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
9899 if(OlePres
.dwSize
> 0)
9901 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
9903 IStream_Release(pStream
);
9907 /*************************************************************************
9908 * OLECONVERT_CreateOle10NativeStream [Internal]
9910 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9913 * pStorage [I] Dest storage to create the stream in
9914 * pData [I] Ole10 Native Data (ex. bmp)
9915 * dwDataLength [I] Size of the Ole10 Native Data
9921 * This function is used by OleConvertOLESTREAMToIStorage only.
9923 * Might need to verify the data and return appropriate error message
9926 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
9930 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9932 /* Create the Ole10Native Stream */
9933 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9934 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9938 /* Write info to stream */
9939 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
9940 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
9941 IStream_Release(pStream
);
9946 /*************************************************************************
9947 * OLECONVERT_GetOLE10ProgID [Internal]
9949 * Finds the ProgID (or OleTypeID) from the IStorage
9952 * pStorage [I] The Src IStorage to get the ProgID
9953 * strProgID [I] the ProgID string to get
9954 * dwSize [I] the size of the string
9958 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9961 * This function is used by OleConvertIStorageToOLESTREAM only.
9965 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
9969 LARGE_INTEGER iSeekPos
;
9970 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
9971 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9973 /* Open the CompObj Stream */
9974 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9975 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9979 /*Get the OleType from the CompObj Stream */
9980 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
9981 iSeekPos
.u
.HighPart
= 0;
9983 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9984 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
9985 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
9986 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9987 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
9988 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
9989 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9991 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
9994 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
9996 IStream_Release(pStream
);
10001 LPOLESTR wstrProgID
;
10003 /* Get the OleType from the registry */
10004 REFCLSID clsid
= &(stat
.clsid
);
10005 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
10006 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
10009 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
10010 CoTaskMemFree(wstrProgID
);
10017 /*************************************************************************
10018 * OLECONVERT_GetOle10PresData [Internal]
10020 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10023 * pStorage [I] Src IStroage
10024 * pOleStream [I] Dest OleStream Mem Struct
10030 * This function is used by OleConvertIStorageToOLESTREAM only.
10032 * Memory allocated for pData must be freed by the caller
10036 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10041 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10043 /* Initialize Default data for OLESTREAM */
10044 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10045 pOleStreamData
[0].dwTypeID
= 2;
10046 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10047 pOleStreamData
[1].dwTypeID
= 0;
10048 pOleStreamData
[0].dwMetaFileWidth
= 0;
10049 pOleStreamData
[0].dwMetaFileHeight
= 0;
10050 pOleStreamData
[0].pData
= NULL
;
10051 pOleStreamData
[1].pData
= NULL
;
10053 /* Open Ole10Native Stream */
10054 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10055 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10059 /* Read Size and Data */
10060 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
10061 if(pOleStreamData
->dwDataLength
> 0)
10063 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
10064 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
10066 IStream_Release(pStream
);
10072 /*************************************************************************
10073 * OLECONVERT_GetOle20PresData[Internal]
10075 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10078 * pStorage [I] Src IStroage
10079 * pOleStreamData [I] Dest OleStream Mem Struct
10085 * This function is used by OleConvertIStorageToOLESTREAM only.
10087 * Memory allocated for pData must be freed by the caller
10089 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10093 OLECONVERT_ISTORAGE_OLEPRES olePress
;
10094 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10096 /* Initialize Default data for OLESTREAM */
10097 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10098 pOleStreamData
[0].dwTypeID
= 2;
10099 pOleStreamData
[0].dwMetaFileWidth
= 0;
10100 pOleStreamData
[0].dwMetaFileHeight
= 0;
10101 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
10102 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10103 pOleStreamData
[1].dwTypeID
= 0;
10104 pOleStreamData
[1].dwOleTypeNameLength
= 0;
10105 pOleStreamData
[1].strOleTypeName
[0] = 0;
10106 pOleStreamData
[1].dwMetaFileWidth
= 0;
10107 pOleStreamData
[1].dwMetaFileHeight
= 0;
10108 pOleStreamData
[1].pData
= NULL
;
10109 pOleStreamData
[1].dwDataLength
= 0;
10112 /* Open OlePress000 stream */
10113 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10114 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10117 LARGE_INTEGER iSeekPos
;
10118 METAFILEPICT16 MetaFilePict
;
10119 static const char strMetafilePictName
[] = "METAFILEPICT";
10121 /* Set the TypeID for a Metafile */
10122 pOleStreamData
[1].dwTypeID
= 5;
10124 /* Set the OleTypeName to Metafile */
10125 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
10126 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
10128 iSeekPos
.u
.HighPart
= 0;
10129 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
10131 /* Get Presentation Data */
10132 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10133 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
10134 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
10135 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
10137 /*Set width and Height */
10138 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
10139 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
10140 if(olePress
.dwSize
> 0)
10143 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
10145 /* Set MetaFilePict struct */
10146 MetaFilePict
.mm
= 8;
10147 MetaFilePict
.xExt
= olePress
.dwExtentX
;
10148 MetaFilePict
.yExt
= olePress
.dwExtentY
;
10149 MetaFilePict
.hMF
= 0;
10151 /* Get Metafile Data */
10152 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
10153 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
10154 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
10156 IStream_Release(pStream
);
10160 /*************************************************************************
10161 * OleConvertOLESTREAMToIStorage [OLE32.@]
10163 * Read info on MSDN
10166 * DVTARGETDEVICE parameter is not handled
10167 * Still unsure of some mem fields for OLE 10 Stream
10168 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10169 * and "\001OLE" streams
10172 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
10173 LPOLESTREAM pOleStream
,
10175 const DVTARGETDEVICE
* ptd
)
10179 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10181 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
10183 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10187 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10190 if(pstg
== NULL
|| pOleStream
== NULL
)
10192 hRes
= E_INVALIDARG
;
10197 /* Load the OLESTREAM to Memory */
10198 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
10203 /* Load the OLESTREAM to Memory (part 2)*/
10204 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
10210 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
10212 /* Do we have the IStorage Data in the OLESTREAM */
10213 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
10215 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10216 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
10220 /* It must be an original OLE 1.0 source */
10221 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10226 /* It must be an original OLE 1.0 source */
10227 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10230 /* Create CompObj Stream if necessary */
10231 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
10234 /*Create the Ole Stream if necessary */
10235 STORAGE_CreateOleStream(pstg
, 0);
10240 /* Free allocated memory */
10241 for(i
=0; i
< 2; i
++)
10243 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10244 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
10245 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
10250 /*************************************************************************
10251 * OleConvertIStorageToOLESTREAM [OLE32.@]
10253 * Read info on MSDN
10255 * Read info on MSDN
10258 * Still unsure of some mem fields for OLE 10 Stream
10259 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10260 * and "\001OLE" streams.
10263 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
10265 LPOLESTREAM pOleStream
)
10268 HRESULT hRes
= S_OK
;
10270 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10271 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10273 TRACE("%p %p\n", pstg
, pOleStream
);
10275 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10277 if(pstg
== NULL
|| pOleStream
== NULL
)
10279 hRes
= E_INVALIDARG
;
10283 /* Get the ProgID */
10284 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
10285 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
10289 /* Was it originally Ole10 */
10290 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10293 IStream_Release(pStream
);
10294 /* Get Presentation Data for Ole10Native */
10295 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
10299 /* Get Presentation Data (OLE20) */
10300 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
10303 /* Save OLESTREAM */
10304 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
10307 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
10312 /* Free allocated memory */
10313 for(i
=0; i
< 2; i
++)
10315 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10321 enum stream_1ole_flags
{
10322 OleStream_LinkedObject
= 0x00000001,
10323 OleStream_Convert
= 0x00000004
10326 /***********************************************************************
10327 * GetConvertStg (OLE32.@)
10329 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
10331 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10332 static const DWORD version_magic
= 0x02000001;
10337 TRACE("%p\n", stg
);
10339 if (!stg
) return E_INVALIDARG
;
10341 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10342 if (FAILED(hr
)) return hr
;
10344 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10345 IStream_Release(stream
);
10346 if (FAILED(hr
)) return hr
;
10348 if (header
[0] != version_magic
)
10350 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header
[0]);
10354 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
10357 /***********************************************************************
10358 * SetConvertStg (OLE32.@)
10360 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
10362 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10363 DWORD flags
= convert
? OleStream_Convert
: 0;
10368 TRACE("(%p, %d)\n", storage
, convert
);
10370 hr
= IStorage_OpenStream(storage
, stream_1oleW
, NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10373 if (hr
!= STG_E_FILENOTFOUND
)
10376 return STORAGE_CreateOleStream(storage
, flags
);
10379 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10382 IStream_Release(stream
);
10386 /* update flag if differs */
10387 if ((header
[1] ^ flags
) & OleStream_Convert
)
10389 LARGE_INTEGER pos
= {{0}};
10391 if (header
[1] & OleStream_Convert
)
10392 flags
= header
[1] & ~OleStream_Convert
;
10394 flags
= header
[1] | OleStream_Convert
;
10396 pos
.QuadPart
= sizeof(DWORD
);
10397 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
10400 IStream_Release(stream
);
10404 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
10407 IStream_Release(stream
);
10411 /******************************************************************************
10412 * StgIsStorageFile [OLE32.@]
10413 * Verify if the file contains a storage object
10419 * S_OK if file has magic bytes as a storage object
10420 * S_FALSE if file is not storage
10423 StgIsStorageFile(LPCOLESTR fn
)
10429 TRACE("%s\n", debugstr_w(fn
));
10430 hf
= CreateFileW(fn
, GENERIC_READ
,
10431 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
10432 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
10434 if (hf
== INVALID_HANDLE_VALUE
)
10435 return STG_E_FILENOTFOUND
;
10437 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
10439 WARN(" unable to read file\n");
10446 if (bytes_read
!= 8) {
10447 TRACE(" too short\n");
10451 if (!memcmp(magic
,STORAGE_magic
,8)) {
10452 TRACE(" -> YES\n");
10456 TRACE(" -> Invalid header.\n");
10460 /***********************************************************************
10461 * WriteClassStm (OLE32.@)
10463 * Writes a CLSID to a stream.
10466 * pStm [I] Stream to write to.
10467 * rclsid [I] CLSID to write.
10471 * Failure: HRESULT code.
10473 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
10475 TRACE("(%p,%p)\n",pStm
,rclsid
);
10477 if (!pStm
|| !rclsid
)
10478 return E_INVALIDARG
;
10480 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
10483 /***********************************************************************
10484 * ReadClassStm (OLE32.@)
10486 * Reads a CLSID from a stream.
10489 * pStm [I] Stream to read from.
10490 * rclsid [O] CLSID to read.
10494 * Failure: HRESULT code.
10496 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
10501 TRACE("(%p,%p)\n",pStm
,pclsid
);
10503 if (!pStm
|| !pclsid
)
10504 return E_INVALIDARG
;
10506 /* clear the output args */
10507 *pclsid
= CLSID_NULL
;
10509 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
10514 if (nbByte
!= sizeof(CLSID
))
10515 return STG_E_READFAULT
;