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 ((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
,
6998 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
6999 return chainEntry
.startingBlock
;
7002 return BLOCK_END_OF_CHAIN
;
7005 /******************************************************************************
7006 * BlockChainStream_GetCount
7008 * Returns the number of blocks that comprises this chain.
7009 * This is not the size of the stream as the last block may not be full!
7011 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
7013 return This
->numBlocks
;
7016 /******************************************************************************
7017 * BlockChainStream_ReadAt
7019 * Reads a specified number of bytes from this chain at the specified offset.
7020 * bytesRead may be NULL.
7021 * Failure will be returned if the specified number of bytes has not been read.
7023 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
7024 ULARGE_INTEGER offset
,
7029 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7030 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7031 ULONG bytesToReadInBuffer
;
7034 ULARGE_INTEGER stream_size
;
7036 BlockChainBlock
*cachedBlock
;
7038 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
7041 * Find the first block in the stream that contains part of the buffer.
7043 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, blockNoInSequence
);
7047 stream_size
= BlockChainStream_GetSize(This
);
7048 if (stream_size
.QuadPart
> offset
.QuadPart
)
7049 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7054 * Start reading the buffer.
7056 bufferWalker
= buffer
;
7060 ULARGE_INTEGER ulOffset
;
7064 * Calculate how many bytes we can copy from this big block.
7066 bytesToReadInBuffer
=
7067 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7069 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToReadInBuffer
);
7076 /* Not in cache, and we're going to read past the end of the block. */
7077 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7080 StorageImpl_ReadAt(This
->parentStorage
,
7083 bytesToReadInBuffer
,
7088 if (!cachedBlock
->read
)
7091 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7092 return STG_E_READFAULT
;
7094 cachedBlock
->read
= TRUE
;
7097 memcpy(bufferWalker
, cachedBlock
->data
+offsetInBlock
, bytesToReadInBuffer
);
7098 bytesReadAt
= bytesToReadInBuffer
;
7101 blockNoInSequence
++;
7102 bufferWalker
+= bytesReadAt
;
7103 size
-= bytesReadAt
;
7104 *bytesRead
+= bytesReadAt
;
7105 offsetInBlock
= 0; /* There is no offset on the next block */
7107 if (bytesToReadInBuffer
!= bytesReadAt
)
7114 /******************************************************************************
7115 * BlockChainStream_WriteAt
7117 * Writes the specified number of bytes to this chain at the specified offset.
7118 * Will fail if not all specified number of bytes have been written.
7120 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
7121 ULARGE_INTEGER offset
,
7124 ULONG
* bytesWritten
)
7126 ULONG blockNoInSequence
= offset
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7127 ULONG offsetInBlock
= offset
.QuadPart
% This
->parentStorage
->bigBlockSize
;
7130 const BYTE
* bufferWalker
;
7132 BlockChainBlock
*cachedBlock
;
7135 bufferWalker
= buffer
;
7139 ULARGE_INTEGER ulOffset
;
7140 DWORD bytesWrittenAt
;
7143 * Calculate how many bytes we can copy to this big block.
7146 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
7148 hr
= BlockChainStream_GetBlockAtOffset(This
, blockNoInSequence
, &cachedBlock
, &blockIndex
, size
== bytesToWrite
);
7150 /* BlockChainStream_SetSize should have already been called to ensure we have
7151 * enough blocks in the chain to write into */
7154 ERR("not enough blocks in chain to write data\n");
7160 /* Not in cache, and we're going to write past the end of the block. */
7161 ulOffset
.QuadPart
= StorageImpl_GetBigBlockOffset(This
->parentStorage
, blockIndex
) +
7164 StorageImpl_WriteAt(This
->parentStorage
,
7172 if (!cachedBlock
->read
&& bytesToWrite
!= This
->parentStorage
->bigBlockSize
)
7175 if (FAILED(StorageImpl_ReadBigBlock(This
->parentStorage
, cachedBlock
->sector
, cachedBlock
->data
, &read
)) && !read
)
7176 return STG_E_READFAULT
;
7179 memcpy(cachedBlock
->data
+offsetInBlock
, bufferWalker
, bytesToWrite
);
7180 bytesWrittenAt
= bytesToWrite
;
7181 cachedBlock
->read
= TRUE
;
7182 cachedBlock
->dirty
= TRUE
;
7185 blockNoInSequence
++;
7186 bufferWalker
+= bytesWrittenAt
;
7187 size
-= bytesWrittenAt
;
7188 *bytesWritten
+= bytesWrittenAt
;
7189 offsetInBlock
= 0; /* There is no offset on the next block */
7191 if (bytesWrittenAt
!= bytesToWrite
)
7195 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7198 /******************************************************************************
7199 * BlockChainStream_Shrink
7201 * Shrinks this chain in the big block depot.
7203 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
7204 ULARGE_INTEGER newSize
)
7211 * Figure out how many blocks are needed to contain the new size
7213 numBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7215 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7221 * Go to the new end of chain
7223 blockIndex
= BlockChainStream_GetSectorOfOffset(This
, numBlocks
-1);
7225 /* Mark the new end of chain */
7226 StorageImpl_SetNextBlockInChain(
7227 This
->parentStorage
,
7229 BLOCK_END_OF_CHAIN
);
7231 This
->tailIndex
= blockIndex
;
7235 if (This
->headOfStreamPlaceHolder
!= 0)
7237 *This
->headOfStreamPlaceHolder
= BLOCK_END_OF_CHAIN
;
7241 DirEntry chainEntry
;
7242 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7244 StorageImpl_ReadDirEntry(
7245 This
->parentStorage
,
7246 This
->ownerDirEntry
,
7249 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7251 StorageImpl_WriteDirEntry(
7252 This
->parentStorage
,
7253 This
->ownerDirEntry
,
7257 This
->tailIndex
= BLOCK_END_OF_CHAIN
;
7260 This
->numBlocks
= numBlocks
;
7263 * Mark the extra blocks as free
7265 while (This
->indexCacheLen
&& This
->indexCache
[This
->indexCacheLen
-1].lastOffset
>= numBlocks
)
7267 struct BlockChainRun
*last_run
= &This
->indexCache
[This
->indexCacheLen
-1];
7268 StorageImpl_FreeBigBlock(This
->parentStorage
,
7269 last_run
->firstSector
+ last_run
->lastOffset
- last_run
->firstOffset
);
7270 if (last_run
->lastOffset
== last_run
->firstOffset
)
7271 This
->indexCacheLen
--;
7273 last_run
->lastOffset
--;
7277 * Reset the last accessed block cache.
7281 if (This
->cachedBlocks
[i
].index
>= numBlocks
)
7283 This
->cachedBlocks
[i
].index
= 0xffffffff;
7284 This
->cachedBlocks
[i
].dirty
= FALSE
;
7291 /******************************************************************************
7292 * BlockChainStream_Enlarge
7294 * Grows this chain in the big block depot.
7296 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
7297 ULARGE_INTEGER newSize
)
7299 ULONG blockIndex
, currentBlock
;
7301 ULONG oldNumBlocks
= 0;
7303 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
7306 * Empty chain. Create the head.
7308 if (blockIndex
== BLOCK_END_OF_CHAIN
)
7310 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7311 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
7313 BLOCK_END_OF_CHAIN
);
7315 if (This
->headOfStreamPlaceHolder
!= 0)
7317 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
7321 DirEntry chainEntry
;
7322 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
7324 StorageImpl_ReadDirEntry(
7325 This
->parentStorage
,
7326 This
->ownerDirEntry
,
7329 chainEntry
.startingBlock
= blockIndex
;
7331 StorageImpl_WriteDirEntry(
7332 This
->parentStorage
,
7333 This
->ownerDirEntry
,
7337 This
->tailIndex
= blockIndex
;
7338 This
->numBlocks
= 1;
7342 * Figure out how many blocks are needed to contain this stream
7344 newNumBlocks
= newSize
.QuadPart
/ This
->parentStorage
->bigBlockSize
;
7346 if ((newSize
.QuadPart
% This
->parentStorage
->bigBlockSize
) != 0)
7350 * Go to the current end of chain
7352 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
7354 currentBlock
= blockIndex
;
7356 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
7359 currentBlock
= blockIndex
;
7361 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
7366 This
->tailIndex
= currentBlock
;
7369 currentBlock
= This
->tailIndex
;
7370 oldNumBlocks
= This
->numBlocks
;
7373 * Add new blocks to the chain
7375 if (oldNumBlocks
< newNumBlocks
)
7377 while (oldNumBlocks
< newNumBlocks
)
7379 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
7381 StorageImpl_SetNextBlockInChain(
7382 This
->parentStorage
,
7386 StorageImpl_SetNextBlockInChain(
7387 This
->parentStorage
,
7389 BLOCK_END_OF_CHAIN
);
7391 currentBlock
= blockIndex
;
7395 This
->tailIndex
= blockIndex
;
7396 This
->numBlocks
= newNumBlocks
;
7399 if (FAILED(BlockChainStream_UpdateIndexCache(This
)))
7405 /******************************************************************************
7406 * BlockChainStream_SetSize
7408 * Sets the size of this stream. The big block depot will be updated.
7409 * The file will grow if we grow the chain.
7411 * TODO: Free the actual blocks in the file when we shrink the chain.
7412 * Currently, the blocks are still in the file. So the file size
7413 * doesn't shrink even if we shrink streams.
7415 BOOL
BlockChainStream_SetSize(
7416 BlockChainStream
* This
,
7417 ULARGE_INTEGER newSize
)
7419 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
7421 if (newSize
.QuadPart
== size
.QuadPart
)
7424 if (newSize
.QuadPart
< size
.QuadPart
)
7426 BlockChainStream_Shrink(This
, newSize
);
7430 BlockChainStream_Enlarge(This
, newSize
);
7436 /******************************************************************************
7437 * BlockChainStream_GetSize
7439 * Returns the size of this chain.
7440 * Will return the block count if this chain doesn't have a directory entry.
7442 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
7444 DirEntry chainEntry
;
7446 if(This
->headOfStreamPlaceHolder
== NULL
)
7449 * This chain has a directory entry so use the size value from there.
7451 StorageImpl_ReadDirEntry(
7452 This
->parentStorage
,
7453 This
->ownerDirEntry
,
7456 return chainEntry
.size
;
7461 * this chain is a chain that does not have a directory entry, figure out the
7462 * size by making the product number of used blocks times the
7465 ULARGE_INTEGER result
;
7467 (ULONGLONG
)BlockChainStream_GetCount(This
) *
7468 This
->parentStorage
->bigBlockSize
;
7474 /******************************************************************************
7475 ** SmallBlockChainStream implementation
7478 SmallBlockChainStream
* SmallBlockChainStream_Construct(
7479 StorageImpl
* parentStorage
,
7480 ULONG
* headOfStreamPlaceHolder
,
7483 SmallBlockChainStream
* newStream
;
7485 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
7487 newStream
->parentStorage
= parentStorage
;
7488 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
7489 newStream
->ownerDirEntry
= dirEntry
;
7494 void SmallBlockChainStream_Destroy(
7495 SmallBlockChainStream
* This
)
7497 HeapFree(GetProcessHeap(), 0, This
);
7500 /******************************************************************************
7501 * SmallBlockChainStream_GetHeadOfChain
7503 * Returns the head of this chain of small blocks.
7505 static ULONG
SmallBlockChainStream_GetHeadOfChain(
7506 SmallBlockChainStream
* This
)
7508 DirEntry chainEntry
;
7511 if (This
->headOfStreamPlaceHolder
!= NULL
)
7512 return *(This
->headOfStreamPlaceHolder
);
7514 if (This
->ownerDirEntry
)
7516 hr
= StorageImpl_ReadDirEntry(
7517 This
->parentStorage
,
7518 This
->ownerDirEntry
,
7521 if (SUCCEEDED(hr
) && chainEntry
.startingBlock
< BLOCK_FIRST_SPECIAL
)
7522 return chainEntry
.startingBlock
;
7525 return BLOCK_END_OF_CHAIN
;
7528 /******************************************************************************
7529 * SmallBlockChainStream_GetNextBlockInChain
7531 * Returns the index of the next small block in this chain.
7534 * - BLOCK_END_OF_CHAIN: end of this chain
7535 * - BLOCK_UNUSED: small block 'blockIndex' is free
7537 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
7538 SmallBlockChainStream
* This
,
7540 ULONG
* nextBlockInChain
)
7542 ULARGE_INTEGER offsetOfBlockInDepot
;
7547 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
7549 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7552 * Read those bytes in the buffer from the small block file.
7554 res
= BlockChainStream_ReadAt(
7555 This
->parentStorage
->smallBlockDepotChain
,
7556 offsetOfBlockInDepot
,
7561 if (SUCCEEDED(res
) && bytesRead
!= sizeof(DWORD
))
7562 res
= STG_E_READFAULT
;
7566 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
7573 /******************************************************************************
7574 * SmallBlockChainStream_SetNextBlockInChain
7576 * Writes the index of the next block of the specified block in the small
7578 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7579 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7581 static void SmallBlockChainStream_SetNextBlockInChain(
7582 SmallBlockChainStream
* This
,
7586 ULARGE_INTEGER offsetOfBlockInDepot
;
7590 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7592 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
7595 * Read those bytes in the buffer from the small block file.
7597 BlockChainStream_WriteAt(
7598 This
->parentStorage
->smallBlockDepotChain
,
7599 offsetOfBlockInDepot
,
7605 /******************************************************************************
7606 * SmallBlockChainStream_FreeBlock
7608 * Flag small block 'blockIndex' as free in the small block depot.
7610 static void SmallBlockChainStream_FreeBlock(
7611 SmallBlockChainStream
* This
,
7614 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
7617 /******************************************************************************
7618 * SmallBlockChainStream_GetNextFreeBlock
7620 * Returns the index of a free small block. The small block depot will be
7621 * enlarged if necessary. The small block chain will also be enlarged if
7624 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
7625 SmallBlockChainStream
* This
)
7627 ULARGE_INTEGER offsetOfBlockInDepot
;
7630 ULONG blockIndex
= This
->parentStorage
->firstFreeSmallBlock
;
7631 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
7633 ULONG smallBlocksPerBigBlock
;
7635 ULONG blocksRequired
;
7636 ULARGE_INTEGER old_size
, size_required
;
7638 offsetOfBlockInDepot
.u
.HighPart
= 0;
7641 * Scan the small block depot for a free block
7643 while (nextBlockIndex
!= BLOCK_UNUSED
)
7645 offsetOfBlockInDepot
.QuadPart
= (ULONGLONG
)blockIndex
* sizeof(ULONG
);
7647 res
= BlockChainStream_ReadAt(
7648 This
->parentStorage
->smallBlockDepotChain
,
7649 offsetOfBlockInDepot
,
7655 * If we run out of space for the small block depot, enlarge it
7657 if (SUCCEEDED(res
) && bytesRead
== sizeof(DWORD
))
7659 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
7661 if (nextBlockIndex
!= BLOCK_UNUSED
)
7667 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
7669 BYTE smallBlockDepot
[MAX_BIG_BLOCK_SIZE
];
7670 ULARGE_INTEGER newSize
, offset
;
7673 newSize
.QuadPart
= (ULONGLONG
)(count
+ 1) * This
->parentStorage
->bigBlockSize
;
7674 BlockChainStream_Enlarge(This
->parentStorage
->smallBlockDepotChain
, newSize
);
7677 * Initialize all the small blocks to free
7679 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
7680 offset
.QuadPart
= (ULONGLONG
)count
* This
->parentStorage
->bigBlockSize
;
7681 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockDepotChain
,
7682 offset
, This
->parentStorage
->bigBlockSize
, smallBlockDepot
, &bytesWritten
);
7684 StorageImpl_SaveFileHeader(This
->parentStorage
);
7688 This
->parentStorage
->firstFreeSmallBlock
= blockIndex
+1;
7690 smallBlocksPerBigBlock
=
7691 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
7694 * Verify if we have to allocate big blocks to contain small blocks
7696 blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
7698 size_required
.QuadPart
= (ULONGLONG
)blocksRequired
* This
->parentStorage
->bigBlockSize
;
7700 old_size
= BlockChainStream_GetSize(This
->parentStorage
->smallBlockRootChain
);
7702 if (size_required
.QuadPart
> old_size
.QuadPart
)
7704 BlockChainStream_SetSize(
7705 This
->parentStorage
->smallBlockRootChain
,
7708 StorageImpl_ReadDirEntry(
7709 This
->parentStorage
,
7710 This
->parentStorage
->base
.storageDirEntry
,
7713 rootEntry
.size
= size_required
;
7715 StorageImpl_WriteDirEntry(
7716 This
->parentStorage
,
7717 This
->parentStorage
->base
.storageDirEntry
,
7724 /******************************************************************************
7725 * SmallBlockChainStream_ReadAt
7727 * Reads a specified number of bytes from this chain at the specified offset.
7728 * bytesRead may be NULL.
7729 * Failure will be returned if the specified number of bytes has not been read.
7731 HRESULT
SmallBlockChainStream_ReadAt(
7732 SmallBlockChainStream
* This
,
7733 ULARGE_INTEGER offset
,
7739 ULARGE_INTEGER offsetInBigBlockFile
;
7740 ULONG blockNoInSequence
=
7741 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7743 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
7744 ULONG bytesToReadInBuffer
;
7746 ULONG bytesReadFromBigBlockFile
;
7748 ULARGE_INTEGER stream_size
;
7751 * This should never happen on a small block file.
7753 assert(offset
.u
.HighPart
==0);
7757 stream_size
= SmallBlockChainStream_GetSize(This
);
7758 if (stream_size
.QuadPart
> offset
.QuadPart
)
7759 size
= min(stream_size
.QuadPart
- offset
.QuadPart
, size
);
7764 * Find the first block in the stream that contains part of the buffer.
7766 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7768 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
7770 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7773 blockNoInSequence
--;
7777 * Start reading the buffer.
7779 bufferWalker
= buffer
;
7781 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
7784 * Calculate how many bytes we can copy from this small block.
7786 bytesToReadInBuffer
=
7787 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
7790 * Calculate the offset of the small block in the small block file.
7792 offsetInBigBlockFile
.QuadPart
=
7793 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
7795 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
7798 * Read those bytes in the buffer from the small block file.
7799 * The small block has already been identified so it shouldn't fail
7800 * unless the file is corrupt.
7802 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
7803 offsetInBigBlockFile
,
7804 bytesToReadInBuffer
,
7806 &bytesReadFromBigBlockFile
);
7811 if (!bytesReadFromBigBlockFile
)
7812 return STG_E_DOCFILECORRUPT
;
7815 * Step to the next big block.
7817 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7819 return STG_E_DOCFILECORRUPT
;
7821 bufferWalker
+= bytesReadFromBigBlockFile
;
7822 size
-= bytesReadFromBigBlockFile
;
7823 *bytesRead
+= bytesReadFromBigBlockFile
;
7824 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7830 /******************************************************************************
7831 * SmallBlockChainStream_WriteAt
7833 * Writes the specified number of bytes to this chain at the specified offset.
7834 * Will fail if not all specified number of bytes have been written.
7836 HRESULT
SmallBlockChainStream_WriteAt(
7837 SmallBlockChainStream
* This
,
7838 ULARGE_INTEGER offset
,
7841 ULONG
* bytesWritten
)
7843 ULARGE_INTEGER offsetInBigBlockFile
;
7844 ULONG blockNoInSequence
=
7845 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7847 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
7848 ULONG bytesToWriteInBuffer
;
7850 ULONG bytesWrittenToBigBlockFile
;
7851 const BYTE
* bufferWalker
;
7855 * This should never happen on a small block file.
7857 assert(offset
.u
.HighPart
==0);
7860 * Find the first block in the stream that contains part of the buffer.
7862 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7864 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
7866 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
7867 return STG_E_DOCFILECORRUPT
;
7868 blockNoInSequence
--;
7872 * Start writing the buffer.
7875 bufferWalker
= buffer
;
7876 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
7879 * Calculate how many bytes we can copy to this small block.
7881 bytesToWriteInBuffer
=
7882 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
7885 * Calculate the offset of the small block in the small block file.
7887 offsetInBigBlockFile
.QuadPart
=
7888 (ULONGLONG
)blockIndex
* This
->parentStorage
->smallBlockSize
;
7890 offsetInBigBlockFile
.QuadPart
+= offsetInBlock
;
7893 * Write those bytes in the buffer to the small block file.
7895 res
= BlockChainStream_WriteAt(
7896 This
->parentStorage
->smallBlockRootChain
,
7897 offsetInBigBlockFile
,
7898 bytesToWriteInBuffer
,
7900 &bytesWrittenToBigBlockFile
);
7905 * Step to the next big block.
7907 res
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
7910 bufferWalker
+= bytesWrittenToBigBlockFile
;
7911 size
-= bytesWrittenToBigBlockFile
;
7912 *bytesWritten
+= bytesWrittenToBigBlockFile
;
7913 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
7916 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
7919 /******************************************************************************
7920 * SmallBlockChainStream_Shrink
7922 * Shrinks this chain in the small block depot.
7924 static BOOL
SmallBlockChainStream_Shrink(
7925 SmallBlockChainStream
* This
,
7926 ULARGE_INTEGER newSize
)
7928 ULONG blockIndex
, extraBlock
;
7932 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
7934 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
7937 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
7940 * Go to the new end of chain
7942 while (count
< numBlocks
)
7944 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7951 * If the count is 0, we have a special case, the head of the chain was
7956 DirEntry chainEntry
;
7958 StorageImpl_ReadDirEntry(This
->parentStorage
,
7959 This
->ownerDirEntry
,
7962 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
7964 StorageImpl_WriteDirEntry(This
->parentStorage
,
7965 This
->ownerDirEntry
,
7969 * We start freeing the chain at the head block.
7971 extraBlock
= blockIndex
;
7975 /* Get the next block before marking the new end */
7976 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
7980 /* Mark the new end of chain */
7981 SmallBlockChainStream_SetNextBlockInChain(
7984 BLOCK_END_OF_CHAIN
);
7988 * Mark the extra blocks as free
7990 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
7992 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
7995 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
7996 This
->parentStorage
->firstFreeSmallBlock
= min(This
->parentStorage
->firstFreeSmallBlock
, extraBlock
);
7997 extraBlock
= blockIndex
;
8003 /******************************************************************************
8004 * SmallBlockChainStream_Enlarge
8006 * Grows this chain in the small block depot.
8008 static BOOL
SmallBlockChainStream_Enlarge(
8009 SmallBlockChainStream
* This
,
8010 ULARGE_INTEGER newSize
)
8012 ULONG blockIndex
, currentBlock
;
8014 ULONG oldNumBlocks
= 0;
8016 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8019 * Empty chain. Create the head.
8021 if (blockIndex
== BLOCK_END_OF_CHAIN
)
8023 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8024 SmallBlockChainStream_SetNextBlockInChain(
8027 BLOCK_END_OF_CHAIN
);
8029 if (This
->headOfStreamPlaceHolder
!= NULL
)
8031 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
8035 DirEntry chainEntry
;
8037 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8040 chainEntry
.startingBlock
= blockIndex
;
8042 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
8047 currentBlock
= blockIndex
;
8050 * Figure out how many blocks are needed to contain this stream
8052 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
8054 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
8058 * Go to the current end of chain
8060 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
8063 currentBlock
= blockIndex
;
8064 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
8069 * Add new blocks to the chain
8071 while (oldNumBlocks
< newNumBlocks
)
8073 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
8074 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
8076 SmallBlockChainStream_SetNextBlockInChain(
8079 BLOCK_END_OF_CHAIN
);
8081 currentBlock
= blockIndex
;
8088 /******************************************************************************
8089 * SmallBlockChainStream_SetSize
8091 * Sets the size of this stream.
8092 * The file will grow if we grow the chain.
8094 * TODO: Free the actual blocks in the file when we shrink the chain.
8095 * Currently, the blocks are still in the file. So the file size
8096 * doesn't shrink even if we shrink streams.
8098 BOOL
SmallBlockChainStream_SetSize(
8099 SmallBlockChainStream
* This
,
8100 ULARGE_INTEGER newSize
)
8102 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
8104 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
8107 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
8109 SmallBlockChainStream_Shrink(This
, newSize
);
8113 SmallBlockChainStream_Enlarge(This
, newSize
);
8119 /******************************************************************************
8120 * SmallBlockChainStream_GetCount
8122 * Returns the number of small blocks that comprises this chain.
8123 * This is not the size of the stream as the last block may not be full!
8126 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
8131 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
8133 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
8137 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
8138 blockIndex
, &blockIndex
)))
8145 /******************************************************************************
8146 * SmallBlockChainStream_GetSize
8148 * Returns the size of this chain.
8150 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
8152 DirEntry chainEntry
;
8154 if(This
->headOfStreamPlaceHolder
!= NULL
)
8156 ULARGE_INTEGER result
;
8157 result
.u
.HighPart
= 0;
8159 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
8160 This
->parentStorage
->smallBlockSize
;
8165 StorageImpl_ReadDirEntry(
8166 This
->parentStorage
,
8167 This
->ownerDirEntry
,
8170 return chainEntry
.size
;
8173 static HRESULT
create_storagefile(
8177 STGOPTIONS
* pStgOptions
,
8181 StorageBaseImpl
* newStorage
= 0;
8182 HANDLE hFile
= INVALID_HANDLE_VALUE
;
8183 HRESULT hr
= STG_E_INVALIDFLAG
;
8187 DWORD fileAttributes
;
8188 WCHAR tempFileName
[MAX_PATH
];
8191 return STG_E_INVALIDPOINTER
;
8193 if (pStgOptions
->ulSectorSize
!= MIN_BIG_BLOCK_SIZE
&& pStgOptions
->ulSectorSize
!= MAX_BIG_BLOCK_SIZE
)
8194 return STG_E_INVALIDPARAMETER
;
8196 /* if no share mode given then DENY_NONE is the default */
8197 if (STGM_SHARE_MODE(grfMode
) == 0)
8198 grfMode
|= STGM_SHARE_DENY_NONE
;
8200 if ( FAILED( validateSTGM(grfMode
) ))
8203 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
8204 switch(STGM_ACCESS_MODE(grfMode
))
8207 case STGM_READWRITE
:
8213 /* in direct mode, can only use SHARE_EXCLUSIVE */
8214 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
8217 /* but in transacted mode, any share mode is valid */
8220 * Generate a unique name.
8224 WCHAR tempPath
[MAX_PATH
];
8225 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
8227 memset(tempPath
, 0, sizeof(tempPath
));
8228 memset(tempFileName
, 0, sizeof(tempFileName
));
8230 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
8233 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
8234 pwcsName
= tempFileName
;
8237 hr
= STG_E_INSUFFICIENTMEMORY
;
8241 creationMode
= TRUNCATE_EXISTING
;
8245 creationMode
= GetCreationModeFromSTGM(grfMode
);
8249 * Interpret the STGM value grfMode
8251 shareMode
= GetShareModeFromSTGM(grfMode
);
8252 accessMode
= GetAccessModeFromSTGM(grfMode
);
8254 if (grfMode
& STGM_DELETEONRELEASE
)
8255 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
8257 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
8261 hFile
= CreateFileW(pwcsName
,
8269 if (hFile
== INVALID_HANDLE_VALUE
)
8271 if(GetLastError() == ERROR_FILE_EXISTS
)
8272 hr
= STG_E_FILEALREADYEXISTS
;
8279 * Allocate and initialize the new IStorage32object.
8281 hr
= Storage_Construct(
8288 pStgOptions
->ulSectorSize
,
8296 hr
= IStorage_QueryInterface(&newStorage
->IStorage_iface
, riid
, ppstgOpen
);
8297 IStorage_Release(&newStorage
->IStorage_iface
);
8300 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
8305 /******************************************************************************
8306 * StgCreateDocfile [OLE32.@]
8307 * Creates a new compound file storage object
8310 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
8311 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
8312 * reserved [ ?] unused?, usually 0
8313 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
8316 * S_OK if the file was successfully created
8317 * some STG_E_ value if error
8319 * if pwcsName is NULL, create file with new unique name
8320 * the function can returns
8321 * STG_S_CONVERTED if the specified file was successfully converted to storage format
8324 HRESULT WINAPI
StgCreateDocfile(
8328 IStorage
**ppstgOpen
)
8330 STGOPTIONS stgoptions
= {1, 0, 512};
8332 TRACE("(%s, %x, %d, %p)\n",
8333 debugstr_w(pwcsName
), grfMode
,
8334 reserved
, ppstgOpen
);
8337 return STG_E_INVALIDPOINTER
;
8339 return STG_E_INVALIDPARAMETER
;
8341 return create_storagefile(pwcsName
, grfMode
, 0, &stgoptions
, &IID_IStorage
, (void**)ppstgOpen
);
8344 /******************************************************************************
8345 * StgCreateStorageEx [OLE32.@]
8347 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8349 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8350 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8352 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
8354 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
8355 return STG_E_INVALIDPARAMETER
;
8358 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8360 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
8361 return STG_E_INVALIDPARAMETER
;
8364 if (stgfmt
== STGFMT_FILE
)
8366 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8367 return STG_E_INVALIDPARAMETER
;
8370 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
8372 STGOPTIONS defaultOptions
= {1, 0, 512};
8374 if (!pStgOptions
) pStgOptions
= &defaultOptions
;
8375 return create_storagefile(pwcsName
, grfMode
, grfAttrs
, pStgOptions
, riid
, ppObjectOpen
);
8379 ERR("Invalid stgfmt argument\n");
8380 return STG_E_INVALIDPARAMETER
;
8383 /******************************************************************************
8384 * StgCreatePropSetStg [OLE32.@]
8386 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
8387 IPropertySetStorage
**propset
)
8389 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, propset
);
8391 return STG_E_INVALIDPARAMETER
;
8393 return IStorage_QueryInterface(pstg
, &IID_IPropertySetStorage
, (void**)propset
);
8396 /******************************************************************************
8397 * StgOpenStorageEx [OLE32.@]
8399 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
8401 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
8402 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
8404 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
8406 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
8407 return STG_E_INVALIDPARAMETER
;
8413 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
8414 return STG_E_INVALIDPARAMETER
;
8416 case STGFMT_STORAGE
:
8419 case STGFMT_DOCFILE
:
8420 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
8422 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
8423 return STG_E_INVALIDPARAMETER
;
8425 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
8429 WARN("STGFMT_ANY assuming storage\n");
8433 return STG_E_INVALIDPARAMETER
;
8436 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
8440 /******************************************************************************
8441 * StgOpenStorage [OLE32.@]
8443 HRESULT WINAPI
StgOpenStorage(
8444 const OLECHAR
*pwcsName
,
8445 IStorage
*pstgPriority
,
8449 IStorage
**ppstgOpen
)
8451 StorageBaseImpl
* newStorage
= 0;
8456 LPWSTR temp_name
= NULL
;
8458 TRACE("(%s, %p, %x, %p, %d, %p)\n",
8459 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
8460 snbExclude
, reserved
, ppstgOpen
);
8464 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
8465 hr
= StorageBaseImpl_GetFilename((StorageBaseImpl
*)pstgPriority
, &temp_name
);
8466 if (FAILED(hr
)) goto end
;
8467 pwcsName
= temp_name
;
8468 TRACE("using filename %s\n", debugstr_w(temp_name
));
8473 hr
= STG_E_INVALIDNAME
;
8479 hr
= STG_E_INVALIDPOINTER
;
8485 hr
= STG_E_INVALIDPARAMETER
;
8489 if (grfMode
& STGM_PRIORITY
)
8491 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
8492 return STG_E_INVALIDFLAG
;
8493 if (grfMode
& STGM_DELETEONRELEASE
)
8494 return STG_E_INVALIDFUNCTION
;
8495 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
8496 return STG_E_INVALIDFLAG
;
8497 grfMode
&= ~0xf0; /* remove the existing sharing mode */
8498 grfMode
|= STGM_SHARE_DENY_NONE
;
8502 * Validate the sharing mode
8504 if (grfMode
& STGM_DIRECT_SWMR
)
8506 if ((STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_WRITE
) &&
8507 (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_DENY_NONE
))
8509 hr
= STG_E_INVALIDFLAG
;
8513 else if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
8514 switch(STGM_SHARE_MODE(grfMode
))
8516 case STGM_SHARE_EXCLUSIVE
:
8517 case STGM_SHARE_DENY_WRITE
:
8520 hr
= STG_E_INVALIDFLAG
;
8524 if ( FAILED( validateSTGM(grfMode
) ) ||
8525 (grfMode
&STGM_CREATE
))
8527 hr
= STG_E_INVALIDFLAG
;
8531 /* shared reading requires transacted or single writer mode */
8532 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
8533 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
8534 !(grfMode
& STGM_TRANSACTED
) && !(grfMode
& STGM_DIRECT_SWMR
))
8536 hr
= STG_E_INVALIDFLAG
;
8541 * Interpret the STGM value grfMode
8543 shareMode
= GetShareModeFromSTGM(grfMode
);
8544 accessMode
= GetAccessModeFromSTGM(grfMode
);
8548 hFile
= CreateFileW( pwcsName
,
8553 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
8556 if (hFile
==INVALID_HANDLE_VALUE
)
8558 DWORD last_error
= GetLastError();
8564 case ERROR_FILE_NOT_FOUND
:
8565 hr
= STG_E_FILENOTFOUND
;
8568 case ERROR_PATH_NOT_FOUND
:
8569 hr
= STG_E_PATHNOTFOUND
;
8572 case ERROR_ACCESS_DENIED
:
8573 case ERROR_WRITE_PROTECT
:
8574 hr
= STG_E_ACCESSDENIED
;
8577 case ERROR_SHARING_VIOLATION
:
8578 hr
= STG_E_SHAREVIOLATION
;
8589 * Refuse to open the file if it's too small to be a structured storage file
8590 * FIXME: verify the file when reading instead of here
8592 if (GetFileSize(hFile
, NULL
) < 0x100)
8595 hr
= STG_E_FILEALREADYEXISTS
;
8600 * Allocate and initialize the new IStorage32object.
8602 hr
= Storage_Construct(
8615 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8617 if(hr
== STG_E_INVALIDHEADER
)
8618 hr
= STG_E_FILEALREADYEXISTS
;
8622 *ppstgOpen
= &newStorage
->IStorage_iface
;
8625 CoTaskMemFree(temp_name
);
8626 if (pstgPriority
) IStorage_Release(pstgPriority
);
8627 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
8631 /******************************************************************************
8632 * StgCreateDocfileOnILockBytes [OLE32.@]
8634 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
8638 IStorage
** ppstgOpen
)
8640 StorageBaseImpl
* newStorage
= 0;
8643 if ((ppstgOpen
== 0) || (plkbyt
== 0))
8644 return STG_E_INVALIDPOINTER
;
8647 * Allocate and initialize the new IStorage object.
8649 hr
= Storage_Construct(
8664 *ppstgOpen
= &newStorage
->IStorage_iface
;
8669 /******************************************************************************
8670 * StgOpenStorageOnILockBytes [OLE32.@]
8672 HRESULT WINAPI
StgOpenStorageOnILockBytes(
8674 IStorage
*pstgPriority
,
8678 IStorage
**ppstgOpen
)
8680 StorageBaseImpl
* newStorage
= 0;
8683 if ((plkbyt
== 0) || (ppstgOpen
== 0))
8684 return STG_E_INVALIDPOINTER
;
8686 if ( FAILED( validateSTGM(grfMode
) ))
8687 return STG_E_INVALIDFLAG
;
8692 * Allocate and initialize the new IStorage object.
8694 hr
= Storage_Construct(
8709 *ppstgOpen
= &newStorage
->IStorage_iface
;
8714 /******************************************************************************
8715 * StgSetTimes [ole32.@]
8716 * StgSetTimes [OLE32.@]
8720 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
8721 FILETIME
const *patime
, FILETIME
const *pmtime
)
8723 IStorage
*stg
= NULL
;
8726 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
8728 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
8732 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
8733 IStorage_Release(stg
);
8739 /******************************************************************************
8740 * StgIsStorageILockBytes [OLE32.@]
8742 * Determines if the ILockBytes contains a storage object.
8744 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
8746 BYTE sig
[sizeof(STORAGE_magic
)];
8747 ULARGE_INTEGER offset
;
8750 offset
.u
.HighPart
= 0;
8751 offset
.u
.LowPart
= 0;
8753 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), &read
);
8755 if (read
== sizeof(sig
) && memcmp(sig
, STORAGE_magic
, sizeof(sig
)) == 0)
8761 /******************************************************************************
8762 * WriteClassStg [OLE32.@]
8764 * This method will store the specified CLSID in the specified storage object
8766 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
8769 return E_INVALIDARG
;
8772 return STG_E_INVALIDPOINTER
;
8774 return IStorage_SetClass(pStg
, rclsid
);
8777 /***********************************************************************
8778 * ReadClassStg (OLE32.@)
8780 * This method reads the CLSID previously written to a storage object with
8781 * the WriteClassStg.
8784 * pstg [I] IStorage pointer
8785 * pclsid [O] Pointer to where the CLSID is written
8789 * Failure: HRESULT code.
8791 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
8796 TRACE("(%p, %p)\n", pstg
, pclsid
);
8798 if(!pstg
|| !pclsid
)
8799 return E_INVALIDARG
;
8802 * read a STATSTG structure (contains the clsid) from the storage
8804 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
8807 *pclsid
=pstatstg
.clsid
;
8812 /***********************************************************************
8813 * OleLoadFromStream (OLE32.@)
8815 * This function loads an object from stream
8817 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
8821 LPPERSISTSTREAM xstm
;
8823 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
8825 res
=ReadClassStm(pStm
,&clsid
);
8828 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
8831 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
8833 IUnknown_Release((IUnknown
*)*ppvObj
);
8836 res
=IPersistStream_Load(xstm
,pStm
);
8837 IPersistStream_Release(xstm
);
8838 /* FIXME: all refcounts ok at this point? I think they should be:
8841 * xstm : 0 (released)
8846 /***********************************************************************
8847 * OleSaveToStream (OLE32.@)
8849 * This function saves an object with the IPersistStream interface on it
8850 * to the specified stream.
8852 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
8858 TRACE("(%p,%p)\n",pPStm
,pStm
);
8860 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
8862 if (SUCCEEDED(res
)){
8864 res
=WriteClassStm(pStm
,&clsid
);
8868 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
8871 TRACE("Finished Save\n");
8875 /****************************************************************************
8876 * This method validate a STGM parameter that can contain the values below
8878 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8879 * The stgm values contained in 0xffff0000 are bitmasks.
8881 * STGM_DIRECT 0x00000000
8882 * STGM_TRANSACTED 0x00010000
8883 * STGM_SIMPLE 0x08000000
8885 * STGM_READ 0x00000000
8886 * STGM_WRITE 0x00000001
8887 * STGM_READWRITE 0x00000002
8889 * STGM_SHARE_DENY_NONE 0x00000040
8890 * STGM_SHARE_DENY_READ 0x00000030
8891 * STGM_SHARE_DENY_WRITE 0x00000020
8892 * STGM_SHARE_EXCLUSIVE 0x00000010
8894 * STGM_PRIORITY 0x00040000
8895 * STGM_DELETEONRELEASE 0x04000000
8897 * STGM_CREATE 0x00001000
8898 * STGM_CONVERT 0x00020000
8899 * STGM_FAILIFTHERE 0x00000000
8901 * STGM_NOSCRATCH 0x00100000
8902 * STGM_NOSNAPSHOT 0x00200000
8904 static HRESULT
validateSTGM(DWORD stgm
)
8906 DWORD access
= STGM_ACCESS_MODE(stgm
);
8907 DWORD share
= STGM_SHARE_MODE(stgm
);
8908 DWORD create
= STGM_CREATE_MODE(stgm
);
8910 if (stgm
&~STGM_KNOWN_FLAGS
)
8912 ERR("unknown flags %08x\n", stgm
);
8920 case STGM_READWRITE
:
8928 case STGM_SHARE_DENY_NONE
:
8929 case STGM_SHARE_DENY_READ
:
8930 case STGM_SHARE_DENY_WRITE
:
8931 case STGM_SHARE_EXCLUSIVE
:
8934 if (!(stgm
& STGM_TRANSACTED
))
8944 case STGM_FAILIFTHERE
:
8951 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8953 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
8957 * STGM_CREATE | STGM_CONVERT
8958 * if both are false, STGM_FAILIFTHERE is set to TRUE
8960 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
8964 * STGM_NOSCRATCH requires STGM_TRANSACTED
8966 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
8970 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8971 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8973 if ( (stgm
& STGM_NOSNAPSHOT
) &&
8974 (!(stgm
& STGM_TRANSACTED
) ||
8975 share
== STGM_SHARE_EXCLUSIVE
||
8976 share
== STGM_SHARE_DENY_WRITE
) )
8982 /****************************************************************************
8983 * GetShareModeFromSTGM
8985 * This method will return a share mode flag from a STGM value.
8986 * The STGM value is assumed valid.
8988 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
8990 switch (STGM_SHARE_MODE(stgm
))
8993 assert(stgm
& STGM_TRANSACTED
);
8995 case STGM_SHARE_DENY_NONE
:
8996 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
8997 case STGM_SHARE_DENY_READ
:
8998 return FILE_SHARE_WRITE
;
8999 case STGM_SHARE_DENY_WRITE
:
9000 case STGM_SHARE_EXCLUSIVE
:
9001 return FILE_SHARE_READ
;
9003 ERR("Invalid share mode!\n");
9008 /****************************************************************************
9009 * GetAccessModeFromSTGM
9011 * This method will return an access mode flag from a STGM value.
9012 * The STGM value is assumed valid.
9014 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
9016 switch (STGM_ACCESS_MODE(stgm
))
9019 return GENERIC_READ
;
9021 case STGM_READWRITE
:
9022 return GENERIC_READ
| GENERIC_WRITE
;
9024 ERR("Invalid access mode!\n");
9029 /****************************************************************************
9030 * GetCreationModeFromSTGM
9032 * This method will return a creation mode flag from a STGM value.
9033 * The STGM value is assumed valid.
9035 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
9037 switch(STGM_CREATE_MODE(stgm
))
9040 return CREATE_ALWAYS
;
9042 FIXME("STGM_CONVERT not implemented!\n");
9044 case STGM_FAILIFTHERE
:
9047 ERR("Invalid create mode!\n");
9053 /*************************************************************************
9054 * OLECONVERT_LoadOLE10 [Internal]
9056 * Loads the OLE10 STREAM to memory
9059 * pOleStream [I] The OLESTREAM
9060 * pData [I] Data Structure for the OLESTREAM Data
9064 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
9065 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
9068 * This function is used by OleConvertOLESTREAMToIStorage only.
9070 * Memory allocated for pData must be freed by the caller
9072 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
9075 HRESULT hRes
= S_OK
;
9079 pData
->pData
= NULL
;
9080 pData
->pstrOleObjFileName
= NULL
;
9082 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
9085 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9086 if(dwSize
!= sizeof(pData
->dwOleID
))
9088 hRes
= CONVERT10_E_OLESTREAM_GET
;
9090 else if(pData
->dwOleID
!= OLESTREAM_ID
)
9092 hRes
= CONVERT10_E_OLESTREAM_FMT
;
9103 /* Get the TypeID... more info needed for this field */
9104 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9105 if(dwSize
!= sizeof(pData
->dwTypeID
))
9107 hRes
= CONVERT10_E_OLESTREAM_GET
;
9112 if(pData
->dwTypeID
!= 0)
9114 /* Get the length of the OleTypeName */
9115 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9116 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9118 hRes
= CONVERT10_E_OLESTREAM_GET
;
9123 if(pData
->dwOleTypeNameLength
> 0)
9125 /* Get the OleTypeName */
9126 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9127 if(dwSize
!= pData
->dwOleTypeNameLength
)
9129 hRes
= CONVERT10_E_OLESTREAM_GET
;
9135 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
9136 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
9138 hRes
= CONVERT10_E_OLESTREAM_GET
;
9142 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
9143 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
9144 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
9145 if(pData
->pstrOleObjFileName
)
9147 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
9148 if(dwSize
!= pData
->dwOleObjFileNameLength
)
9150 hRes
= CONVERT10_E_OLESTREAM_GET
;
9154 hRes
= CONVERT10_E_OLESTREAM_GET
;
9159 /* Get the Width of the Metafile */
9160 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9161 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9163 hRes
= CONVERT10_E_OLESTREAM_GET
;
9167 /* Get the Height of the Metafile */
9168 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9169 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9171 hRes
= CONVERT10_E_OLESTREAM_GET
;
9177 /* Get the Length of the Data */
9178 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9179 if(dwSize
!= sizeof(pData
->dwDataLength
))
9181 hRes
= CONVERT10_E_OLESTREAM_GET
;
9185 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
9187 if(!bStrem1
) /* if it is a second OLE stream data */
9189 pData
->dwDataLength
-= 8;
9190 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
9191 if(dwSize
!= sizeof(pData
->strUnknown
))
9193 hRes
= CONVERT10_E_OLESTREAM_GET
;
9199 if(pData
->dwDataLength
> 0)
9201 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
9203 /* Get Data (ex. IStorage, Metafile, or BMP) */
9206 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
9207 if(dwSize
!= pData
->dwDataLength
)
9209 hRes
= CONVERT10_E_OLESTREAM_GET
;
9214 hRes
= CONVERT10_E_OLESTREAM_GET
;
9223 /*************************************************************************
9224 * OLECONVERT_SaveOLE10 [Internal]
9226 * Saves the OLE10 STREAM From memory
9229 * pData [I] Data Structure for the OLESTREAM Data
9230 * pOleStream [I] The OLESTREAM to save
9234 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9237 * This function is used by OleConvertIStorageToOLESTREAM only.
9240 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
9243 HRESULT hRes
= S_OK
;
9247 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
9248 if(dwSize
!= sizeof(pData
->dwOleID
))
9250 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9255 /* Set the TypeID */
9256 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
9257 if(dwSize
!= sizeof(pData
->dwTypeID
))
9259 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9263 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
9265 /* Set the Length of the OleTypeName */
9266 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
9267 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
9269 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9274 if(pData
->dwOleTypeNameLength
> 0)
9276 /* Set the OleTypeName */
9277 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
9278 if(dwSize
!= pData
->dwOleTypeNameLength
)
9280 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9287 /* Set the width of the Metafile */
9288 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
9289 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
9291 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9297 /* Set the height of the Metafile */
9298 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
9299 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
9301 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9307 /* Set the length of the Data */
9308 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
9309 if(dwSize
!= sizeof(pData
->dwDataLength
))
9311 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9317 if(pData
->dwDataLength
> 0)
9319 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
9320 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
9321 if(dwSize
!= pData
->dwDataLength
)
9323 hRes
= CONVERT10_E_OLESTREAM_PUT
;
9331 /*************************************************************************
9332 * OLECONVERT_GetOLE20FromOLE10[Internal]
9334 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
9335 * opens it, and copies the content to the dest IStorage for
9336 * OleConvertOLESTREAMToIStorage
9340 * pDestStorage [I] The IStorage to copy the data to
9341 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
9342 * nBufferLength [I] The size of the buffer
9351 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
9355 IStorage
*pTempStorage
;
9356 DWORD dwNumOfBytesWritten
;
9357 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9358 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9360 /* Create a temp File */
9361 GetTempPathW(MAX_PATH
, wstrTempDir
);
9362 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9363 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
9365 if(hFile
!= INVALID_HANDLE_VALUE
)
9367 /* Write IStorage Data to File */
9368 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
9371 /* Open and copy temp storage to the Dest Storage */
9372 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
9375 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
9376 IStorage_Release(pTempStorage
);
9378 DeleteFileW(wstrTempFile
);
9383 /*************************************************************************
9384 * OLECONVERT_WriteOLE20ToBuffer [Internal]
9386 * Saves the OLE10 STREAM From memory
9389 * pStorage [I] The Src IStorage to copy
9390 * pData [I] The Dest Memory to write to.
9393 * The size in bytes allocated for pData
9396 * Memory allocated for pData must be freed by the caller
9398 * Used by OleConvertIStorageToOLESTREAM only.
9401 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
9405 DWORD nDataLength
= 0;
9406 IStorage
*pTempStorage
;
9407 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
9408 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
9412 /* Create temp Storage */
9413 GetTempPathW(MAX_PATH
, wstrTempDir
);
9414 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
9415 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
9419 /* Copy Src Storage to the Temp Storage */
9420 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
9421 IStorage_Release(pTempStorage
);
9423 /* Open Temp Storage as a file and copy to memory */
9424 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
9425 if(hFile
!= INVALID_HANDLE_VALUE
)
9427 nDataLength
= GetFileSize(hFile
, NULL
);
9428 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
9429 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
9432 DeleteFileW(wstrTempFile
);
9437 /*************************************************************************
9438 * STORAGE_CreateOleStream [Internal]
9440 * Creates the "\001OLE" stream in the IStorage if necessary.
9443 * storage [I] Dest storage to create the stream in
9444 * flags [I] flags to be set for newly created stream
9447 * HRESULT return value
9451 * This stream is still unknown, MS Word seems to have extra data
9452 * but since the data is stored in the OLESTREAM there should be
9453 * no need to recreate the stream. If the stream is manually
9454 * deleted it will create it with this default data.
9457 HRESULT
STORAGE_CreateOleStream(IStorage
*storage
, DWORD flags
)
9459 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
9460 static const DWORD version_magic
= 0x02000001;
9464 hr
= IStorage_CreateStream(storage
, stream_1oleW
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stream
);
9467 struct empty_1ole_stream
{
9468 DWORD version_magic
;
9470 DWORD update_options
;
9472 DWORD mon_stream_size
;
9474 struct empty_1ole_stream stream_data
;
9476 stream_data
.version_magic
= version_magic
;
9477 stream_data
.flags
= flags
;
9478 stream_data
.update_options
= 0;
9479 stream_data
.reserved
= 0;
9480 stream_data
.mon_stream_size
= 0;
9482 hr
= IStream_Write(stream
, &stream_data
, sizeof(stream_data
), NULL
);
9483 IStream_Release(stream
);
9489 /* write a string to a stream, preceded by its length */
9490 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
9497 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
9498 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
9503 str
= CoTaskMemAlloc( len
);
9504 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
9505 r
= IStream_Write( stm
, str
, len
, NULL
);
9506 CoTaskMemFree( str
);
9510 /* read a string preceded by its length from a stream */
9511 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
9514 DWORD len
, count
= 0;
9518 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
9521 if( count
!= sizeof(len
) )
9522 return E_OUTOFMEMORY
;
9524 TRACE("%d bytes\n",len
);
9526 str
= CoTaskMemAlloc( len
);
9528 return E_OUTOFMEMORY
;
9530 r
= IStream_Read( stm
, str
, len
, &count
);
9535 CoTaskMemFree( str
);
9536 return E_OUTOFMEMORY
;
9539 TRACE("Read string %s\n",debugstr_an(str
,len
));
9541 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
9542 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
9545 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
9548 CoTaskMemFree( str
);
9556 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
9557 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
9561 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9563 static const BYTE unknown1
[12] =
9564 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9565 0xFF, 0xFF, 0xFF, 0xFF};
9566 static const BYTE unknown2
[16] =
9567 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9568 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9570 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
9571 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
9572 debugstr_w(szProgIDName
));
9574 /* Create a CompObj stream */
9575 r
= IStorage_CreateStream(pstg
, szwStreamName
,
9576 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
9580 /* Write CompObj Structure to stream */
9581 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
9583 if( SUCCEEDED( r
) )
9584 r
= WriteClassStm( pstm
, clsid
);
9586 if( SUCCEEDED( r
) )
9587 r
= STREAM_WriteString( pstm
, lpszUserType
);
9588 if( SUCCEEDED( r
) )
9589 r
= STREAM_WriteString( pstm
, szClipName
);
9590 if( SUCCEEDED( r
) )
9591 r
= STREAM_WriteString( pstm
, szProgIDName
);
9592 if( SUCCEEDED( r
) )
9593 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
9595 IStream_Release( pstm
);
9600 /***********************************************************************
9601 * WriteFmtUserTypeStg (OLE32.@)
9603 HRESULT WINAPI
WriteFmtUserTypeStg(
9604 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
9608 WCHAR szwClipName
[0x40];
9610 LPWSTR wstrProgID
= NULL
;
9613 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
9615 /* get the clipboard format name */
9618 n
= GetClipboardFormatNameW( cf
, szwClipName
,
9619 sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
9623 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
9625 r
= IStorage_Stat(pstg
, &stat
, STATFLAG_NONAME
);
9631 ProgIDFromCLSID(&clsid
, &wstrProgID
);
9633 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
9635 r
= STORAGE_WriteCompObj( pstg
, &clsid
, lpszUserType
,
9636 cf
? szwClipName
: NULL
, wstrProgID
);
9638 CoTaskMemFree(wstrProgID
);
9644 /******************************************************************************
9645 * ReadFmtUserTypeStg [OLE32.@]
9647 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
9651 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
9652 unsigned char unknown1
[12];
9653 unsigned char unknown2
[16];
9655 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
9658 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
9660 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
9661 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
9664 WARN("Failed to open stream r = %08x\n", r
);
9668 /* read the various parts of the structure */
9669 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
9670 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
9672 r
= ReadClassStm( stm
, &clsid
);
9676 r
= STREAM_ReadString( stm
, &szCLSIDName
);
9680 r
= STREAM_ReadString( stm
, &szOleTypeName
);
9684 r
= STREAM_ReadString( stm
, &szProgIDName
);
9688 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
9689 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
9692 /* ok, success... now we just need to store what we found */
9694 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
9696 if( lplpszUserType
)
9698 *lplpszUserType
= szCLSIDName
;
9703 CoTaskMemFree( szCLSIDName
);
9704 CoTaskMemFree( szOleTypeName
);
9705 CoTaskMemFree( szProgIDName
);
9706 IStream_Release( stm
);
9712 /*************************************************************************
9713 * OLECONVERT_CreateCompObjStream [Internal]
9715 * Creates a "\001CompObj" is the destination IStorage if necessary.
9718 * pStorage [I] The dest IStorage to create the CompObj Stream
9720 * strOleTypeName [I] The ProgID
9724 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9727 * This function is used by OleConvertOLESTREAMToIStorage only.
9729 * The stream data is stored in the OLESTREAM and there should be
9730 * no need to recreate the stream. If the stream is manually
9731 * deleted it will attempt to create it by querying the registry.
9735 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
9738 HRESULT hStorageRes
, hRes
= S_OK
;
9739 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
9740 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9741 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
9743 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9744 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
9746 /* Initialize the CompObj structure */
9747 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
9748 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
9749 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
9752 /* Create a CompObj stream if it doesn't exist */
9753 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9754 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9755 if(hStorageRes
== S_OK
)
9757 /* copy the OleTypeName to the compobj struct */
9758 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
9759 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
9761 /* copy the OleTypeName to the compobj struct */
9762 /* Note: in the test made, these were Identical */
9763 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
9764 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
9767 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
9768 bufferW
, OLESTREAM_MAX_STR_LEN
);
9769 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
9775 /* Get the CLSID Default Name from the Registry */
9776 hErr
= open_classes_key(HKEY_CLASSES_ROOT
, bufferW
, MAXIMUM_ALLOWED
, &hKey
);
9777 if(hErr
== ERROR_SUCCESS
)
9779 char strTemp
[OLESTREAM_MAX_STR_LEN
];
9780 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
9781 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
9782 if(hErr
== ERROR_SUCCESS
)
9784 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
9790 /* Write CompObj Structure to stream */
9791 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
9793 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
9795 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
9796 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
9798 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
9800 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
9801 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
9803 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
9805 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
9806 if(IStorageCompObj
.dwProgIDNameLength
> 0)
9808 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
9810 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
9811 IStream_Release(pStream
);
9817 /*************************************************************************
9818 * OLECONVERT_CreateOlePresStream[Internal]
9820 * Creates the "\002OlePres000" Stream with the Metafile data
9823 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
9824 * dwExtentX [I] Width of the Metafile
9825 * dwExtentY [I] Height of the Metafile
9826 * pData [I] Metafile data
9827 * dwDataLength [I] Size of the Metafile data
9831 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9834 * This function is used by OleConvertOLESTREAMToIStorage only.
9837 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
9841 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9842 BYTE pOlePresStreamHeader
[] =
9844 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
9845 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9846 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9847 0x00, 0x00, 0x00, 0x00
9850 BYTE pOlePresStreamHeaderEmpty
[] =
9852 0x00, 0x00, 0x00, 0x00,
9853 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9854 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9855 0x00, 0x00, 0x00, 0x00
9858 /* Create the OlePres000 Stream */
9859 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9860 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9865 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
9867 memset(&OlePres
, 0, sizeof(OlePres
));
9868 /* Do we have any metafile data to save */
9869 if(dwDataLength
> 0)
9871 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
9872 nHeaderSize
= sizeof(pOlePresStreamHeader
);
9876 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
9877 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
9879 /* Set width and height of the metafile */
9880 OlePres
.dwExtentX
= dwExtentX
;
9881 OlePres
.dwExtentY
= -dwExtentY
;
9883 /* Set Data and Length */
9884 if(dwDataLength
> sizeof(METAFILEPICT16
))
9886 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
9887 OlePres
.pData
= &(pData
[8]);
9889 /* Save OlePres000 Data to Stream */
9890 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
9891 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
9892 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
9893 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
9894 if(OlePres
.dwSize
> 0)
9896 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
9898 IStream_Release(pStream
);
9902 /*************************************************************************
9903 * OLECONVERT_CreateOle10NativeStream [Internal]
9905 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9908 * pStorage [I] Dest storage to create the stream in
9909 * pData [I] Ole10 Native Data (ex. bmp)
9910 * dwDataLength [I] Size of the Ole10 Native Data
9916 * This function is used by OleConvertOLESTREAMToIStorage only.
9918 * Might need to verify the data and return appropriate error message
9921 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
9925 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9927 /* Create the Ole10Native Stream */
9928 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
9929 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
9933 /* Write info to stream */
9934 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
9935 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
9936 IStream_Release(pStream
);
9941 /*************************************************************************
9942 * OLECONVERT_GetOLE10ProgID [Internal]
9944 * Finds the ProgID (or OleTypeID) from the IStorage
9947 * pStorage [I] The Src IStorage to get the ProgID
9948 * strProgID [I] the ProgID string to get
9949 * dwSize [I] the size of the string
9953 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9956 * This function is used by OleConvertIStorageToOLESTREAM only.
9960 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
9964 LARGE_INTEGER iSeekPos
;
9965 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
9966 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9968 /* Open the CompObj Stream */
9969 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
9970 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
9974 /*Get the OleType from the CompObj Stream */
9975 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
9976 iSeekPos
.u
.HighPart
= 0;
9978 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
9979 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
9980 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
9981 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9982 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
9983 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
9984 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
9986 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
9989 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
9991 IStream_Release(pStream
);
9996 LPOLESTR wstrProgID
;
9998 /* Get the OleType from the registry */
9999 REFCLSID clsid
= &(stat
.clsid
);
10000 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
10001 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
10004 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
10005 CoTaskMemFree(wstrProgID
);
10012 /*************************************************************************
10013 * OLECONVERT_GetOle10PresData [Internal]
10015 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
10018 * pStorage [I] Src IStroage
10019 * pOleStream [I] Dest OleStream Mem Struct
10025 * This function is used by OleConvertIStorageToOLESTREAM only.
10027 * Memory allocated for pData must be freed by the caller
10031 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10036 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10038 /* Initialize Default data for OLESTREAM */
10039 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10040 pOleStreamData
[0].dwTypeID
= 2;
10041 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10042 pOleStreamData
[1].dwTypeID
= 0;
10043 pOleStreamData
[0].dwMetaFileWidth
= 0;
10044 pOleStreamData
[0].dwMetaFileHeight
= 0;
10045 pOleStreamData
[0].pData
= NULL
;
10046 pOleStreamData
[1].pData
= NULL
;
10048 /* Open Ole10Native Stream */
10049 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10050 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10054 /* Read Size and Data */
10055 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
10056 if(pOleStreamData
->dwDataLength
> 0)
10058 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
10059 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
10061 IStream_Release(pStream
);
10067 /*************************************************************************
10068 * OLECONVERT_GetOle20PresData[Internal]
10070 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
10073 * pStorage [I] Src IStroage
10074 * pOleStreamData [I] Dest OleStream Mem Struct
10080 * This function is used by OleConvertIStorageToOLESTREAM only.
10082 * Memory allocated for pData must be freed by the caller
10084 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
10088 OLECONVERT_ISTORAGE_OLEPRES olePress
;
10089 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
10091 /* Initialize Default data for OLESTREAM */
10092 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
10093 pOleStreamData
[0].dwTypeID
= 2;
10094 pOleStreamData
[0].dwMetaFileWidth
= 0;
10095 pOleStreamData
[0].dwMetaFileHeight
= 0;
10096 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
10097 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
10098 pOleStreamData
[1].dwTypeID
= 0;
10099 pOleStreamData
[1].dwOleTypeNameLength
= 0;
10100 pOleStreamData
[1].strOleTypeName
[0] = 0;
10101 pOleStreamData
[1].dwMetaFileWidth
= 0;
10102 pOleStreamData
[1].dwMetaFileHeight
= 0;
10103 pOleStreamData
[1].pData
= NULL
;
10104 pOleStreamData
[1].dwDataLength
= 0;
10107 /* Open OlePress000 stream */
10108 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
10109 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10112 LARGE_INTEGER iSeekPos
;
10113 METAFILEPICT16 MetaFilePict
;
10114 static const char strMetafilePictName
[] = "METAFILEPICT";
10116 /* Set the TypeID for a Metafile */
10117 pOleStreamData
[1].dwTypeID
= 5;
10119 /* Set the OleTypeName to Metafile */
10120 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
10121 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
10123 iSeekPos
.u
.HighPart
= 0;
10124 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
10126 /* Get Presentation Data */
10127 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
10128 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
10129 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
10130 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
10132 /*Set width and Height */
10133 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
10134 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
10135 if(olePress
.dwSize
> 0)
10138 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
10140 /* Set MetaFilePict struct */
10141 MetaFilePict
.mm
= 8;
10142 MetaFilePict
.xExt
= olePress
.dwExtentX
;
10143 MetaFilePict
.yExt
= olePress
.dwExtentY
;
10144 MetaFilePict
.hMF
= 0;
10146 /* Get Metafile Data */
10147 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
10148 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
10149 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
10151 IStream_Release(pStream
);
10155 /*************************************************************************
10156 * OleConvertOLESTREAMToIStorage [OLE32.@]
10158 * Read info on MSDN
10161 * DVTARGETDEVICE parameter is not handled
10162 * Still unsure of some mem fields for OLE 10 Stream
10163 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10164 * and "\001OLE" streams
10167 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
10168 LPOLESTREAM pOleStream
,
10170 const DVTARGETDEVICE
* ptd
)
10174 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10176 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
10178 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10182 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
10185 if(pstg
== NULL
|| pOleStream
== NULL
)
10187 hRes
= E_INVALIDARG
;
10192 /* Load the OLESTREAM to Memory */
10193 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
10198 /* Load the OLESTREAM to Memory (part 2)*/
10199 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
10205 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
10207 /* Do we have the IStorage Data in the OLESTREAM */
10208 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
10210 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10211 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
10215 /* It must be an original OLE 1.0 source */
10216 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10221 /* It must be an original OLE 1.0 source */
10222 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
10225 /* Create CompObj Stream if necessary */
10226 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
10229 /*Create the Ole Stream if necessary */
10230 STORAGE_CreateOleStream(pstg
, 0);
10235 /* Free allocated memory */
10236 for(i
=0; i
< 2; i
++)
10238 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10239 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
10240 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
10245 /*************************************************************************
10246 * OleConvertIStorageToOLESTREAM [OLE32.@]
10248 * Read info on MSDN
10250 * Read info on MSDN
10253 * Still unsure of some mem fields for OLE 10 Stream
10254 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
10255 * and "\001OLE" streams.
10258 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
10260 LPOLESTREAM pOleStream
)
10263 HRESULT hRes
= S_OK
;
10265 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
10266 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
10268 TRACE("%p %p\n", pstg
, pOleStream
);
10270 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
10272 if(pstg
== NULL
|| pOleStream
== NULL
)
10274 hRes
= E_INVALIDARG
;
10278 /* Get the ProgID */
10279 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
10280 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
10284 /* Was it originally Ole10 */
10285 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
10288 IStream_Release(pStream
);
10289 /* Get Presentation Data for Ole10Native */
10290 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
10294 /* Get Presentation Data (OLE20) */
10295 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
10298 /* Save OLESTREAM */
10299 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
10302 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
10307 /* Free allocated memory */
10308 for(i
=0; i
< 2; i
++)
10310 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
10316 enum stream_1ole_flags
{
10317 OleStream_LinkedObject
= 0x00000001,
10318 OleStream_Convert
= 0x00000004
10321 /***********************************************************************
10322 * GetConvertStg (OLE32.@)
10324 HRESULT WINAPI
GetConvertStg(IStorage
*stg
)
10326 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10327 static const DWORD version_magic
= 0x02000001;
10332 TRACE("%p\n", stg
);
10334 if (!stg
) return E_INVALIDARG
;
10336 hr
= IStorage_OpenStream(stg
, stream_1oleW
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10337 if (FAILED(hr
)) return hr
;
10339 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10340 IStream_Release(stream
);
10341 if (FAILED(hr
)) return hr
;
10343 if (header
[0] != version_magic
)
10345 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header
[0]);
10349 return header
[1] & OleStream_Convert
? S_OK
: S_FALSE
;
10352 /***********************************************************************
10353 * SetConvertStg (OLE32.@)
10355 HRESULT WINAPI
SetConvertStg(IStorage
*storage
, BOOL convert
)
10357 static const WCHAR stream_1oleW
[] = {1,'O','l','e',0};
10358 DWORD flags
= convert
? OleStream_Convert
: 0;
10363 TRACE("(%p, %d)\n", storage
, convert
);
10365 hr
= IStorage_OpenStream(storage
, stream_1oleW
, NULL
, STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stream
);
10368 if (hr
!= STG_E_FILENOTFOUND
)
10371 return STORAGE_CreateOleStream(storage
, flags
);
10374 hr
= IStream_Read(stream
, header
, sizeof(header
), NULL
);
10377 IStream_Release(stream
);
10381 /* update flag if differs */
10382 if ((header
[1] ^ flags
) & OleStream_Convert
)
10384 LARGE_INTEGER pos
= {{0}};
10386 if (header
[1] & OleStream_Convert
)
10387 flags
= header
[1] & ~OleStream_Convert
;
10389 flags
= header
[1] | OleStream_Convert
;
10391 pos
.QuadPart
= sizeof(DWORD
);
10392 hr
= IStream_Seek(stream
, pos
, STREAM_SEEK_SET
, NULL
);
10395 IStream_Release(stream
);
10399 hr
= IStream_Write(stream
, &flags
, sizeof(flags
), NULL
);
10402 IStream_Release(stream
);
10406 /******************************************************************************
10407 * StgIsStorageFile [OLE32.@]
10408 * Verify if the file contains a storage object
10414 * S_OK if file has magic bytes as a storage object
10415 * S_FALSE if file is not storage
10418 StgIsStorageFile(LPCOLESTR fn
)
10424 TRACE("%s\n", debugstr_w(fn
));
10425 hf
= CreateFileW(fn
, GENERIC_READ
,
10426 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
10427 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
10429 if (hf
== INVALID_HANDLE_VALUE
)
10430 return STG_E_FILENOTFOUND
;
10432 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
10434 WARN(" unable to read file\n");
10441 if (bytes_read
!= 8) {
10442 TRACE(" too short\n");
10446 if (!memcmp(magic
,STORAGE_magic
,8)) {
10447 TRACE(" -> YES\n");
10451 TRACE(" -> Invalid header.\n");
10455 /***********************************************************************
10456 * WriteClassStm (OLE32.@)
10458 * Writes a CLSID to a stream.
10461 * pStm [I] Stream to write to.
10462 * rclsid [I] CLSID to write.
10466 * Failure: HRESULT code.
10468 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
10470 TRACE("(%p,%p)\n",pStm
,rclsid
);
10472 if (!pStm
|| !rclsid
)
10473 return E_INVALIDARG
;
10475 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
10478 /***********************************************************************
10479 * ReadClassStm (OLE32.@)
10481 * Reads a CLSID from a stream.
10484 * pStm [I] Stream to read from.
10485 * rclsid [O] CLSID to read.
10489 * Failure: HRESULT code.
10491 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
10496 TRACE("(%p,%p)\n",pStm
,pclsid
);
10498 if (!pStm
|| !pclsid
)
10499 return E_INVALIDARG
;
10501 /* clear the output args */
10502 *pclsid
= CLSID_NULL
;
10504 res
= IStream_Read(pStm
, pclsid
, sizeof(CLSID
), &nbByte
);
10509 if (nbByte
!= sizeof(CLSID
))
10510 return STG_E_READFAULT
;